Try our conversational search powered by Generative AI!

Normal Site map in 8 MVC

Vote:
 

Hi,

Is it possible to generate a normal Sitemap for a site - not an XML site map but a navigation type site map that shows all the published pages on a site.

I can do it as a webform but for this project we are using MVC.

Thanks

Jon

#131857
Aug 07, 2015 9:16
Vote:
 

Something like this?

public class SitemapNode
{
    public string Title { get; set; }
    public string Url { get; set; }
    public List<SitemapNode> Nodes { get; set; }
}
public class FilterPublicAccess : PageFilterBase
{
    private readonly IPrincipal _principal;

    public FilterPublicAccess()
    {
        _principal = new GenericPrincipal(new GenericIdentity("visitor"), new[] { "Everyone" });
    }

    public override bool ShouldFilter(PageData page)
    {
        return !page.ACL.QueryDistinctAccess(_principal, AccessLevel.Read);
    }

    public override bool ShouldFilter(IContent content)
    {
        var page = content as PageData;
        if (page == null)
        {
            return false;
        }

        return ShouldFilter(page);
    }
}
public class SitemapGenerator
{
    private readonly IContentLoader _contentLoader;
    private readonly UrlResolver _urlResolver;

    public SitemapGenerator(IContentLoader contentLoader, UrlResolver urlResolver)
    {
        _contentLoader = contentLoader;
        _urlResolver = urlResolver;
    }

    public SitemapNode GetNodes(PageReference startPage)
    {
        var page = _contentLoader.Get<PageData>(startPage);

        var node = new SitemapNode
        {
            Title = page.Name,
            Url = GetExternalUrl(page.PageLink)
        };

        GetChildNodes(page, node);

        return node;
    }

    private void GetChildNodes(PageData page, SitemapNode node)
    {
        var publicAccessFilter = new FilterPublicAccess();
        var publishedFilter = new FilterPublished();

        var children = _contentLoader.GetChildren<PageData>(page.ContentLink)
                                        .Where(x =>
                                            // skip container pages
                                            x.HasTemplate() &&
                                            // skip pages that have 'Display in navigation' unchecked
                                            x.VisibleInMenu &&
                                            // skip pages that are not visible to everyone
                                            !publicAccessFilter.ShouldFilter(x) &&
                                            // skip unpublished pages
                                            !publishedFilter.ShouldFilter(x))
                                        .ToList();

        if (children.Count == 0) return;

        node.Nodes = new List<SitemapNode>(children.Count);

        foreach (var child in children)
        {
            var sitemapNode = new SitemapNode
            {
                Title = child.Name,
                Url = GetExternalUrl(child.PageLink)
            };

            GetChildNodes(child, sitemapNode);

            node.Nodes.Add(sitemapNode);
        }
    }

    private string GetExternalUrl(PageReference pageReference)
    {
        var internalUrl = _urlResolver.GetUrl(pageReference);
        var url = new UrlBuilder(internalUrl);
        EPiServer.Global.UrlRewriteProvider.ConvertToExternal(url, null, Encoding.UTF8);

        return url.ToString();
    }
}

In page controller, you can generate sitemap like this:

var siteMapGenerator = ServiceLocator.Current.GetInstance<SitemapGenerator>();
var node = siteMapGenerator.GetNodes(ContentReference.StartPage);


Result from Alloy:

https://www.dropbox.com/s/oyp0jq8wt50fm7i/sitemap.png?raw=1

Hope this helps :)

#131861
Aug 07, 2015 10:30
Vote:
 

How do you set up the cshtml page?

#131866
Aug 07, 2015 11:11
Vote:
 

You can create a helper class like this:

public static class SitemapHelper
{
    public static void GenerateSitemap(this HtmlHelper html, SitemapNode sitemap)
    {
        html.ViewContext.Writer.WriteLine("<ul>");
        WriteNode(html, sitemap);
        html.ViewContext.Writer.WriteLine("</ul>");
    }

    private static void WriteNode(this HtmlHelper html, SitemapNode node)
    {
        html.ViewContext.Writer.WriteLine("<li>");
        html.ViewContext.Writer.WriteLine("<a href=\"{0}\">{1}</a>", node.Url, node.Title);
        if (node.Nodes != null && node.Nodes.Count > 0)
        {
            html.ViewContext.Writer.WriteLine("<ul>");
            foreach (var childNode in node.Nodes)
            {
                WriteNode(html, childNode);
            }

            html.ViewContext.Writer.WriteLine("</ul>");
        }
        html.ViewContext.Writer.WriteLine("</li>");
    }
}

I don't know how your viewmodel looks like, but you could have something like this in controller:

var viewModel = new MyViewModel(currentPage);

var siteMapGenerator = ServiceLocator.Current.GetInstance<SitemapGenerator>();
var sitemap = siteMapGenerator.GetNodes(ContentReference.StartPage);

viewModel.Sitemap = sitemap;

return View(viewModel);

View:

@model MyViewModel

@{ Html.GenerateSitemap(Model.Sitemap); }

It should generate the following HTML for Alloy:

<ul>
    <li>
        <a href="/">Start</a>
        <ul>
            <li>
                <a href="/alloy-plan/">Alloy Plan</a>
                <ul>
                    <li>
                        <a href="/alloy-plan/download-alloy-plan/">Download Alloy Plan</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="/alloy-track/">Alloy Track</a>
                <ul>
                    <li>
                        <a href="/alloy-track/download-alloy-track/">Download Alloy Track</a>
                    </li>
                    <li>
                        <a href="/alloy-track/download-whitepaper-alloy-track/">Whitepaper</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="/alloy-meet/">Alloy Meet</a>
                <ul>
                    <li>
                        <a href="/alloy-meet/download-alloy-meet/">Download Alloy Meet</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="/about-us/">About us</a>
                <ul>
                    <li>
                        <a href="/about-us/news-events/">News & Events</a>
                        <ul>
                            <li>
                                <a href="/about-us/news-events/events/">Events</a>
                                <ul>
                                    <li>
                                        <a href="/about-us/news-events/events/reporting-made-simple/">Reporting Made Simple</a>
                                    </li>
                                    <li>
                                        <a href="/about-us/news-events/events/collaboration-made-simple/">Collaboration Made Simple</a>
                                    </li>
                                    <li>
                                        <a href="/about-us/news-events/events/risk-management-in-complex-projects/">Risk Management</a>
                                    </li>
                                </ul>
                            </li>
                            <li>
                                <a href="/about-us/news-events/press-releases/">Press Releases</a>
                                <ul>
                                    <li>
                                        <a href="/about-us/news-events/press-releases/alloy-pays-it-forward/">Alloy Pays it Forward</a>
                                    </li>
                                    <li>
                                        <a href="/about-us/news-events/press-releases/newworld-wildlife-fund-chooses-alloy/">Alloy Saves Bears</a>
                                    </li>
                                    <li>
                                        <a href="/about-us/news-events/press-releases/alloy-plan-enhances-risk-management/">Enhances Risk Management</a>
                                    </li>
                                    <li>
                                        <a href="/about-us/news-events/press-releases/alloy-meet-acclaimed-for-top-collaboration-technology/">Top Collaboration Technology</a>
                                    </li>
                                </ul>
                            </li>
                        </ul>
                    </li>
                    <li>
                        <a href="/about-us/management/">Management</a>
                    </li>
                    <li>
                        <a href="/about-us/contact-us/">Contact us</a>
                    </li>
                    <li>
                        <a href="/about-us/become-a-reseller/">Become a reseller</a>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
</ul>
#131871
Aug 07, 2015 11:52
Vote:
 

HI,

Can I ask what your viewmodel looks like please? It's always tricky looking at someone elses code and your thoughts are all blank :).

#131873
Edited, Aug 07, 2015 12:10
Vote:
 

Hi,

Let's say you have sitemap page type defined like this:

[ContentType(GUID = "d6030985-2cb1-4c03-801e-a282a8122bd0")]
public class SitemapPage : PageData
{
    // ...
}

If you want to use PageViewModel<T> approach from Alloy:

public class MyViewModel : PageViewModel<StartPage>
{
    public MyViewModel(SitemapPage currentPage) : base(currentPage)
    {
    }

    public SitemapNode Sitemap { get; set; }
}

Otherwise you can define your viewmodel like this:

public class MyViewModel
{
    public MyViewModel(SitemapPage currentPage)
    {
        CurrentPage = currentPage;
    }

    public SitemapPage CurrentPage { get; private set; }
    public SitemapNode Sitemap { get; set; }
}
#131876
Aug 07, 2015 12:55
Vote:
 

Again, thaks for your help.

In your Controller - what is currentPage?

#131878
Aug 07, 2015 13:13
Vote:
 

Page type:

[ContentType(GUID = "d6030985-2cb1-4c03-801e-a282a8122bd0")]
public class SitemapPage : PageData
{
    // ...
}

View model:

public class SitemapViewModel
{
    public SitemapViewModel(SitemapPage currentPage)
    {
        CurrentPage = currentPage;
    }

    public SitemapPage CurrentPage { get; private set; }
    public SitemapNode Sitemap { get; set; }
}

Controller:

public class SitemapPageController : PageController<SitemapPage>
{
    public ActionResult Index(SitemapPage currentPage)
    {
        var viewModel = new SitemapViewModel(currentPage);

        var siteMapGenerator = ServiceLocator.Current.GetInstance<SitemapGenerator>();
        var sitemap = siteMapGenerator.GetNodes(ContentReference.StartPage);

        viewModel.Sitemap = sitemap;

        return View(viewModel);
    }
}

View (you may need to change the namespace):

@model SitemapDemo.Controllers.SitemapViewModel

@{ Html.GenerateSitemap(Model.Sitemap); }
#131879
Aug 07, 2015 13:28
Vote:
 

Hi, Many thanks for your patience - this is exactly what I needed and works like a charm - thansk for all your help,

:)

Jon

#131884
Aug 07, 2015 14:57
Vote:
 
#132780
Aug 19, 2015 3:12
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.