tag:blogger.com,1999:blog-53460279727482359462024-03-14T08:25:40.124-04:00Sitecore TacticsSharing Sitecore CMS knowledge, ideas and tactics!Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.comBlogger58125tag:blogger.com,1999:blog-5346027972748235946.post-55048255717758639982016-09-22T07:42:00.000-04:002017-08-01T22:04:15.316-04:00Sitecore Cookbook for Developers - A development guide<div dir="ltr" style="text-align: left;" trbidi="on">
My heartfelt thanks to readers of my book - <a href="https://goo.gl/dDfPJJ" target="_blank">"Sitecore Cookbook for Developers"</a>, and those who gave positive feedback on <a href="https://goo.gl/dDfPJJ" target="_blank">Amazon</a> and <a href="https://goo.gl/kPtV9j" target="_blank">PacktPub</a>. Thank you all for appreciating my efforts of 1.5 years I put into in finishing the book! I'm really happy to see how the Sitecore community welcomed it.<br />
<br />
If someone takes 2-5 hours to publish a quality blog post, then a quality recipe covered in this book will surely take 15-20 hours to finish it with better content and quality, and yet by strictly following <a href="https://www.packtpub.com/" target="_blank">PACKT Publishing</a> multi-stage workflow. The effort was not about just writing 83 recipes for this book, but also for taking care of keeping table of content up-to-date, keeping latest content, taking and editing screenshots, bettering the content, maintaining context for the reader, providing content for beginners to expert level readers, providing useful and meaningful knowledge in limited pages, etc. and that is without being a professional or experienced author. That's why sadly saying, more than 20 well cooked recipes had to give sacrifice. After all, the intention after this book was not money, but giving a better book to Sitecore community!<br />
<br />
This overburdened situation I named as <b>"Sitecore Overloaded"</b> :). And that was the main reason I kept myself far away from writing blogs and forums for last few months.<br />
<br />
<div>
Last week, I attended Sitecore MVP Summit and Symposium 2016, which gave me chance to meet other MVPs, and many Sitecorians. I also met some Sitecore friends who were looking for a good Sitecore development training or book or tutorials, and seriously they were not aware of this book or its content. And I am delighted, it helped them. Intention for writing this blog is providing Table of Contents of this book, which some Sitecore community members requested me while Symposium.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://goo.gl/dDfPJJ" target="_blank"><img alt="Sitecore Cookbook for Developers - A Development Guide" border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQv6EjmXHZEKs-vyG5ownUhCrX4e3w26ubD7TmF5I_eTqDcXgIUR18hc6VV8T5NYD-Lr2so4_VR2zPA7pHfuDt5ykBlSTXE6x0XHsliu04EUxzZV6kNW3jtqfLn9I4jJXJJMYv5XtGu3Y/s400/cookbook-ori.jpg" title="Sitecore Cookbook for Developers - A Development Guide" width="323" /></a></div>
<br />
<h2>
Table of Contents of Sitecore Cookbook for Developers</h2>
<h3>
Chapter 1: Basic Presentation Components</h3>
<ul>
<li>Creating a simple content page using template and layout</li>
<li>Creating a sidebar menu using view rendering and RenderingModel</li>
<li>Creating breadcrumb using the view and custom model</li>
<li>Creating carousel using view and controller renderings</li>
<li>Placing renderings dynamically using placeholders</li>
<li>Empowering the Experience Editor using placeholder settings</li>
<li>Restricting or swapping rendering controls on placeholders</li>
</ul>
<h3>
Chapter 2: Extending Presentation Components</h3>
<ul>
<li>Altering rendering behavior using component properties</li>
<li>Creating strongly typed rendering parameters</li>
<li>Achieving asynchronous operations using a custom device</li>
<li>Creating multilingual content pages</li>
<li>Generating RSS feeds for syndicated items</li>
<li>Improving site performance by caching renderings</li>
<li>Personalizing components</li>
</ul>
<h3>
Chapter 3: Customizing the User Interface Framework</h3>
<ul>
<li>Adding a custom command to item context menu</li>
<li>Creating a gutter to show unpublished items</li>
<li>Creating a Sheer UI application using XAML control to list products</li>
<li>Creating a SPEAK application to list and sort products</li>
<li>Searching and fi ltering products using SPEAK</li>
<li>Building a custom form to bind product details using SPEAK</li>
<li>Creating a custom editor tab in the Content Editor</li>
<li>Creating a custom experience button using the Field Editor</li>
<li>Creating a custom rule to validate item fields</li>
<li>Creating a custom sorting routine to sort the content tree items</li>
<li>Creating a custom fi eld to save the date time with time zones</li>
</ul>
<h3>
Chapter 4: Leveraging the Sitecore Backend</h3>
<ul>
<li>Working with multiple sites</li>
<li>Customizing pipelines to achieve a custom 404 page</li>
<li>Creating a custom event handler to auto-publish on an item save</li>
<li>Achieving a site-specifi c URL pattern for a multisite environment</li>
<li>Initializing hooks to subscribe events to prepare an audit trail</li>
<li>Creating jobs to accomplish long-running operations</li>
<li>Using a scheduling agent to delete older item versions</li>
<li>Scheduling database tasks</li>
</ul>
<h3>
Chapter 5: Making Content Management More Efficient</h3>
<ul>
<li>Using dictionary domains for multilingual sites on multisite environment</li>
<li>Creating vanity URLs for marketing purposes using an alias item</li>
<li>Centralizing common content using a clone item</li>
<li>Using a wildcard item to integrate external content</li>
<li>Placing dynamic content in the Rich Text Editor by replacing tokens</li>
<li>Adding a custom tool to the Rich Text Editor to generate tokens</li>
<li>Dealing with user-generated content using an Item Web API</li>
<li>Storing external content using a custom cache</li>
</ul>
<h3>
Chapter 6: Working with Media</h3>
<ul>
<li>Restricting malicious fi les being uploaded to the media library</li>
<li>Downloading the media library folder</li>
<li>Protecting media fi les under a disclaimer</li>
<li>Achieving responsive images</li>
<li>Serving media fi les from CDN or external storage</li>
</ul>
<h3>
Chapter 7: Workfl ow and Publishing</h3>
<ul>
<li>Creating a custom action using workflow</li>
<li>Achieving time-based automated publishing</li>
<li>Unpublishing of items</li>
<li>Using publishing events to send a publish completion e-mail</li>
<li>Publishing fi le-based items using web deploy</li>
<li>Clearing an HTML cache based on published items for multisite environment</li>
<li>Customizing the publishItem pipeline to avoid duplicate names on a live site</li>
</ul>
<h3>
Chapter 8: Security</h3>
<ul>
<li>Working with a custom user profile</li>
<li>Creating custom access rights for an item</li>
<li>Achieving a single sign-on by creating a virtual user with custom roles and rights</li>
<li>Preventing Sitecore from applying security</li>
<li>Implementing extranet login</li>
</ul>
<h3>
Chapter 9: Sitecore Search</h3>
<ul>
<li>Indexing, searching, sorting, and paging content using a search query</li>
<li>Creating a computed index fi eld for categorization</li>
<li>Refining search results by tagging based facets</li>
<li>Achieving the autocomplete feature with a wildcard</li>
<li>Influencing search results with boosting</li>
<li>Hunting MoreLikeThis results</li>
<li>Correcting a search with did you mean</li>
<li>Managing millions of items using an item bucket</li>
</ul>
<h3>
Chapter 10: Experience Personalization and Analytics Using xDB</h3>
<ul>
<li>Personalizing experience based on goals and Engagement Values</li>
<li>Personalizing content by predicting a visitor's profile</li>
<li>Storing visitor information in xDB contacts</li>
<li>Extending xDB by creating a custom contact facet</li>
<li>Creating a custom rule and condition for personalization</li>
<li>Automating the engagement plan</li>
<li>Finding nearby places using the Geolocation service</li>
<li>Aggregating xDB data to generate custom reports</li>
<li>Extending analytics reports using custom dimensions</li>
<li>Creating section-specifi c analytics reports using custom dimensions</li>
</ul>
<h3>
Chapter 11: Securing, Scaling, Optimizing, and Troubleshooting</h3>
<ul>
<li>Profiling and tracing content pages to find out the slowest operations</li>
<li>Transferring items from one database to another</li>
<li>Making security-hardened environments</li>
<li>Adding multiple publishing targets for scalability or preproduction</li>
<li>Creating clustered instances for scalability and performance</li>
<li>Getting high availability of Sitecore instances</li>
<li>Improving the performance of Sitecore instances</li>
</ul>
<h3>
Appendix A: Getting Started with Sitecore</h3>
<ul>
<li>Installing Sitecore</li>
<li>Creating a Visual Studio project</li>
<li>Debugging a Sitecore application</li>
</ul>
<h3>
Appendix B: Tools and Resources for Sitecore Developers</h3>
<ul>
<li>Useful tools for Sitecore developers</li>
<li>Useful resources for Sitecore developers</li>
</ul>
<h2>
Looking for the sample chapter?</h2>
You can check it out from <a href="https://goo.gl/e2x6e1" target="_blank">Scribd</a>.<br />
<br />
This book is a good resource for beginners to experts, or even trainees in Sitecore, If you like the book, please do not hesitate to give feedback or reviews on <a href="https://goo.gl/dDfPJJ" target="_blank">Amazon</a> or <a href="https://goo.gl/kPtV9j" target="_blank">PacktPub</a>. Or even if you have suggestions for next editions, please do not keep them in your mind, it will help others in future.<br />
<br />
<b>P.S.:</b> <b>Getting likes or reviews on a book is as difficult as easy it is to get likes on a short social media post.</b></div>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com6tag:blogger.com,1999:blog-5346027972748235946.post-60178439182886780682015-11-19T08:45:00.002-05:002015-11-19T23:17:47.280-05:00Things to remember while using CDN for Sitecore websites<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
We had many learnings while using CDN for our different types of Sitecore websites, so thought to share here, if get useful to others! Before that I want to share one of few interesting incidents that left a message to us for configuring CDN carefully.<br />
<br />
<div>
We believe that Sitecore resolves the Site using host name from the request URL, so for serving media requests we do not need to forward cookies or any other parameters from CDN to Sitecore CD servers. That's very true, but partially. If you are thinking for providing the best user experience to end-users, you need to take care more than this.<br />
<br />
Now, consider a case that a user is publishing a new Content Page that have few images related to it also got published on your one more Publihing Target Databases. First request of this page came to one of many clustered servers on the same time when your publishing got finished. What are the chances that all those images will be visible to that first view of the page, 100%? No, not at all. Let's see why.</div>
<div>
<br /></div>
<div>
You know that CDN manages sticky session using a cookie (i.e, <i>AWSELB </i>for AWS CloudFront). We normally need sticky session for content pages. So, we never forget forwarding Cookies from CDN to CD servers and don't do the same for media files as media files has nothing to do with Cookies. In such cases, the first request of content page went to a server i.e, A. But, the images might get served from different servers say B, C, etc. (as we set them not to carry cookies) and think the images are yet to get reflected on any of these servers due to publishing or caching delay of a second. So, content of the page will be served properly but images will return 404 and CDN will cache the response for few minutes. It means we are still leaving with chances that end-users will get disturbed page layout for few minutes. This also gets applied to Stylesheet or Javascript files as well if they are served from Sitecore items. We can fix the issue if we apply sticky session forwarding for media requests as well.</div>
<h3>
CDN Configurations for caching media files</h3>
<div>
<ol style="text-align: left;">
<li>Forward Cookies those play role in maintaining sticky session (i.e. <i>AWSELB</i> cookie<b>) </b>for all media items (To fix above explained issue)</li>
<li>Forward <b>Querystrings </b>(To support Media Querystring parameters explained here for responsive websites)</li>
<li>Never cache such media files those are protected i.e, those have disclaimers. You can keep them in a separate media folder and apply rule not to cache such URL patterns.</li>
<li>If your media items are getting changed rarely, keep bigger caching duration i.e, 1 hour, otherwise keep it little as 5 minutes. Or to get more accurate results, Instead of all above rules, you can also get benefits of 304 <b>if-modified-since</b> header to serve media requests.</li>
</ol>
</div>
<h3>
CDN Configurations for caching content pages</h3>
<div>
<ol style="text-align: left;">
<li>If it's a pure static site without any user logins or protections, you can serve the site without forwarding any parameter.</li>
<li>If your site is developed for multi device support, you must forward <b>Referrer</b> and <b>User-Agent</b> request headers.</li>
<li>If your site is having any kind of login facility or requires session or has cookie-oriented responsive or adaptive architecture, you must forward <b>Cookies</b> header.</li>
<li>If you have implemented security based on IP Addresses, you must forward <b>X-Forwarded-For</b> header.</li>
<li>If you have implemented Browser Based Content Negotiation, you must forward <b>Accept</b>, <b>Accept-Language</b>, etc. parameters.</li>
<li>If you are using functionalities like personalization, secured content, etc. you can avoid content caching on CDN.</li>
<li>Never cache HTML content served through other than GET request.</li>
</ol>
<div>
<br /></div>
<div>
<div>
So, for getting best usage of CDN with best user experience, you must have knowledge how your website are developed and behaves.</div>
</div>
</div>
<div>
<br /></div>
</div>
<ol style="text-align: left;">
</ol>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com2tag:blogger.com,1999:blog-5346027972748235946.post-62082487166787618262015-11-13T01:33:00.001-05:002016-01-30T01:26:10.329-05:00Avoid UNC file share to prevent File Change Notification issues<div dir="ltr" style="text-align: left;" trbidi="on">
Recently we were experienced many unexpected application pool recycling with <b>File Change Notifications (FCN)</b> on our newly created Sandbox Sitecore CM instances. Every few hours (ranging from 20-40 hours) only one of two CM instances was getting restarted automatically with below error.
<br />
<br />
<div class="highlight">
Change Notification for critical directories. File Change Notification Error in App_LocalResources HostingEnvironment initiated shutdown CONFIG change HostingEnvironment caused shutdown
</div>
<h3>
What we tried</h3>
After spending a good amount of time on it, we hypothetical thought for few workarounds to do like
<ul style="text-align: left;">
<li>Stop Anti-virus on server</li>
<li>Use Process Monitor to check a File System for Web-root and Temporary Internet Files, etc. folders to know what's actually causing this</li>
<li>Check there's no access given on the web-root to any unauthorized user. </li>
</ul>
But all looked good for us, even not getting much help from Google as well. So final option we had to debug the crash dumps.<br />
<br />
One more thought came in mind that recycling is happening only on one instance, which is using Sublayouts from other Sitecore instance using UNC file share, and that other instance never got recycled! We know that DFS was the best option here, but we were not able to digest that UNC share can really cause this issue. At the same time we found a nice post where someone already faced the same issue and they fixed it by stopping UNC path sharing for sublayouts. And yes, it worked for us too.<br />
(<b>Note:</b> Recycling is not happening because of number of re-compilations - "numRecompilesBeforeAppRestart")<br />
<br />
- <a href="http://www.dnnsoftware.com/forums/threadid/318762/scope/posts/file-change-notification-issues-web-farm-over-unc-share" target="_blank">http://www.dnnsoftware.com/forums/threadid/318762/scope/posts/file-change-notification-issues-web-farm-over-unc-share</a><br />
- <a href="http://blogs.msdn.com/b/tess/archive/2006/08/02/686373.aspx" target="_blank">http://blogs.msdn.com/b/tess/archive/2006/08/02/686373.aspx</a><br />
<h3>
What we Learned and Investigated:</h3>
<div class="highlight">
- We learnt one more reason that can recycle the application pool<br />
- Avoid using UNC sharing for ASPX, ASCX, RESX, App_Code files, etc. compilable files and Use DFS for them, what Sitecore recommends in Sitecore Scalability Guide.
</div>
<br /></div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-82696977565043902242015-10-28T04:59:00.000-04:002016-09-02T11:45:58.793-04:00 Sitecore media streaming issue after publishing!<div dir="ltr" style="text-align: left;" trbidi="on">
Recently we came across a strange behaviour of Sitecore Media streaming in MediaCache that <i>"Overwritten media files are not getting reflected after publish"</i>. Just to have clear idea, this is <b>not</b> a browser caching issue mentioned in <a href="https://kb.sitecore.net/articles/218124" target="_blank">Sitecore KB article</a>.
<br />
<h3>
What's the issue?</h3>
We created a media item on CM and published it, and was visible on live site. Now we overwritten a new media file to the same media item and published it again. (Either using Detach/Attach from Content Editor or using Overwrite existing media from Page Editor.) Surprisingly, we were still getting older media file! We published again, again and again, but newly published media not getting updated.
<br />
<br />
And yet, this is a very random issue and occurs very rarely.
<br />
<h3>
How we tried to troubleshoot?</h3>
<ol>
<li>
We have multiple servers in cluster with 2 target databases. We found that few servers of both target databases are serving older media file and rest of them serving latest one.
</li>
<li>
Then we thought there might be some <b>Item Path Cache</b> or <b>Item Cache</b> clearing issue (Which happens on Sitecore some times). So, we cleared both these caches for this media item using Sitecore API. But result was same.
</li>
<li>
Then we cleared whole <b>Item Cache</b> and <b>Data Cache</b> using Sitecore API. The result was same.
</li>
<li>
Then we cleared <b>All Sitecore Caches</b> using <a href="javascript:">http://<domain .com="">/sitecore/admin/cache.aspx</domain></a> page. The result was same.
</li>
<li>
Final option we had to clear all media cache physical files (Website\App_Data\MediaCache) so that Sitecore will create new media cache from database and can serve latest one. Even after deleting all files and folder from it, new files got generated but still were older one.
</li>
</ol>
So, no solution at all after applying these many tricks!
<br />
<h3>
How we fixed?</h3>
We had no other option but recycling the Application Pool. Finally, the master key worked for us. :)
<br />
<h3>
What we concluded and what's the solution?</h3>
The only conclusion we had that Sitecore is storing media files somewhere in Server memory as well. Strange, right?<br />
<br />
We raised to Sitecore Support for further investigation. Many thanks to <b>Andrey Krupskiy</b> from support who investigated and confirmed that Sitecore is really storing media files in RAM as well that might have caused this and provided below solution. <br />
<br />
<div class="highlight">
There is an internal media cache in RAM. This cache is used when media is not yet saved to the filesystem. Even, if you check code of <b>Sitecore.Resources.Media.MediaCache</b> class, in Reflector, it says the same. Sitecore serves media file RAM before its actual file cache gets generated on disk (might be to serve media faster), which is default behaviour of Sitecore. We can disable this behaviour by changing below configuration in Web.Config.
<br />
<pre class="brush:xml"><setting name="Media.StreamPartiallyCachedFiles" value="false" />
</pre>
</div>
<br />
We disabled the <b>Media.StreamPartiallyCachedFiles</b> setting as shown above on CM and CD servers.<br />
<br />
Now it has more than a month now, we haven't faced the issue again.
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com4tag:blogger.com,1999:blog-5346027972748235946.post-90331491908560355192015-10-13T09:48:00.002-04:002016-09-02T11:47:19.638-04:00Dealing with duplicate item names in Sitecore publishing<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
A very common challenge in Sitecore is to deal with duplicate item name inside a single parent. Many times our content authors complaint that they are not able to see latest content for an item even after publishing it many times. Let's see why users were getting older content.<br />
<h3>
What they actually do?</h3>
<h4>
1. We have one item say <b>Test</b> under Home and published it.</h4>
<table>
<tbody>
<tr>
<td><div style="text-align: center;" width="100%">
<b>On CM Server</b></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpoVhl8h2PhVNeOKdLlacjbyyO7_qmf3zRSiq-QBrwY8B-Lu2TPFaRg-JpKfYsAOx2kvo2BZUHQnaxAnF2eUvsNN2Bdq5LnVHAFTU6-luhgqSfeAHhJ_iETdFYi-FepGOGWV4ZHgvEc-4/s1600/sitecore-duplicate-itemname-publish-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpoVhl8h2PhVNeOKdLlacjbyyO7_qmf3zRSiq-QBrwY8B-Lu2TPFaRg-JpKfYsAOx2kvo2BZUHQnaxAnF2eUvsNN2Bdq5LnVHAFTU6-luhgqSfeAHhJ_iETdFYi-FepGOGWV4ZHgvEc-4/s320/sitecore-duplicate-itemname-publish-1.png" width="320" /></a></div>
</td>
<td><div style="text-align: center;" width="100%">
<b>On CD Server</b></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB58E8K_ndyDQi7UGXR6gqsqUBCYzzm3wuKALOg4BhZx4NNbOz7vsadFYrOUwgkAzYt9X2k45maUS__gcCsInWsXi4E4OX4E-XoBVdajPQH5xKirE3cnoXyY7dvLf1pehgfLLoHT3KxH4/s1600/sitecore-duplicate-itemname-publish-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB58E8K_ndyDQi7UGXR6gqsqUBCYzzm3wuKALOg4BhZx4NNbOz7vsadFYrOUwgkAzYt9X2k45maUS__gcCsInWsXi4E4OX4E-XoBVdajPQH5xKirE3cnoXyY7dvLf1pehgfLLoHT3KxH4/s320/sitecore-duplicate-itemname-publish-1.png" width="320" /></a></div>
</td>
</tr>
</tbody></table>
<h4>
2. Now, deleted the Test item, and created another item with Test name itself and published<br />(Or created new item named Test and deleted older one.)</h4>
<table>
<tbody>
<tr>
<td><div style="text-align: center;" width="100%">
<b>On CM Server</b></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM51Sys0E8miYkJCiBaqntTHA1jqHyq3g0XUG_0K_r44D-RXWSeeLBEleP-72JiLZZ-So_5TDpisC3lx1jiDW_OmCe9jcKAHvQy-gRwFs04Ke1Z5UVyMVW7o13zIdhbEgwdPSYNfJtXT0/s1600/sitecore-duplicate-itemname-publish-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM51Sys0E8miYkJCiBaqntTHA1jqHyq3g0XUG_0K_r44D-RXWSeeLBEleP-72JiLZZ-So_5TDpisC3lx1jiDW_OmCe9jcKAHvQy-gRwFs04Ke1Z5UVyMVW7o13zIdhbEgwdPSYNfJtXT0/s320/sitecore-duplicate-itemname-publish-2.png" width="320" /></a></div>
<br /></td>
<td><div style="text-align: center;" width="100%">
<b>On CD Server</b></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW67l2QsDQVePAAUEF1tDrNZyLq8R5PnSJ8_y3wHriaogKQBFfGzS5YxAIZ__bX0EaxO7ubVvAEEOVSfiyh2mGJ9YEX3Pk2l7X36CJWkSihOC_ouV76JThabG0T26Mrhqfgu6psH_cFDw/s1600/sitecore-duplicate-itemname-publish-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="101" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW67l2QsDQVePAAUEF1tDrNZyLq8R5PnSJ8_y3wHriaogKQBFfGzS5YxAIZ__bX0EaxO7ubVvAEEOVSfiyh2mGJ9YEX3Pk2l7X36CJWkSihOC_ouV76JThabG0T26Mrhqfgu6psH_cFDw/s320/sitecore-duplicate-itemname-publish-3.png" width="320" /></a></div>
<br /></td>
</tr>
</tbody></table>
</div>
Uff, how can we get latest content, when live database contains two items with same name under a single parent? Now, when you request for <a href="javascript:">http://sitecoretactics/test/</a>, it will surely going to render any random item. But due to increasing number of such cases, we thought to have a permanent solution to have unique item<br />
<h3>
Solution to have unique item on live sites</h3>
We will create a custom processor in <i><u><publishItem></u></i> pipeline that will take care for this. When the item Test gets published, it will check any other item with same name exist on target database or not. If exist with different ItemId, then it will delete that old item.<br />
<br />
Create a <b>PublishItemProcessor</b> class as below.
<br />
<pre class="brush:csharp">namespace SitecoreTactics.Publishing
{
public class RemoveDuplicateItems : PublishItemProcessor
{
Item sourceItem = context.PublishHelper.GetSourceItem(context.ItemId);
if (sourceItem != null)
{
Item targetItem = context.PublishOptions.TargetDatabase.GetItem(sourceItem.Paths.Path);
if (targetItem != null && targetItem.ID != sourceItem.ID)
{
context.PublishHelper.DeleteTargetItem(targetItem.ID);
}
}
}
}
</pre>
Using a patch config, add this processor inside <publishitem> pipeline as below. You may need to change processor's order if you have any customizations done in the pipeline.<br />
</publishitem><br />
<pre class="brush:xml"><configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<publishItem>
<processor type="SitecoreTactics.Publishing.RemoveDuplicateItems, SitecoreTactics" />
</publishItem>
</pipelines>
</sitecore>
</configuration>
</pre>
Now repeat the same steps above, you will find older item gets deleted!</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-61595378727098644632015-08-06T05:01:00.001-04:002016-09-02T11:48:03.561-04:00Remove trailing slash from Sitecore URLs<div dir="ltr" style="text-align: left;" trbidi="on">
Our clients once reported that many pages of their site is appearing twice in Google Analytics reports like <a href="javascript:">http://mydomain.com/about-us</a> and <a href="javascript:">http://mydomain.com/about-us/</a>. If they have hundreds of pages in site then their report is going to be very time <i>consuming to collect unique pages and their count</i>. Even having duplicate URLs for a common page can <i>lower down the page rank while SEO indexing</i>.<br />
<br />
By default Sitecore (<span class="keyword">ItemManager.GetItemUrl(Item item)</span>) does not append slash at the end of auto-generated URL (for non .aspx URLs). So, if we use this API properly, no chances of getting duplicate URLs. But chances that developer or Content Author by mistake added a slash in URL or end-user intentionally added slash, then such URLs are surely going to be tracked in Analytic data.<br />
<br />
Earlier we thought to create a custom processor in httpRequestBegin pipeline. The same approach we found very well mentioned in <a href="https://aidandegraaf.wordpress.com/tag/sitecore-pipeline-processor-google-search-index-trailing-slash/" target="_blank">https://aidandegraaf.wordpress.com/tag/sitecore-pipeline-processor-google-search-index-trailing-slash/</a>.<br />
<br />
But we do not want to give extra load to Sitecore engine and yet this approach needs extra efforts of development & QA to make full justice to any URL. Later on, we learned <span class="keyword">IIS URLRewrite</span> can also serves the same purpose and thought to use it instead of our custom code as below.<br />
<h3>
Step 1: Open URL Rewrite Module</h3>
- Open IIS Manager.<br />
- Click your Website on left pane.<br />
- Click on "URL Rewrite" under IIS section as shown in below image.<br />
- If you cannot find it, you have to install URL Rewrite IIS module.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKAoQP8m9Bg6uO3vJRkyaG0oLpzoCJPsn4U2Yi7GWnTIEI9b14U1i8Yj3tXvf8AoaiPZuMECcKB4P3Kdap3hU8RjOGCxranxZz0Xk26ehc-HfaSpU9jGRNYR1jb7aQ1PIkCrcHE-UdC0A/s1600/iis-url-rewrite.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKAoQP8m9Bg6uO3vJRkyaG0oLpzoCJPsn4U2Yi7GWnTIEI9b14U1i8Yj3tXvf8AoaiPZuMECcKB4P3Kdap3hU8RjOGCxranxZz0Xk26ehc-HfaSpU9jGRNYR1jb7aQ1PIkCrcHE-UdC0A/s1600/iis-url-rewrite.png" /></a></div>
<h3>
Step 2: Add a "Append or remove the trailing slash symbol" Rule</h3>
- Click on "Add Rule(s)..."<br />
- Select "Append or remove the trailing slash symbol" from SEO section
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcyc_1YkzKwSDc2TS1mmiDJdQIF9AdR6Uv3gGPPvjmx4BJpix8bLbwMN8nEppfT0ODnrcjaz9o2yLseV-0If0Dr_YI1vgeVd3HlI8aPF7iXN7aLwoACLaJFt1Qjs42QMkJYvVuCWPno0g/s1600/create-slash-rule.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcyc_1YkzKwSDc2TS1mmiDJdQIF9AdR6Uv3gGPPvjmx4BJpix8bLbwMN8nEppfT0ODnrcjaz9o2yLseV-0If0Dr_YI1vgeVd3HlI8aPF7iXN7aLwoACLaJFt1Qjs42QMkJYvVuCWPno0g/s1600/create-slash-rule.png" /></a></div>
<h3>
Step 3: Set rule to "Remove trailing slash if exists"</h3>
- From the dropdown select "Removed if it exists" and click on OK.
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglL1BuBYj9_Qpj6Rk3Krw_UBJUH2TFY9Wf0FVHU8HSwPnqoneFGD8qq_eG7kFhEU0GbMaWTPEl2pV90JJTkTj5N3xesBUS3AooOpXY7DwHRUdABU_eu_zXVvxAtIxAmvWfeF_zRqgxLqo/s1600/remove-slash.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglL1BuBYj9_Qpj6Rk3Krw_UBJUH2TFY9Wf0FVHU8HSwPnqoneFGD8qq_eG7kFhEU0GbMaWTPEl2pV90JJTkTj5N3xesBUS3AooOpXY7DwHRUdABU_eu_zXVvxAtIxAmvWfeF_zRqgxLqo/s1600/remove-slash.png" /></a></div>
<br />
<h3>
Alternative of above step:</h3>
As an alternate of above steps, you can directly write below code in your Web.config file. (You must have URL Rewrite installed for this as well)<br />
<b>Note:</b> If you do first 3 steps from IIS Manager, ultimately IIS is going to write below code in your application's Web.config any how. So, both the steps are doing same thing.
<br />
<pre class="brush:xml"><rewrite>
<rules>
<rule name="myRemoveTrailingSlashRule" stopProcessing="true">
<match url="(.*)/$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}" />
</rule>
</rules>
</rewrite>
</pre>
<br />
That's it, we have a better way to provide unique URLs by removing trailing salsh to have better Site Analytics and SEO indexing!<br />
<br />
<div class="highlight">
Apart from this, URL Rewrite is very powerful module, which we can use for multipurpose like,<br />
- <a href="http://sitecoreblog.patelyogesh.in/2014/05/improve-sitecore-media-performance.html" target="_blank">Creating Reverse Proxy</a> using IIS.<br />
- <a href="http://www.iis.net/learn/extensions/configuring-application-request-routing-(arr)/http-load-balancing-using-application-request-routing" target="_blank">Creating Load Balanced Web Farm using IIS</a><br />
- <a href="http://www.iis.net/learn/extensions/url-rewrite-module/creating-rewrite-rules-for-the-url-rewrite-module" target="_blank">Other URL rewrites</a></div>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-54618282187257951262015-08-04T07:06:00.000-04:002016-09-02T11:49:01.227-04:00Our Learning on Publishing Sitecore Sublayouts<div dir="ltr" style="text-align: left;" trbidi="on">
After we started using <a href="http://sitecoreblog.patelyogesh.in/2015/07/sitecore-sublayout-publish-using-webdeploy.html" target="_blank">Web Deploy for publishing sublayouts</a> on our multisite environment, we were getting random caching issues on our CD servers. We have multiple publishing target databases and load balanced multiple CD servers. So, as I mentioned in my previous blog, when a sublayout is published, it is published with help of Web Deploy to one server and then the sublayout is replicated to all other servers using DFS.<br />
<h3>
What kind of issues we found?</h3>
Sometimes we found that the published sublayout gets reflected on few servers, on few servers we still get older content. But surprisingly, the published sublayout was replicated on all servers and still we were getting different output from different servers. So, when this happens, we used to clear caches on servers where such issues occurred assuming this might be because of Sitecore caching issues, later on we found workaround to publishing those sublayouts again after 1 or 2 minutes.<br />
<br />
Now you will understand how critical it would be to publish sublayouts and getting them reflected on live servers quickly to make go-lives, re-brandings, news releases or press releases successful in one go.<br />
<br />
<b>Note:</b> We found this only for those <b>Cacheable</b> sublayouts.<br />
<h3>
Our learnings to fix such issues</h3>
<h4>
1. Web Deploy publishing should be synchronous.</h4>
We analyzed below sequence happened rarely.<br />
<ol>
<li>First we started publishing, so as per my previous blog, Web Deploy will start deploying sublayout in async mode (<i>By default Web Deploy is configured on Sitecore is asynchronous</i>). So, sublayout item publishing and sublayout physical file deploying are done in parallel.</li>
<li>So, chances that item gets published before sublayout file is copied.</li>
<li>Now, item is published, so CD servers will invoke "publish:end:remote" event and clear HTML (sublayout) cache.</li>
<li>Now, before the new published sublayout file gets deployed to CD server, end-user requested a page which uses the same sublayout. So, the HTML cache will be generated again for older Sublayout.</li>
<li>Now, Web Deploy sent a new Sublayout. (So, we have HTML cache of older sublayout)</li>
</ol>
<br />
<b>Learning:</b> Sublayout publishing should always be synchronous, so publishing will get on hold till the sublayout is not deployed to the CD server, which can be configured in WebDeploy.config as below.<br />
<pre class="brush:xml"><event name="publish:begin">
<handler type="SitecoreTactics.Publishing.BeginWebDeploy, SitecoreTactics" method="PublishSublayouts">
<synchronous>true</synchronous>
<tasks hint="list:AddTask">
......
</tasks>
</handler>
</event>
</pre>
<h4>
2. Target Database should be the first publishing target.</h4>
We have 3 publishing targets say, <b>web</b>, <b>web-2</b>, <b>web-3</b>. Means, any publishing will be done in this sequence itself. Earlier we found the target database inside the WebDeploy.config was set as last publishing target, means <b>web-3</b>. We might have done this in past to make sure the item is published before sublayout getting deployed.<br />
<br />
But, as per our recent experiences and findings, we should keep it as first publishing target. So, in our case, it should be <b>web</b>. This will help us when there are multiple servers in DFS, so when DFS is taking some more time in replicating to other servers. So, for reducing such chances of delayed Sublayout deploy, we should keep web as the publishing target in WebDeploy.config as below.
<br />
<pre class="brush:xml"><tasks hint="list:AddTask">
<default type="Sitecore.Publishing.WebDeploy.Task">
<!-- It should be the first Publishing target database -->
<targetDatabase>web</targetDatabase>
</default>
</tasks>
</pre>
<h4>
3. We can make few seconds delay in clearing HTML Cache.</h4>
If we have done above cases, there are no chances that the CD server where Web Deploy is sending Sublayout file will get any issues. But just consider a worst case where DFS is taking more time to replicate sublayouts to other servers. So, chances that HTML Cache will get cleared before the sublayout replication is done.<br />
<br />
To avoid such cases, we can add a delay of few seconds, say 2 or 3 seconds before clearing HTML Cache (Only when the sublayouts are getting published)<br />
<br />
Finally, we have hassle free one-time Sublayout publishing working without any caching issues very well! I'm sure this will be helpful to others who are facing same kind of issues.<br />
<br /></div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-46925679846886156232015-07-31T04:57:00.002-04:002016-09-02T11:49:18.476-04:00Sitecore Publish Selected Sublayouts using WebDeploy<div dir="ltr" style="text-align: left;" trbidi="on">
On our Multisite Sitecore instance, we have thousands of sublayouts. So, content authors or developers should be able to modify and publish selected sublayouts. Means, only selected sublayout should be deployed to CD servers along with the items.<br />
<br />
We achieved this using Web Deploy, that can be configurable as guided is Sitecore Scalability Guide.<br />
<h3>
What approach we chose to conditionally sync Sublayouts?</h3>
<ol>
<li>On CM environment, we have all sublayouts stored in a folder <span class="keyword">SiteSublayouts</span>, now on publishing if sync this folder with CD server's relevant folder, then all sublayouts will get synced instead of just publishing the selected one. So, we applied an idea create another directory <span class="keyword">PublishedSublayouts</span> on same level.</li>
<li>So, on publishing a sublayout, it will be first copied from <span class="keyword">SiteSublayouts</span> to <span class="keyword">PublishedSublayouts</span> folder and then invoke WebDeploy. So,this will sync all sublayouts from <span class="keyword">PublishedSublayouts</span> (Actually published or publishable sublayouts) to live server's SiteSublayouts folder.</li>
</ol>
<div>
<b>Note:</b> Here, we created SiteSublayouts and PublishedSublayouts folders outside the Webroot to ease of use.
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJfCBqC5DPWwM9W7jr4dvNfrRqtcjYnYG2OwAXon2Ii4UbEG7TdF7zAydJHlfDDYjwCJfcn7xZ_zH_uSWIQvdusykSXhyKD8X0ia1eEWy3BM7LJqLikLwAd98GYDg5kFNkKxjk0THFSds/s1600/sublayouts-structure.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJfCBqC5DPWwM9W7jr4dvNfrRqtcjYnYG2OwAXon2Ii4UbEG7TdF7zAydJHlfDDYjwCJfcn7xZ_zH_uSWIQvdusykSXhyKD8X0ia1eEWy3BM7LJqLikLwAd98GYDg5kFNkKxjk0THFSds/s1600/sublayouts-structure.png" /></a>
</div>
<h3>
How we implemented this approach?</h3>
<ol>
<li>Configured WebDeploy settings in Sitecore. Enable App_Config\Include\Webdeploy.config file and do changes as below.
<pre class="brush:xml"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<sitecore>
<events>
<event name="publish:begin">
<handler type="SitecoreTactics.SublayoutPublish, SitecoreTactics" method="SublayoutPublish">
<tasks hint="list:AddTask">
<default type="Sitecore.Publishing.WebDeploy.Task">
<!-- Publishing to the target database will trigger this deployment task. -->
<!-- You should prefer to write here first publishing target (if have multiple target DBs) -->
<targetDatabase>web</targetDatabase>
<!-- Target server is where we want to send sublayouts. If omitted, operation is performed on the local server. -->
<targetServer>x.x.x.x</targetServer>
<!-- userName and password are optional. If omitted, local user identity or credentials saved in Windows Vault will be used to connect to the server. -->
<userName>Administrator</userName>
<password>Password</password>
<!-- localRoot is optional. If omitted, the website root is used. -->
<localRoot>E:\CMS\Sitecore\PublishedSublayouts</localRoot>
<!-- remoteRoot is physical path where sublayouts are stored on remote server -->
<remoteRoot>E:\CMS\Sitecore\SiteSublayouts</remoteRoot>
<!-- Paths, relative to the localRoot, which will be deployed to the remote location. -->
<items hint="list:AddPath">
<media>SiteSublayouts/</media>
</items>
</default>
</tasks>
</handler>
</event>
</events>
</sitecore>
</configuration>
</pre>
Here, we synced the <span class="keyword">PublishedSublayouts</span> folder of CM server with relevant <span class="keyword">SiteSublayouts</span> folder of CD server using Web Deploy. And we customized Sitecore's default WebDeploy handler for copying sublayouts mentioned in step 2. </li>
<li>Create a class as below by inheriting with Sitecore.Publishing.WebDeploy.PublishHandler as below. Here, when any sublayout is started publishing, on <b>begin:publish</b> event we defined in step 1, we copy the sublayout from <i>SiteSublayouts </i>folder to <span class="keyword">PublishedSublayouts</span> folder and invoke WebDeploy as below code.</li>
<li><pre class="brush:csharp" name="code">namespace SitecoreTactics
{
public class SublayoutPublish : Sitecore.Publishing.WebDeploy.PublishHandler
{
string SourceFolder = "E:\CMS\Sitecore\SiteSublayouts";
string DeployFolder = "E:\CMS\Sitecore\PublishedSublayouts";
protected void DeploySublayout(object Sender, EventArgs args)
{
Item RootItem = ((Sitecore.Publishing.Publisher)(((Sitecore.Events.SitecoreEventArgs)(args)).Parameters[0])).Options.RootItem;
if (RootItem.Paths.Path.ToLower().IndexOf("/sitecore/layout/sublayouts/") >= 0)
{
string sublayoutSourceFolder = SourceFolder + <Relative Path of the sublayout>;
string sublayoutDeployFolder = DeployFolder + <Relative Path of the sublayout>;
// Copy publishing sublayout to Deployable folder
File.Copy(sublayoutSourceFolder, sublayoutDeployFolder);
// Invoke WebDeploy to sync published sublayouts
base.OnPublish(Sender, args);
}
}
}
}</pre>
</li>
<li>
We have multiple CM servers, so we replicated all these published sublayouts with help of DFS across all servers.</li>
</ol>
It's done! We can also use the same approach for publishing file based media items.<br />
<br />
Very soon I will post few leanings we had after implementing sublayouts publishing!<br />
<br /></div>Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-73500785996627366782015-04-10T06:45:00.000-04:002016-09-02T11:49:33.986-04:00Sitecore Lock / Unlock Item without modifying Statistics<div dir="ltr" style="text-align: left;" trbidi="on">
Sitecore updates the item statistics (<b>Updated</b> and <b>Updated by</b> fields) on each lock or unlock operation on an item. Sometimes this is misleading for content authors. <br/>
<h3>When this is misleading</h3>
<ul>
<li>When a user is just locking an item and not done any modifications in it but this creates dilemma for other users that the user already had some changes on that item or not. In our multisite environment where hundreds of users work on a single Sitecore instance, this confusion occurs frequently.</li>
<li>Even some content authors demand to auto-unlock their items after publishing without updating modified Date time.</li>
</ul>
<h3>How we can achieve this?</h3>
We will see how we can lock or unlock items without modifying item statistics.<br/>
<h4>Approach#1</h4>
The best way to achieve it is using Item's <b>RuntimeSettings </b>itself.
<pre class="brush:csharp">
public void LockItem(Item item)
{
if (!item.Locking.IsLocked())
{
item.RuntimeSettings.ReadOnlyStatistics = true;
item.Locking.Lock();
item.RuntimeSettings.ReadOnlyStatistics = false;
}
}
public void UnlockItem(Item item)
{
if (item.Locking.IsLocked())
{
item.RuntimeSettings.ReadOnlyStatistics = true;
item.Locking.Unlock();
item.RuntimeSettings.ReadOnlyStatistics = false;
}
}
</pre>
Here, <i>item.RuntimeSettings.ReadOnlyStatistics = true;</i> will not update statistics for current context of item. So, while locking or unlocking, it will not update statistics.
<h4>Approach#2</h4>
Another simple way is to update the <b>__lock</b> field though APIs with updateStatistics to set as false as below. I personally do <b>not recommend</b> this approach, but still works great in many cases where above approach does not work.<br />
<pre class="brush:csharp">
public void LockItem(Item item)
{
if (!item.Locking.IsLocked())
{
using (new EditContext(item, false, false))
{
item["__lock"] = "<r owner=\"" + Context.User.Name + "\" date=\"" + DateTime.Now.ToString("yyyyMMddTHHmmss") + "\" />";
}
}
}
public void UnlockItem(Item item)
{
if (item.Locking.IsLocked())
{
using (new EditContext(item, false, false))
{
item["__lock"] = "<r />";
}
}
}
</pre>
Happy to see it works and happy to see our content authors happy!
</div>Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-29880186641868801332015-04-08T10:52:00.000-04:002016-09-02T11:50:22.972-04:00Get Optimized Images For Sitecore Responsive Websites<div dir="ltr" style="text-align: left;" trbidi="on">
If you are having Responsive or Adaptive websites built in Sitecore and using <a href="http://sitecoreblog.patelyogesh.in/2013/06/sitecore-image-control-parameters.html" target="_blank">Sitecore Image Parameters</a> to resize images on-the-fly, this post is helpful to you!<br />
<br />
Recently while working for responsive websites, we found that while resizing image with less dimensions, Sitecore produces image with more file size than its original one. It's really not expected because it also increases page load time.<br />
<h3>
Example</h3>
Below is the image I found from Sitecore Website's Homepage, which is having dimensions of <b>660px × 441px</b> and having size of <b>48.45 kB</b> (49,614 bytes). Image: <a href="http://dijaxps1e29ue.cloudfront.net/~/media/Redesign/Common/Heros/600x441/Homepage_hero_600x441.ashx?ts=121514021455931&la=en">http://dijaxps1e29ue.cloudfront.net/~/media/Redesign/Common/Heros/600x441/Homepage_hero_600x441.ashx?ts=121514021455931&la=en</a><br />
<br />
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYbSs7SF4KL0WUf-yf_9i6M5AS1yHYcvu3stxvpvTpaFOXG60iQlBxwYW95fCIihPH17R3aCsAkADEym3lwbOvtG1cFF5oMhlvlbgajYTUC2IzpkdOcwLOEYD-wvwtIvp-eAWoHvPWfs8/s1600/sitecore-default-image.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="544" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYbSs7SF4KL0WUf-yf_9i6M5AS1yHYcvu3stxvpvTpaFOXG60iQlBxwYW95fCIihPH17R3aCsAkADEym3lwbOvtG1cFF5oMhlvlbgajYTUC2IzpkdOcwLOEYD-wvwtIvp-eAWoHvPWfs8/s1600/sitecore-default-image.png" width="640" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Now if I request Sitecore to produce image with less resolution. i.e., with width of 600px. So, it generates an image of dimensions of <b>600px × 401px</b>
and having size of <b>57.4 kB</b> (58,778 bytes). Image: <a href="http://dijaxps1e29ue.cloudfront.net/~/media/Redesign/Common/Heros/600x441/Homepage_hero_600x441.ashx?ts=121514021455931&la=en&w=600">http://dijaxps1e29ue.cloudfront.net/~/media/Redesign/Common/Heros/600x441/Homepage_hero_600x441.ashx?ts=121514021455931&la=en&w=600</a><br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEy0Wk-o0PkrQ73RZ6y2aMgntp73hOxOyeqdm8Um5nb3IfJ-zYh7ZeTHpvKWlPGsFzy1Oq4Fb5P8gzQluzajWUtZxKknn1oyHIAR7IKr1U7uTGvlix-mZNoK_NK8rrqRDtN9wITQdyYYY/s1600/sitecore-image-resized-with-less-dimensions.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="510" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEy0Wk-o0PkrQ73RZ6y2aMgntp73hOxOyeqdm8Um5nb3IfJ-zYh7ZeTHpvKWlPGsFzy1Oq4Fb5P8gzQluzajWUtZxKknn1oyHIAR7IKr1U7uTGvlix-mZNoK_NK8rrqRDtN9wITQdyYYY/s1600/sitecore-image-resized-with-less-dimensions.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Is it a bug from Sitecore?</h3>
No. But Sitecore by default uses "Lossy Compression Algorithm" to resize images, so reducing image dimensions will not reduce file size. Also, Sitecore uses 95% of image quality by default, that will generate image with bigger file size. To know more about it, you can check code from <b>Sitecore.Resources.Media.ImageEffectsResize</b> class <b>ResizeImageStream()</b> function.
<br />
<pre class="brush:xml"><setting name="Media.UseLegacyResizing" value="false" />
<setting name="Media.Resizing.Quality" value="95" />
</pre>
Reducing above quality setting may give us image with less file size but should we compromize with quality of image?
<br />
<h3>
Then how to solve this?</h3>
There is an alternate way Sitecore gives which was the default behavior in Sitecore in earlier versions that is by enabling Sitecore's ImageLegacyResizing. You can know more about it from <b>Sitecore.Resources.Media.ImageEffectsResize</b> class <b>ResizeLegacy()</b> function. You can do below settings for getting reduced file size. Thank to Sitecore Support guy (Paul Kravchenko) for guiding me in this direction.<br />
<pre class="brush:xml"><setting name="Media.UseLegacyResizing" value="true" />
<setting name="Media.InterpolationMode" value="Low" />
</pre>
<br />
<div class="highlight">
<b>Media.UseLegacyResizing</b><br />
This setting controls whether to use legacy resizing (ie. bypass the Sitecore.ImageLib library).<br />
<br />
<u>Possible values can be:</u><br />
true<br />
<i>false (Sitecore's default value)
</i><br />
<i><br /></i></div>
<br />
<div class="highlight">
<b>Media.InterpolationMode</b><br />
The interpolation mode to use when resizing images, which are available on System.Drawing.Drawing2D.InterpolationMode enum. Read more about <a href="https://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.interpolationmode%28v=vs.110%29.aspx" target="_blank">InterpolationMode</a>. We can any of below values as per our need.<br />
<br />
<u>Possible values can be:</u><br />
Bicubic<br />
Bilinear<br />
Default<br />
<i>High (Sitecore's default value)</i><br />
HighQualityBicubic<br />
HighQualityBilinear<br />
Low<br />
NearestNeighbor
<br />
<br /></div>
Again, Sitecore defines these settings in configuration file so values of the setting remains same for each image resize, so not that much useful to get a generic solution. Eager to know if someone has such generic solution to resize any kind of image, by maintaining quality with reduced file size what Photoshop or Paint.Net gives.</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com5tag:blogger.com,1999:blog-5346027972748235946.post-76433997606589858152015-01-28T10:40:00.000-05:002015-01-28T10:57:24.456-05:00Sitecore MVP 2015 - Achievement Unlocked Once Again!<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA9tgtKM8rWlqvDK8vnNTRIisXGogP_DiDvO8aWb6tWJps_qN6ac-sHtqgjEG-594ezokEUjInzCbGxyFvHzEcUmWlUIgGhJoPTQEe338VsofLayl7yQRPHaa9278M22M2WbcS-4PDqLU/s1600/Yogesh-patel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA9tgtKM8rWlqvDK8vnNTRIisXGogP_DiDvO8aWb6tWJps_qN6ac-sHtqgjEG-594ezokEUjInzCbGxyFvHzEcUmWlUIgGhJoPTQEe338VsofLayl7yQRPHaa9278M22M2WbcS-4PDqLU/s1600/Yogesh-patel.png" height="77" width="400" /></a></div>
<br />
Feeling proud that I am honored to be selected as <b>"Technology Most Valuable Professional (MVP)"</b> once again by Sitecore. Here is the list of of all Sitecore Technology MVPs 2015: <a href="http://www.sitecore.net/Events/Public-MVP-site/MVPs-2015/Technology.aspx">http://www.sitecore.net/Events/Public-MVP-site/MVPs-2015/Technology.aspx</a><br />
<br />
I want to thank everyone who frequented my blogs, my family, coworkers, Sitecore community, my employer - Investis and yes Sitecore!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-AEUt6wyf9SQNtE9AhVWKfKKWtcWeTiNqzhDi0dM3NvFThxGOas7NatOI8yoxokvNRGMrE2aFI2gfdz8c5WAJAsSmGpV__M3USmNzV4TWsbZmqSj4aFAxWcGiCb5ZgP1NU4qxUafE4UE/s1600/tech-750.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-AEUt6wyf9SQNtE9AhVWKfKKWtcWeTiNqzhDi0dM3NvFThxGOas7NatOI8yoxokvNRGMrE2aFI2gfdz8c5WAJAsSmGpV__M3USmNzV4TWsbZmqSj4aFAxWcGiCb5ZgP1NU4qxUafE4UE/s1600/tech-750.jpg" height="195" width="200" /></a></div>
<br /></div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-83514733598990367392014-10-21T08:48:00.004-04:002016-09-02T11:51:15.867-04:00Sitecore Multilingual Item Alias<div dir="ltr" style="text-align: left;" trbidi="on">
Sitecore by default provides Item Aliases for single language, refer my earlier post regarding <a href="http://sitecoreblog.patelyogesh.in/2013/08/sitecore-item-alias-alternate-url.html" target="_blank">Sitecore Aliases</a>. One good request came to Sitecore SDN Forums for achieving aliases for multiple languages in Sitecore so thought to post about it.<br />
<br />
What we want to achieve is, when a request come for an alias, the content of the Linked Item should come with selected language.<br />
<br />
<h3>
How to achieve?</h3>
<ol>
<li>In the <b>Alias</b> template (/sitecore/templates/System/Alias), add a new field <b>Linked Language</b> with <b>DropLink</b> field. <br />So, when a request comes like <a href="http://domain/idioma/">http://domain/idioma/</a>, it should serve the <b>Products</b> page with <b>es-ES</b> language.<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWEgpCB4yHg-lJ3xUNdRE0vDRRM_be68Ym3VG0F6Jfg6d1zj_lXYIE27ImPYpw1g9VQ8nOKx_phwyxcaayMD0yV0e6S6r9WXME81mpWaYybBliFru1WiHNBFIf-8104R8WTKdwseEGCnE/s1600/Linked-Language.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWEgpCB4yHg-lJ3xUNdRE0vDRRM_be68Ym3VG0F6Jfg6d1zj_lXYIE27ImPYpw1g9VQ8nOKx_phwyxcaayMD0yV0e6S6r9WXME81mpWaYybBliFru1WiHNBFIf-8104R8WTKdwseEGCnE/s1600/Linked-Language.png" /></a></div>
<br />
</li>
<li>Override the <b>AliasResolver</b> like below:<br />
<pre class="brush:csharp">namespace SitecoreTactics.HttpRequestPipeline
{
public class MultilingualAliasResolver : HttpRequestProcessor
{
public override void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull((object)args, "args");
if (!Settings.AliasesActive)
{
Tracer.Warning((object)"Aliases are not active.");
}
else
{
Database database = Context.Database;
if (database == null)
{
Tracer.Warning((object)"There is no context database in AliasResover.");
}
else
{
Profiler.StartOperation("Resolve alias.");
if (database.Aliases.Exists(args.LocalPath) && !this.ProcessItem(args))
this.ProcessExternalUrl(args);
Profiler.EndOperation();
}
}
}
private void ProcessExternalUrl(HttpRequestArgs args)
{
string targetUrl = Context.Database.Aliases.GetTargetUrl(args.LocalPath);
if (targetUrl.Length <= 0)
return;
this.ProcessExternalUrl(targetUrl);
}
private void ProcessExternalUrl(string path)
{
if (Context.Page.FilePath.Length > 0)
return;
Context.Page.FilePath = path;
}
private bool ProcessItem(HttpRequestArgs args)
{
bool aliasFound = false;
string alias = args.LocalPath;
if (alias.Length > 0)
{
Item obj = ItemManager.GetItem(FileUtil.MakePath("/sitecore/system/aliases", alias, '/'), Language.Invariant,
Version.First, Sitecore.Context.Database, SecurityCheck.Disable);
if (obj != null)
{
LinkField itemField = (LinkField)obj.Fields["linked item"];
string language = obj["linked language"];
if (!string.IsNullOrEmpty(language) && itemField!= null)
{
Item langItem = Sitecore.Context.Database.GetItem(new ID(language));
if (langItem != null)
{
Language lang = Language.Parse(langItem.Name);
Item item = Sitecore.Context.Database.GetItem(itemField.TargetID, lang);
if (item != null)
{
this.ProcessItem(args, item);
aliasFound = true;
}
}
}
else if (itemField!=null)
{
Item item = Sitecore.Context.Database.GetItem(itemField.TargetID);
if (item != null)
{
this.ProcessItem(args, item);
aliasFound = true;
}
}
}
}
return aliasFound;
}
private void ProcessItem(HttpRequestArgs args, Item target)
{
if (Context.Item != null)
return;
Context.Item = target;
Context.Language = target.Language;
}
}
}
</pre>
</li>
<li>In Web.config, replace the Sitecore's AliasResolver and add our custom overridden MultilingualAliasResolver as below:
<pre class="brush:xml"><!--<processor type="Sitecore.Pipelines.HttpRequest.AliasResolver, Sitecore.Kernel" />-->
<processor type="SitecoreTactics.HttpRequestPipeline.MultilingualAliasResolver, SitecoreTactics" />
</pre>
</li>
</ol>
<br />
Finally we are able to use multilingual aliases.
<br />
<br />
<b>Note:</b>Above code works only for Linked Items which are Sitecore Internal Links.
<br />
<br /></div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com2tag:blogger.com,1999:blog-5346027972748235946.post-81938181358105814052014-10-21T03:47:00.000-04:002016-09-02T11:51:39.225-04:00Browser Language Based Content Negotiation<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
Are you willing to change sitecore website content language based on user's browser preferences? We can achieve this using Content negotiation.
<b>Content negotiation</b> is a mechanism to serve different versions of a page or document based on browser's preferences.<br />
<br /></div>
For example, a user wants to see information in <b>German</b>, if possible, else <b>English</b> will do. Browsers indicate their preferences by headers in the request. To request only German representations, the browser would send below headers:<br />
<br />
<div class="highlight2">
Accept-Language: de
</div>
<br />
As an example of a more complex request, this browser has been configured to accept German and English, but prefer German. In such situation, browser would send below headers:<br />
<br />
<div class="highlight2">
Accept-Language: de; q=1.0, en; q=0.5
</div>
<br />
<br />
<b>Users can set these preferences in the browser itself. Below is given example of Firefox.</b>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz-h18IiAKrjiCNta6iVwvyWA6YfrILLa-C6A8NnYcL5EA7Gz7fqh2sWdo9DMOnyni-smJToPyOZZ2XBqgxramkM5wFmtRHbN5iQKARgxIUEiv67ffFFL6xOFI0pfdrFsZQreBQ08fvT4/s1600/content-negotiation.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz-h18IiAKrjiCNta6iVwvyWA6YfrILLa-C6A8NnYcL5EA7Gz7fqh2sWdo9DMOnyni-smJToPyOZZ2XBqgxramkM5wFmtRHbN5iQKARgxIUEiv67ffFFL6xOFI0pfdrFsZQreBQ08fvT4/s640/content-negotiation.png" /></a></div>
<br />
<h3>
How Browser Based Language Content Negotiation will work</h3>
Sitecore provides LanguageResolver, which first checks that the Http Request contains language in Query String (using sc_lang parameter) or in File Path. If the language is found, it will use that language as a context language, otherwise will consider site's default language as the context language.
<br />
<br />
To achieve our requirement, if the language is not found in the Query String or File Path, we will add one more check to see Language in User Languages in Request Header (Or Accept-Language request header). We can get multiple languages in the request header. So, we will give first priority to the language which is available to serve from our site.
<br />
<br />
We can get the browser's preferred languages using below code in .NET.
<br />
<br />
<div class="highlight2">
string[] languages = HttpContext.Current.Request.UserLanguages;
</div>
<br />
<h3>
Let's see how to achieve in Sitecore</h3>
</div>
<h4>
Step 1: Override LanguageResolver</h4>
Do below code to override Sitecore's default LanguageResolver:<br />
<pre class="brush:csharp">namespace SitecoreTactics.HttpRequestPipeline
{
public class LanguageResolver : Sitecore.Pipelines.HttpRequest.LanguageResolver
{
public override void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull((object)args, "args");
Language languageFromRequest = this.GetLanguageFromRequest(args.Context.Request);
if (languageFromRequest == (Language)null)
return;
Context.Language = languageFromRequest;
}
private Language ExtractLanguageFromQueryString(HttpRequest request)
{
string name = request.QueryString["sc_lang"];
if (string.IsNullOrEmpty(name))
return null;
Language result;
if (!Language.TryParse(name, out result))
return null;
else
return result;
}
private Language GetLanguageFromRequest(HttpRequest request)
{
Language languageFromQueryString = this.ExtractLanguageFromQueryString(request);
if (languageFromQueryString != null)
return languageFromQueryString;
else if (Context.Data.FilePathLanguage != null)
return Context.Data.FilePathLanguage;
else
{
return GetLanguageFromBrowser(request);
}
}
// Added function to allow browser based language content negotiation
private Language GetLanguageFromBrowser(HttpRequest request)
{
Language result;
if (HttpContext.Current.Request.UserLanguages!= null && HttpContext.Current.Request.UserLanguages.Length > 0)
{
string[] languages = HttpContext.Current.Request.UserLanguages;
string chosenLanguage = string.Empty;
foreach (string language in languages)
{
string[] langParts = language.Split(';');
if (langParts.Length > 0)
{
if (Sitecore.Context.Database.GetItem("/sitecore/system/languages/" + langParts[0]) != null)
{
chosenLanguage = langParts[0];
break;
}
}
}
if (!Language.TryParse(chosenLanguage, out result))
return (Language)null;
else
return result;
}
else
return (Language)null;
}
}
}
</pre>
<h4>
Step 2: Configure overridden LanguageResolver in web.config</h4>
In Web.config, replace the Sitecore's LanguageResolver and add our custom overridden LanguageResolver as below:
<br />
<pre class="brush:xml"><httpRequestBegin>
<!--<processor type="Sitecore.Pipelines.HttpRequest.LanguageResolver, Sitecore.Kernel" />-->
<processor type="SitecoreTactics.HttpRequestPipeline.LanguageResolver, SitecoreTactics" />
</httpRequestBegin>
</pre>
<br />
<h3>
There's more</h3>
Same way, we can use many other request parameters to negotiate user experience like.<br />
<ul style="text-align: left;">
<li><b>User-Agent: </b> User agent of the browser, which Sitecore uses in DeviceResolver </li>
<li><b>Accept: </b> Content types the browser prefers, which we can use to provide preferable content types of media or content.</li>
<li><b>Accept-DateTime: </b> It is a header to retrieve content of any specific date-time.</li>
</ul>
<br />
<b>We can use all these locale settings to enable them to provide information in the local format.</b>
<br />
<ul style="text-align: left;">
<li> What numeric formats does the user expect?</li>
<li>How should dates and times be formatted?</li>
<li>Should measurements be metric (centimeters, kilometers, liters) or imperial (inches, miles, gallons)?</li>
<li>What is the user's time zone?</li>
<li>Does the user use Letter size paper, or A4?</li>
<li>What shoe and clothing sizing systems should be used?</li>
<li>What is the user's physical location?</li>
</ul>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-89211953227207586072014-08-09T02:37:00.000-04:002016-09-02T11:52:02.824-04:00Dot or Space in URL gives 404 Error - Fixed<div dir="ltr" style="text-align: left;" trbidi="on">
Our application security certification program raised a security issue on our Sitecore Staging and Live environment.
"Any URL where folder or file name ends with Space or Dot gives <b>404 - The resource cannot be found</b> error."
<br />
<br />
For example, accessing URLs like<br />
- <a href="https://www.blogger.com/blogger.g?blogID=5346027972748235946#">http://sitecore72/sitecore%20/login</a><br />
or<br />
- <a href="https://www.blogger.com/blogger.g?blogID=5346027972748235946#">http://sitecore72/sitecore./login</a><br />
<br />
Both gives below error. Same, if we do same kind of request on any live site, it gives same error like for URL: <a href="https://www.blogger.com/blogger.g?blogID=5346027972748235946#" rel="nofollow" target="_blank">http://sitecore/CMS /Sitecore/</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKB6nRSf09dHbs34DK-sPHNSU8DWjIcQQywNs4gHPxDUmhJ9R8V5DDr05Ve5D7YEiXOyUZYHdTISET5EtfNHUasaB3yPPZW7HOxP4ok_TTqLC1a54x0Vu3zEnbz_oqeOceNhIR_Q7tPFY/s1600/folder-ends-with-space-error.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="276" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKB6nRSf09dHbs34DK-sPHNSU8DWjIcQQywNs4gHPxDUmhJ9R8V5DDr05Ve5D7YEiXOyUZYHdTISET5EtfNHUasaB3yPPZW7HOxP4ok_TTqLC1a54x0Vu3zEnbz_oqeOceNhIR_Q7tPFY/s1600/folder-ends-with-space-error.png" width="640" /></a></div>
<h3>
Tricks we already applied, what failed:</h3>
<ul style="text-align: left;">
<li>Tried to catch this error from Application_Error event from Global.asax, but no luck found. </li>
<li>We also tried to handle it from web.config using CustomErrors, but again no luck. </li>
</ul>
<h3>
Solution we found at last:</h3>
After checking many ASP.NET sites, including Microsoft website, this issue was found on majority of sites. For example, <a href="http://www.microsoft.com/security%20/default.aspx" rel="nofollow" target="_blank">http://www.microsoft.com/security /default.aspx</a>, it gives same error. So finally we concluded that this bug is not from Sitecore side, but it's something what .NET is playing with.<br />
<br />
After reading more on net, found below setting. The value of <b>relaxedUrlToFileSystemMapping</b> attribute should be <b>true </b>to solve this issue, by default its value is false in ASP.NET and Sitecore:<br />
<br />
<pre class="brush:xml"> <httpruntime relaxedurltofilesystemmapping="true" />
</pre>
Now, after applying this fix, the above URL on Staging environment sends user to NotFound error page and on live environment, it opens a valid page by ignoring Dot or Space.
<br />
<h3>
What relaxedUrlToFileSystemMapping attribute does?</h3>
It indicates whether the URL in an HTTP request is required to be a valid Windows file path. It determines how the URL in an incoming HTTP request will be validated.<br />
<br />
If this property is <b>false</b>, the URL is validated by using the same rules that determine whether a Windows file system path is valid.<br />
If it is <b>true</b>, it will not validate any folder or file name.<br />
<br />
<br />
<b>Note: </b>Later on we found that Sitecore has solved this bug from Sitecore 7.2 Update-2 by changing same attribute, they have not mentioned this bugfix in their release notes.<br />
<br />
<br /></div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-39392903950944318492014-07-18T14:16:00.000-04:002016-09-02T11:53:16.019-04:00Solved Sitecore PDF Loading Issue<div dir="ltr" style="text-align: left;" trbidi="on">
Are getting problem in viewing PDF files in your newly upgraded Sitecore environment? We came across the same bug while upgrading Sitecore to version 7.2 that in some browsers while opening PDF files, they get stuck. Surprisingly after that, pressing <b>Ctrl+F5</b> was allowing to view PDF file. See below screenshot when the downloading stuck:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5tRgc-vjL01UVwwWu7tgBKsAYIKKf8eXEauGcaLv4ooHG2Xx2aBSImdS10eYVMReDNkUrknfkWuK040nxwkEvrfxVuLP8t1Do4qqGD1FtuS_FF9AnQEI5m3vddjDL0uFLndRJNugXPgk/s1600/sitecore-pdf-download-stuck.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Sitecore PDF viewing stuck randomly" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5tRgc-vjL01UVwwWu7tgBKsAYIKKf8eXEauGcaLv4ooHG2Xx2aBSImdS10eYVMReDNkUrknfkWuK040nxwkEvrfxVuLP8t1Do4qqGD1FtuS_FF9AnQEI5m3vddjDL0uFLndRJNugXPgk/s1600/sitecore-pdf-download-stuck.png" title="Sitecore PDF viewing stuck randomly" /></a></div>
<br />
<h3>
Workaround from Sitecore</h3>
In Sitecore 7.2, they gave workaround to solve the issue by forcing download of PDFs by doing below setting:
<br />
<pre class="brush:xml"> <mediaType name="PDF file" extensions="pdf">
<mimeType>application/pdf</mimeType>
<forceDownload>true</forceDownload>
</mediaType>
</pre>
But, this is not the solution for our problem. We can't say our clients to download and view the PDF, it should be viewed in browser itself. We applied many tricks, and finally we solved it.
<br />
<h3>
So, what was the issue?</h3>
After reverse engineering <b>MediaRequestHandler</b>, we came to know that Sitecore is downloading PDF content partially in range using <b>HTTP 206 Status Code</b>, which might be creating problems in different browser. In Sitecore versions earlier than before 6.5, Sitecore was downloading PDF or any other media in one go, which never failed.<br />
<br />
So, one thing was sure that this bug was just because of Range Retrieval mechanism provided by Sitecore or the plug-in our browser does not support it. See below screenshot which shows how Sitecore downloads PDF content in range.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnUvehXFa7gloZyat8eUwOYXF1EF_CzD8qCEcpqPaOjNy3Z1tbFRcxq4ya1wC5tUQG-wJmqwo9qL6hNuMtv_rgEuNpp6-PaRc0M8K_WxJPIk-y1MKMrUWNyzkoCLMvdUpK5vAxCcBd7lU/s1600/sitecore-pdf-download-watch-httpfox.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Sitecore downloads PDF content partially in range" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnUvehXFa7gloZyat8eUwOYXF1EF_CzD8qCEcpqPaOjNy3Z1tbFRcxq4ya1wC5tUQG-wJmqwo9qL6hNuMtv_rgEuNpp6-PaRc0M8K_WxJPIk-y1MKMrUWNyzkoCLMvdUpK5vAxCcBd7lU/s1600/sitecore-pdf-download-watch-httpfox.png" title="Sitecore downloads PDF content partially in range" /></a><br />
<h3>
Final Solution</h3>
We just tried disabling the Range Retrieval Mechanism as below and found the bug resolved.
<br />
<pre class="brush:xml"> <setting name="Media.EnableRangeRetrievalRequest" value="false" />
</pre>
Many thanks to our colleague <b>Sandeep Gupta</b> who helped in the investigation.<br />
<br />
Enjoy accurate media download now!!<br />
<h3>
Other Sitecore 7.2 Bugs Solved!</h3>
<ul>
<li>
<a href="http://sitecoreblog.patelyogesh.in/2014/07/sitecore-72-bug-item-reference-publish.html">
Nasty Bug Found in Sitecore 7.2 Publish Related Items
</a>
</li>
<li>
<a href="http://sitecoreblog.patelyogesh.in/2014/07/sitecore-72-attach-detach-are-slower.html">
Sitecore Media Attach Detach Getting Slower
</a>
</li>
</ul>
<br />
<br /></div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com1tag:blogger.com,1999:blog-5346027972748235946.post-57137795653220632842014-07-15T12:11:00.000-04:002016-09-02T11:53:34.044-04:00Sitecore Bug - Media Attach Detach Getting Slower<div dir="ltr" style="text-align: left;" trbidi="on">
While upgrading to Sitecore 7.2, we faced another big bug regarding slowness. When we attach or detach media file or edit image file online, we found heavy load and slowness on DB server which subsides subsequently, but the browser becomes unresponsive during this time. And slowness is observed across CM platform. (We have not checked but this issue is found from Sitecore 6.5 onwards)
<br />
<br />
We have <b>New Relic</b> configured on CM server, which shows slow transactions. The stacktrace in all cases has same portion mentioned below:
<br />
<pre class="brush:csharp" name="code">Sitecore.Data.DataProviders.Sql.SqlDataApi.CreateReader(:0)
Sitecore.Data.DataProviders.Sql.SqlDataProvider.Exists(:0)
Sitecore.Data.DataProviders.Sql.SqlDataProvider.CheckIfBlobShouldBeDeleted(:0)
Sitecore.Data.DataProviders.Sql.SqlDataProvider.RemoveOldBlobs(:0)
Sitecore.Data.DataProviders.Sql.SqlDataProvider.SaveItem(:0)
</pre>
Alongwith above stacktrace, we also found following as slowest SQL operation which is implemented in <b>CheckIfBlobShouldBeDeleted </b>method, which tells whether blob is used or not:
<br />
<pre class="brush:sql" name="code">SELECT tmp.[Id] FROM
(SELECT sf.[Id] FROM [SharedFields] sf
WHERE sf.[Value] LIKE @blobId
UNION
SELECT uf.[Id] FROM [UnversionedFields] uf
WHERE uf.[Value] LIKE @blobId
UNION
SELECT vf.[Id] FROM [VersionedFields] vf
WHERE vf.[Value] LIKE @blobId
UNION
SELECT af.[FieldId] [Id] FROM [ArchivedFields] af
WHERE af.[Value] LIKE @blobId
) tmp
</pre>
Credit goes to <b>Muktesh Mehta</b> for drilling down the issue and <b>Sergey Kravchenko</b> from Sitecore Support who gave us solution for it.
<br />
<br />
We raised this to Sitecore with all above information, Sitecore provided a new config file and assembly for overriding SqlServerDataProvider, which solved our issue. You can get the assembly and config from Sitecore Support using <b>Ticket# 412563</b>.
<br />
<br />
Hope, this will help you if you are facing the same issue!
<br />
<br />
<b>Media Performance Related Posts:</b><br />
- <a href="http://sitecoreblog.patelyogesh.in/2014/05/sitecore-media-slowness-irequiressessionstate.html">IRequiresSessionState slowing down Sitecore Media Requests</a><br />
- <a href="http://sitecoreblog.patelyogesh.in/2014/05/improve-sitecore-media-performance.html">Improve Sitecore Media Performance using Reverse Proxy</a><br />
- <a href="http://sitecoreblog.patelyogesh.in/2013/09/sitecore-upload-media-selected-language.html">Upload Sitecore media items in selected languages</a>
<br/>
<h3>Other Sitecore 7.2 Bugs Solved!</h3>
<ul>
<li>
<a href="http://sitecoreblog.patelyogesh.in/2014/07/sitecore-72-bug-item-reference-publish.html">
Nasty Bug Found in Sitecore 7.2 Publish Related Items
</a>
</li>
<li>
<a href="http://sitecoreblog.patelyogesh.in/2014/07/solved-sitecore-pdf-loading-issue.html">
Solved Sitecore PDF Loading Issue
</a>
</li>
</ul>
<br/>
<br/>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-37647834672092329792014-07-05T09:08:00.002-04:002016-09-02T11:56:22.484-04:00Bug Fix in Sitecore 7.2 Publish Related Items<div dir="ltr" style="text-align: left;" trbidi="on">
While testing our newly upgraded Sitecore 7.2 solution, we faced a nasty bug in newly released feature of <b>Publish Related Items</b><br>
<br>
When we ticked <b>Publish Related Items</b> checkbox to publish all references, it publishes all the reference items three times. You are not believing, right? Even we too when our QA raised this to us. Such a nasty bug this is!!
<br>
<br>
If you have enabled traceToLog like below for UpdateStatistics processor,
<br>
<pre class="brush:xml" name="code"> <processor type="Sitecore.Publishing.Pipelines.PublishItem.UpdateStatistics, Sitecore.Kernel" runIfAborted="true">
<traceToLog>true</traceToLog>
</processor>
</pre>
you will find below logs which tells the story of the bug that item named Images gets published three times.
<br>
<pre class="brush:csharp" name="code">17268 16:13:23 INFO ##Publish Item: Name=Images, Uri=sitecore://master/{15451229-7534-44EF-815D-D93D6170BFCB}?lang=en&ver=1, Operation=Updated, ChildAction=Allow, Explanation=Version 'en_1' was published.
17268 16:13:23 INFO ##Publish Item: Name=Images, Uri=sitecore://master/{15451229-7534-44EF-815D-D93D6170BFCB}?lang=en&ver=1, Operation=Updated, ChildAction=Allow, Explanation=Version 'en_1' was published.
17268 16:13:23 INFO ##Publish Item: Name=Images, Uri=sitecore://master/{15451229-7534-44EF-815D-D93D6170BFCB}?lang=en&ver=1, Operation=Updated, ChildAction=Allow, Explanation=Version 'en_1' was published.
</pre>
<h4>
How we solved this bug:</h4>
While investigating and spending few hours we found that Sitecore is not able to remove duplicate items which are added as reference items, which we can solve by overriding <b>RemoveDuplicateReferrers</b> method of <b>ProcessQueue </b>processor as below:
<br>
<h4>
Step 1: Replace below line from web.config </h4>
<pre class="brush:xml" name="code"> <processor type="Sitecore.Publishing.Pipelines.Publish.ProcessQueue, Sitecore.Kernel"/>
</pre>
with:
<br>
<pre class="brush:xml" name="code"> <processor type="SitecoreTactics.Publishing.Pipelines.Publish.ProcessQueue, SitecoreTactics"/>
</pre>
<h4>
Step 2: Override the ProcessQueue processor</h4>
We have two alternatives to solve this, one is given by <b>Ivan Sheyenko</b> from Sitecore Support and one solved by our colleague <b>Muktesh Mehta.</b><br>
<b><br></b>
Below is the solution provided by <b>Sitecore Support</b>, which still needs improvements:
<br>
<pre class="brush:csharp" name="code">public class ProcessQueue : ProcessQueue
{
// Methods
protected override IEnumerable<publishingcandidate> RemoveDuplicateReferrers(IEnumerable<publishingcandidate> referredItems, PublishContext context)
{
Assert.ArgumentNotNull(referredItems, "referredItems");
Assert.ArgumentNotNull(context, "context");
List<publishingcandidate> list = new List<publishingcandidate>();
foreach (IEnumerable<publishingcandidate> enumerable in context.Queue)
{
foreach (PublishingCandidate candidate in enumerable)
{
list.Add(candidate);
}
}
List<publishingcandidate> list2 = new List<publishingcandidate>();
foreach (PublishingCandidate candidate2 in referredItems)
{
if (!(list.Contains(candidate2) || list2.Contains(candidate2)))
{
list2.Add(candidate2);
}
}
return list2;
}
}
</pre>
<br>
Below is the solution done by <b>Muktesh Mehta</b>, which is more useful in our architecture:
<br>
<pre class="brush:csharp" name="code">public class ProcessQueue : ProcessQueue
{
// Methods
protected virtual System.Collections.Generic.IEnumerable<publishingcandidate> RemoveDuplicateReferrers(System.Collections.Generic.IEnumerable<publishingcandidate> referredItems, PublishContext context)
{
Assert.ArgumentNotNull(referredItems, "referredItems");
Assert.ArgumentNotNull(context, "context");
List<id> idCollection = new List<id>();
System.Collections.Generic.List<publishingcandidate> finalReferredItems = new System.Collections.Generic.List<publishingcandidate>();
foreach (var referred in referredItems)
{
if(!idCollection.Contains(referred.ItemId))
{
idCollection.Add(referred.ItemId);
finalReferredItems.Add(referred);
}
}
return finalReferredItems.AsEnumerable();
}
}
</pre>
Now, enjoy bug-free Sitecore Related Item Publishing.. Njoy!
<br>
<h3>Other Sitecore 7.2 Bugs Solved!</h3>
<ul>
<li>
<a href="http://sitecoreblog.patelyogesh.in/2014/07/sitecore-72-attach-detach-are-slower.html">
Sitecore Media Attach Detach Getting Slower
</a>
</li>
<li>
<a href="http://sitecoreblog.patelyogesh.in/2014/07/solved-sitecore-pdf-loading-issue.html">
Solved Sitecore PDF Loading Issue
</a>
</li>
</ul>
<br>
<br>
</div>Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com2tag:blogger.com,1999:blog-5346027972748235946.post-52517105690707411532014-06-27T04:46:00.000-04:002014-07-05T02:13:33.745-04:00Sitecore Tactics Blog Completed One Year!
<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<div dir="ltr" style="text-align: left;" trbidi="on">
Today is a special day for the Sitecore Tactics, which completed 1 year today. I started blogging on Sitecore exact one year back on the same date with lot of excitement, passion and dream. I continued putting my efforts and serving best knowledge and tactics to my readers so that I can contribute in this blogging community.<br />
<br />
Today, it is great co-incidence, I wrote my first Sitecore Blog on <b class="highlight">27</b>th June, 2013, today is <b class="highlight">27</b>th June, 2014, and today my Sitecore blog visits crossed figure of <b class="highlight">27</b>,000 and my birthday comes on date <b class="highlight">27</b>th (What a coincidence). For total 40 posts, average 74 pageviews came to this blog per day in last one year and 4,000+ requests per month from last 3 months.
<br />
<br />
Thank you all my blog readers all around the world for reading my blogs. Increasing blog visits really encouraged me to write more and more blogs.</div>
</div>
<style>
.highlight
{
color: #d52a33;
}
</style>Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com2tag:blogger.com,1999:blog-5346027972748235946.post-75794473697866621722014-05-24T06:32:00.000-04:002016-09-02T11:56:39.893-04:00IRequiresSessionState slowing down Sitecore Media Requests<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<div dir="ltr" style="text-align: left;" trbidi="on">
For last few weeks we faced a performance issue on our Sitecore production servers. Page response time was substantially increased by 50% and getting slowness in full page loading. While investigation we found that this was just because of our customized <b>MediaRequestHandler</b>.<br />
<br />
Our requirement was to display some Disclaimer Page when a user request for some Sitecore media files (not for any image), after accepting disclaimer we can continue to download requested media.
All other media items should be served directly.<br />
<br />
We used <b>session</b> for managing this disclaimer. For using session in the MediaRequestHandler, we implemented <b>iRequiresSessionState</b> interface in it. See below code snippet<br />
<pre class="brush:csharp" name="code"> class CustomRequestHandler : Sitecore.Resources.Media.MediaRequestHandler, IRequiresSessionState
{
protected override bool DoProcessRequest(HttpContext context)
{
if (context.Request.Url.ToString().ToLower().Contains("/~/media/files/"))
{
// Check session, if session does not available, then send it to disclaimer
// Once user accepts Disclaimer, generate a session with user details.
}
else
{
// Proceed all image requests
base.DoProcessRequest(...);
}
}
}
</pre>
<br />
So, what the reason for the slower page rendering? Yes, that's <b>IRequiresSessionState</b> which was a halts in performance.<br />
<h3>
Can IRequiresSessionState really make my page slower?</h3>
Below image shows how ASP.NET Session state works:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAmZlnAfFudP9gfTmccNSnk6sDneb_DDPKSnBgVoaZ0ubo2imb6GyBR4cUCKbAFJS7QyzS4FQFUADmOLChlLSn8dOMOiUh4S_R7QRbbsz9rvAZaj8yAZhhO6SgNAZsVt2ld-X5kUgOhyQ/s1600/session-state.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAmZlnAfFudP9gfTmccNSnk6sDneb_DDPKSnBgVoaZ0ubo2imb6GyBR4cUCKbAFJS7QyzS4FQFUADmOLChlLSn8dOMOiUh4S_R7QRbbsz9rvAZaj8yAZhhO6SgNAZsVt2ld-X5kUgOhyQ/s1600/session-state.png" height="294" width="320" /></a></div>
<br />
<div class="highlight">
Now we can definitely say YES, it can slow down. State access requires that the aspx pages or handler, run sequentially. Each page/handler may read from and write to the session state. To handle this without creating any errors in the session state, .NET will only run one at a time using locking mechanism, so no handlers will run when the aspx page itself is running.<br />
<br />
<b>Tactics:</b><br />
<ul style="text-align: left;">
<li>Can we customize handler not to use session state?<br />- <b>No</b>, this will disable session so we cannot achieve disclaimer functionality</li>
<li>We can decide the request needs session or not and use session dynamically.<br />- Is it possible? <b>Yes</b>, it is, using HttpModules</li>
</ul>
</div>
<h3>
Solved by Dynamically deciding Session State Behavior programmatically</h3>
<div class="highlight">
Changing ASP.NET session state behavior dynamically is possible only using <b>HttpModules</b> or <b>Global.asax</b>. It is not possible in HttpHandlers or other code behind. This solution is possible only in <b>.NET 4.0 and later</b>. Yes, we can define it at Page Directives but that's static.</div>
<br />
As per our requirement, we have to show Dislaimer for selected Sitecore Media Files only (Not for any Media Images), we thought to create a HttpModule, which will
check whether the request is for media files or images. If it is a file, then enable session state and if it is an image, disable session state.<br />
<br />
<h4>
Step-1:</h4>
We removed <b>IRequiresSessionState </b>from our CustomMediaRequestHandler, so it will not use Session State directly.
<br />
<h4>
Step-2:</h4>
Created <b>HttpModule</b>, which will decide whether to use Session State or not.<br />
<pre class="brush:csharp" name="code"> public class CustomMediaRequestSessionModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
var CurrentContext = HttpContext.Current;
if (CurrentContext.Request.Url.ToString().ToLower().Contains("/~/media/files/"))
{
// Files need Disclaimer functionality, so session needed
CurrentContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
else if (CurrentContext.Request.Url.ToString().ToLower().Contains("/~/media/images/"))
{
// Images do not need Disclaimer, so no session needed
CurrentContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{
}
}
</pre>
<h4>
Step-3:</h4>
Now, in web.config, register this module under <b>system.webServer/modules</b> section:
<br />
<pre class="brush:xml" name="code"> <add type="SitecoreTactics.Resources.Media.CustomMediaRequestSessionModule, SitecoreTactics" name="CustomMediaRequestSessionModule" /> </pre>
<br />
And now we checked Page Response time, now feeling heavenly. All problem gone, Page Response Time now dropped down to earlier stage.<br />
<br />
<h3>
Good reads for improving Sitecore Media Item Performance</h3>
<ul style="text-align: left;">
<li><a href="http://sitecoreblog.patelyogesh.in/2013/11/sitecore-media-and-browser-cache.html" target="_blank">Improve media item performance by applying Browser Caching</a></li>
<li><a href="http://sitecoreblog.patelyogesh.in/2014/05/improve-sitecore-media-performance.html" target="_blank">Improve media item performance by using Reverse Proxy</a></li>
</ul>
</div>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com3tag:blogger.com,1999:blog-5346027972748235946.post-11833848898247942232014-05-03T08:09:00.000-04:002016-09-02T11:56:51.450-04:00Improve Sitecore Media Performance using Reverse Proxy<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div>
- Are you facing slowness in serving media library items?<br />
- Are you getting increased response time due to media requests?<br />
- Is your Sitecore instance serves media slower even if you have applied output (response) cache?<br />
<br />
Then this post will surely help you, which describe how we can improve Sitecore Media Library performance by implementing Reverse Proxy Server.</div>
<h3>
How Reverse Proxy will help to improve media performance?</h3>
The Reverse Proxy will play a role being a proxy between the client and Sitecore web server. Reverse Proxy provides caching mechanism, which caches all media items.
So, once any user has requested any media file from server, will be get cached on Reverse Proxy itself. So, from second time onwards, Reverse Proxy will not get media from Sitecore Web Server but will serve it from its own cache.<br />
<br />
We can use <b>URL Rewrite Module</b> and <b>Application Request Routing</b> (ARR) to implement a Reverse Proxy Server.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi63KyZP6j0Wj1sEvvoOOhZpRhjVvKMYDtZ02UqDHge6003rdF2VOFuwGSf8Ekvt_xQGrdGnnt8PHlnLCAX0UzdqtHdrAoOpfvbfeDp7DTtapAE7QuMaCQ29hkBpmMT6Jd7rQccAzlxbu8/s1600/Reverse_Proxy_setup.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi63KyZP6j0Wj1sEvvoOOhZpRhjVvKMYDtZ02UqDHge6003rdF2VOFuwGSf8Ekvt_xQGrdGnnt8PHlnLCAX0UzdqtHdrAoOpfvbfeDp7DTtapAE7QuMaCQ29hkBpmMT6Jd7rQccAzlxbu8/s1600/Reverse_Proxy_setup.gif" height="320" width="298" /></a></div>
<br />
<h3>
Step - 1 : Install ARR and Url Rewrite on IIS?</h3>
<ol>
<li>Setup IIS 7.0+ on your Server which will work as Proxy.</li>
<li>Install URL Rewrite module. You can download it from <a href="http://www.iis.net/learn/extensions/url-rewrite-module/using-the-url-rewrite-module">here</a>.</li>
<li>Install ARR module. You can download it from <a href="http://www.iis.net/learn/extensions/planning-for-arr/using-the-application-request-routing-module">here</a>.</li>
</ol>
<h3>
Step - 2 : Configure URL Rewrite module:</h3>
<ol>
<li>Create a new Website in IIS or use Default website, and click on Website, then click on <b>URL Rewrite</b> option under IIS section<br />
</li>
<li>Edit inbound rules as below:<br /><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0IwZmA_z6-ePqzGeIzJgIBmSqobuIzfXDU8YM78w460ZHFuVRaRFFKfIdAc4pKGEilyD85LGdggwPUg0WQj8EsXs9sqx77qLp6RPHnNejcSL6Az6gBq7TfBnWdaBqAsgQMYiJCS1phxc/s1600/urlrewrite-2.jpg" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0IwZmA_z6-ePqzGeIzJgIBmSqobuIzfXDU8YM78w460ZHFuVRaRFFKfIdAc4pKGEilyD85LGdggwPUg0WQj8EsXs9sqx77qLp6RPHnNejcSL6Az6gBq7TfBnWdaBqAsgQMYiJCS1phxc/s1600/urlrewrite-2.jpg" /></a><br /><br />
<br />
<br />Above configuration shows that if requested host is <b>www.patelyogesh.in</b>, then this rule will be applied for its all requests (*).</li>
<li>Configure Rewrite URL for above configurations as below. This will make sure that all requests coming from <b>http://www.patelyogesh.in/</b> will be rewrited to <b>http://rp.patelyogesh.in/</b>*.
<br /><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikND87N-IQsAoyEUZOCwAcK6sI-MrkyyzlUyRjlMIqyX_3LVCXk2JhFZyQ9YXco_FxrZV4pIfWdy9-MM9AFmXgdIOOiEd4unSDI1yN3V0NYXHOjlHR_JaRqjDkVwqoIs5qnevlJMkrwok/s1600/urlrewrite-3.jpg"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikND87N-IQsAoyEUZOCwAcK6sI-MrkyyzlUyRjlMIqyX_3LVCXk2JhFZyQ9YXco_FxrZV4pIfWdy9-MM9AFmXgdIOOiEd4unSDI1yN3V0NYXHOjlHR_JaRqjDkVwqoIs5qnevlJMkrwok/s1600/urlrewrite-3.jpg" height="350" width="640" /></a>
<br /><br />
When we apply above configurations, it will generate a web.config file under the website directory, which will look like below:
<br />
<br />
<pre class="brush:xml" name="code"><system.webServer>
<rewrite>
<rules>
<rule name="Sitecore-Production" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://rp.patelyogesh.in/{R:1}" />
<conditions>
<add input="{HTTP_HOST}" pattern="www.patelyogesh.in" />
</conditions>
</rule>
</rules>
</rewrite>
</system.webServer>
</pre>
<br />
We can also configure multiple domain's URL rewrites in the same way.<br />
<br />
</li>
</ol>
<h3>
Step - 3 : Configure ARR (Application Request Routing) Cache</h3>
<ol>
<li>Select the Server Node, now select <b>Application Request Routing Cache</b> option.
<br /><br />
</li>
<li><b>Add Drive where the caching will be stored by ARR Module. </b>The below image shows how we can configure ARR module and how it will look.
</li>
<li>Make sure the Identity user of the Application Pool should have read/write access of the drives configured here. So, ARR will store all cache files here only.<br /><br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTYKLt0JX7NPClaDSounF6GW2oyVIh2IbnmxUBLjczrZgNrjwNKGEzoeT5QVzdOGI75JpQL-z6ToSNqGKpGmnI9DMEwqhL9X0tbehMH6PWILLsWFUoFXIAe4r9Q3RCXyYjv8jXzaIKZpo/s1600/ARR-2.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTYKLt0JX7NPClaDSounF6GW2oyVIh2IbnmxUBLjczrZgNrjwNKGEzoeT5QVzdOGI75JpQL-z6ToSNqGKpGmnI9DMEwqhL9X0tbehMH6PWILLsWFUoFXIAe4r9Q3RCXyYjv8jXzaIKZpo/s1600/ARR-2.jpg" /></a></div>
</li>
<li>Enable Proxy
As per below image, click on <b>Proxy Settings</b> on the right side bar and enable Proxy.
<br /><br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2OunGQBnTfldzEniptsXk5egFvEbkeg1X8WsoDQt5DxEqZPlCWwdJde0JVMZsK40v88YC_Hwco7u30f6ysSht7ek7naW4Ps4DsvovmVeFhfHgY8w_E_HPY69Tf9-UwO8iBO2RU808sTo/s1600/ARR-3.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2OunGQBnTfldzEniptsXk5egFvEbkeg1X8WsoDQt5DxEqZPlCWwdJde0JVMZsK40v88YC_Hwco7u30f6ysSht7ek7naW4Ps4DsvovmVeFhfHgY8w_E_HPY69Tf9-UwO8iBO2RU808sTo/s1600/ARR-3.jpg" /></a></div>
</li>
</ol>
<br />
Finally, Reverse Proxy setup finished, took just 15 minutes only!! <br />
<br />
Now, Request to <b>http://www.patelyogesh.in/</b>. This will serve you content from <b>http://rp.patelyogesh.in</b> itself by traversing through Reverse Proxy. It's really easy and simple, isn't it?<br />
<br />
<h3>
How to confirm Reverse Proxy working fine?
</h3>
We can provide our <b>Custom Response Headers</b> using Proxy Settings from ARR Cache option as shown in above image. So, if Reverse Proxy is working well, we will get those headers in response.
<br />
<h3>
How to confirm ARR Caching working fine?
</h3>
In File Explorer, open the Drive folder we configured in ARR Cache settings. We will get all files cached by ARR<b>.</b>
<br />
<br />
Cache for the URL: <b>http://www.patelyogesh.in/~/media/Images/yogi.png</b> (Rewrited URL: <b>http://rp.patelyogesh.in/~/media/Images/yogi.png</b>) will be stored at : <b><website root>\rp.patelyogesh.in\~\media\Images\yogi.png.full</b>.</div>
<br />
<h3>
How to delete ARR cache programmatically?
</h3>
In ARR Cache settings, you will find a button <b>Delete Specific Cached Objects</b> which can clear specific URL cache. It also supports wild cards for clearing cache.<br />
<br />
We can create a web service on the ARR website, which will get a URL as input and will clear cache accordingly using below code. Now, on each media item publish from Sitecore, we will clear the ARR cache. We can decice How and when to make a call to the ARR web service to cache clear according to our architecture.<br />
<br />
Source code to clear ARR cache programmatically:
<br />
<pre class="brush:csharp" name="code">[WebMethod]
public static ClearCache(string urlToCacheClear)
{
var url = urlToCacheClear == "ALL" ? string.Empty : urlToCacheClear;
var m = new ServerManager();
var x = m.GetApplicationHostConfiguration().GetSection("system.webServer/diskCache");
var method = x.Methods["FlushUrl"].CreateInstance();
method.Input.SetAttributeValue("url", url);
method.Execute();
}
</pre>
<br />
Enjoy Reverse Proxy! Enjoy improved Sitecore Media Library performance!!
<br />
<br />
<b>Good reads on Reverse Proxy:</b>
<br />
- <a href="http://www.agarwalnishant.com/2013/04/improve-sitecore-media-library.html" target="_blank">http://www.agarwalnishant.com/2013/04/improve-sitecore-media-library.html</a>
</div>
<br />
- <a href="http://www.iis.net/learn/extensions/url-rewrite-module/reverse-proxy-with-url-rewrite-v2-and-application-request-routing" target="_blank">http://www.iis.net/learn/extensions/url-rewrite-module/reverse-proxy-with-url-rewrite-v2-and-application-request-routing</a>
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-18197705702663532232014-04-05T07:44:00.002-04:002016-09-02T11:57:03.132-04:00How Sitecore media cache Works?<div dir="ltr" style="text-align: left;" trbidi="on">
Sitecore stores all media cache to file system, unlike all other caches, stored in RAM. Media items are stored in database, so media cache is required to reduce database calls and serve media files faster to end-user. Let's understand Sitecore media cache mechanism.<br />
<h3>
How Media Cache Created?</h3>
- When we upload a new media file to Sitecore, its media cache is created in <b>Website\App_Data\MediaCache\<sitename>\<u><Hashcode of MediaId></u></b> folder. Sitecore assigns unique <b>MediaId </b>to each media item, which gets changed on each modification of media item.<br />
<br />
- For each media item, Sitecore creates an INI file with name of MediaId in the respective folder, which stores different attributes of the media file inside it.<br />
<br />
See below image as a reference:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM8lKTYwYO_wmojv44i4wMfvD74QkL_9UIoqPwRXYLdL8FJA2ynu7xmH1jqnJPOxHpNYNR3foKqBdA6VkXqzyQglHiQmaohEoD8rMXjhV88moLFT4XHG6qLOIsGkIhunJw4lTjq4ptUzE/s1600/sitecore-Media-cache.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM8lKTYwYO_wmojv44i4wMfvD74QkL_9UIoqPwRXYLdL8FJA2ynu7xmH1jqnJPOxHpNYNR3foKqBdA6VkXqzyQglHiQmaohEoD8rMXjhV88moLFT4XHG6qLOIsGkIhunJw4lTjq4ptUzE/s1600/sitecore-Media-cache.png" height="231" width="640" /></a></div>
<br />
<br />
In above case, this media's MediaId is "<span style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 13px;">8c683332453741038b8876bf5915d188", so</span><b style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 13px;"> </b>the ini file (<b>8c683332453741038b8876bf5915d188.ini</b>) is generated with name of MediaId. On right side, all information is stored in same file. dataFile shows physical media file name <b>7b4a5e3934914d57a390bedcab67380c.jpg</b>.
<br />
<br />
Different attributes in INI file:<br />
<table class="mytable">
<tbody>
<tr>
<th>Attribute</th>
<th>Description</th>
</tr>
<tr>
<td>Key</td>
<td>height, width, thumbnail, background color, etc. image parameters passed by query string. You can get better idea by reading my earlier Blog Post on <a href="http://sitecoreblog.patelyogesh.in/2013/06/sitecore-image-control-parameters.html">Sitecore Image Control Parameters</a></td>
</tr>
<tr>
<td>extension</td>
<td>Extension of media file.</td>
</tr>
<tr>
<td>headers</td>
<td>Content Type, etc.</td>
</tr>
<tr>
<td>datafile</td>
<td>Physical file name stored as media cache in same folder.</td>
</tr>
</tbody></table>
<br />
<h3>
How Media Cache Deleted?</h3>
Sitecore provides a cleanup agent to clear older media files, which clears media files every specified interval of time. By default it clears all media cache files created 90 days ago. See below settings in web.config:<br />
<pre class="brush:xml" name="code">
<agent type="Sitecore.Tasks.CleanupAgent" method="Run" interval="06:00:00">
<!-- Specifies files to be cleaned up.
If rolling="true", [minCount] and [maxCount] will be ignored.
[minAge] and [maxAge] must be specified as [days.]hh:mm:ss. The default value
of [minAge] is 30 minutes.
[strategy]: number of files within hour, day, week, month, year
[recursive=true|false]: descend folders?
-->
<files hint="raw:AddCommand">
<remove folder="/App_Data/MediaCache" pattern="*.*" maxAge="90.00:00:00" recursive="true" />
</files>
</agent> </pre>
<h3>
Media Cache Hidden Secrets</h3>
<ul>
<li>
If a user has requested a media file with
different querystring parameters, then Sitecore creates different media
files runtime and stores all those files in same folder where original media file is stored. Also, all these combinations are stored in the same INI
file itself.<br />
<br />
You can try accessing your media image with different parameters like below and check media cache:<br />
-<a href="javascript:">http://sitecoreblog.patelyogesh.in/~/media/Images/myimage.jpg</a><span id="goog_893991530"></span><span id="goog_893991531"></span>
<br />
-<a href="javascript:">http://sitecoreblog.patelyogesh.in/~/media/Images/myimage.jpg?h=100</a>
<br />
-<a href="javascript:">http://sitecoreblog.patelyogesh.in/~/media/Images/myimage.jpg?w=200</a>
</li>
<li>
When a media item is updated, Sitecore
creates a new media cache with new INI file and a new media file even
though the media file(blob) remains same or item already has same media
cache. So, there will be duplicate media cache but Sitecore will refer to latest file only.</li>
<li>
As we know Sitecore creates media cache in folder with name of Context Site. So, if one media file is accessed by two different Sites (SiteContext), then media cache will be generated for
both sites, means in both sites' folders. For example, When media item
is accessed by Content Editor, cache will be created for Shell site and
when accessed by Website, then it will be created for Website.</li>
</ul>
<h3>
How to read/create media cache programatically?</h3>
Yes, it's possible to create or read media cache by Sitecore API:
<br />
<pre class="brush:csharp" name="code">
///////////////////////////////////
// Read Media Cache
///////////////////////////////////
MediaRequest request; // media parameters like height, width, etc.
Sitecore.Resources.Media.Media media // item for which you want to read media cache.
// set request.Options
// set media
// This is the media cache stored in MediaStream
MediaStream mStream = MediaManager.Cache.GetStream(media, request.Options);
///////////////////////////////////
// Write Media Stream to Media Cache
///////////////////////////////////
// Manipulate your media file and set it into MediaStream
// store the media stream to media cache.
MediaManager.Cache.AddStream(media, request.Options, mStream, out outStream);
</pre>
We can know more about Media Cache understanding below classes using Reflector:<br />
- Sitecore.Resources.Media.MediaCache<br />
- Sitecore.Resources.Media.MediaCacheRecord
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-45115741512146640292014-02-09T12:43:00.000-05:002016-09-02T11:57:18.786-04:00Sitecore Parallel Publishing Implementation<div dir="ltr" style="text-align: left;" trbidi="on">
Many readers of my blogs asked me the practical way to implementing parallel publishing using multiple publish queue on Sitecore. Earlier I have posted a blog on it explaining theoretical approach for <a href="http://sitecoreblog.patelyogesh.in/2013/10/sitecore-multiple-publish-queue-parallel.html">Parallel Publishing in Sitecore</a> using custom EventQueue. It is really difficult to explain that approach on a blog. So here, I am going to explain easiest and quickest implementation of multiple publish queue practically.<br />
<h3>Why Parallel Publishing from different instances?</h3>
In our multisite environment, hundreds of users work at a time, add/update thousands of items and publish them frequently. Daily 15,000+ items get published. Sometimes this number crosses 50,000 per day. So, mostly we see long publishing queue and users have to wait for minutes to get their items published. To make publishing more scalable, we thought to have multiple publishing instances in our CM environment and we dedicated them for different types of users based on business need.
<h3>
What we wanted to achieve:</h3>
We wanted to have three different Publishing Instances for our users.
<br />
<b>Publish Instance - 1(PI-1)</b> Dedicated to Client Content Authors.
<br />
<b>Publish Instance - 2(PI-2)</b> Dedicated to Internal Content Authors.
<br />
<b>Publish Instance - 3(CM-1)</b> Dedicated to all other users. This is the Content Management Server, where users use Page Editor or Content Editor.
<br />
<br />
For this, we created three different roles named <b>Publish Instance One</b>, <b>Publish Instance Two</b> and <b>Content Management Instance</b>. So, depending on role assigned, we will allocate Publishing Instance to the user.
<br />
<h3>
How we hooked Publishing Process</h3>
Below image shows how we hooked and modified <b>startPublishing</b> event to choose Publish Instance depending on user publishes.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7LHiIS9YxjLQ5uVq9SfZXfNel1Teom2Wqe4DcuNRi9V3_VR9e8LhWTwxbhOUnjBUavD4yxZjYB-fKQqpkMf5HbjPJk-M0f2Bunkqon38mlWm1u7xl7aOywCVqXq3GyZ7lxH4wrMl-tSM/s1600/parallel-publishing-architecture.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Parallel Publishing Architecture with Multiple Sitecore Instances" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7LHiIS9YxjLQ5uVq9SfZXfNel1Teom2Wqe4DcuNRi9V3_VR9e8LhWTwxbhOUnjBUavD4yxZjYB-fKQqpkMf5HbjPJk-M0f2Bunkqon38mlWm1u7xl7aOywCVqXq3GyZ7lxH4wrMl-tSM/s1600/parallel-publishing-architecture.png" height="592" title="Parallel Publishing Architecture with Multiple Sitecore Instances" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<h3>
Let's see practical implementation of Parallel Publish in Sitecore?</h3>
You can see <a href="http://youtu.be/Q2KtpHRbqKw">How Parallel Publishing works on Sitecore</a> in below video.<br />
In this video, below is the allocation done for users:<br />
<br />
<table class="mytable">
<tbody>
<tr>
<th>User</th>
<th>Role</th>
<th>Allocated Publishing Instance</th>
</tr>
<tr>
<td>sitecore\admin</td>
<td>Publish Instance One</td>
<td>Machine-PI-1</td>
</tr>
<tr>
<td>sitecore\yogesh</td>
<td>Publish Instance Two</td>
<td>Machine-PI-2</td>
</tr>
<tr>
<td>All other users</td>
<td>Content Management Instance</td>
<td>Machine-CM</td>
</tr>
</tbody></table>
<br />
<b>So, we have three different Publishing Queues available</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='710' height='532' src='https://www.youtube.com/embed/Q2KtpHRbqKw?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<br />
Now see how we can achieve this in simple steps below:
<br />
<ol>
<li><b>Setup three different CM instances</b> (in a cluster) with same master, web and core databases.</li>
Parallel publishing requires 1+ Sitecore Instances So indirectly we can say parallel publishing is possible on n number of CM instances.<br /><br />
<li><b>Enable EventQueue</b> on each instance </li>
On all the Sitecore Instances, it is must to enable EventQueue from web.config or App_config\Include\ScalabilitySettings.config file.
<pre class="brush:xml" name="code"> <setting name="EnableEventQueues" value="true" />
</pre>
<li><b>Configure allocation of Roles & Publishing Instances</b></li>
On all the Sitecore Instances, create a config file: App_config\Include\ParallelPublish.config file. This file should remain same on each instance.
<pre class="brush:xml" name="code"><configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<parallelpublishRoles>
<!-- Roles needed for Parallel Publising -->
<role name="sitecore\Publish Instance One" publishinstance="Machine-Sitecore-PI-1" />
<role name="sitecore\Publish Instance Two" publishinstance="Machine-Sitecore-PI-2" />
<role name="sitecore\Content Management Instance" publishinstance="Machine-Sitecore-CM" />
</parallelpublishRoles>
</sitecore>
</configuration>
</pre>
<li>Override <b>publish:startPublishing Event</b> with your custom code as below</li>
<pre class="brush:csharp" name="code">namespace SitecoreTactics.Publishing
{
public class RemotePublishingEventHandler : Sitecore.Publishing.RemotePublishingEventHandler
{
public override void OnStartPublishing(object sender, EventArgs args)
{
RemoteEventArgs<Sitecore.Publishing.StartPublishingRemoteEvent> args2 = args as RemoteEventArgs<Sitecore.Publishing.StartPublishingRemoteEvent>;
if (args2 == null)
{
throw new InvalidOperationException("Unexpected event args: " + args.GetType().FullName);
}
Sitecore.Globalization.Language lang = Sitecore.Globalization.Language.Parse(args2.Event.ClientLanguage);
StartPublishingRemoteEvent myArgs = new StartPublishingRemoteEvent(args2.Event.Options, args2.Event.StatusHandle, args2.Event.UserName, lang);
// Findout Dedicated Publishing Instance for current user
string allocatedPublishInstance;
using (new SecurityDisabler())
{
User user = User.FromName(args2.Event.UserName, true);
allocatedPublishInstance = GetPublishInstanceForUser(user);
}
// Allocate Publishing Instance to current publishing
myArgs.SetPublishInstance(allocatedPublishInstance);
// Allocated Publishing Instance will pick up the publishing
if (this.IsPublishingServer(myArgs))
{
Sitecore.Publishing.DistributedPublishingManager.StartPublishing(myArgs);
}
}
protected string GetPublishInstanceForUser(User user)
{
// Find role and allocated publishing instance
foreach (XmlNode node in Factory.GetConfigNodes("parallelpublishRoles/role"))
{
string role = XmlUtil.GetAttribute("name", node);
string publishInstance = XmlUtil.GetAttribute("publishinstance", node);
if (user.IsInRole(role))
return publishInstance;
}
// Return default Publish Instance
return Settings.Publishing.PublishingInstance;
}
}
public class StartPublishingRemoteEvent : Sitecore.Publishing.StartPublishingRemoteEvent
{
public StartPublishingRemoteEvent(Sitecore.Publishing.DistributedPublishOptions[] options, Sitecore.Handle statusHandle, string userName, Sitecore.Globalization.Language clientLanguage)
: base(options, statusHandle, userName, clientLanguage)
{
}
// Methods
[Obsolete("Use StartPublishingRemoteEvent(DistributedPublishOptions[] options, Handle statusHandle, string userName, Language clientLanguage) instead.")]
public StartPublishingRemoteEvent(Sitecore.Publishing.DistributedPublishOptions[] options, Sitecore.Handle statusHandle, string userName) :
base(options, statusHandle, userName, Sitecore.Data.Managers.LanguageManager.DefaultLanguage)
{
}
public void SetPublishInstance(string piName)
{
base.PublishingServer = piName;
}
}
}
</pre>
<li>Update your custom publish:startPublishing event in web.config with below changes</li>
<pre class="brush:xml" name="code"><event name="publish:startPublishing">
<!-- handler type="Sitecore.Publishing.RemotePublishingEventHandler, Sitecore.Kernel" method="OnStartPublishing" / -->
<handler type="SitecoreTactics.Publishing.RemotePublishingEventHandler, SitecoreTactics.Publishing" method="OnStartPublishing" />
</event>
</pre>
</ol>
Now try in your environment, parallel publishing is surely working for you too. If it's not working, check
<br />
<ul>
<li>EventQueue is disabled, enable it</li>
<li>All CM instance should have exact time. If two instances have time difference of 5 minutes, then there the instance running late will get updates of other instance after 5 minutes, so sync will never be done between them. </li>
<li>Give proper Roles Name and Publishing Instance Name in the ParallelPublish.config file.</li>
<li>Check all your Sitecore instances are pointing to same databases - master, web, core.</li>
<li>Configure the publish:startPublishing event and ParallelPublish.config on each Sitecore Instance.</li>
<li>ParallelPublish.config file should have same contents on all the instances.</li>
<li>Instance which needs to do publish should be up and running.</li>
</ul>
<br />
<br />
Now enjoy parallel publishing without waiting time, without queue stuck!
</div>Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com3tag:blogger.com,1999:blog-5346027972748235946.post-46707931966168638902014-01-28T10:10:00.003-05:002014-01-28T10:10:59.589-05:00Sitecore MVP 2014 Award<div dir="ltr" style="text-align: left;" trbidi="on">
Today, total 107 impeccable Sitecore community leaders around the world received the Sitecore Technology MVP 2014 Award. There are more than 10,000 certified Sitecore developers currently worldwide, and being a 2014 winner as a part of this very elite group of MVPs, gives so deep pleasant feeling and satisfaction.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkwdv2Vm3_aMVxIjVs_CM_9Z4MjH28oWD_UaCSw2EQYqRLV4OpJgJVIJkrW9LSKMFAOUBdMn4nzHHhO36ZV1B-PUQR3dOvi9ELEk-R_w5RECSc8uo5FVaNAzQix1OaRlJSPHX0g8SbDaU/s1600/MVP+2014+-Technology.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Sitecore MVP 2014 award" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkwdv2Vm3_aMVxIjVs_CM_9Z4MjH28oWD_UaCSw2EQYqRLV4OpJgJVIJkrW9LSKMFAOUBdMn4nzHHhO36ZV1B-PUQR3dOvi9ELEk-R_w5RECSc8uo5FVaNAzQix1OaRlJSPHX0g8SbDaU/s1600/MVP+2014+-Technology.jpg" title="Sitecore MVP 2014 award" /></a></div>
<br />
<br />
The Sitecore MVP Award is given to exceptional technical community leaders who foster the free and objective exchange of knowledge by actively sharing their real-world expertise with technology users.<br />
<br />
The Sitecore MVP Award celebrates the most active Sitecore community members from around the world who provide invaluable online and offline expertise that enriches the community experience and makes a difference.<br />
<br />
For me, winning an MVP award means to become more aware of the meaning of connectivity and responsibility, and continue same kind of contribution to Sitecore community.</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0tag:blogger.com,1999:blog-5346027972748235946.post-46157565123443137222013-12-30T10:44:00.002-05:002016-09-02T11:58:43.596-04:00Invoke Sitecore Scheduling Agent Programmatically<div dir="ltr" style="text-align: left;" trbidi="on">
Sitecore provides facility to run scheduled jobs at specified regular interval like <b>DatabaseAgent</b>, <b>UrlAgent</b>, etc. We created our own agent to make scheduled publishing. So, this agent is executed every one hour and starts pending publishing, which can take several minutes say 20-30 minutes.
<br />
<br />
This can be achieved by creating a scheduling agent in web.config as below:
<br />
<pre class="brush:xml" name="code"> <agent name="ScheduledPublish" type="SitecoreTactics.Tasks.StartPublish" method="StartPublish" interval="01:00:00" />
</pre>
Here, every 1 hour, Sitecore invokes the agent named <b>ScheduledPublish</b> and executes <b>StartPublish</b> method of <b>SitecoreTactics.Tasks.PublishManager</b> class.
<br />
<br />
<h3>
Requirement to invoke Sitecore Agent</h3>
Now, we had a requirement to invoke our custom agent in between through a Web Page when user wants to do instant publish instead of waiting for next turn of its execution. Should we call our code from the web page itself or invoke the Agent directly programmatically? There was no clear idea which can be a better approach? Below are two approaches we worked on.
<br />
<h3>
Wrong approach we chose earlier to execute code directly</h3>
Earlier, we called the PublishManager's StartPublish method directly from the web page, when user clicks on a button as below.
<br />
<pre class="brush:csharp" name="code">protected void btnStartPublish_Click(object sender, EventArgs e)
{
SitecoreTactics.Tasks.PublishManager publisher = new SitecoreTactics.Tasks.PublishManager();
publisher.StartPublish();
}
</pre>
The limitation in the approach was, the code was executed by the thread of web page or a web request, which needed to be executed more than 30 minutes. But, after the Server's Script timeout (default 5 minutes), the execution was stopped every time and could not complete full job of 30+ minutes. But if same code was executed as a Sitecore Agent, it finished all execution, because Sitecore Agents are executed by separate ManagedThreadPools, which never has TimeOut until they finish job or Application ends.
<br />
<h3>
Correct approach to invoke Sitecore Scheduling Agent</h3>
We searched a lot on Google about invoking Sitecore Agent. Later on, Reflector came to our help. Using it we found the code from where Sitecore invokes its agents and we implemented the same code as below:
<br />
<pre class="brush:csharp" name="code">protected void btnStartPublish_Click(object sender, EventArgs e)
{
foreach (XmlNode node in Factory.GetConfigNodes("scheduling/agent"))
{
object obj = Factory.CreateObject(node, true);
string name = XmlUtil.GetAttribute("name", node);
if (name == "ScheduledPublish") // Scheduling Agent Name
{
string method = XmlUtil.GetAttribute("method", node);
JobOptions options = new JobOptions(name, "", "", obj, method);
options.AtomicExecution = true; // Allow multiple instances of the agent running simultaneously
JobManager.Start(options);
break;
}
}
}
</pre>
Now, we are not worried for executing agents's code. Agent's process needs to be executed for one hour, two hours, ten hours? No worries, we can now call Sitecore Jobs programmatically!!</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com2tag:blogger.com,1999:blog-5346027972748235946.post-4842152921827763492013-12-30T08:18:00.001-05:002013-12-30T08:18:44.756-05:00Custom Google Search Engine for Sitecore Technical Blogs<div dir="ltr" style="text-align: left;" trbidi="on">
One day I was thinking that how difficult it is to find out technical help from thousands of blogs written on net. Finding best blogs out of them is really difficult.
<br />
<br />
There are many ways to make a collection of Sitecore Technical Blogs like Feedly, NewsBlur, InoReader, etc. But, here I have prepared a Custom Google Search Engine which has pre-built indexes for good Sitecore technical blogs and Sitecore Community.
<br />
<br />
<h3>
How to access this Custom Google Search Engine for Sitecore?</h3>
Open: <a href="http://goo.gl/YzZuCm">http://goo.gl/YzZuCm</a>
<br />
OR<br />
<a href="https://www.google.com/cse/publicurl?cx=003865006081614653083:n809uqjt03c">https://www.google.com/cse/publicurl?cx=003865006081614653083:n809uqjt03c</a>
<br />
<br />
This is a Google's custom Search Engine, which gives results from selected different Sitecore bloggers, Sitecore Communities, etc.
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjupRh5QrAVLIZTDJJR6VWEhmfLm5ae3Qd2UpZDfI-Qy2OdGllfDU4V9rXHNYETUB_cwriLSRkDlWa-u5FscIrnvgpFuBsNX_6zCd1vRQbUpybd8hTuu0G6U6x8JNbkJGtxj4xIYHOL6zo/s1600/sitecore-custom-search-engine.png" imageanchor="1"><img border="0" height="476" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjupRh5QrAVLIZTDJJR6VWEhmfLm5ae3Qd2UpZDfI-Qy2OdGllfDU4V9rXHNYETUB_cwriLSRkDlWa-u5FscIrnvgpFuBsNX_6zCd1vRQbUpybd8hTuu0G6U6x8JNbkJGtxj4xIYHOL6zo/s640/sitecore-custom-search-engine.png" width="640" /> </a><br />
<br />
Enjoy searching Sitecore stuffs easily and quickly!!
</div>
Sitecore Tacticshttp://www.blogger.com/profile/14381991251507357511noreply@blogger.com0