Join us this Friday for AI in Action at the Virtual Happy Hour! This free virtual event is open to all—enroll now on Academy and don’t miss out.

 

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.