<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Repo Journal</title><link href="http://world.optimizely.com" /><updated>2021-10-06T09:35:31.0000000Z</updated><id>https://world.optimizely.com/blogs/repo-journal/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>How to exclude pages from your search index, sitemaps and internet search engines </title><link href="https://world.optimizely.com/blogs/repo-journal/dates/2021/7/how-to-exclude-content-from-your-search-index-sitemaps-and-search-engines-/" /><id>&lt;p&gt;Your client wants to exclude certain pages from showiing up in your site&#39;s search results, they also want prevent these pages from being crawled by external search engines (Google, Bing etc.) and lastly they want them removed from their sitemap.xml file.&amp;nbsp; All of this can be achieved and controlled with a checkbox property on the Settings tab in the editor. I&#39;ll demonstrate below.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Create an interface with a boolean property that inherits IContent&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;	public interface IDisableIndexing : IContent
	{
		bool DisableIndex { get; set; }
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add it to your base class or any page type&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    public abstract class SitePageData : PageData, IDisableIndex
    {
        [CultureSpecific]
		[Display(Name = &quot;Disable Indexing&quot;,
			Description = &quot;Removes the page from search index, sitemap and search engines&quot;,
			GroupName = SystemTabNames.Settings,
			Order = 10)]
		public virtual bool DisableIndex { get; set; }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Optimizely Search &amp;amp; Navigation&lt;/h3&gt;
&lt;p&gt;You can filter&amp;nbsp; from your Optimizely Search &amp;amp; Navigation search index by creating a module dependency class for your search conventions (this where IContent is implemented)&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;	[ModuleDependency(typeof(IndexingModule))]
	public class FindConventionsInitialization : IInitializableModule
	{
		public void Initialize(InitializationEngine context)
		{
			var client = SearchClient.Instance;
			ContentIndexer.Instance.Conventions.ForInstancesOf&amp;lt;IHasDisableIndex&amp;gt;().ShouldIndex(x =&amp;gt; !x.DisableIndex);
		}

		public void Uninitialize(InitializationEngine context) { }
	}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;External Search Engines&lt;/h3&gt;
&lt;p&gt;Use the same boolean property to add instructions for search robots from the &amp;lt;head&amp;gt;&amp;lt;/head&amp;gt; element in your layout view&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    @if (Model.DisableIndex)
    {
		&amp;lt;meta name=&quot;ROBOTS&quot; content=&quot;noindex, nofollow&quot; /&amp;gt;
    }
&amp;lt;/head&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Sitemaps&lt;/h3&gt;
&lt;p&gt;If you&#39;re using the Geta Sitemap generator you can extend it and filter these pages from being added to when the xml file is being generated. Create a class that inherits from the abstract base &lt;em&gt;SitemapXmlGenerator &lt;/em&gt;class and interface &lt;em&gt;ICommerceAndStandardSitemapXmlGenerator&lt;/em&gt;. Override the &lt;em&gt;AddFilteredContentElement &lt;/em&gt;method and from there you can exlude the pages with IDisableIndex.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    public class CommerceAndStandardSitemapXmlGenerator : SitemapXmlGenerator, ICommerceAndStandardSitemapXmlGenerator
	{
		public CommerceAndStandardSitemapXmlGenerator(
			ISitemapRepository sitemapRepository, 
			IContentRepository contentRepository, 
			UrlResolver urlResolver, 
			ISiteDefinitionRepository siteDefinitionRepository, 
			ILanguageBranchRepository languageBranchRepository, 
			IContentFilter contentFilter) 
			: base(sitemapRepository,  contentRepository, urlResolver, siteDefinitionRepository, languageBranchRepository, contentFilter)
		{
		}

		//Filter content from xml sitemap
		protected override void AddFilteredContentElement(CurrentLanguageContent languageContentInfo, IList&amp;lt;XElement&amp;gt; xmlElements)
		{
			var sitemapContent = languageContentInfo.Content as IHasDisableIndex;

			if (sitemapContent != null &amp;amp;&amp;amp; sitemapContent.DisableIndex)
			{
				return;
			}

			base.AddFilteredContentElement(languageContentInfo, xmlElements);
		}
	}&lt;/code&gt;&lt;/pre&gt;</id><updated>2021-10-06T09:35:31.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizing your Optimizely Search &amp; Navigation service for large files</title><link href="https://world.optimizely.com/blogs/repo-journal/dates/2021/4/optimizing-your-asset-indexing-with-conventions/" /><id>&lt;p&gt;Awhile ago I had a client with an excess of large files. I had increased their upload size limit to 2 GB and many of their documents were between 50 MB; a dozen or so files were between 1 and 2 GB.&amp;nbsp; Episerver recommends not exceeding the&amp;nbsp; by default 50 MB maximum request size.&lt;/p&gt;
&lt;p&gt;Not surprisingly the indexing job started timing out and required immediate attention.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I found there were several ways to tweak the the performance by filtering these files from the indexing job.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I created an initialization module and changed the default batch sizes for the Find service. ContentBatchSize is used for the find index job, MediaBatchSize is for the event-driven indexing on media types.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [InitializableModule]
    [ModuleDependency(typeof(IndexingModule))]
    public class FileIndexingConventions : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            ContentIndexer.Instance.MediaBatchSize = 3;     // Default is 5
            ContentIndexer.Instance.ContentBatchSize = 50;  // Default is 100
        }

        public void Uninitialize(InitializationEngine context)
        {
            throw new NotImplementedException();
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had several ways to filter out these large files. I could filter out IContentMedia from the index entirely or do the same with a custom type for pdfs and zip extensions.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;ContentIndexer.Instance.Conventions.ForInstancesOf&amp;lt;MyPdfMediaType&amp;gt;().ShouldIndex(x =&amp;gt; false);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, I could stop the binary data from being indexed by decorating the propery with the &lt;strong&gt;[JsonIgnore]&amp;nbsp;&lt;/strong&gt;attribute:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    public class MyPdfMediaType : MediaData
    {
        [JsonIgnore]
        public override Blob BinaryData { get; set; }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But since the client wanted to have the file content searchable, I decided only to filter the property when the filesize reached the find service limit.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code&gt;ContentIndexer.Instance.Conventions.ForInstancesOf&amp;lt;IContentMedia&amp;gt;().IndexAttachment(x =&amp;gt; !IsFileSizeLimitReached(x));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...and for this I used an extention method to check against filesize binary data:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;        private static bool IsFileSizeLimitReached(IBinaryStorable binaryContent)
        {
            // Note: 37 MB max. size refers to the base64 encoded file size .
            const int limitKb = 37000;

            try
            {
                var blobByte = (binaryContent.BinaryData as AzureBlob)?.ReadAllBytes() ??
                               (binaryContent.BinaryData as FileBlob)?.ReadAllBytes();

                if (blobByte == null)
                    return false;

                double fileSize = blobByte.Length;

                var isLimitReached = (int)(fileSize / 1024) &amp;gt;= limitKb;

                return isLimitReached;
            }
            catch
            {
                return false;
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once in place I was able to run the job with no exceptions, no timeouts and a happy client!&lt;/p&gt;</id><updated>2021-05-01T00:43:42.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Retrieving, storing file size information during upload</title><link href="https://world.optimizely.com/blogs/repo-journal/dates/2021/4/retrieving-saving-media-type-file-sizes-during-upload/" /><id>&lt;p&gt;While presenting media content available for public download, it might be helpful to provide the file size, particularly for mobile users who may not want to save large files to their phone.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This can be performed on any IContentMedia type by extending the content model and using an initialization module to retrieve and save the file size information.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For this example, I&#39;ll create a custom media type for storing PDF, Word and Excel documents with a read only property to store the file size:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [MediaDescriptor(ExtensionString = &quot;pdf,docx,xlsx&quot;)]
    public class DocumentMediaType : MediaData, IHasFileSize
    {
        [Editable(false)]
        [Display(Order = 10, GroupName = SystemTabNames.Settings, Name = &quot;File Size&quot;)]
        public virtual string FileSize { get; set; }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;m implementing an interface I called IHasFileSize:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    public interface IHasFileSize
    {
        string FileSize { get; set; }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that I can create the initialization module and attach to the save and create events to store the file size data:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [InitializableModule]
    [ModuleDependency(typeof(ServiceContainerInitialization))]
    public class FileBasedEventsInitialization : IInitializableModule
    {
        // attach to both the CreatingContent and SavingContent events
        public void Initialize(InitializationEngine context)
        {
            IContentEvents eventRegistry = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();

            eventRegistry.CreatingContent += SavingMedia;
            eventRegistry.SavingContent += SavingMedia;
        }

        private void SavingMedia(object sender, EPiServer.ContentEventArgs e)
        {

            if (!(e.Content is IContentMedia))
                return;

            IContentMedia media = e.Content as IContentMedia;

            string fileSize = GetFileSizeDisplay(media);

            if (media is IHasFileSize file)
            {
                file.FileSize = fileSize;
            }
        }

        private string GetFileSizeDisplay(IContentMedia media)
        {
            if (media?.BinaryData != null)
            {
                using (var stream = media.BinaryData.OpenRead())
                {
                    return FormatBytes(stream.Length);
                }
            }

            return string.Empty;
        }

        public void Uninitialize(InitializationEngine context)
        {
            IContentEvents eventRegistry = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();

            eventRegistry.CreatingContent -= SavingMedia;
            eventRegistry.SavingContent -= SavingMedia;
        }

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method is used for formatting the file size with the correct unit of measurement:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;        private static string FormatBytes(long bytes)
        {
            string[] uom = { &quot;B&quot;, &quot;KB&quot;, &quot;MB&quot;, &quot;GB&quot; };

            int i;

            double size = bytes;

            for (i = 0; i &amp;lt; uom.Length &amp;amp;&amp;amp; bytes &amp;gt;= 1024; i++, bytes /= 1024)
            {
                size = bytes / 1024.0;
            }

            return $&quot;{size:0.##} {uom[i]}&quot;;
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks for reading, and enjoy displaying your new custom FileSize property!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/296b33776eb942b088a6678b93b59974.aspx&quot; width=&quot;396&quot; height=&quot;451&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</id><updated>2021-04-22T00:14:04.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>