If you have a lot of media items in your Sitecore instance (thousands, ten of thousands), your content author would have difficulty in finding the media items that they want, thus making them frustrated trying do a simple task.
As a good developer you are, you try to help the content author by making their life easier. You start to think about using tags, by using tags the content author could filter the media items to a specific tags and refine their search from there.
So you sat down and make some notes of what you had in your mind
- thousands of media items in the media library and the content author is having difficulties in filtering them
- how about we apply tag(s) to the media item itself so that we can filter the media items based on those tags
- from the content author perspective they would see a new section in the media library dialog to filter media items based on those tags
- if by default when the media library opens it’s going to say 30,000 items, when the content author click on one of the tags it’s going to narrow down the result to 500 items and then the content author can further narrow it down by searching the file name
So you start of by buildling a POC, you downloaded https://marketplace.sitecore.net/Modules/Sitecore_Instance_Manager.aspx?sc_lang=en to help you quickly install/uninstall sitecore instances.
After the new Sitecore instance has been created, you start checking what the current media library dialog window looks like
You noticed that there’s three filter already built out-of-the-box which is Dimensions, Media type and Date uploaded. You want to create a new column in there called Tags, and you want to know how it work so you start by finding out how the Dimensions column works.
— you did your research and now ready to build your own custom filter based on tags
You’ve defined the following steps required to build the functionality
- Create a new class (MediaItemTagsIndexField) that extends AbstractComputedIndexField to extract the value of the assigned tags of each media items
- Create a new field and call it mediaitemtags in the index config file so that it would store the value returned by the MediaItemTagsIndexField
- Create a new Media Item Tags folder and create new tags item underneath it
- Update the image template in the master db both versioned and unversioned, add new field called Tags with Treelist as the media type and point the datasource to the Media Item Tags folder
- For POC purposes modify the default /sitecore/content/home item and add a new field with type image in it’s template definition
- Create a new class which extends the IFacetProvider which responsible for querying to the search provider and filter the results for you
- in the core db under the facets folder, create a new facet item called Tags and set the value of the fieldTypeName to the class that extends the IFacetProvider
- Test the functionality
MediaItemTagsIndexField.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
using System.Collections.Generic; using System.Linq; using Sitecore.ContentSearch; using Sitecore.ContentSearch.ComputedFields; using Sitecore.Data; using Sitecore.Data.Items; namespace RR.Poc.ContentSearch.ComputedFields { public class MediaItemTagsIndexField : AbstractComputedIndexField { public override object ComputeFieldValue(IIndexable indexable) { Item obj = indexable as SitecoreIndexableItem; if (obj == null) return null; var list = new List<string>(); if (obj.Paths.IsMediaItem) { var field = obj.Fields["Tags"]; if (field != null && !string.IsNullOrEmpty(field.Value)) { var itemIds = field.Value.Split('|').Select(x => new ID(x)); var imageCustomSectionNames = GetImageCustomSectionNames(itemIds, obj.Database); list.AddRange(imageCustomSectionNames); return list; } } return list; } private IEnumerable<string> GetImageCustomSectionNames(IEnumerable<ID> itemIds, Database database) { var result = new List<string>(); foreach (var itemId in itemIds) { var item = database.GetItem(itemId); if (item != null) { result.Add(item.DisplayName); } } return result; } } } |
zzz.RR.lucene.index.config
This is a patch file used to update the sitecore_marketing_asset_index_master index, we’re adding a new mediaitemtags field to the document
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?xml version="1.0"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <contentSearch> <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch"> <indexes hint="list:AddIndex"> <index id="sitecore_marketing_asset_index_master" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider"> <param desc="name">$(id)</param> <param desc="folder">$(id)</param> <!-- This initializes index property store. Id has to be set to the index id --> <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" /> <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration"> <fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch"> <fieldNames hint="raw:AddFieldByFieldName"> <field fieldName="mediaitemtags" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider"> <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" /> </field> </fieldNames> </fieldMap> <fields hint="raw:AddComputedIndexField"> <field fieldName="mediaitemtags">RR.Poc.ContentSearch.ComputedFields.MediaItemTagsIndexField,RR.Poc</field> </fields> </configuration> <strategies hint="list:AddStrategy"> <!-- NOTE: order of these is controls the execution order --> <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/syncMaster" /> </strategies> <commitPolicyExecutor type="Sitecore.ContentSearch.CommitPolicyExecutor, Sitecore.ContentSearch"> <policies hint="list:AddCommitPolicy"> <policy type="Sitecore.ContentSearch.TimeIntervalCommitPolicy, Sitecore.ContentSearch" /> </policies> </commitPolicyExecutor> <locations hint="list:AddCrawler"> <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch"> <Database>master</Database> <Root>/sitecore/media library</Root> </crawler> </locations> </index> </indexes> </configuration> </contentSearch> </sitecore> </configuration> |
In the config file we’re targeting for the marketing_asset_index_master index however if some reason you did not use this file you could target the sitecore_master_index instead as Sitecore will fallback to use that index instead if marketing_asset_index_master is not available; or define it in the Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config file instead to apply it to all index files that refers to the default index configuration.
Create media item tags folder and tags item beneath it
Update the image template to include the custom field to assign tags
Do this for for this two Image templates and set the datasource to the media item tags folder
- /sitecore/templates/System/Media/Unversioned/Image
- /sitecore/templates/System/Media/Versioned/Image
Add a new field for the home item for the content author to be able to select an image
Now the content author can select an image for the home item, however we haven’t provide them with a way to filter the search results in the media library dialog yet. To do this first we need to create a new class that extends IFacetProvider that’s responsible to apply filtering on the search result.
MediaItemsTagFacetProvider.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
using System; using System.Linq; using Sitecore; using Sitecore.ContentSearch.Linq; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.ItemWebApi.Facets; using Sitecore.ItemWebApi.Pipelines.Search; using FacetValue = Sitecore.ItemWebApi.Facets.FacetValue; namespace RR.Poc.ItemWebApi.Facets { public class MediaItemsTagFacetProvider : IFacetProvider { private string _facetDisplayName; public string IndexName { get; set; } public bool CanHandle(string facetName) { Assert.ArgumentNotNull(facetName, "facetName"); return facetName == "mediaitemtags"; } public IQueryable<ConvertedSearchResultItem> FacetOn(IQueryable<ConvertedSearchResultItem> queryable, Item facetItem) { Assert.ArgumentNotNull(queryable, "queryable"); Assert.ArgumentNotNull(facetItem, "facetItem"); _facetDisplayName = facetItem.DisplayName; return queryable.FacetOn(i => i["mediaitemtags"]); } public Facet GetFacet(string name) { Assert.ArgumentNotNull(name, "name"); return new Facet(_facetDisplayName); } public FacetValue GetValue(Facet facet, string value) { Assert.ArgumentNotNull(value, "value"); return new FacetValue("mediaitemtags:" + value, value, 0) { DisplayText = StringUtil.Capitalize(value) }; } } } |
Next we need to add a new column in the media library dialog UI so that the content author can use that to filter the media library search result. To do this switch to the core db and create a new item with facet template under /sitecore/client/Applications/Dialogs/SelectMediaDialog/PageSettings/Facets
Now switch back to the master db and click on the home item and click on the browse link
And now the content author has a new way to filter those media items based on the assigned tags!.
Here’s the files used in this post:
Hope this helps.