How to find out with which Sitecore site an item is associated?
Asked Answered
S

9

11

We have a multi-site solution (Site 1 and Site 2), and I need to be able to determine if an item for which we're getting the URL (in the LinkProvider, which is custom) belongs to the current context site (Sitecore.Context.Site), or is part of a different site. Is there a good way to do this?

Basically, we just need to be able to find out to which site the item is associated. We can do the comparison between that value and the current context site.

Septum answered 7/1, 2013 at 17:21 Comment(2)
An item can "belong" to multiple sites. I don't think there will be a generic solution for this, it will depend on your configuration.Balneology
Yes, define "belongs to". If you mean "is beneath the current context site root path" then Ruud's answer is what you need.Milliary
B
10

I suggest you make an extension method for the Item class that returns a SiteInfo object containing the definition of the site it belongs to.

Unfortunately I don't have my laptop here with all my code, so I just typed it in Visual Studio and made sure it build, but I'm pretty sure it works:

public static class Extensions
{
    public static Sitecore.Web.SiteInfo GetSite(this Sitecore.Data.Items.Item item)
    {
        var siteInfoList = Sitecore.Configuration.Factory.GetSiteInfoList();

        foreach (Sitecore.Web.SiteInfo siteInfo in siteInfoList)
        {
            if (item.Paths.FullPath.StartsWith(siteInfo.RootPath))
            {
                return siteInfo;
            }
        }

        return null;
    }
}

So now you can call the GetSite() method on all Item objects and retrieve the SiteInfo for that item. You can use that to check if it matches your Sitecore.Context.Site, for example by doing:

SiteInfo siteInfo = itemYouNeedToCheck.GetSite();
bool isContextSiteItem = Sitecore.Context.Site.SiteInfo.Equals(siteInfo);

EDIT: I just thought that you could also do it shorter, like this:

public static Sitecore.Web.SiteInfo GetSite(this Sitecore.Data.Items.Item itemYouNeedToCheck)
{
    return Sitecore.Configuration.Factory.GetSiteInfoList()
        .FirstOrDefault(x => itemYouNeedToCheck.Paths.FullPath.StartsWith(x.RootPath));
}

So pick whatever you like best :)

Babbittry answered 7/1, 2013 at 17:52 Comment(4)
This only works if there is only one possible context site per item, which isn't the case if you have multiple domains pointing to the same home item (e.g. to support country/language specific domains). There are actually different problems in the question: 1) is this item part of the current site content tree, and 2) if not, which site does it belong to.Milliary
The order of the site definitions in web.config is important as it dictates how items that 'belong' to more than one site are resolved.Demonetize
if you have multiple sites, some of which have shorter paths which conflict with others, you can OrderByDescending on RootPath.Length so you match the most specific site first.Harrold
You may also want to check the domain of the site, to make sure it's not the sitecore shell. Something like if (item.Paths.FullPath.StartsWith(siteInfo.RootPath) && siteInfo.Domain != "sitecore")Phalan
H
8
/// <summary>
/// Get the site info from the <see cref="SiteContextFactory"/> based on the item's path.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>The <see cref="SiteInfo"/>.</returns>
public static SiteInfo GetSiteInfo(this Item item)
{
  return SiteContextFactory.Sites
    .Where(s => !string.IsNullOrWhiteSpace(s.RootPath) && item.Paths.Path.StartsWith(s.RootPath, StringComparison.OrdinalIgnoreCase))
    .OrderByDescending(s => s.RootPath.Length)
    .FirstOrDefault();
}
Harrold answered 16/10, 2015 at 1:32 Comment(1)
I dig it @HarroldPhalan
B
7

I upvoted Ruud van Falier's answer then after a few round of testing I realized that it only works in certain scenarios. Couldn't cancel the vote, so I made some modification to the code here:

    public static SiteInfo GetSite(this Item item)
    {
        var siteInfoList = Sitecore.Configuration.Factory.GetSiteInfoList();

        SiteInfo currentSiteinfo = null;
        var matchLength = 0;
        foreach (var siteInfo in siteInfoList)
        {
            if (item.Paths.FullPath.StartsWith(siteInfo.RootPath, StringComparison.OrdinalIgnoreCase) && siteInfo.RootPath.Length > matchLength)
            {
                matchLength = siteInfo.RootPath.Length;
                currentSiteinfo = siteInfo;
            }
        }

        return currentSiteinfo;
    }

So the issue was that other built-in sites normally have shorter paths like "/sitecore/content" which will match with your content path before it reaches the actual site configuration. So this code is trying to return the best match.

Biamonte answered 4/6, 2015 at 0:36 Comment(2)
` public static SiteInfo GetSite(this Item item) { return Factory.GetSiteInfoList().OrderByDescending(i => i.RootPath) .FirstOrDefault(x => item.Paths.FullPath.StartsWith(x.RootPath)); }`Dance
@jrap: OrderByDescending(i => i.RootPath) should be OrderByDescending(i => i.RootPath.Length)Elnaelnar
O
4

If you are using Sitecore 9.3+ then you will want to use IItemSiteResolver through dependency injection instead.

IItemSiteResolver _siteResolver;
public MyClass(Sitecore.Sites.IItemSiteResolver siteResolver) {
    _siteResolver = siteResolver;
}

public void DoWork(Item item) {
    Sitecore.Web.SiteInfo site = _siteResolver.ResolveSite(item);
    ...
}
Oppression answered 7/10, 2020 at 11:13 Comment(1)
Can you provide an example direct instantiation of a siteResolver ?Demonetize
E
2
public static SiteInfo GetSiteInfo(this Item item)
{
    return Sitecore.Links.LinkManager.ResolveTargetSite(item);
}
Elnaelnar answered 15/5, 2018 at 9:32 Comment(2)
This doesn't always work. I've tried that and in the event that you have a structure where, for example, you have content that resides outside of any site's start item hierarchy but still want it to resolve to maybe a site based on being a descendent of a "SiteRoot" item, it will fail to properly resolve the site and return back the default "Website"Keeper
This has become obsolete in Sitecore 9.3Oppression
S
0

This is what I use for our multisite solution.

The FormatWith is just a helper for string.Format.

 public static SiteInfo GetSite(this Item item)
    {
        List<SiteInfo> siteInfoList = Factory.GetSiteInfoList();
        SiteInfo site = null;
        foreach (SiteInfo siteInfo in siteInfoList)
        {
            var siteFullPath = "{0}{1}".FormatWith(siteInfo.RootPath, siteInfo.StartItem);
            if (string.IsNullOrWhiteSpace(siteFullPath))
            {
                continue;
            }
            if (item.Paths.FullPath.StartsWith(siteFullPath, StringComparison.InvariantCultureIgnoreCase))
            {
                site = siteInfo;
                break;
            }
        }
        return site;
    }
Serin answered 31/7, 2013 at 20:7 Comment(0)
H
0

And to avoid dependencies, for unit test purposes, I've created a method to extract this information from web.config directly:

    public static SiteInfoVM GetSiteInfoForPath(string itemPath)
    {
        var siteInfos = GetSiteInfoFromXml();

        return siteInfos
            .Where(i => i.RootPath != "/sitecore/content" && itemPath.StartsWith(i.RootPath))
            //.Dump("All Matches")
            .OrderByDescending(i => i.RootPath.Length).FirstOrDefault();
    }

    static List<SiteInfoVM> GetSiteInfoFromXml()
    {

        XmlNode sitesNode = Sitecore.Configuration.ConfigReader.GetConfigNode("sites");//.Dump();
        var result = sitesNode.Cast<XmlNode>()
        .Where(xn => xn.Attributes != null && xn.Attributes["rootPath"] != null
        //&& (xn.Attributes["targetHostName"]!=null ||  xn.Attributes["name"].Value)
        )
        .Select(xn => new {
            Name = xn.Attributes["name"].Value,
            RootPath = xn.Attributes["rootPath"].Value,
            StartItem = xn.Attributes["startItem"].Value,
            Language = xn.Attributes["language"] != null ? xn.Attributes["language"].Value : null,
            TargetHostName = (xn.Attributes["targetHostName"] != null) ? xn.Attributes["targetHostName"].Value : null,
            SiteXml = xn.OuterXml
        })
        .Select(x => new SiteInfoVM(x.Name, x.RootPath, x.StartItem, x.Language, x.TargetHostName, x.SiteXml))
        .ToList();
        return result;
    }


    public class SiteInfoVM
    {

        public SiteInfoVM(string name, string rootPath, string startItem, string lang, string tgtHostName, string siteXml)
        {
            Name = name;
            TargetHostName = tgtHostName;
            RootPath = rootPath;
            StartItem = startItem;
            Language = lang;
            SiteXml = siteXml;


        }
        public string Name { get; set; }
        public string RootPath { get; set; }
        public string StartItem { get; set; }
        public string Language { get; set; }
        public string TargetHostName { get;set; }
        public string SiteXml { get; set; }
    }
Harrold answered 16/10, 2015 at 1:36 Comment(0)
F
0
public static class SiteResolver
{
    public static SiteContext ResolveSitebyItem(Item contentItem)
    {
        var site = Factory.GetSiteInfoList().FirstOrDefault(
            x => contentItem.Paths.Path.Contains(x.RootPath) &&
            x.RootPath != "/sitecore/content" &&
            x.Domain == "extranet"
            );

        if (site is SiteInfo siteInfo)
        {
            return new SiteContext(siteInfo);
        }

        return Sitecore.Context.Site;
    }
}
Floorage answered 20/4, 2022 at 19:8 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Headstock
P
-1

I believe this is better solution http://firebreaksice.com/sitecore-context-site-resolution/

public static Sitecore.Web.SiteInfo GetSite(Sitecore.Data.Items.Item item)
{
    var siteInfoList = Sitecore.Configuration.Factory.GetSiteInfoList();

    foreach (Sitecore.Web.SiteInfo siteInfo in siteInfoList)
    {
        var homePage = Sitecore.Context.Database.GetItem(siteInfo.RootPath + siteInfo.StartItem);

        if (homePage != null && homePage.Axes.IsAncestorOf(item))
        {
            return siteInfo;
        }
    }
    return null;
}
Palaver answered 14/12, 2017 at 10:7 Comment(1)
Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please edit your answer to add some explanation, including the assumptions you’ve made.Nickeliferous

© 2022 - 2024 — McMap. All rights reserved.