Tag Archives: sitecore

Sitecore CMS

SXA Best Practices Audit

Sitecore SXA Best Practices Audit

Are you following best practices in your Sitecore SXA implementation? If not, there can be consequences down the road in the form of technical debt or a full re-implementation down the road sooner than expected if the best practices were never adhered to in the beginning of development. A Sitecore SXA audit is the way to go up front, during, and after your implementation to ensure that your Sitecore SXA development team are giving you the most robust implementation that will stand the test of time with upgrades to SXA and Sitecore while providing your Content Authors the flexibility they need to author content.

Preparing for a Sitecore SXA audit can help you to examine the current architecture for your client, then compare to the best practices recommended by the Sitecore SXA Team, then make recommendations based on your findings. These findings can, and should, steer development efforts after going through your audit. If there is a commitment to adhere to best practices, based on the audit, then there may be low – high impact to your solution and Sitecore architecture based on the technical debt accumulated over SXA development efforts in the past.

Below in each of the recommended practices, I will provide my thoughts on what may be necessary to bring the solution and Sitecore architecture to be in line with Sitecore SXA best practices before, during, and post development.

These best practices came from the SXA Team via an SXA Recommended Practices document that was put together awhile back by Adam Najmanowicz. This original documentation was used as the basis for the documentation that is in place now as official Sitecore documentation on SXA recommended practices. Big ups to Adam for putting that initial documentation together that we can use today as a baseline for a solid SXA implementation for clients!

Page Structure

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–structuring-pages.html

  • Design page layout in Partial Designs

    Partial designs will be your best friend with SXA along with Page Designs. Just think of a partial design as the old Standard Values for Page Types. You will use Partial Designs and then use them with Page Designs to create your pages. Using Standard Values is not recommended so you will need to get out of that thinking. Standard Values will still work, but it’s NOT considered best practice.

    Partial Designs Folder

    Partial Designs Folder

    If you revert to this best practice down the road you are looking at medium level of rework but high level of regression testing.

  • Put complex reusable structures in Partial Designs or composite components

    The point of SXA is to make your life easier not harder so put complex reusable into composite renderings like SXA has Carousel, Accordion, and Tabs out of the box. Those are typically complex components so if you have one that you are set to create then put it into a composite component like a Snippet, which has many components in one. These are things you will want to teach your developers as you build your SXA site out to be aware of. Snippets are very powerful so use them!

  • Remove unnecessary components from your site’s “Available renderings” to allow usage of styled components only

    This is basically to only show what your Content Editors will actually use, that of which is styled and looking good. If it’s not pull it out so nobody uses it. Be kind to your Content Authors.

    Available Renderings Folder

    Available Renderings Folder

    There is no real impact on what has already been done here so low level of rework here.

  • Set up placeholder restrictions

    This is so you can stop your Content Authors from inserting a component into a placeholder that they shouldn’t causing confusion and introducing possible bugs when the component should never have even been allowed in the first place.

    Placeholder Settings Folder

    Placeholder Settings Folder

    Your Content Authors will love you for helping them help themselves and is low effort to fix.

  • Do not use Standard Values to set up presentation details on your pages

    As I stated earlier, use Partial designs instead or possibly Snippets. Standard Values are a thing of the past with SXA.

    If you revert to this best practice down the road you are looking at medium level of rework but high level of regression testing.

  • Make use of Partial Design inheritance where it makes sense

    This should be thought out up front BEFORE development as changing this down the road can be erroneous and lead to data loss if you try to inherit from base partial designs post-development.

    Base Partial Design Inheritance

    Base Partial Design Inheritance on Partial Design Item

    If you revert to this best practice down the road you are looking at medium level of rework but high level of regression testing.

  • Prioritize setting component size directly on the component over using splitters

    This is more of a Content Author activity that can enhance the speed of the Experience Editor. Ensure that the Content Authors know this approach and the Front-End developers can account for this in CSS/JS and such. No need to change anything here, but good to know as a best practice.

    Component Sizing

    Component Sizing

Use of Renderings

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–using-renderings.html

  • Use proper renderings for the job

    This should be thought out up front with your developers on how best to put together the component in question. A lot of the times the OOTB components can work for a lot of the needs. When there is a one-off then clone a very similar OOTB component and extend it for your needs. With that being said, though shalt not clone everything and make everything custom in my honest opinion. Take a hard look at the OOTB componets and see if you can get what you need accomplished with OOTB components and/or a Rendering Variant and then if you can’t make it happen ONLY then go custom with cloning. There is not a need to refactor every single rendering down the road as that would be a high level of effort, but do adjust to this best practice by knowing it up front and preach it to your SXA Development Team.

    SXA Out of the Box Examples

    SXA Out of the Box Examples

  • Use Snippet rendering to prepare sets of components

    Snippets are able to house multiple components essentially so if you have pages that use the same set of components then consider using a Snippet. The same snippet can be used with many different pages and all point the same datasource so changing the original Snippet will change them for all referencing that datasource.

    Snippet Component

    Snippet Component

    There is no hard and fast rule on this one I think, it’s based on the project, but if you do go down this path later to use as a best practice it can be a medium level of effort with high regression testing.

  • Do not use Plain HTML component for content that is edited by Authors

    This is almost always a bad idea unless truly warranted. Don’t let the Content Author have the whole rope, they may hang themselves. Hence, think about if there is a better alternative that will restrict them a little the help them from frustration and mistakes.

    Plain HTML and Plain HTML (Reusable)

    Plain HTML and Plain HTML (Reusable)

    Modifying this down the road to refactor open components like this would be a low level of effort with better results for Content Authors.

  • Rich Text field content should be fully editable using the Design view

    This one is great and is a great bang for your buck with your Content Authors, which is giving them the ability to choose a CSS class from within the Rich Text Editor keeping them from using the HTML tab to have to add in a CSS class in the other interface.

    Rich Text Editor using Class Drop-Down

    Rich Text Editor using Class Drop-Down

    This is very low effort to implement to help out your Content Authors. Here is a link to instructions on how easy it is to Add Custom Classes to the Rich Text Editor.

  • Provide previews for rendering variants

    This is subject to opinion, but I am firm proponent of all preview images that help the Content Authors make their job easier, and a picture is worth a thousand words! The ability for the Author to see what it is that they are about to add as a component leads to less confusion and can go a long way in keeping our Content Authors, who are our end user as a developer, very happy!

    This is considered low-moderate effort to go back and do but is well worth it!

Proper use of Rendering Variants

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–extending-sxa.html

  • Limit the number of rendering variants to 15 (preferably below 10)

    This is going to be a task you will want to work with at the beginning of development of the components at hand to ensure you DO have some variants, but don’t overdo it. But DO make sure to give meaningful names to your variants when you have many. Make it easy for your Content Authors to understand what the variant is directly from the drop-down in the Experience Editor!

Development

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–extending-sxa.html

  • Never put any custom items in SXA controlled branches of the tree

    This is simple, never do it, and preach it to your SXA development team! During your PR reviews ensure that this violation is not happening, otherwise your SXA components will be defaulted back to the original state and/or worse your SXA instance is broken.

    Experience Accelerator Folders

    Experience Accelerator Folders

    This is a must do item in any SXA audit and is moderate effort that can lead to high effort to rectify so best to adhere to and have knowledge of from day 1!

  • Do not modify the OOBT SXA items

    This is simple, never do it, and preach it to your SXA development team! During your PR reviews ensure that this violation is not happening, otherwise your SXA components will be defaulted back to the original state and/or worse your SXA instance is broken.

    Experience Accelerator Folders

    Experience Accelerator Folders

    This is a must do item in any SXA audit and is moderate effort that can lead to high effort to rectify so best to adhere to and have knowledge of from day 1!

  • Do not replace the SXA “MVC Layout” with a custom implementation

    This is directly from the words of the SXA Team, “By replacing the layout with your custom implementation, you risk losing compatibility with future versions of SXA thus increasing the maintenance cost for the site owner in the future considerably.”

    SXA MVC Layout

    SXA MVC Layout

    Hence, heed the warning, and to go back and fix this would be in my opinion high effort in regression testing every component that would now go back to using the default version.

  • Create an SXA module for your components

    For simplicity’s sake this will aid in adding in custom components via an SXA module, and will help with new sites / tenants. May be on a case by case basis depending on your implementation but keep in mind that if you choose to go down a custom path for a component then this would be considered a best practice.

    Adding a Site Module

    Adding a Site Module

    This is considered low effort, but will help with future tenant / site implementations.

  • Follow Helix principles when adding functionality to your solution

    Make no mistake about it, SXA follows the Helix Patterns, Principles, and Conventions, and your development team should as well in working with SXA. When working inside Sitecore, ensure you are following Helix by creating sibling folders to the Experience Accelerator folders. If you have never worked before with Helix this is your opportunity to see exactly how Helix is used widely in the Sitecore architecture as well as in the solution. You can even gain an immense amount of knowledge from reflecting on the .dll’s with DotPeek or .NET Reflector to see what’s going on under the covers! Learn from those at the SXA Team that have come before you, and adhere. SXA is built to take full advantage of Helix!

  • Consider using existing renderings before building a new one

    This is not only a recommendation, but it should be a standard practice for an SXA Development Team to sit down with the Architect, or the Architect to provide guidance on the “how to” build for the component. One of the major benefits of SXA is that you have out-of-the-box components that truly are good enough to get a lot of what you need done. Hence, ALWAYS consider using the OOTB components versus reinventing the wheel. It will save you time and the client money. Once they are built, they are now Technical Debt if you come to find out later that you could have used an OOTB component for what you built a custom component for so be diligent in these efforts and put this down as a sub-task on the User Story to research the OOTB component to see if it will work for your component requirements unless your Architect had the time to give the direction and/or component stubbed out ahead of time to make that decision for you. Rendering Variants and Snippets are your best friend!

    SXA Out of the Box Examples

    SXA Out of the Box Examples

  • Consider cloning existing renderings before building a new one

    If you MUST go custom for your component, choose the component that is closest to yours and clone it. Cloning is the way to go always, as this will do everything behind the scenes for you. You CAN do this via reflection using your favorite tool, but it is considered a best practice to use the cloning feature provided by SXA.

  • Limit scope of fields linking to items

    One of the coolest features that you can take advantage of with SXA are the tokens that limit the scope of items that Content Authors can choose. You can use these tokens in most data source fields and they are powerful to help you give the best experience to your Content Authors to place them in the correct folder the minute they attempt to choose any data source for the component!

    Examples of SXA Tokens in Datasource Fields

    Examples of SXA Tokens in Datasource Fields

    To fix this is considered low effort for a big win with Content Authors!

Theming

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–working-with-themes.html

  • Do not modify platform themes

    This is simple, never do it, and preach it to your SXA development team! During your PR reviews ensure that this violation is not happening, otherwise your SXA components will be defaulted back to the original state and/or worse your SXA instance is broken. This is a must do item in any SXA audit and is moderate effort that can lead to high effort to rectify so best to adhere to and have knowledge of from day 1!

  • Assign custom styles to components

    This is similar to setting up Allowed Renderings or for only Allowed Components to use a Placeholder. With SXA you can setup a Style to only be used with certain components, otherwise it will be available to all components (which is a no-go). You definitely don’t want Content Authors to use a style that is NOT made for their components leading to a bug, confusion, etc. so make sure to set this and have your FE Team aware of this. Going back to do this would be a low effort, but is definitely worth it in terms of less bugs, questions, and Content Author happiness.

  • Clean your styles folder of unused styles

    Simply stated this cleans up unused styles and is another big win for your Content Authors so use it, so the Content Author happiness remains in bliss. This is extremely low effort at any time.

  • Place your style items in sub-folders of your site’s Styles folder

    Honestly, what can be easier than this? SXA even organizing things for you, awesome! You can simply click on “Organize Styles” button and it takes care of everything! Yes, use it for Content Author bliss. This is extremely low effort at any time.

Data sources and media

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–working-with-data-sources-and-media.html

  • Don’t put media items directly under the site’s Media folder

    Here is what is also nice about SXA is that your Media folder is in the main content tree available at any time for your given site. It has it’s own set of folders to be used and those are expected to be used by SXA, not the typical folders you can use in a traditional Sitecore implementation. Use them, they are there to make your life during development easier, as well as the Content Author’s life easier as well. Fixing this if it hasn’t been done is low effort, but definitely should be done as an SXA best practice.

    Media Folder in SXA Site

    Media Folder in SXA Site

  • Give site data sources meaningful names

    If you have a multi-site implementation for SXA, then make sure to enforce a “meaningful name” convention policy with your Content Authoring team so they can adopt best practices for Content Authoring when data sources are shared across sites. This is a best practice overall in my honest opinion, both locally at the page level as well as shared, but most definitely at the shared level. A data source that says, “FAQ Accordion for 2019” is much more valuable than “Accordion 1” that nobody has a clue what is in it until they open it, and start looking through the item details. Set this standard at the Content Governance level with the Content Authoring team early. Depending on how many data sources there are in shared, this can be low – moderate effort to change all the names, but is definitely worth it for Content Author bliss.

  • Organize site data sources in folders

    This is very similar to organizing your styles to keep things nice and tidy for Content Authors. Again, if you think of your Content Authors as your end user, you will succeed in your Sitecore efforts most, if not all, of the time. This is extremely low effort at any time.

  • Clean up unused site data sources

    This is made simple with the Cleanup Data Folder script provided by SXA. Use it to ensure that your Content Authors are not selecting unused data sources by accident, that are no longer valid, etc. in your shared site Data folder. This is extremely low effort at any time.

    SXA Data Folder Cleanup

    SXA Data Folder Cleanup

Multisite & content sharing

  • Consider using Shared site as the exclusive style container in a tenant

    Nobody likes duplication so when you are in a multi-site SXA implementation be sure to remove styles from other sites, and use the shared site styles so there is no duplication in the Styles section of your components. If you need to do this after an SXA site is up, but this hasn’t been done, I would work with your FE team to first Cleanup the Styles, then start removing. This would be considered low-moderate effort after setup, so best to think about up front during SXA implementation.

  • Consider defining your rendering variants in the shared site and remove their redundant versions from the local sites.

    This is definitely considered in my opinion a definite to-do when using multi-site. I have seen the duplicates in the Experience Editor, and leads to confusion and can be considered a bug. This is low-moderate effort to fix, but should definitely be a top priority!

  • Consider using delegated areas for pages that share content across multiple sites

    This is considered a best practice, and is very similar to the cloning in traditional Sitecore development initiatives. A great example is blogs. Modify a blog post in your main delegated area, and it propagates to all the sites that are using it. Very effective for content used across multiple sites. To do this after an SXA implementation is setup you I would consider this low-moderate effort based on the content and how many sites are going to reuse the content.

  • Consider creation of blueprint/master site to be cloned for roll out to new markets

    This is considered a best practice when starting up new sites, and seems like a dream, but is achievable after you have your components and architecture in place in one site. you can then clone it, and you have a site setup with all the basics. Think about this when you know that you will be bringing in other sites / tenants to make life easy!

  • Consider defining your Page Designs and Partial design in the shared site and reuse them in other sites in a tenant

    This is definitely a best practice when you know you can use your Page Designs and Partial Designs across sites. Make use of this as it will help with the next site you build out so you can reuse instead of reinvent the wheel. You can move your Page Designs and Partial Designs to your shared site later, but you will need to regression test all the pages and components after doing this just to ensure all is working correctly as intended, so I would consider this a high amount of effort. Think of this up front when you know you are bringing on a new site / tenant.

Language & Globalization

  • Always configure and create items in the language of the content

    Simply stated above. Even if you create the site in a single language it’s considered a best practice.

Performance

https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/recommendations–enhancing-sxa-performance.html

  • Limit the number of components directly on a page to under 30

    Too many components will contribute to slow Experience Editor issues so the recommendation is to put common elements in the Partial Designs so they are not directly design-able. Well, in order to do this after an SXA site is up I would consider this to be moderate effort to analyze those common elements, and then get them into Partial Designs.

  • Disable Content Testing for editors that do not require the functionality

    Speed up time in the Experience Editor! No brainer here. This is extremely low effort at any time.

  • Consider configuring HTML Caching settings for components

    Truly this should be considered across the board with your components along. Caching can really help out with performance optimization, and SXA provides 4 different ways of caching. Use them up front during architecture setup, and component development as well. To enable after an SXA site implementation is considered low effort, but is a “must-do” in my opinion.

  • Verify that Asset optimizer is enabled for your site

    This is recommended to be enabled and used in Production, and while I can’t verify the performance increase gained by the usage, I would recommend as well and is considered a low amount of effort for a good gain in performance with concatenation of files into one asset and caching.

To summarize, you have things to think about up front with your SXA implementation/s. Think early, and often when working with SXA about best practices as their is an adherence with SXA that ensures that you will have a successful implementation each and every time, maintain that SXA implementation all the while making your Content Authors excited to work with SXA. Happy SXA’ing Sitecore community!

Install Multiple Instances of SOLR for Sitecore Development

Did you know that you can run multiple instances of SOLR on a machine for local development as a Sitecore developer?

Since we have moved on from Lucene to SOLR in our development efforts starting with 9 it makes sense to move to SOLR starting in version 8 as the move will be inevitable. With Lucene as our old index provider this didn’t used to be a problem having several instances of Sitecore on our local machines, but when you start using SOLR you need to be cognizant of a few things so you can set up your development environments quickly.

I read a great blog post earlier that gave me what I needed to get 2 SOLR instances setup on my local machine. This can easily be increased as Sitecore projects come into play in your Sitecore travels. Below is the blog post I mentioned, which was a great starting point:

Install Multiple Instances of SOLR for Sitecore on a single server

What you need to know when setting up many instances is that there needs to be a common JRE that will work for ALL SOLR instances on your local machine!

I am currently using SOLR 6.6.2 for SItecore 9, and SOLR 5.5.2 for Sitecore 8.2. When I tried to start my SOLR 5.5.2 instance I got an error that I needed “Java 1.7 or higher” even though I had JRE 9.0.1. Makes no sense right? Bottom line is that SOLR 5.5.2 doesn’t play well with the later versions of Java.

Again, I needed to have a common JRE that worked with both instances of SOLR (5.5.2 & 6.6.2) and that was Java 8 (8u162). Once I uninstalled 9.0.1, installed Java 8 (8u162), and then modified my JAVA_HOME environment variables to point to the freshly installed Java 8 (8u162) instance, I was able to start both SOLR instances as Windows services with NSSM on port 8985 (5.5.2) and port 8986 (6.6.2) respectively.

Once you make these changes you will then need to set your Sitecore instances to point to the correct SOLR instance accordingly using a patch config in the following config file “App_Config\Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config” in the “ContentSearch.Solr.ServiceBaseAddress” setting below:

<!-- SERVICE BASE ADDRESS
Base url of the Solr server. (minus any cores and minus a trailing slash)
-->
<setting name="ContentSearch.Solr.ServiceBaseAddress" value="http://localhost:8985/solr" />

Once you have that in place per Sitecore instance you are all set. Happy coding!

Sitecore Ignition Logo

Accelerating your Sitecore solutions with Helix and Sitecore Ignition Development Accelerator

Over the past month or so, I have had the opportunity to work with some fine talent here at Perficient who are showing me the ropes on the Sitecore Ignition Development Accelerator as well as how this framework adheres to the Helix patterns, principles, and conventions. Helix is a growing passion of mine, along with our development team, and the more I develop using these patterns, principles, and conventions along with the Sitecore Ignition development accelerator, I am quickly seeing the benefits of Helix & Sitecore Ignition as a great development practice.

Helix gives me the ability to maintain enterprise level Sitecore solutions a lot more easily with the Project, Feature, Foundation approach as well as SOLID patterns, principles, and conventions behind the reason why you should develop in this manner. If you have not started development with Helix, truly consider a shift toward this as it is now considered a best practice, and recommended by Sitecore at this point.

The Ignition framework allows me to hit the quick-start button when starting a new project for a new client, but in addition it allows me to work with Helix patterns, principles, and conventions to accelerate development time. There is more than meets the eyes with the Sitecore Ignition framework, and I felt it best to share my findings now that I have some hands-on experience in both areas.

I started out speaking with Jon Upchurch, major contributor to the Ignition team, who gave me a handful of links to “Ignite the Helix”, those of which are below:

GitHub repo for Ignition: https://github.com/sitecoreignition/SitecoreIgnition

Sitecore Ignition Documentation: http://sitecoreignition.readthedocs.io/en/latest/

Getting Started with Sitecore Ignition (Initial Items & Project Template for VS): https://github.com/sitecoreignition/Ignition.Foundation/wiki

Getting Started with Sitecore Ignition (10 Minute Video): http://blogs.perficient.com/microsoft/2016/06/sitecore-ignition-getting-started/

Getting Started with Sitecore Ignition 2.0 (50 Minute Video): https://www.youtube.com/watch?v=ZX4sAcQFqGM

Webinar on Sitecore Ignition:http://blogs.perficient.com/microsoft/2016/06/sitecore-ignition-a-webinar-overview/

Sitecore Ignition 101 Blog Post by George Chang: http://blogs.perficient.com/microsoft/2016/06/sitecore-ignition-101-overview/

Facebook Page: https://www.facebook.com/Sitecore-Ignition-1714108435506468/

Twitter Ignition Team: @ignition_sc

The links above will help you to learn more about the Sitecore Ignition accelerator as well as stay informed via your social media favorites Facebook & Twitter.

Initial setup of Sitecore Ignition is not very hard at all if you are following the directions in documentation and videos, and rather than go through the setup in this post, my focus is really on the benefits of why you should consider using the Sitecore Ignition development accelerator for your Helix based Sitecore projects. Below, are a few benefits in relation to Helix that will speed up your Helix initiatives:

  • Ease of setup to initially get started to add into your Helix based Sitecore solution
  • A Visual Studio Sitecore Ignition project template
    • When you add a new “Feature” project, which could be many, then this ability alone to have a template will speed things up dramatically in your daily development.
    • Sitecore Ignition project template comes pre-loaded with a Controllers, ViewModels, and Views folder for you to start creating your modules and get to work quickly.
    • Pre-configured Unicorn predicates for the project setup in the App_Config/Include folder.
    • When using symbolic links with Link Shell Extensions, then you won’t even have to set up the serialization folder for the project because Unicorn will place the serialized items in the right spots in your feature to be included for check-in to your repo. Brilliant!
  • Ignition.Foundation.Core includes SimpleInjector for IoC, and Glass.Mapper as the ORM. So when you create the new project, all you need to do is reference Sitecore.Kernel.dll & Sitecore.Mvc.dll from the official NuGet feed for Sitecore. This will make Glass.Mapper happy during Ignition.Foundation.Core package installation. Once the Sitecore Ignition.Foundation.Core NuGet package is installed, your project is ready to go!

Below are some benefits I have run into in a short period of time using the Sitecore Ignition framework:

  • No Global.asax or RouteConfig.cs you need to worry about in the solution. Which is just less files/folders you need to worry about and more clean in my opinion, which is always good. When doing AJAX work you simply need to setup on your JSON returning methods for them to use [PublicRoute] along with [HttpPost] and that’s it. The rest is done behind the scenes for routing. Pretty slick!
  • Experience Editor friendly just met developer friendly when the Sitecore Ignition team created a way for you to simply name a .cshtml file with a certain naming convention i.e. “_EE”, and now that view is automatically Experience Editor friendly. Big win for teams with juniors!
  • Dynamic placeholders are OOTB with this accelerator so there is no need to roll your own, download one off the Sitecore Marketplace, or pull one in from NuGet.

Overall, I am very excited about working with Helix patterns, principles, and conventions now in my every day Sitecore development role as well as the ability to accelerate that development with the use of Sitecore Ignition. Thanks for the hard work Sitecore Ignition team!

I have a good feeling that I will be working with the contribution team when I can to help make this accelerator even better than it already is for the Sitecore community. Looking forward to when that time  comes. Now go and “Ignite the Helix”!

 

Parameter Templates for Pointing to Content Items

Dynamic Parameter Templates for Pointing to Content Items

Have you ever been in a predicament, in code, where you need to point to an item in the Sitecore content tree?

For instance, you are building a Search Component, and when the user clicks search, you are going to send over the keyword/s in a query string to the Search Results Page for use in processing and displaying the results. In code, I will need that URL in my Search Component in order for me to send over the results. Let’s analyze what our options are on this scenario:

For sake of simplicity, I will leave the null checking and exception handling to you. These are simply options to get ultimately to my point:

Option 1 (Rookie Option):

var searchResultsPage = Sitecore.Context.Database.GetItem("/sitecore/content/Home/Search Results");
var searchPageUrl = LinkManager.GetItemUrl(searchResultsPage);

We can get the item using the path, then get the item URL at that point. However, we NEVER want to do this with content items! If that path changes then now you have a broken Search Component. Just because we can use paths to get items in Sitecore, doesn’t mean it’s a good practice. As a matter of fact, I would consider it bad practice all around, so steer clear of this method.

Option 2 (Better Option):

var searchResultsPage = Sitecore.Context.Database.GetItem(ID.Parse("F6778EA3-1B76-4BBF-AD21-63E9EA847FDE"));
var searchPageUrl = LinkManager.GetItemUrl(searchResultsPage);

A better method to pointing to items is with the ID using the item GUID. Why? Performance reasons. Sitecore will pick that item up faster using the ID. More importantly the ID is tied to that item ONLY and anywhere the Content Editor moves the item, Sitecore will still be able to fetch it, and nothing will break on the Search Component.

Option 3 (Even Better Option):

Typically, in most solutions that I have come across for Sitecore, there is Constants file that has ID’s in the Constants file something like this:

public class ItemIDs
{            
     public const string NewsCategoriesFolder = ID.Parse("{B866EC76-54ED-447D-9AB2-5E05A0699586}");
     public const string SearchResultsPage= ID.Parse("{F6778EA3-1B76-4BBF-AD21-63E9EA847FDE}");
}

As you can see from above there is an item for the News Categories Folder and the Search Results Page. Both of which need to be referenced in code, and the reason why they are in the Constants file. Using the method of putting those ID’s in the Constants file makes it easier to manage those ID’s in one place. When doing so, you can then get your item more clean and like so, in this case the Search Results page:

var searchResultsPage = Sitecore.Context.Database.GetItem(Constants.ItemIDs.SearchResultsPage);
var searchPageUrl = LinkManager.GetItemUrl(searchResultsPage);

If you make a change to the Item ID, then it will make that change across your solution versus having that ID out there in 5 other components you will need to change manually.

Option 4 (Best Option using TDS & Glass.Mapper with Code Generation):

Option 3 is definitely considered a best practice, but what if we can make this even easier to manage dynamically using TDS & Glass.Mapper, and keep ID’s out of the Constants file altogether except for rare circumstances?

Now that you have seen a few ways to point to the item in the Content Tree, let’s discuss Parameter Templates. Parameter templates are a way for you to add fields to your rendering that can help out the Content Editor in their daily Content Editing experience. Below are links to helpful information regarding Parameter Templates:

Now, that you have a good idea of what Parameter Templates are and how to set them up, let’s talk about why we would want to use them as a best practice in components for item pointing.

Parameter templates help to manage the solution while providing a better experience for your Content Editors. We simply create a Parameter Template to be used with the Search Component that has this criteria:

  • Name: Search Results Page
  • Field Type: DropTree
  • Data Source: /sitecore/content/Home/

We create a Standard_Values for the Parameter Template and set it to the Search Results Page by default.

When added to the Search Component, it is by default set to the Search Results page, but if the Content Editor ever wanted to point to an alternative Search Results page (for whatever reason), they now have that option. But more importantly, you bypass having to now add this to the Constants file, if your Parameter Template is strongly typed as one of your Models. Using TDS with Glass.Mapper “Code Generation”, this will allow you to create your Parameter Template for the Search Component, then have it auto-generate the Model in your solution for use where you can then call on the Parameter Template like so using Glass.Mapper (notice Search_Results_Parameter):

var renderingParameters = GetRenderingParameters<Search_Results_Parameter>();
var item = Context.Database.GetItem(ID.Parse(renderingParameters.Search_Results_Page));
var pathInfo = LinkManager.GetItemUrl(item, UrlOptions.DefaultOptions);

The Search Results Parameter is now dynamically generated anytime there is a change in the item in Sitecore. Hence, you never had a need to create the model OR add the item ID/path to the Constants file.

Keep in mind you can still do this without TDS/Glass.Mapper “Code Generation” feature, but then you will need to create a model manually for your Parameter Template. The benefit is that you have given the Content Editor the ability the point to the Search Results page versus having this functionality in code that needs to be changed by a developer, and that is the benefit to the client. To my knowledge, you can also do this same type of “code generation” functionality with Unicorn/Synthesis as well.

I have found going this route equates to maximum productivity for the development team once the concept is understood, and helps in manageability of your solution overall. Happy coding!

Rebuilding Reporting Database for Experience Profile Data

Are you familiar with the Experience Profile in Sitecore?

This is a phenomenal tool for Marketers that have audacious goals for their organization. The Sitecore marketing team talks about the Experience Profile as such, “Data-driven marketers who want a 360-degree view of their customer’s interaction with all digital channels over time and in real-time will find just that in the Sitecore Experience Profile”. If you have never taken a good look at what is possible with the Experience Profile, check it out!

Now, what does this have to do with rebuilding the reporting database? Good question! Experience Profile is completely dependent upon the Sitecore_Analytics_Index. If there is no data in the Sitecore_Analytics_Index then there will be no data in the Experience Profile. So, how does data get into the Sitecore_Analytics_Index? Let’s a take a look:

In this example using a typical model of a 1 CM (Content Management Server) with 2 CD (Content Delivery Server) environment, the CD server/s will be delivering customer experience data to MongoDB during the customer session/s. When the session/s ends, typically after 20 minutes (unless set to end earlier) then the data gets flushed using the xDB Processing Server/s. If you have not setup the xDB Processing Server/s on a standalone server then the processing will happen on Content Management server. The processing will take this data and migrate it to your Reporting database, where it will reside, and then be indexed, for performance reasons, by the Sitecore_Analytics_Index for use in the Experience Profile.

Now, why might be ever want to rebuild the reporting database and how do we even do that? Good question! If you don’t see any data in the Experience Profile, and you have a license for the functionality, then, “Houston…we have a problem”. Below are some steps to troubleshoot:

  1. Check the logs using Sitecore Log Analyzer on the Sitecore Marketplace, and see if you have any Mongo connection errors. Connectivity may either have been disrupted or never configured properly. If you are using SOLR look for SOLR errors relating to the Sitecore_Analytics_Index as well.
  2. Ensure you have MongoDB connectivity by using RoboMongo to ensure that data is truly being collected in the collections.
  3. Check the Reporting database tables to see if there is any data being collected. If not, then the xDB processing may not be working for some reason OR you have a connection string missing or incorrect to the Reporting database.
  4. Ensure if you are using SOLR that your cores are setup correctly along with configsets particularly for Sitecore_Analytics_Index.
  5. Check the Sitecore_Analytics_Index for any indexed records. More than likely you will see no records.

If you went through 1-4 and the data is all there, but still don’t have any records in your Sitecore_Analytics_Index then you are now looking at rebuilding your reporting database.

But wait, why don’t we just rebuild the index for the SItecore_Analytics_Index? Good question! Adam Conn explains this in his blog post, but we cannot manually do this, as it is done behind the scenes in Sitecore. There is a workaround to get the Sitecore_Analyics_Index to show up with the rest in the Select Search Index dialog box, but it can be problematic as Adam shows in his post.

Now is when the Rebuild Reporting Database tool comes to the rescue. When you rebuild the reporting database you also then set the Sitecore_Analytics_Index to be re-indexed through this process.

To rebuild your reporting database you will actually need to create a second reporting database to use with the Rebuild Reporting Database administration tool. Instructions on how to do that can be found here. Now, once you have a second reporting database in place you can then use this tool in the Administration Tools area of Sitecore:

Rebuild Reporting Database

Rebuild Reporting Database

When you run this tool you will rebuild the reporting database thereby re-indexing the Sitecore_Analytics_Index. At this point, you should start seeing data flow into the Experience Profile. Happy coding!

Find Text In Sitecore Files

Using Notepad++ To Find All Text In Sitecore Files In A Folder

Have you ever wanted to search for text in all files of a folder? Windows search will allow you to search through a folder, but will return the keyword in the filename by default. That’s great if you want to find a file in the folder, but what if you want to go deeper and search through all files in those folders for a certain keyword? I ran across this scenario recently, and I wanted to share with the community of Sitecore developers because it can be a very useful tool when performing some Development Operations type of tasks to help you troubleshoot when in certain situations as you will see. If you have seen this trick before then, “Amen”, but if not then make this a part of your toolbox!

Let’s just say you are troubleshooting a CD server in a typical 1 CM | 2 CD distributed environment that is having issues still referencing the “master” database. You simply want to find out where all the “master” references are in the App_Config folder and sub-folders without having to open each file manually and check. Can you imagine having to do that? As Kamruz Jaman would say, “Ain’t nobody got time for that!” Notepad++ to the rescue!

All you have to do is the following:

  1. Download Notepad++
  2. Open up Notepad++
  3. Either do a “Ctrl-Shift-F” OR click on “Search” tab then click on “Find in Files…”

    Find In Files Search Option

    Find In Files Search Option

  4. Once you click on that option you will get this “Find in Files” dialog box where you can set your settings for your search. For this example, we will set to “master” in the “Find what” text box and set the “Directory” to “D:\App_Config”. Lastly, we will just ensure the “In all sub-folders” is checked as well as seen below. Please note that you can also do a “Replace in Files” as well and even search in hidden folders:

    Find in Files Dialog Box

    Find in Files Dialog Box

  5. Click “Find All” and watch the all the goodness that returns to you!

As you will see below you now have that keyword brought to you in all the files in that sub-folder as well as the filename and path.  Not only that but when you double-click on the line item it will appear above in the text editor so you can view and modify.

Find in Files Search Results

Find in Files Search Results

With this knowledge, now you are armed like a boss to find keywords in files throughout your Sitecore development daily grind that will make you a more productive member of the team. Happy coding!

Sitecore Training

Sitecore Training Is Always Valuable

When I first started down the path of doing Sitecore development back in 2008, I was told by the company that I worked for at the time that they had a new CMS system they were going to be using to manage all the customer websites. The company was in the restaurant advertising business, and when they got new a new restaurant we would take the website they had currently, and add them into Sitecore to be managed. Back at that time, CMS systems were just gaining some ground in popularity, and .NET web development was a hot topic. I had been doing some work before with ASP but not a ton of .NET web development much less with enterprise level CMS systems. I was at a Junior level in my development efforts in .NET with lots of knowledge, but not a lot of practical uses of said knowledge in the form of implementations. With that being said, I knew that I had a lot of work ahead of me to learn .NET Web Development, and then learn Sitecore intimately from every angle in order for me to be able to deliver solid solutions. At that time in 2008, the version that had everyone excited was version 6.0. Sitecore, in my opinion, has always been good at providing documentation. However, with that being said, I still had to rely mostly upon Google searches to find most of what I needed to help me out in any way shape or form. It was at this time that John West and his blog posts generally saved the day for most of us that come from that era of Sitecore development, and the reason why he is a lifetime Sitecore MVP. He literally paved the way for us current MVP’s to share our knowledge as back then we were just looking for nuggets anywhere we could get them. From all of us in the community, thank you John West!

Now, back on topic, my company saw that this was a tall order for me to take on, and decided to send me to Sitecore certification training in San Francisco, CA. This turned out to be the best move they could make and money well spent to get me up to speed VERY quickly. I flew out to San Francisco, and for a week learned the nuts and bolts about how to develop a website into the Sitecore CMS system. To be honest, it was a lot to take in and being new to .NET development, some of it was over my head, and I struggled to gain the deep understanding that I very much desired. Luckily, all my documentation I read through before the course and the Google searches paid off because I was more so gaining the validity to what I had been doing on my own versus learning brand new concepts. Days were filled with training and examples, and nights were filled with soaking up what I learned that day and studying for the exam at the end of the course. At the end of the week, I flew back home as a Sitecore Certified Developer.

Armed with my knowledge, certification, and newfound passion for Sitecore, I set out to start building websites. I was able to build my first website in 6.0 that had around 20 micro-sites within it that we were able to manage easily in a very short period of time!

Fast-forward 8+ years later, my previous employer, had an instructor fly out, and provide a group training in SItecore 8.1+ . First, we started out with training on how Content Editors use the system from Content Editor as well as heavy use of the Experience Editor, and all it’s functionality and tools to edit content. Then for the rest of the week, we took a deep dive into how to develop websites into the Sitecore CMS using the latest and greatest tools utilizing best practices for Sitecore development. Having been through Sitecore Certification Training in the past, I was sure that I would not see much new that I didn’t know. To my disbelief, I was blown away at all that I learned that I didn’t know about, in addition to using tools that would help my team’s productivity overall. For instance, in the course, we made extensive use of SIM (Sitecore Instance Manager) and Sitecore Rocks for our development. I have used Sitecore Rocks before, here and there for certain tasks, but not strictly for development forcing me to have to get used to use the tool. It was great see just how easy things were to get what I wanted accomplished without ever leaving Visual Studio! Now, I am using Sitecore Rocks more than ever to help me with my day-to-day Sitecore tasks.

The moral of this story is that Sitecore training is ALWAYS valuable for partners as well as clients! Make no mistake about it, you are only as effective as what your level of knowledge is when it comes to Sitecore. Once you get the training you need in your role, you will be more confident, productive, and ultimately happy working inside the Sitecore CMS. Happy coding!

Sitecore Developer Toolbox Chunk

Sitecore Developer Toolbox Module for Sitecore 7.2+

Have you ever tried to remember what the URL is to the Show Config or the Cache page in your Sitecore instance when using the Administration Tools?

Did you know that there is a Database Browser that the old-schoolers use to Brute Force work they need to get done with Sitecore OR an XPath building tool you can lean on to help you with Sitecore queries?

If you don’t know what I am speaking of, I am eluding to some old-school tools that can be very useful for every day administration and development. For example, the Administration tools page with links to all kinds of tools from the backend, XPath Builder to help you write and validate queries/fast queries, and File Explorer to help you view Website folders/files on the Sitecore instance you have the Sitecore Developer Toolbox (SDT) installed on. When you install the Sitecore Developer Toolbox, you get quick access to these tools via the Developer tab in the Content Editor.

Here is a look at what the module will add to your Content Editor Contextual Ribbon:

Sitecore Developer Toolbox Chunk

Sitecore Developer Toolbox – Chunk

Administration Tools:

Sitecore Developer Toolbox Admin Tools

Sitecore Developer Toolbox – Admin Tools

XPath Builder:

Sitecore Developer Toolbox XPath Builder

Sitecore Developer Toolbox – XPath Builder

File Explorer:

Sitecore Developer Toolbox File Explorer

Sitecore Developer Toolbox – File Explorer

Here is the link to the module on the Sitecore Marketplace: https://marketplace.sitecore.net/en/Modules/S/Sitecore_Developer_Toolbox.aspx

If you like the module please recommend to the community, provide a rating, and/or review. Thanks and happy coding!

How To Setup, Configure, and Connect to xDB Cloud 2.0 – Part 1

In this post, I am going to run through my experience of setting up, configuring, and connecting to xDB Cloud 2.0. However, this is going to be a multi-post, because there are some steps that you need to take place so you can have the xDB Cloud set provisioned before you can connect. Once we get through the initial setup and provisioning by Sitecore, we will  go a little deeper on the connection that may need to take place depending on your environment.

First, I started my journey off by taking a look at some basic documentation on xDB Cloud and realized that I needed to use Version 2.0 as seen below, since the agreement was signed before July 21st:

Use Version 2 Instead of Version 1

Use Version 2 Instead of Version 1

Once I knew I needed to work with Version 2, I started looking through some documentation on Version 2 specifically here. When looking through the overviews, I needed to find out what xDB Cloud Set was compatible with my version of Sitecore, which is Sitecore 8.1 rev.160302 (Update 2). The xDB Cloud Service Compatibility Tables can be found here. I see that my version is supported for creating new xDB Cloud Sets and keep on trucking.

I move on to the “Connect to xDB Cloud 2.0” documentation. My Prerequisites are met so my next step is to “Request the xDB Cloud Customer Set” by putting in a Sitecore Support ticket with the following information:

  • License ID (Sitecore License ID & xDB License ID)
  • Deployment ID (This is the name you want to give to the set i.e. “companynameprod1”)
  • Sitecore Version (Sitecore 8.1 rev.160302 (Update 2)
  • Preferred Location (West US)

Then I waited for Sitecore Support to get back to me with the connection strings, and reporting service settings.

In Part 2, we will take a look at making the connections, and any other configurations that may need to take place to connect to your xDB Cloud set.

jQuery UI AutoComplete

jQuery AutoComplete using MVC with Lucene Index Computed Fields in Sitecore 8.1

In this post I am going to give an example of how to wire up suggestive search, otherwise known as AutoComplete, using the jQuery UI when using ASP.NET MVC along with Lucene Computed Index Fields in Sitecore 8.1.  An AutoComplete is as quoted from jQuery UI, “Enables users to quickly find and select from a pre-populated list of values as they type, leveraging searching and filtering“.

I started this off by working through the examples out of the most recent book put out by Phil Wicklund and Jason Wilkerson, Professional Sitecore 8 Development.  If you haven’t read this book, get it! It is filled with a lot of knowledge that you can add to your toolbox. I started with some basic examples from that book, and modified to get what I needed to meet my requirements for this task.

My requirement for this task was to create an AutoComplete that had a First Name and Last Name. The problem is that in my indexes they are stored as 2 separate fields in Lucene, as you will see below:

<field fieldName="first name" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
   <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>
<field fieldName="last name" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
   <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>

In order for me to bring them both together in my AutoComplete I created a Computed Index Field. A Computed Index Field is a custom field that stores data in Lucene that is calculated at index time versus on the fly. I needed to query a full name not a first name field AND a last name field. Below is the code to create a Computed index Field called FullNameField.cs:

FullNameField.cs

using System;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.ComputedFields;
using Sitecore.Diagnostics;
 
namespace SitecoreSandbox.Website.Search.ComputedFields
{
    public class FullNameField : IComputedIndexField
    {
        public string FieldName { get; set; }
 
        public string ReturnType { get; set; }
 
        public object ComputeFieldValue(IIndexable indexable)
        {
            Assert.ArgumentNotNull(indexable, "indexable");
 
            try
            {
                var indexItem = indexable as SitecoreIndexableItem;
 
                if (indexItem == null)
                    return null;
 
                var item = indexItem.Item;
 
                if (item != null)
                {
                    return $"{item["First Name"]} {item["Last Name"]}";
                }
            }
            catch (Exception ex)
            {
                Log.Error($"An error occurred when indexing {indexable.Id}: {ex.Message}", ex, this);
            }
 
            return null;
        }
    }
}

Now, that I have my Computed Index Field, I need to add it to my index. First, I added my field to the fieldmap>fieldnames node to be included in the index:

<field fieldName="full name" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
   <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
</field>

Then, I added my Computed Index Field to the documentOption>fields node to be included as a Computed Index Field:

<field fieldName="full name" storageType="YES" indexType="UNTOKENIZED">SitecoreSandbox.Website.Search.ComputedFields.FullNameField, SitecoreSandbox.Website</field>

You will notice that I added in indexType=”UNTOKENIZED” and that is because I want the value to not be split up. For example, if the name is John Doe, I want the results to come back as “John Doe” (UNTOKENIZED) versus “John” or “Doe” (TOKENIZED).

Now that my index is set up with my “full name” Computed Index Field all I need to do now is publish out to my Website folder and re-index. Once, I re-index I can see my field with the full names now being stored. I can easily view my index using LUKE – Lucene Index Toolbox, which I highly recommend, and it’s free. Below you will see that full names are left out for privacy purposes but you can see the field IS being indexed by Lucene and I assert to you the full names are there:

Luke (Lucene Index Toolbox)

Luke (Lucene Index Toolbox)

After verifying in Luke that my full names are being indexed, I added my new field to my PeopleSearchResultItem class so I can start using in code:

[IndexField("full name")]
public string FullName { get; set; }

Next. I created my interface for what is going to be used to implement my Search Service:

ISearchService.cs

using System.Collections.Generic;
 
namespace SitecoreSandbox.Website.Search.AutoComplete
{
    public interface ISearchService
    {
        IEnumerable<string> GetSearchSuggestions(string searchTerm);
    }
}

Then, I implemented my Interface in my Search Service class:

SearchService.cs

using System.Collections.Generic;
using System.Linq;
 
namespace SitecoreSandbox.Website.Search.AutoComplete
{
    public class SearchService : ISearchService
    {
        private readonly SearchManager _searchManager = new SearchManager();
 
        /// <summary>
        /// Gets the search suggestions from the computed index field for full name
        /// </summary>
        /// <param name="searchTerm"></param>
        /// <returns></returns>
        public IEnumerable<string> GetSearchSuggestions(string searchTerm)
        {
            var suggestions = new List<string>();
 
            var results = _searchManager.GetNamesByLetters(searchTerm);
 
            if (!results.Any())
                return suggestions;
 
            suggestions.AddRange(results.Select(result => result.FullName));
 
            return suggestions;
        }
    }
}

The GetSearchSuggestions method will make a call to my SearchManager class where my Lucene search logic is taking place and return me my results. In this case, I needed to pass in letters as the user types to return me back my results for my AutoComplete. The GetNamesByLetters method is seen below:

/// <summary>
/// Gets the the names with letters used with AutoComplete
/// </summary>
/// <param name="letters"></param>
/// <returns></returns>
public List<PeopleSearchResultItem> GetNamesByLetters(string letters)
{ 
    var searchIndex = ContentSearchManager.GetIndex("people_index");
 
    using (var context = searchIndex.CreateSearchContext())
    {
        return context.GetQueryable<PeopleSearchResultItem>()
            .Where(x => x.FullName.Contains(letters)).ToList();
    }
}

Now, that my logic is in place to return my results I just need to set up my Controller to handle the post and my View to make an AJAX call to the AutoComplete function in the jQuery UI. Below is the Controller logic:

private readonly SearchService _searchService = new SearchService();

[HttpPost]
public JsonResult GetSuggestions(PeopleSearchViewModel viewModel)
{
    if (!string.IsNullOrWhiteSpace(viewModel?.SearchTerm))
    {
        return Json(_searchService.GetSearchSuggestions(viewModel.SearchTerm));
    }
 
    return Json(new {});
}

Below is the jQuery code to handle the event for my field that is utilizing the AutoComplete. I added this to my .cshtml file. Keep in mind that my main layouts are already using jQuery and referencing the library in code so I am not including that script here. Also, the jQuery UI <script> and <link> elements can be found on the jQuery UI website:

    $(function () {
        $('#people-name').autocomplete({
            source: function(request, response) {
                $.ajax({
                    url: "/peoplesearch/getsuggestions",
                    type: "POST",
                    dataType: "json",
                    data: {
                        searchTerm: request.term
                    },
                    success: function(data) {
                        response(data.length === 1 && data[0].length === 0 ? [] : data);
                    }
                });
            }
        });
    });

The input text box HTML is below:

<input id=”people-name” placeholder=”Enter name” type=”text”>

The last thing I needed to do was just make sure that my route was set in the RouteConfig.cs file as such:

// Used with the AutoComplete for People Search
RouteTable.Routes.MapRoute("GetSuggestions", "PeopleSearch/GetSuggestions",
   new {controller = "PeopleSearch", action = "GetSuggestions"});

That’s all it takes to get the jQuery UI AutoComplete plug-in working for you to bring back results from your Lucene indexes. Happy coding!