Links and references between cloned items in Sitec

2019-04-04 00:44发布

问题:

I'm building a data repository site that I will then clone in its entirety to provide multiple clone sites, enabling localistaion of global content.

What I need to do is to ensure that all references between items in the repository site (links in rich text fields, item references to pull in "related items" spots etc) are overridden to refer to the relevant clones instead of the original items in the repository.

This will likely involve e.g. customising the LinkManager and maybe GetItem(itemID) with some additional logic to find the correct clone.

What I need to know is which bits of the API do I need to worry about? Is there a single modification I can make which will inherit to link rendering in a rich text field in .Net components, item references fed to a sublayout from drop list, renderings through XSLT etc? I need an item ID to work as an alias for its clone when in the context of the clone site. Context.Database.GetItem(ID) needs to return a clone when in the clone site context.

I'm basically looking for a mechanism that will translate "Data/Home/Products/Product A" to "clone/Home/Products/ProductA" whenever and however I use it in the context of a clone site.

Where do I need to implement this logic, how many places?

Cross posted to SDN http://sdn.sitecore.net/SDN5/Forum/ShowPost.aspx?PostID=35598

This is related to an earlier question Handling internal links in Sitecore 6.4 cloned sites , but contains more detail and is more specific.

EDIT: though the ideal solution would place this functionality deep within Sitecore it is important that this only applies to content as viewed on the actual website, i.e. it must not interfere with Sitecore pipelines for e.g. creating, cloning and deleting items.

回答1:

I recommend you take a different approach. Rather than changing the links themselves, you can add code to the HttpRequestPipeline to resolve "Data/Home/Products/Product A" as "clone/Home/Products/ProductA". A similar approach is described in Reusing and Sharing Data:

A CMS user could use the Rich Text Editor, rendering properties, or other features to link to an item based on an item selection data template. Account for these conditions in your code. You can configure an httpRequestBegin pipeline processor to handle HTTP requests for items [...]

To apply this approach to your scenario, add the custom implementation of HttpRequestProcessor after <processor type="Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel"/> in the HttpRequestBegin pipeline in web.config.

Here's the logic to implement:

  1. Use HttpContext.Current.Request.UrlReferrer to determine the referring site.
  2. Verify that the referring site is in your list of cloned sites.
  3. Check if Sitecore.Context.Item is in the source site.
  4. Create a string with the new path, and verify that this item exists using Database.GetItem().
  5. Modify Sitecore.Context.Item to the new item.

The advantage of this approach is that you do not need to intercept the many ways that a link can be created, and you keep your path rewriting logic in one place. In effect, you will be creating an alias from "Data/Products/ProductA" to "clone/Home/ProductA" that will only take effect if your site is in your list of clones.

Update: I did a test of this approach in Office Core. I created a second site, AlternalteSite, with a child node Our-Process. The AlternateSite home page has a link to /home/Our-Process. When the code below is added to the HttpRequestBegin pipeline, the link directs to the /AlternateSite/Our-Process item.

public class SiteChanger : HttpRequestProcessor
 {
   // Some definitions removed...
    public override void Process(HttpRequestArgs args)
    {

        if (ReferringSiteIsTarget())
        {
            Item targetItem = GetTargetItem();
            if (targetItem != null)
            {
                Sitecore.Context.Item = targetItem;
            }
        }
    }

    private Item GetTargetItem()
    {
        string currentItemPath = Sitecore.Context.Item.Paths.FullPath;
        string newPath;

        if (currentItemPath.Contains(SourcePath))
        {
            newPath = currentItemPath.Replace(SourcePath, TargetPath);
            return Sitecore.Context.Database.GetItem(newPath);
        }
        return null;
    }
}

Update2: As James Walford points out in the comments, this approach only works if the clones are not renamed. Sitecore does not, to my knowledge, provide a way of traversing from a source item to its clones in the web database. In master, you can use the Link Database to get from an item to its clones (see this forum post by John West), but after publication, clones become normal items, so presumably will not be included in the Link Database.

One approach would be to add a multilist of links to clones to the standard template, and add logic to populate this as part of the uiCloneItems pipeline, and to use this data in the HttpRequestProcessor code. This will preserve the Link Database relationship, but will put overhead on both the cloning process and the Http Request resolution process, as your code would need to iterate through all the clones to determine which one lived in the requesting website.