Set queryable source on Rendering Parameter Templa

2019-04-28 14:23发布

问题:

I have a Rendering Parameter template applied to a sublayout. It has a single Droptree field on it, and I want to set the Source of that field to a Sitecore query so I can limit the options available for that field.

Source can be:

query:./*

or

query:./ancestor-or-self::*[@@templatename='MyTemplate']/

The query just needs to grab items relative to the content item that we're on. This normally works with Droptree fields in the content editor.

However I'm finding that the query isn't working here because we're in the rendering parameters, so it's not using the content item as it's context. The query fails and I just get the full Sitecore tree.

I found this can be fixed up for the Datasource field with 'Queryable Datasource Locations' at this link:- http://www.cognifide.com/blogs/sitecore/reduce-multisite-chaos-with-sitecore-queries/

However I don't know where to start to get this working for other rendering parameter fields.

Any ideas? (I'm using Sitecore 6.6 Update 5)

回答1:

Unfortunately, the pipeline mentioned in Adam Najmanowicz's answer works for some other types, like Droplink and Multilist, but the pipeline isn't run for Droptree fields.

After looking into this deeper I found that the Source of a Droptree field IS using the wrong context item, as Adam mentioned, but the code comes from the Droptree field itself:-

Sitecore.Shell.Applications.ContentEditor.Tree, Sitecore.Kernel

Utilising the query string code from Adam's answer, we can create a 'fixed' Droptree custom field, that is almost the same as the regular Droptree but will use the correct context item instead. The code will inherit from the normal Tree control, and only change the way that the Source property is set.

public class QueryableTree : Sitecore.Shell.Applications.ContentEditor.Tree
{
    // override the Source property from the base class
    public new string Source
    {
        get
        {
            return StringUtil.GetString(new string[]
            {
                base.Source       // slightly altered from the original
            });
        }
        set
        {
            Assert.ArgumentNotNull(value, "value");
            if (!value.StartsWith("query:", StringComparison.InvariantCulture))
            {
                base.Source = value;         // slightly altered from the original
                return;
            }
            Item item = Client.ContentDatabase.GetItem(this.ItemID);

            // Added code that figures out if we're looking at rendering parameters, 
            // and if so, figures out what the context item actually is.
            string url = WebUtil.GetQueryString();
            if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
            {
                FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
                var currentItemId = parameters["contentitem"];
                if (!string.IsNullOrEmpty(currentItemId))
                {
                    Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
                    item = Sitecore.Data.Database.GetItem(contentItemUri);
                }
            }

            if (item == null)
            {
                return;
            }
            Item item2 = item.Axes.SelectSingleItem(value.Substring("query:".Length));
            if (item2 == null)
            {
                return;
            }
            base.Source = item2.ID.ToString();         // slightly altered from the original
        }
    }

The above code is pretty much the same as the Source property on the base Tree field, except that we figure out the proper context item from the URL if we've detected that we're in the rendering parameters dialog.

To create the custom field, you just need to edit the Web.Config file as described here. Then add the custom field to the core database as described here.

This means that parameters can now have queries for their source, allowing us to limit the available items to the content editor. (Useful for multi-site solutions).



回答2:

The key here would be to set the Field Editor's context to be relative to the item you are editing instead of the Rendering parameters (that I think it has by default). So you could have processor:

public class ResolveRelativeQuerySource
{
    public void Process(GetLookupSourceItemsArgs args)
    {
        Assert.IsNotNull(args, "args");
        if (!args.Source.StartsWith("query:"))
            return;
        Item contextItem = null;
        string url = WebUtil.GetQueryString();
        if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
        {
            FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
            var currentItemId = parameters["contentitem"];
            if (!string.IsNullOrEmpty(currentItemId))
            {
                Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
                contextItem = Sitecore.Data.Database.GetItem(contentItemUri);
            }
        }
        else
        {
            contextItem = args.Item;
        }
    }
}

hooked as:

<sitecore>
  <pipelines>
    <getLookupSourceItems>
    <processor patch:before="*[@type='Sitecore.Pipelines.GetLookupSourceItems.ProcessQuerySource, Sitecore.Kernel']"
        type="Cognifide.SiteCore.Logic.Processors.ResolveRelativeQuerySource, Cognifide.SiteCore" />
    </getLookupSourceItems>
  </pipelines>
</sitecore>

Together with ResolveQueryableDatasources from Przemek's blog this should solve your problem.