Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • André Lange 108 posts 410 karma points
    May 31, 2019 @ 13:00
    André Lange
    0

    Umbraco 8 Surfacecontroller get root

    I am trying to create an xml sitemap surface controller, so i will not have to use a view.

    But current problem is that in Umbraco 8 i cannot figure out how to get the Home (root) page...

    my code:

    public class XmlSitemapSurfaceController : SurfaceController
        {
            // {domain}/umbraco/surface/xmlsitemapsurface/generatexmlsitemap
            public FileResult GenerateXmlSiteMap()
            {
                // Get all registered Domains in Umbraco
                var home = CurrentPage.AncestorOrSelf<Home>(); // find the home node.
                var children = home.Descendants(); // get needed content from each page.
                var sitemapList = new List<SitemapModel>(); //initiate new list.
    
    
                foreach (var item in children)
                {
                    if (!item.Value<bool>("hideFromSitemap"))
                    {
                        var sitemapItem = new SitemapModel()
                        {
                            Loc = item.UrlAbsolute(),
                            LastUpdated = item.UpdateDate,
                            Priority = "0.5",
                            ChangeFreq = "weekly"
                        };
                        sitemapList.Add(sitemapItem);
                    }
                }
    
    
                // Generate XML in proper format.
                var xml = GenerateXml(sitemapList);
                // return as XML.
                var stream = new MemoryStream();
                var writer = XmlWriter.Create(stream);
                xml.WriteTo(writer);
                writer.Flush();
                stream.Seek(0, SeekOrigin.Begin);
                return File(stream, "text/xml", "sitemap.xml"); // returns an XML file with name sitemap.xml
            }
            public XDocument GenerateXml(IEnumerable<SitemapModel> data)
            {
                XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
                XNamespace xsiNs = "http://www.w3.org/2001/XMLSchema-instance";
                XDocument doc = new XDocument(
                      new XDeclaration("1.0", "UTF-8", "no"),
                      new XElement(ns + "urlset",
                      new XAttribute(XNamespace.Xmlns + "xsi", xsiNs),
                      new XAttribute(xsiNs + "schemeLocation",
                             "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"),
                      GenerateElements(data, ns)
    
                ));
                return doc;
            }
            public IEnumerable<XElement> GenerateElements(IEnumerable<SitemapModel> data, XNamespace ns)
            {
                List<XElement> elements = new List<XElement>();
                foreach (var item in data)
                {
                    var element = new XElement(ns + "url",
                                                       new XElement(ns + "loc", item.Loc),
                                                       new XElement(ns + "lastmod", item.Lastmod),
                                                       new XElement(ns + "changefreq", item.ChangeFreq),
                                                       new XElement(ns + "priority", item.Priority)
                                                );
                    elements.Add(element);
                }
                return elements;
            }
        }
    

    Anyone know what i can do to get the Home (root) node ? The idea behind the above is what ever domain i access the surface controller through, it should just get all descendants of that root (home) page.

  • Marc Goodson 2138 posts 14321 karma points MVP 8x c-trib
    May 31, 2019 @ 20:36
    Marc Goodson
    0

    Hi André

    The problem is the Surface Controller takes the 'context' of it's CurrentPage from the template of the document type it has been added to, but in your suggestion above you are calling the surface controller 'directly' via

    {domain}/umbraco/surface/xmlsitemapsurface/generatexmlsitemap

    and Umbraco does not know which IPublishedContent item in umbraco should be the 'CurrentPage'

    If you only have one site, then you could take this approach and query for all the 'root level content' and pick the home node (it would likely be the only one)

    var home = Umbraco.ContentAtRoot().Where(f=>f.Name == "Home").FirstOrDefault(); // find the home node.

    Then your approach would work.

    But if you have multiple sites in Umbraco with different domains, then I'd probably create an XmlSiteMap document type, - and create a 'SiteMap' node in each site's content tree - and use route hijacking to create a RenderMvcController to handle any requests to the XmlSiteMap page - now this page 'would have the context' of the site that it is published in, and then you could find your homepage, via the Umbraco.Web namespace's Site() extension method... and build your sitemap from there.

    regards

    marc

  • André Lange 108 posts 410 karma points
    Jun 03, 2019 @ 06:30
    André Lange
    100

    I ended up doing this:

    in my surface controller calling a helper class i created.

    var home = this.nodeHelper.GetFrontpage(); // find the home node.
    

    and the helper:

    public class NodeHelper
        {
            private readonly HttpContextBase httpContext;
            private readonly UmbracoHelper umbracoHelper;
    
            public NodeHelper(
                HttpContextBase httpContext,
                UmbracoHelper umbracoHelper)
            {
                this.httpContext = httpContext;
                this.umbracoHelper = umbracoHelper;
            }
            public Home GetFrontpage()
            {
                Uri url = this.httpContext.Request.Url;
    
                var registeredDomains = Umbraco.Core.Composing.Current.Services.DomainService.GetAll(false).ToList();
    
                var domains = (from d in registeredDomains
                              where d.DomainName.StartsWith($"{url.Scheme}://{url.Authority}") ||
                              d.DomainName.StartsWith($"{url.Scheme}://{url.Host}") ||
                              d.DomainName.Contains(url.Authority) ||
                              d.DomainName.Contains(url.Host)
                              select d).ToList();
    
                var domain = domains.FirstOrDefault();
    
                return this.GetFrontpage(domain);
            }
    
            public Home GetFrontpage(IDomain domain)
            {
                if (domain == null || domain.RootContentId.HasValue == false)
                {
                    return null;
                }
                else
                {
                    var content = this.umbracoHelper.Content(domain.RootContentId.Value) as Home;
                    return content;
                }
            }
        }
    

    This should give me the frontpage node based on my URL so i can call the xmlsitemap surfacecontroller no matter what site i am on. Then it will just generate the sitemap for that "site".

Please Sign in or register to post replies

Write your reply to:

Draft