Sitecore Workflows – Fun Little Surprises

workflows

Sitecore life is funny,  just when you think you know everything, there is something that shows up and suggests that there is always something new you can learn with Sitecore even in tiny section or feature when compared to Sitecore on the whole.   This has been proven time and time again. 🙂  But, I love it that way and I think surprises is directly proportional to fun.

I would like to share some of those Aha moments I have had with while working with Workflows.  Now,  in huge transformation projects such as say we are building a brand new site in Sitecore or not much of a legal compliant client for example we usually get away by building a simple workflow, more or less OOTB one that comes with Sitecore installation with few more commands based on requirements.  When you do that, there is not much you bump in to actually as Sitecore does the magic kind of fine in those instances.  Now, what if you get to work on workflows that are heavy complex, customized and awful lot of scenarios?  Guess?? You will learn and wait for it….and say Aha a whole lot more 😉

Here are few of those that I encountered –

Do you know TDS/Sitecore actually shares relationships/associations of roles in counter intuitive way? Do not get what I mean, let me expound a little.  Say, you have a role called Content Author and now you have this another role called Content Author Base.   You went ahead and added Content Author base as a member of Content Author.  Now, if you look at your definition file in TDS, you will notice something funny, you will see that Content Author item is not modified, it is Content Author base that is modified to add that association.

The second one is a bit of sad surprise actually I am just a tad bit sad that I could not achieve what I wanted easily and obviously workaround was much easier so I went right ahead.  So, if you have a custom comment template defined at workflow command level, the expectation is it would show up on workbox when the command is clicked both on item level and on bulk level.  So, turns out the version we were using did not have that configuration and all bulk operations did not show any comment.  Our comments are mission critical in our case, meaning we can not skip them as there is some decision making involved in each of these commands and states.   In later versions it seems there is a configuration that controls to show or not show comments on bulk operations ( I did not test this tough, kind of trusting sitecore support)

<setting name=”Workbox.SingleCommentForBulkOperation” value=”true” />

In our case though upgrade was not an option for this tiny use case, so, we decided to hide all the bulk buttons by replicating Sitecore.Shell.Applications.Workbox.WorkboxForm in to our own custom class and using this class instead on our workbox.xml

I tried hard to see if there is any way I can ask Sitecore to show my custom comment template rather than not showing anything at all or showing some custom parameters built on the same class I stated above.  There is a good stack overflow thread that talks about how you can show a comment instead, but, in my case it would not work as I do need a custom comment template to show up and I do not want to manually add it as suggested on the post.  If you are curious here is the post –

https://stackoverflow.com/questions/22947912/sitecore-workbox-does-not-prompt-for-comment-when-approving-multiple-items

I could have gotten away with this if a simple comment would work.   Another suggestion that Sitecore support offered was to pass in Default Comment Template on the workflow definition item level, as it turns out it is a good hack if you have one comment template that you need to show across the workflow.  In our case, it is specific to command and nope will not work.   I wish there was an easier than this, but, hey, we convinced the folks to ease some pain by choosing to just disable bulk commands, turns out they want the specific roles to not have these bulk commands and would like them to pay close attention to what they are approving, so, works in our favor.

If you are curious, how we hid the buttons,  again, takes us back to same magic class.  Pay attention to HideButtonsForCommand.  All this function would do is check against app settings of some sort and do not add buttons if command matches to our needs.

foreach (WorkflowCommand command in workflow.GetCommands(state.StateID))
{
if (stateItems.CommandIds.Contains<string>(command.CommandID) && !HideButtonsForCommand(command.CommandID))
{
XmlControl webControl1 = Resource.GetWebControl("WorkboxCommand") as XmlControl;
Assert.IsNotNull((object)webControl1, "workboxCommand is null");
webControl1["Header"] = (object)(command.DisplayName + " " + Translate.Text("(selected)"));
webControl1["Icon"] = (object)command.Icon;
webControl1["Command"] = (object)("workflow:sendselected(command=" + command.CommandID + ",ws=" + state.StateID + ",wf=" + workflow.WorkflowID + ")");
border2.Controls.Add((System.Web.UI.Control)webControl1);
XmlControl webControl2 = Resource.GetWebControl("WorkboxCommand") as XmlControl;
Assert.IsNotNull((object)webControl2, "workboxCommand is null");
webControl2["Header"] = (object)(command.DisplayName + " " + Translate.Text("(all)"));
webControl2["Icon"] = (object)command.Icon;
webControl2["Command"] = (object)("workflow:sendall(command=" + command.CommandID + ",ws=" + state.StateID + ",wf=" + workflow.WorkflowID + ")");
border2.Controls.Add((System.Web.UI.Control)webControl2);
}
}

I bumped in to crazy language specific security issues.  On few languages, I started seeing a prompt on content editor warnings section that I do not have access to this language and it suggested me to swap the language, so, I can continue editing.   This was insane as I knew there was no language based security that was intended nor there.   I went to my user/role and checked on access viewer to make sure I see full green for two important security flags when language is concerned.

What could be wrong, I first thought since my item’s owner was Sitecore\Anonymous in that language, I thought Sitecore was using sitecore\Anonymous security in which it made sense, but, nope, I changed the owner to some sane one and still same issue.   Finally, I cross verified what is on /system/languages and what was on my content item languages.  It seems some language specific cultures did not have the node in /system/languages and when Sitecore is asked to respect the configurations on read and write language flags and it can not find such item on the location for languages, it interprets as a “NO” to be on safe side.  That was exactly the problem on my hand, those missing language nodes in combination with Sitecore configuration was not working.

Last one, here it goes.  Have you ever wondered what can be done to take down Lock and Edit completely for few roles if needed.  I am sure there are multiple ways to approach this, but, when I dug deep in to how Sitecore is pulling which commands to show on Review tab under workflow panel, I bumped in to below piece of code which we had to modify to take down Lock and Edit.  Along the side, we also took down Content Editor Warnings to ensure we do not show a prompt of Lock and Edit as well.   Now, I am sure there could be other ways, but, the below is just one way.  I am usually against swapping pipelines, but, it is important and have to, I caution around least invasion and propose thorough testing.

So, for not displaying content editor warnings, you can inherit your class from the below and do what you got to do in that class.

<processor type="Sitecore.Pipelines.GetContentEditorWarnings.IsLocked, Sitecore.Kernel" />

For the workflow panel override to not show Lock and Edit under Review ribbon, make your own custom WorkflowPanel class.   Swap the core item to use this new Workflow Panel class instead of default one.   The core item is located under –

 /sitecore/content/Applications/Content Editor/Ribbons/Chunks/Workflow/WorkflowPanel

 

 

 

 

 

 

 

 

Did you Retro?

lemonade

What a week at Orlando!  Adrenaline rush finally calmed down, but, experience will last a lifetime, what a feeling to wrap up my first ever Symposium.  Enjoyed every moment, Sitecore does know how to treat us and impress us with the generosity of giving best experiences, It was truly an epitome of elevation. 😉

Now, I usually try and do technical story about how I solved a specific problem. It could be helpful for folks in the same situation to snap out as quickly as possible.  Hopefully, some where down my blogging journey I helped at least one such awesome developer trying to find a solution to the problem they bumped in to.  This post is subtly different and hence special. 🙂

It is more or so in terms of re-alignment and thinking we usually have no time for and most of us keep suffering with out a peep.  Why? Can we do something about it? Answer is of course yes, in fact we already know this living  in a strong SDLC life cycle.  But, honestly, we do not do it enough.

What happens when you truly want to take charge of a situation pouring your heart in is nothing short of magic.  I wanted to share this to any leader who is passionate about changing the direction from not so great to better and exciting.  Though it sounds cliche, I truly believe in the saying — ‘When life gives you lemons, make lemonade’.  I do this on every single assignment and challenge that comes my way.  I completely give in and see what I can do based on situation and it usually and mostly just WORKS.   Try it!!!

By giving this example, I hope many of you, who have been facing few challenges calmly and not doing or unable to do much about it would think for a moment and see what they can change.  A change in good path usually just needs one step and others follow if your one step is powerful enough.   In my recent case, It was a simple Retrospective with the team with a motto of truly opening up the gates (minus clients preferably) to bring in any issues they were facing that we could have done better.  The intent was to extract all issues and then come up with action plan to make things better (not just for internal team, but, for clients as well).

I understand that we do not always have time to implement a full fledged action plan on every single issue on hand, but, start simple and then aim big.  It will take time, but, if you are on right path, nothing would stop you from creating amazing stuff with a super glued team to pair with.  Fun and win from every direction.

Below is a skeleton of questions for the technical leader who is new to a project which would then lead to a productive and healthy discussion among teams.  Always remember to gather trust at the beginning of the meeting, this is the only way to extract the true problems.

  1. What do you need from my role/technical leadership at this time?
    This is a great question to start on a note that I am here to help no matter what it is.  I understand that every company has a role and every role is paired with responsibilities/goals/chores etc., But, every project is different and so are the demands.  As we all understand and appreciate, one solution to all problems is heavily hypothetical and we all live in real world.   If we take this extra step it will almost always bring appreciation at the moment and projected future.
  2. Current challenges and real examples?
    There is no better way to come up with action plan than this per me.   You will be able to quickly find solutions that cater the needs of challenges the team is facing.  For example, say your clients are seeing issues on UAT more frequently than expected.  Think about what you can do on process perspective to fill this gap.  Yes, you guessed it right, add a layer of regression testing in there and you will guard the team better from bitterness. 🙂
    If time permits, you can also add in a layer of code reviews and that could basically help the situation before even it happens.
  3. Listen and Document
    When you gain the momentum and feeling good about getting the information that would help you gauge the situation, pay close attention and start documenting the key points to which you can think of solutions later on for a productive road map.
  4. Time vs Quality
    This is all time debate and we humans are always fall short on it, but, see if there are any gaps on estimations.  Per me estimations are important as they set expectations on actual software development life cycle.  This is very important to check if team has been comfortable with estimates so far or if there is lack of time for a specific task they do on sprints.
  5. Talk Deployments
    Every project runs through it’s own style of deployments, so, it gets important to understand if there are any inefficiencies that you can cater to and subtly mold to something that is still easier to manage, but, could solve the problems if any.

You got all the information, now what, usually, this is thrown in to an email some where and forgotten.  Do not repeat that mistake, carefully think about main points that were gathered and you have to come up with at least one action plan per pain point.   Talk about these action plans with any one who is associated with the project including clients if any.  They will totally appreciate the effort and direction.  Well, at the end of the day, we all want to have best experience and enjoy what we are doing right?

That is all for now.   I will share more life lessons around Technology leadership as and when I encounter them. 🙂

 

 

 

 

MVC Routes, Redirection and Sitecore

I know there has been a loooong pause after my last series, past few months have been a crazy head down time doing not so Sitecorish stuff and more on API front.   I like to blog something new I learnt that is totally Sitecore related or affiliated, so, I had to resist the temptation to blog something that is not so uncommon or known by most of us.

Last week I got a kick of something that should be or was assumed to be straight forward, but, just stumped me that I did not know this before. 🙂

In most of the ideal projects of Sitecore, even if they are MVC driven, we do not need to use the custom routes for the most part.  The default one should usually be good enough and if you did have api specific post/get calls that fall through controller that should also have been working fine with out any additional configuration.

So, what if your implementation calls for rendering a specific content item based on URL specific to your needs.  Well, in this case you will see yourself in some trouble if you have patched your Redirection logic to ItemResolver or your customItemResolver for instance.   The problem is the page does not render and correct controllers do not hit, my pages were literally blank leading me no where, I was seeing a ghost in-front of me. lol

It took me some time to figure out what just happened to a page that was working literally before this build, I had to backtrack my steps and realized I fixed a configuration that has now added a new patch to my ItemResolver.  I could have dropped it, but, we all need that Redirect patch don’t we? The marketing and content authors love it and absolutely need it.   So, I had no option but to fix it.

So, to fix this first ensure your order is correct that is –

Item Resolver -> Your Custom Item Resolver -> No sitecore item found -> Redirect Manager Patch -> Still nothing -> Page Not Found Handling

Now, above is typical intuitive flow, but, do understand all projects and baseline could be different and hence you will have to ensure the order is correct according to your application.

My order seems correct, but, still I see the ghost page.  Why ? Why?

So, here is why, if you see closely on your showconfig.aspx, you will see MVC Routing is happening after the above steps noted as we typically end up patching our Redirect logic to ItemResolver leading to issues.  Now, there are two ways you can fix these issues now that you know the cause.

  1. In your custom item resolver, ensure you are configuring and adding code to resolve item from MVC Routes.  It should be pretty straight forward and there should be bunch of examples I am sure to get this done with lot of duplication of code based on how many routes you have actually got going on your RouteConfig.cs
  2. You could also save your self some copy paste effort(I usually make tons of mistakes when I do copy paste, it makes me lazy. 😉 ) and patch your Redirect logic to right after MVC pipelines.  Below is a screenshot of how that may look like –

Order_redirect

Hopefully it will save you some time thinking why your custom routes are not working when redirect is used along side of ItemResolver.  Happy Sitecoring people!

I promise to blog back pretty soon this time and I am super excited to meet all my favorite people in Orlando, Florida for the symposium.  This is my first symposium attendance and being an MVP this year as well is a total icing on the experience. 🙂

Localization of your search page

speak the language

Couple weeks ago I was psyched to start a series on our first hand experience dealing with Coveo for Sitecore on a multi-tenant/multi-site Sitecore instance.  Check out my first two posts below:

Link Manager Gotcha
Get Your Links right

Now, last, but, not least when you have such huge instance of Sitecore, you are bent to have things you would want to localize due to multiple languages you are supporting.  I usually see the solution in two folds.

  1. Coveo OOTB component localization
  2. Custom string localization – Like Facets or any strings on your search listing card that are computed field for instance.


The first one is quite simple and easy to do as Coveo carries responsibility OOTB to load most close culture based on context language.  If you go to viewsource on your search page, you will see that Coveo has already done some magic for you.  It loads the culture *.js file based on your context language.    This happens right the view based on below line of code on Coveo Search View. 

<script type="text/javascript" src="/Coveo/js/cultures/@(Model.CultureName).js"></script>

That should take care of your result summary on left top corner above your results.   For your sorting components which you may have added using your DateSortView or RelevancySortView, to localize the sorting header such as “Relevancy” or “Date” in context language, Coveo looks for special data attribute on HTML called ‘data-caption’ and if your HTML element for your facet has this attribute loaded with your title and if that is a existing key on locales dictionary object of your interested culture/language then it is respected and corresponding localized string is shown on your page. 

Now, for the second fold, if you would like to localize your Facet values, Headers, etc., There are few things to consider/answer – Do you show just one language content on your search page per site?  If you are – Life is simple, just ensure you have localized content loaded on your Facet field.  If your facet is based on computed field, ensure you are loading language specific value based on currently indexed item language.   For your facet header to be localized, you have couple of options here as well, you can either read it from your localized dictionary on Sitecore if you have one, if not, load up your JS localize dictionary with new values as noted in the link below.

https://docs.coveo.com/en/421/javascript-search-framework/changing-the-language-of-your-search-interface

Now, the problem actually happens when you have more than one language content show up on your search page, in that case, you will have an issue with mixed values which obviously is not UX friendly.    You can see a good solution and huddle between me and my favorite Sitecore MVP and Coveo champ below(@jflh ). 🙂

https://answers.coveo.com/questions/16338/localize-facet-values-on-multi-site-solution-using.html?childToView=16358#comment-16358

This is the last piece of this series and I am sure there will be more to come, this one is near and dear to my heart and the amount of learning and information I gathered through this Implementation is priceless.  Although, Coveo and Sitecore never fail to amaze me, there is more room to learn and grow with these products every single day. 😉

Happy Coveo for sitecore implementation to every one!

Some helpful links:

https://developers.coveo.com/display/public/JsSearch/Localization

https://docs.coveo.com/en/421/javascript-search-framework/changing-the-language-of-your-search-interface

 

Coveo and Sitecore Link Provider

links

My last post was a kick off a series of things to watch out for and pay attention to while implementing Coveo For Sitecore on a Multi-tenant/Multi-Site Sitecore instance.   This is a runner-up for my last post and part of same series.  Let me start this by saying, URL on search result is single most important entry point for your user to see detailed content around what they think is relevant.  To ensure this is rendered right every single time,  It is very critical that you know your Sitecore Link provider or Custom Link provider if any in and out on your Coveo For Sitecore installation.

There are few things that you will need to know in order to fine tune anything or in order to debug issues if any when you see things you do not wish to see in relation to Url’s either on Indexes or on your search page.  If you are lucky and do not have much of customization going in your Sitecore link provider, you should be fine doing nothing as Coveo fires off link provider at it’s two most important phases OOTB.

  1. Index Sync Process
  2. Displaying paged results on Coveo driven search page

Now, the most important thing to remember is that while Coveo does #1 noted above it calls Sitecore Link Provider in “shell” context while doing it’s best to load up UrlOptions object that is passed in as a parameter to Sitecore GetItemURL method of Link Provider with appropriate site details it could resolve the currently indexed item with.   Coveo has their algorithm that runs as part of ResolveItemSiteProcessor located in coveoResolveItemSite pipeline.  Below is the snippet of that processor located on Coveo.SearchProvider.Config

<coveoResolveItemSite>
<processor type="Coveo.SearchProvider.Processors.ResolveItemSiteProcessor, Coveo.SearchProviderBase" />
</coveoResolveItemSite>

If this processor is not working wonders for you, go ahead and remove this in your favorite custom coveo configuration file and add your own.   For curious brains,  I recommend changing your level of logging to DEBUG to understand what happens step by step while Coveo tries syncing your Index.    That was very insightful in understanding sequence of calls and pipelines that fire while Coveo is indexing or doing sync process.  But, do remember change that back to INFO after your appetite is satisfied. 😉

Now, for the last clue,  on #2 stated above when your Coveo Search Page renders your search results for a query, it calls Sitecore link provider again to get the click uri for each search result that goes with each search result.  Now, pay attention here, very important to know that the context site this call runs in is “coveorest”.  If your link provider is super awesome and written very well thought out, you will have no problems regardless on the context site the call is running in.  But, if your link provider is using context.site instead of using site info from UrlOptions or doing some thing really out of this world, you have to know these things to ensure you hit the gold.

Hopefully these tidbits will help you debug any issues while Coveo constructs the URL’s at indexing or run time.  If you have lot of shared content that does not reside under a specific site node, then, it is very hard to get the URL right at indexing time which is why it is even more important to focus on #2 to ensure the end user experience is seamless and correct leading them to correct site, language and context.

There, I gave away a hint, next up, I will be covering language related insights like fall back and localization in my next post.  That is all for now, tune in soon for more.  🙂

 

 

 

 

 

Implement Coveo on Multi Tenant/Multi Language Architecture

I am very excited to start this series as we are almost close to pulling off Coveo Cloud Implementation on a multi site/multi language Sitecore Instance using legacy Coveo For Sitecore Javascript Framework to support web forms Sitecore Implementation.  I am gathering my thoughts around all the challenges we faced while getting Coveo to work seamlessly with complex Sitecore Instance.  Some were very straight forward and some were anything but that. 🙂

I am going to list down each important problem and note options to tackle each.  Do note that though most of the solutions I would attempt to provide are generic, it highly depends on your Sitecore implementation and customization done to Sitecore in general.

One thing that really helped me solve some of these is understanding when and where Coveo connects with Sitecore.   I would recommend to deep dive on this to any one who is attempting to implement Coveo For Sitecore on their multi tenant and multi lingual Sitecore implementation.

I will start by noting the first problem we faced right after Coveo for sitecore installation on our system.  On Diagnostics page, we had the error such as noted in question and answer below.

https://answers.coveo.com/questions/15435/coveo-search-rest-endpoint-invalid-uri-the-format.html?childToView=15530#comment-15530

One of the first question to answer when implementing Coveo For Sitecore is – Does your Sitecore implementation have a custom link manager as that drives what you would need to watch out for or customize if needed, a quick way to check would be

-> Go to your /sitecore/admin/showconfig.aspx and examine what you see in the node below.  if you see default provider as something other than “sitecore”, then, you can ensure that there has been some customization done on this.  To be honest, any medium to high sized Multi tenant instance that has been active on Sitecore for long time would have some customization around it.  So, it is even more important to know how to ensure Coveo works with your custom link provider settings.

link provider section on config

For instance, in our case languageembedding setting on Linkprovider was set to “always” which means that any website/site served by Sitecore, when a link is generated it would append language to it.  That should surely ring some bells, in  Coveo For Sitecore case upon installation of package, you would see three new sites in the <sites> node when you take a peek at showconfig which are named as coveo_website, coveoanalytics, coveorest.  We definitely do not want to add language embedding on these URL’s here to ensure we do not throw off internal business logic on Coveo end.

To gracefully solve this, I would recommend having an ability to use different Link Providers per site.  There is a very decent solution compiled by my favorite Sitecore MVP Jammy Kam below.

Site Specific Link Provider for Multisite Implementation in Sitecore

By doing this we can assure that Coveo specific sites do not have language embedding in it that would cause issue noted on Q&A above.

Of course, if your sitecore implementation was done more on old school style where the answer was – There is a pipeline for that. 🙂

In those cases, check to see where you can ensure to exclude Coveo sites from having language embedding on them.

One open question I do have is would this be any different with Sitecore 9.0+, a short answer might be a NO.  If any one has any input in regards to this, leave a comment.

That is it about our first ever hiccup, more to come soon.  My hope is that this series will help any one who is driven to use Coveo for Sitecore components and power of framework on a complex Sitecore instance, but, are worried that it might be quite a feat or they might be safe using total custom system.  You can re-visit my blog on this topic and I am sure you will get confidence in sticking to framework instead.  🙂

Tune in for more on this series!

 

 

Experiment with Ease

background experiment

When you are stuck in a problem, do not get scared to experiment!  You might be successful at solving the problem or not, but, every time you let go of your fears and experiment, you end up learning some thing new every single time.

On one of our heavy custom implementation with Coveo due to super complex architecture which uses Web forms, I exactly had to to that.  Funny story,  I am so used to MVC that literally when I dream of a solution I do so, you guessed it right in MVC. 🙂

So, we had a situation where we need to use Coveo Search Box component on shared content which could span across multiple sites and hence the URL should not be based on item and should rather be based on context.  While looking for a solution I bumped in to the below Q&A, but, the solution was for MVC.

https://answers.coveo.com/questions/9939/how-to-configure-coveo-search-box-view-rendering-o.html

I wanted to see how I can implement this on web forms and searched to find a parallel solve for the same with no luck.   So, the only option left was to experiment to see if it will actually work.

To prove my theory, I opened up couple Coveo dll’s to see how I can use Inheritance to swap the Model.  Turns out, it is possible to swap the Model out with few extra steps than MVC.  Do note, that in MVC the model swapping could be done on the View Rendering item to use your custom coveo search box model instead of OOTB coveo model.

So, below are the extra steps for web forms to achieve what is noted for MVC on the Q&A.  We tried these below and it works perfectly alright. 🙂 Rest of the steps should be similar to what is noted on the Q&A noted above, so, I will not repeat the awesome steps that Jean-François L’Heureux provided.

  1. In your duplicated Custom Coveo Search Box sublayout, let us call it CustomCoveoSearchBox.ascx change the inherits to your own custom class
    Inherits=”Coveo.UI.CoveoSearchbox”
    Would become
    Inherits=”ProjectName.Search.Extensions.CustomCoveoSearchbox”
    Use your best judgement or project guidelines to decide a namespace for your custom coveo search box class. 🙂
  2. Next step is to pass in your CustomSearchboxModel (a new custom class that would inherit SearchboxModel) in the OnLoad override of your CustomCoveoSearchBox  class.   Your CustomSearchboxModel would provide your new implementation for GetSearchPageUrl() that would have logic to determine the URL based on site context and other decision factors.

A quick screenshot from Dotpeek of Coveo.UI that helped me with the decisions.

CoveoSearchModel

Hope this helps some one battling the situation on web forms based Sitecore instance.

 

 

Coveo Cloud Learnings – Part 3

Follow my trail if you have not  read coveo cloud part 1 and 2.  Get a read on them here –

Coveo Cloud Learning – Part one 

Coveo Cloud Learning – Part two 

This is the last blog of this series, but, of course there is no end to learning.  I will continue to post new blogs as and when I encounter an interesting thing to share with you all as I proceed to take on new challenges and projects.

On this blog, I will cover how we used underscore, organized our views for better maintainability and all of this with Helix in play.

First though, Coveo does do an awesome job of documenting how to split result templates on conditions which comes in handy in various json situations.  Learn more here:
https://docs.coveo.com/en/413/javascript-search-framework/result-templates

After some pondering, we did go with data-condition route on our result templates.  We also have to remember to have a default result template at the last in the pile.  For better maintainability, we chose to add in partial views in MVC.  We do not have dynamic views as we only have a set of types.  If your implementation calls for content author control over these, you might choose to provide that additional layer of flexibility.  For us, it is an overhead in various ways, so, we avoided that and loaded the partial views on our own custom coveo search view.

Some thing like below:

@Html.Partial(“~/Views/Search/ResultTemplates/ProductResultTemplate.cshtml”, Model)
@Html.Partial(“~/Views/Search/ResultTemplates/StandardResultTemplate.cshtml”, Model)
@Html.Partial(“~/Views/Search/ResultTemplates/GeneralResultTemplate.cshtml”, Model)

Also, we definitely needed some same key attributes on all of this views and we do not want the Controller code to actually hit every single time, it would be in-efficient.  So, we chose to have a custom coveo model that inherits from the Search Model available in Coveo.UI.Mvc.Models with the additional attributes we need.   It was quite easy to make this happen, also, these instructions noted here might come in handy if you are trying something similar.   Awesome post by Vincent. 🙂 Basically, chose bits and pieces I needed to pull this off.

https://source.coveo.com/2014/12/10/creating-custom-model-coveo-for-sitecore/

Now, we have manageable pieces, so, we can work parallel on different result templates and also great for the future when it is time for enhancements.   In real life situations, it becomes very important while working with underscore templates to ensure a specific json piece you are looking for is not undefined and has a value in it, we had quite a few requirements to not even show a matching label on the view if a specific json attribute is missing.  We accomplished that with a simple underscore check as below on those applicable.  Below is a snippet for your reference.

{{ if (!_.isUndefined(raw.yearoforigin) && !_.isEmpty(raw.yearoforigin) ) { }}
<li class=”coveo-product-details__item”>
<span class=”coveo-product-details__category”>@Translate.Text(“Year of Origin”)</span>
<span>{{=raw.yearoforigin}}</span>
<a href=”#” class=”more”>(+)</a>
</li>
{{ } }}

Also, note that we continue to follow Sitecore best practices on the side to load all labels from Dictionary instead of hard coding on the view.  Another thing to note, is for the data conditions you need on the result templates, if it is a static value such as a GUID or string, always follow Helix best practice of having these defined in Templates.cs file of your project.  In our case, we maintained this in Feature Search project.  So, the data condition and templates.cs would look like –

<script id=’faqResultTemplate’ class=’result-template’ type=’text/underscore’ data-condition=’raw.contenttype==”@Templates.ContentTypes.FAQType”‘>

public struct ContentTypes
{
public static readonly string FAQType = “FAQs”;
}

I know, I wish this is not a string and is a GUID, but, unfortunately due to content structure we had to work with has these distributed out in content areas rather than defined templates, so, had to go this route.  But, where you can always rely on GUIDS as that is more stable implementation.

Also, we had lot of external fields which need to be used as Facets in our case.  There is documentation on how to make an external field Facetable, but, one thing to remember is actually a combination that you will need to do to make this work.  You have to do what is noted here and also ensure you add the field to external fields section as well.  As always,  it is always recommended to patch using a special config file on your Helix project rather than updating either custom coveo config  or raw coveo config file.

Say, if I had a field on my Cloud Index called contenttype, I would then add the following on this patch config file to ensure it is facetable and hash is excluded considering this is an external index.

<configuration xmlns:patch=”http://www.sitecore.net/xmlconfig/”>
<sitecore>
<coveo>
<defaultIndexConfiguration>
<fieldMap>
<fieldNames hint=”raw:AddFieldByFieldName”>fieldType fieldName=”contenttype” isExternal=”true” settingType=”Coveo.Framework.Configuration.FieldConfiguration, Coveo.Framework” />
</fieldNames>
</fieldMap>
<fieldMap>
<externalFields hint=”raw:AddExternalField”>
<field fieldName=”contenttype” />
</fieldMap>
</defaultIndexConfiguration>
</coveo>
</sitecore>
</configuration>

Also see this interesting post below that we used to migrate all our external fields on to production sandbox when we are ready.  We had lot of custom fields added to sandbox due to what we were trying to accomplish and wanted to eliminate manual work to load these up and replicate to avoid error and mundane task.  Thanks to coveo team for letting us know a better way does exist.  Take a peek of that here

Another interesting thing I bumped in to while working on this project was how some functionality is abstracted out from us on Cloud as compared to on-premise.  On Cloud, especially when working with trail sandboxes, if you queue way too many rebuilds, it might take a while, so, do not panic and give it some time.  All should be fine unless there is something else going on, if your logs are clean and no obvious errors on cloud console logs, you should be fine and it is just a delay on the cloud queue as trial sandboxes might not be given immediate preference.  If in doubt, talk to your Coveo point of contact and they will calm you down. 🙂

Also, when you are in active indexing state, say items are being added to your external index on cloud.  Sometimes, same queries and parameters passed from your Coveo Search Page/Components could yield different results due to elastic nature of Coveo.  Obviously, this should not happen when indexing is done.  But, I was super curious to know the reason behind this as I was doing some smoke test on my end.

That is all for now you all, have fun and enjoy the week ahead. Happy Monday!

Leave me a comment if you are curious to know more.

 

 

 

 

 

 

Coveo Cloud Learnings – Part 2

search

Last week, I kick started a planned series in effort to pour over my side of story using Coveo Cloud platform first hand.  You can read more on the initial piece here

I am hoping this series will help any one starting a Coveo cloud implementation in making appropriate choices along the way based on project and requirements on hand.  I would also post catches/gotchas that I experienced along this journey as well.

Now,  let us move on to more.  After the initial steps, there are more choices to make such as below.  I wish I could say – go with your  gut, lol.  Always go by a thoughtful and knowledgeable call on each of these.  When in question, use rock solid support from Coveo and Sitecore team.   You can ping on slack channel or load a Q&A here .  More than likely, if your decision is a basic one, there could be one already out there. 🙂

Ensure to squeeze out as much as you can from the OOTB product, Coveo Cloud offers Machine learning and AI that could help users find relevant results.  So, embrace it and try to make it work to your architecture and timing.   In our case, we thought query suggestions are very important.  But, what to do when there is not enough data available on Coveo Analytics that would get back ML query suggestions.  Ensure you always have a back up plan!  We turned on result suggestions with Omnibox component as a back up.  Although, be very careful and diligent to drop this off as these queries are accounted towards the quota you signed up for with Coveo.

A parallel note on ML/Query Suggestions and reminder to ensure to change the tuning parameters on your test sandbox to test your ML query suggestions.  If you leave them to default, it might or not bring up query suggestions.   If you do not know where to find them, go to your Coveo Administration console either via sitecore or direct link if you have one on you, then, click on Query Pipelines under Search section, pick a model you want to tune the settings on and you will see the screen below

trainingset

I also was curious to see and test the ML query suggestions, so, I followed the instructions noted here and could quickly create a new report of my own way to track if there are any ML query suggestions available on my model.

https://onlinehelp.coveo.com/en/cloud/reviewing_suggested_queries_by_coveo_machine_learning.htm

Our Architecture was subtly different as it uses older/legacy Sitecore version to push all the data on to Cloud index using Push API.  We had to ensure we are following all the good practices as much as we can to ensure Coveo can then perform and do it’s thing on relevancy.  Here is a good Q&A link and responses I got when I was eyeing and planning to ensure we are covered with this Architectural decisions we have in play.
https://answers.coveo.com/questions/14602/best-practices-when-using-push-api.html

Another little POC that I had to do on my side to ensure our Architecture decisions will not have any unknown risk on main requirements. One such requirement was to have Coveo Stand Alone search box to be available and working on older version of Sitecore site and it would then redirect to the new version of Sitecore search page that was built off of Coveo components and JS framework.  Initially, I had hiccups due to CORS issue as the domain was not matching, but, later all was cool when we had set up load balancer to swap between old and new sitecore versions.   Below is Q&A reference for this step as well, if you are curious
https://answers.coveo.com/questions/14985/can-we-have-a-coveo-component-html-on-a-site-while.html

Next up, I will add my notes around how we built the components and used underscore and any other caveats we encountered during the same.

That is all for now folks, if you need more information on this subtly different architecture we have in play, feel free to drop in a comment.   Have a great weekend! You all deserve it. 🙂

Coveo Cloud Learnings – Part 1

Coveo_Cloud

I am super excited to share my experiences with you as I rolled up my sleeve to play with super cool search tool out there, Coveo Cloud!

Before I got this chance to work intimately with this platform, I did hear a lot and read a lot.  Was wondering and anticipating, when will I get to actually do hands on and I really wished to do that before I head out to Coveo Impact at SanFrancisco this year. Between, crazy excited to hear all the awesome new things and also geared up to participate in their Hackathon event as well.  Know more about their event here below

http://impact.coveo.com/

Now, I plan to write up my experiences with Coveo cloud first hand, the project is near completion on my end and I think this is the perfect time to reflect on all the set of challenges we had while pulling off a simple yet complex due to Architecture decisions involved on the project.

First things first, when you are about to start implementation on Coveo for Sitecore platform, decide on your version and framework you plan to use.  This goes long way to ensure you picked the correct version that supports all your other related third party components and ensure it is indeed a right step towards desired search experience.

On this project, we chose December 2017 version matching the Sitecore version we are using on this instance.  My first comment on getting Coveo for Sitecore up and running on my local and other environments is “Piece of Cake”.  Compared to challenges, hiccups I and my fellow team members had with on premise version of Coveo, this was a total opposite.  It took me less than an hour as opposed two full days(give or take) with on-premise version.  It was an awesome feeling to see the green quite quickly this time.

While you are at this step in your project, also, decide which version of JS framework would you like to use.  I would vote for Hive as it is new, more robust and modular, but, it highly depends on project of course.   You can see compatibility and more decision factors here on their documentation.

https://developers.coveo.com/display/public/SitecoreV4/What%27s+New+In+Coveo+for+Sitecore+4

While I was doing the steps or while trying to understand the new platform, I tried comparing and contrasting to the knowledge I had working with their on-premise version.  It did help me a lot actually trying to figure – “How is this different from before?”

Some helpful notes on Installation and Set up with Cloud.   Do note that your sitecore indexes, both, master and web would be now on Cloud and so are your logs, content browser and other cool things you were used to pulling up from port 8081, it should now all be located on Coveo Cloud Console. Also, understand that some features are now abstracted out, like you can not turn off and on the live monitoring on indexes for instance.  One important thing to realize is all your index rebuilds are queued in some fashion which you may not have control over, so, talk to your Coveo consultant or drop in a support ticket if you encounter some delays more than usual on your rebuilds.

On our end, we always ensure to use cloud sandbox(trial) for all our lower environments.  Note that there could be some limit on number of indexes on Sandbox, so, check with your Coveo rep when in question.

One subtle catch I would like to mention is – after installation of Coveo for Sitecore and hooking up proper Coveo Sandbox configuration and a full site publish, I did see the web index on the cloud from Sitecore perspective, but, note that master did need a manual rebuild by going in to your Indexing Manager on Sitecore Control Panel.

If you got time on you, explore the Coveo Cloud Administration Console, it has different and clearly named sections that should give you fair idea on which is what, but, it is definitely worth going over this as you drill deep in to Cloud Implementation.

Also, note that with Cloud you can build sample search pages on the fly, it is a cool feature to test some of the fields you have on your index or could be a custom build external index that you would like to target, but, if your implementation calls for some thing more complex, I would recommend to house your search pages within your sitecore instance by customizing the components and adding them to your presentation plus creating the templates.

So, you installed Coveo For Sitecore cloud version, got all green in diagnostics, sitecore indexes in check and could see your items and indexed data on cloud console content browser.  Did I hear that right? That is it, we can now jump right ahead and do some cool stuff that is made possible by the platform.

My next blog will focus on more challenges, gotchas and learning we had during this wave of time.   Also, will expound on choices we made at each step as well.

References/Helpful Resources for Coveo For Sitecore Cloud 

Installation guide:
https://developers.coveo.com/display/public/SitecoreV4/Installing+Coveo+for+Sitecore

Architecture and Beyond
https://developers.coveo.com/display/public/SitecoreV4/Understanding+the+Architecture+of+Coveo+for+Sitecore

Framework Choice

https://developers.coveo.com/display/public/SitecoreV4/What%27s+New+In+Coveo+for+Sitecore+4

Coveo Impact

http://impact.coveo.com/

Versions

https://developers.coveo.com/display/public/SitecoreV4/Downloads

Sitecore 9 Installation, My Quest to not give up

I know, I am super late! It has just been a super crazy month on my end due to typical deadlines and crunch times, but, would like to post this before we get on to more awesome things in the coming future.  The speed at which we are racing,  within next two months, I believe Sitecore 9 and anything to do with it would be called essentials. 🙂

I had made a note on my last blog here that I will update every one on one unique caveat and many more known devils I had faced while getting the taste of latest and greatest.  Now, below are the my n different things I had to do to see the beautiful lady on my favorite browser and oh boy! I hopped with joy when I finally did see her.   She looked more gorgeous than ever and yes, I knew I was in the game when I got a glimpse of her.

While I was hitting so many problems and walls, there were so many blogs and findings that helped me debug each and move forward.  Beautiful community!
Except for one issue that stood strong between me and my new toy, just in case if you run in to this, hopefully it will help you and save few pesky hours of debugging.

  1. I used the most favorite and easiest way to get Solr up and running on my local by running a pre-made script noted here – https://gist.github.com/jermdavis/8d8a79f680505f1074153f02f70b9105
  2. Had to get my SQL 2016 as that was stated as a pre-requisite and wanted to ensure I do not miss any thing
  3. Also, ensured I get latest version of powershell by installing updates and patches on my windows.  Those things you put off because you simply do not want to restart your computer. lol, I repented it like always.
  4. Also, ensure you have everything noted up on pre-requisites in installation guide.  Sitecore is not being funny and yep everything noted here is indeed a pre-requisite and is needed including modification of any registry entries to avoid some super weird SQL issues.

Now, after I did all this and followed through a lot of blogs, I still could not overcome one final error on my end on running the powershell script.  I failed and tried again like I do not remember how many times and I also kind of lost count on how many hours I had spent googling if I missed anything else.

Below, was the error I was seeing on the powershell.

“Xconnect windows service fails to start”

My first thought based on many reads was my license file is not legit and is not authorized to be used with Sitecore 9.0+.  And that is what I saw all over the web for an error such as above. But, after I heard from some one else who used exact same license as I did was successful in setting up the site, kind of huge burst of a bubble there, I could literally hear a pop.  I went to event viewer and checked all the errors, I could see a message that license was accepted, so, that was not it.

Now,  I was at verge of giving up as I was totally numb and super freakishly tired.   I am obviously doing something very silly, just could not point what.

While browsing the community, forums, blogs for similar errors, I did find one clue that helped me realize what is it that xconnect was not liking and hence not starting causing the powershell to crash on me.

https://community.sitecore.net/developers/f/5/t/8398

On this thread here, something caught my eye, though this was not the issue I was having on hand, it was worth the shot of running service manually to see if the error on command prompt does give some insight on what the issue is. So, I went to the location where exe is located on my inetpub and gave it a whirl.  I saw an error message relating to format exception on Solr URL.  Crazy right! I did check to ensure my solr url I had configured quite easily on my step #1 is correct.  But, why does it not like it, so, when you configure Solr and try to browse to the url, it actually appends a hash to it by inherent redirect.  That was the URL i had copied to my install script.   It looked like – https://solr:8983/solr/#/

Apparently, xconnect service does not like the url of such format though it is actually a working Solr url. So, pay attention to your url of Solr on PS script. It should look like
https://solr:8983/solr

Once, I did this all was well and I could spin up a Sitecore 9.0+ site on my local.

Hope this helps some one else for easy way of debugging such type of issues!

 

 

 

24 Hours

24 hours

This has been long due and would like to share my hackathon experience before the memory gets a tad bit weak.  First things first, I must say that I handled the competition with way more ease than first time around.  Who ever said it, yes, it is absolutely correct that “Experience does makes you perfect”

I tried to remind myself and my comrade multiple times that hackathon is only an implementation of scratch of the surface stating the future to explore further.  If you loose this thought, you will find yourself being extremely fatigued and will end up doing bunch of mistakes and miss prerequisites.

The goal is to shoot for stars, but, also, ensure you have a clear picture on what you would call a deliverable.  All in all it was fun and calm for the most part with brain juices flowing pretty well.

You can read about what we did and how we did it – up here.

https://github.com/Sitecore-Hackathon/2018-Witty-Geeks/tree/master/documentation

There is a little video we made as well as a proud participants, but, tired ones. 🙂

Here you go – https://recordings.join.me/H8TFZihidUGXBwppem5_pw

In my next post, I wanted to share my Sitecore 9.1 installation experience with SIF tools and all the pitfalls I bumped in to, luckily there was a blog for most of them.  Of course, I had one unique one which might help some, so, will post that soon.

Cya later!

PS:  If you do not know what hackathon I am talking about, see the details below.

http://www.sitecorehackathon.org/sitecore-hackathon-2018/

A Battle – Coveo Search Framework | Coveo Search API

I am going to give it away to you all in who is the winner. 🙂

In reality, I think there is no battle when there is a clear winner and your vision to pursue should always be to use Coveo for Sitecore platform.  When the world turns up side down, you hit the wall like 100 times and when Coveo team also gives up only then you would pick the other option to build ground up.

Folks who love to code to solve problems or passionate to take full control in to their own hands in terms of business logic implementation have to remember and think about why did the executives spent money and time investing in Coveo For Sitecore.  Now, with the cloud version, their biggest selling point is of no suspense, heavy contexual marketing and machine learning are sure a huge cherry on top to their easy to integrate search experience.  Between, if you have installed Coveo for Sitecore on-premise and then also had experience with Coveo For Sitecore cloud, you will have to agree with me.  It took minute slice of earlier to set up cloud platform on a Sitecore instance.

Recently, after we did a thorough comparison between these two options for one of our clients, Coveo team released their best practices, I included the link here.  If you have not already, please check this out, it is very useful.  I definitely learned and shared a thing or two from here with my teams.  Now, this document just solidified what we pitched and I could not be more happier. I mean getting a backing from Technical Architects from Coveo on what we thought was correct approach.  Nice feeling you all!

Coveo Best Practices Document:  https://developers.coveo.com/display/public/SitecoreV4/Coveo+for+Sitecore+Project+Guide

Hint:  Look at Page Number 52 – Section Developing Against the Coveo Search API directly

I would like to share pros/cons of these approaches for any one who is trying to convince their client to not lean towards re-building something they don’t have to. 🙂

To keep this to the point, I will list what you will miss if you go with using Coveo Search API instead of Coveo for Sitecore framework.

Disclaimer:  Got to Hurt…Ouch!!!

  1. You will forgo ability to be able to add in Coveo Custom Components, they are becoming more and more powerful especially if you have Enterprise and above license where you hardly would need to write any JS, just use properties as much as you can to pass in those Filtering and other settings.
  2. Incremental refresh of Indexes – Depending on how huge your solution is, this will hurt you more. 😉 Coveo when integrated with your sitecore deeply you will notice that it taps on to most important pipelines to ensure it keeps your master and web indexes in sync using some clever strategies.  That means it will not rebuild the whole indexes until you request it to do so, saving lot of time and being efficient.  Web sources and other type of indexes you might create might not have this feature.
  3. Machine Learning/Coveo Analytics and any thing beyond.  Now, let me re-iterate the obvious, the world is moving towards AI for better or for worse and this might be one the biggest reasons why Coveo was a big win and hence the decision to go forward with the product.  Coveo Analytics are collected through one of their component that when added to search interface will push all the goodness ML would need to then use awesome Coveo ML features such as query suggestions and ART for instance.  You will miss out on this if you do not install the framework.  Now, I do see some API’s with which you can get usage analytics and either do a read or write to Coveo Analytics using the same, but, why go through this process when you can simply add a component that has been built and tested by Coveo.  If you are curious –

https://usageanalytics.coveo.com/docs/write/

https://usageanalytics.coveo.com/docs/read/

There could be more?  you think you know some? Leave a comment.

Now, remember all this losses while making a decision, but, also do understand that in some really complicated architectures where there seem to be no way that we can seamlessly integrate and use full power of Coveo platform then Coveo Search API in combination of a lot of custom code might be a way.  But, before you go that route I urge you talk to every one you think might know a different, better or easier way to fully juice and extract the goodness of the platform.  At the end of the project, we definitely do not want regret on our plate on this type of decisions.

Now go out there and make some tough decisions. 🙂

Sitecore Image Optimization

Optimize Images

Time and time again you see yourself solving an issue or providing a solution to a problem that seem to exist regardless of breadth of solution.  One such problem I noted on my clients is Images, no matter how advanced the actual sitecore implementation is, for some reason Images and their handling are not taken seriously enough.

On the projects where I am leading the technical effort, I almost always ensure and push the team to think about this because when we launch the site, it is sure very important to get the functionality right, but, what is also important is to ensure tools like Google page speeds and others out there give your effort a good ranking.  You might say, well it is just a number, but, it is important to understand the number is mere representation how fast and efficient your solution is and hence the numbers and issues are important.  At the end of the day, you want to see your website where you put in so much effort and money lands well on Google. 🙂

I am a true believer that we should never be re-inventing the wheel and always see where we can get with couple of smart solutions out there that folks use for this very purpose.  I toyed around with couple of options and compared them on different aspects, so, we can make a decision on next steps to get us where we would like to be.

Below are my findings on this comparison.

Note:  I am not supportive of either of these options I researched and only providing my thoughts around it.  Not meaning to offend any one or praise any one either.  🙂

Please leave a comment if you have any more thoughts or options that could prove to be useful.

Tools investigated are:

Dianoga – https://github.com/kamsar/Dianoga/blob/master/README.md

Image Crunch – https://github.com/1508/SitecoreExtension.ImageCrunch

Comparision Sheet between the favorites

 DianogaKraken Based API calls
Installation and Set upWorks OOTB, no need to tailor or modify unless very specific needs arise
If CDN is in play, additional configuration might be needed. See some more steps noted for CDN set up here:
https://github.com/kamsar/Dianoga
We will need to test with CDN in play to ensure it still works with this additional steps
• Would need some honing as module does not seem to be perfect and need some issues to be addressed. See Git hub notes by me to the author –
https://github.com/vishalniit/InSitecore.Sitecore.ImageCrunch/issues/1
• API keys based on subscription for Kraken
Custom CodeNot needed for optimization specificallyWill be needed more than likely to ensure we tailor the solution to our needs. Can take reference from the module suggested and make it our own.
SubscriptionFreeMonthly subscription needed
https://kraken.io/pricing
Google Page SpeedStill few images not compressed to what Google expects it to be at.
But, I came to know that Dianoga works it's best with scaling parameters, so check that out if your implementation does not have scaling in place.
Compression respected by Google algorithm
Served FromMedia Cache
Image should be available on media cache which is based on request to the resource, so, optimization is on-demand
Media Library
Hook is on media library, so, every time a new image is uploaded or attached Kraken is called to Optimize the same if needed.
For existing images, there are few calls we can make right from content author ribbon.
Publish would be needed to ensure optimized images are being served upon request
Cache should be cleared if not already in play on CDN to ensure newly optimized images will be served

That is it you guys, hopefully this helps some one looking for a solution to optimize their Sitecore imagery.  Both of these options are unique in terms of how they deal with the problem on hand.  Ultimately, both seem to be good, so, picking an option would be mainly based on business and development teams preference based on their implementation.

Maneuver crawler to your will

control

I was reviewing one of our client websites and found few issues which are considered to be SEO pitfalls.  Would like to share this, I am assuming that most of us already know this, but, in this myriad of things we need to keep a tap on, it is almost necessary to remind ourselves just how important SEO aspect for the website you will build actually is.   Sitemap is almost an inherent choice and much needed one to submit to the search engine giants in some way or choice.

But, it is essential to remember what not to push to sitemap as crawler is busy doing it’s thing and indexing every bit of your fresh content as much as it can.   Few things to always remember –

  1. Ensure to not add any urls that either do not have any content or could lead to 404.  This is very essential to make sure we are not asking crawler to even for a milli-second think about pages that are not important and this will help ensure you don’t set up your own pitfall towards having a red mark on Google for instance.  You can do this by providing a way to either exclude content or templates when sitemap is being generated, easy enough.  But, is often missed. 🙂
  2. Now, if you are using any folders or intermediate content for just solely organizational purpose, ensure you have proper redirects in play to make sure if some one is either being smart or received an improper url, your set up does a terrific job of placing the end user where you wish him/her to be looking at when they pull up your site.  Magic, yeah. lol.  For the other side of users which is your content authors, ensure you have proper insert options and templates filled in with beautiful presentation that would then take care of all in the background keeping content authoring simple and easy peasy.

You do not have such fancy sitemap generation on your sitecore instance? Look the following options which are my favorite.  Setting these up should be real simple, but, as always tricky part is maintaining the solution and training some one who uses it to know these things that they can do to keep crawlers in check.

References / Suggestions 

https://marketplace.sitecore.net/Modules/S/SitemapXml.aspx

https://marketplace.sitecore.net/en/Modules/XML_Sitemap_Generator.aspx

https://github.com/JimmieOverby/SitecoreSitemapXML    — My Personal Favorite with more customization

Tons of other modules on market place, explore more:

https://marketplace.sitecore.net/SearchResults#query=sitemap