Friday, July 26, 2019

Yet another Sitecore 9.2 Install Blog


Setting up a developer VM got much easier with 9.2. I'm sure lots of these "how to" posts are going up; here's mine (as much for my own reference as anything else).

I'm assuming you're using a "bare" Azure VM with Server 2016, but the steps shouldn't be much different for other hosting scenarios. I use a DS4 v3. I take liberties with security best practices, since this is a dev box.

Installing SQL

Install en_sql_server_2017_standard_x64_dvd_11294407.iso.

  • Instance ID: MSSQLSERVER
  • Mixed Mode authentication
    • Assign a SA password
    • Add your machine account as an admin.

Install management tools 8.1

  • sitecore/sitecore 
  • Enforce password policy off 
  • Server Roles > sysadmin 

Enable "contained database authentication"

  • Run the SQL script:  EXEC sp_configure 'contained', 1; RECONFIGURE;


Install Solr 7.5

[string]$solrVersion = "7.5.0", 
[string]$installFolder = "C:\solr",
[string]$solrPort = "8983", 
[string]$solrHost = "solr750.local", 
[bool]$solrSSL = $TRUE, 
[string]$nssmVersion = "2.24", 
[string]$keystoreSecret = "secret", 
[string]$KeystoreFile = 'solr-ssl.keystore.jks', 
[string]$SolrDomain = 'solr750.local', 
[switch]$Clobber 

IIS

Sitecore

Install SIF 

  • Register-PSRepository -Name SitecoreGallery -SourceLocation https://sitecore.myget.org/F/sc-powershell/api/v2 
  • Update-Module SitecoreInstallFramework 
  • Validate: Get-Module SitecoreInstallFramework –ListAvailable 

Install Sitecore 

SXA

https://dev.sitecore.net/Downloads/Sitecore_Experience_Accelerator/19/Sitecore_Experience_Accelerator_190.aspx 

JSS


Wednesday, March 27, 2019

Understanding Sitecore SXA: Why SXA?


Traditional Sitecore implementations have always been a “custom build” proposition. And with each custom build often comes a different approach and methodology. How can organizations overcome the cost, time and stress involved with a fully custom build?

Once regarded as a “small site” tool, SXA has evolved into an enterprise-class framework. 
Organizations can embark on projects large and small, secure in the knowledge that best practices are already baked into the solution.

When leveraged to its fullest, SXA allows organizations to redefine the process for building and maintaining sites, in a way that shortens timelines, reduces reliance on development, and promotes team collaboration. Projects are accelerated by utilizing a toolbox of pre-built components, and by embracing a compressed project cadence that promotes collaboration and brings the work of many teams into a more parallel arrangement.

Your New Development Process

With SXA, Sitecore itself becomes central to solution development as well as content development, bringing together the visual and UX designers, content creators, back-end and front-end developers.
Serving as the wireframing tool, SXA allows content creators to develop content and construct pages in a wireframe mode, as designers and front-end developers style the site in parallel. These “themes” are applied iteratively to content being created in flight.

Improved Time-To-Value

With development and content being developed in parallel, project timelines are shortened. Unexpected conflicts are uncovered as design meets real world content, allowing adjustments to be made sooner, with a less regressive remediation cycle.

In a multi-site environment, content and presentation can be shared across sites, streamlining development as additional sites are added.

Lower TCO

Much of the classic development cost for a typical site can be avoided, by leveraging the impressive set of components that ship with SXA. Many tasks once relegated to developers and requiring code deployments are now accomplished in the Sitecore UI and simply published.

Page editors have more control over the styling and composition of common presentation components, freeing developers to focus on domain-specific custom features.

SXA’s framework, with strong separation of presentation from design, promotes multi-site development with heavy re-use of components, further lowering development cost.
Reduced Organizational Stress

SXA allows organizations to start with a strong foundation, using a fully supported and tested framework that makes the entire process more reliable and reduces rework. Solution structure is consistent across sites, making development more predictable and allowing developers to switch between projects with less ramp-up time.

Developers are relieved from mundane tasks, allowing them to focus on more interesting challenges, while content creators are freed from reliance on development to achieve commonplace requirements. Having fewer code deployments reduces friction and removes barriers to content publishing.

Upgrades become more approachable, because less coding means fewer regression issues to remediate.


Home: Understanding Sitecore SXA

Understanding Sitecore SXA: What is SXA?

Previous: What is SXA?

Marketers and technologists often struggle with complex implementations. Faced with a dazzling array of new technologies, organizations everywhere are trying to develop a roadmap that fits the right technologies with their business objectives. But as many have learned, the road to success is often bumpier than expected.

SXA delivers key features out-of-the-box, shifts development away from coding and into UI, and provides a reliable framework for execution.

Projects can now start with a fully-baked scaffolding, that puts mobile first, separates presentation from content, and already contains a host of presentation components. In addition, key features like url management and search are already incorporated, and the execution methodology is clear and well understood.


For Marketers

  • Best practices (“Helix”) baked in
    Consistent structure of content reduces guesswork. Start with a stable solution that is poised to grow as needed.
  • Extensive component suite
    Most of the commonly needed components are ready to go, right out of the box.
  • Search
    No need to invest in a separate search technology for many common search requirements.
  • Multi tenant and siteHost multiple sites in a single infrastructure, with selectively shared content and cross-site link management.
  • Layout and rendering flexibility
    Content editors have more control of layout and appearance. Renderings can be tweaked, extended or created without having to wait for development.
  • Parallel development and content load
    Content pages can be developed in a wireframe mode, and style can be applied is it becomes available.

For Technologists

  • Best practices (“Habitat”) baked inAvoid costly strategic errors in new projects. Coders are immediately familiar with existing projects.
  • Extensive component suiteNo need to develop commonly used components. All pre-built components can be styled, extended or replaced as needed.
  • Search
    Fewer technologies to maintain, fewer integration points to fail.
  • Multi tenant and site
    Less infrastructure to maintain, better re-use of code, and consistent deployment strategy.
  • Layout and rendering flexibility
    Developers focus on new features and capabilities, rather than tweaks to existing artifacts. Fewer regression bugs.
  • Parallel development and content load
    Front end developers can see real-world instances of their design by switching completed pages into their custom theme.

Understanding Sitecore SXA: Which Way to Go?

Previous: Changes to Roles and Processes

When is SXA the right course for a project, and when is a conventional build the right way to go? The answer is, “it depends”.

For smaller projects, where a conventional Sitecore build was too large an undertaking, SXA changes the equation by reducing the build time and technical burden. Customers building smaller sites can now reap the benefits of Sitecore’s engagement and marketing automation capabilities, instead of limiting themselves to a less capable CMS tool.

For large enterprises that face a range of challenges, the choice between SXA and a conventional build becomes more nuanced. In a development-lead culture, or where heavy functional functionality is required, a conventional build may be best. But SXA is a viable, even desirable, choice for organizations experiencing a digital transformation and orienting to a marketing- and business-forward approach.

Organizations with a large amount of web presence can use SXA to “pace layer” their solutions into more manageable groupings that develop at different rates and have different orientations. SXA reduces the friction for building smaller, more nimble projects. An enterprise might have a classic build for portal or corporate sites, and an SXA model for brand sites, location sites, and campaign sites. This allows nimble execution for time-critical marketing demands, alongside a more rigorous model for times when fidelity and precision are crucial.

For battle-scarred managers who have survived painful and expensive builds, SXA is a welcome relief. Those who’ve survived those rocky projects in the past would gladly embrace a more prescriptive process that starts with “best practices”.


Read the full series on SXA: Understanding Sitecore SXA


Understanding Sitecore SXA: Changes to Roles and Processes

Previous: Prescriptive, Yet Flexible


Two of SXA’s key benefits are improved project cadence and less reliance on development. Achieving these goals often means changes in roles and responsibilities.

Development doesn’t necessarily mean coding

With SXA, some of the implementation work that would otherwise be done in back-end code is now done in the Sitecore UI. That doesn’t mean that code is written in a Sitecore UI, but it does mean that some things that would have otherwise required code are now either done in, or supplemented by, activities in the Sitecore UI.

Once upon a time, the Sitecore UI largely insulated the person in the chair from technical “stuff”. Now, quite a bit of that technical stuff is managed in UI. Structuring components was once done entirely in code, now this is done largely in Sitecore. You’ll find yourself declaring element tags and CSS classes, creating data queries and using substitution tokens, and structuring things akin to models and markup. But just because it’s managed in UI doesn’t mean it doesn’t require a technical thinker.

Although much of the methodology shifts from code to configuration, developing functionality in SXA is conceptually more akin to development than content management. Just like in coding, an understanding of what can be done and how things work, leads to effective solutioning. Your technical staff is going to change where and how they do a lot of this work, and the big benefit is that many things that would otherwise have required a code deployment now just require a publish.

This will require a shift in thinking for both I.T. and Marketing. In the early days of CMS it was often hard for I.T. to accept that content management was now a production activity; now they must accept that more things that have been traditionally subjected to development protocols are now becoming production activities as well.

Front-End in the loop

The role of the front end developer isn’t just coding, and it isn’t just design. It’s some of both and other things besides. Since SXA is a framework that manages the methodology for developing both structure and style, the front-end developer’s work becomes part of the SXA flow.

Changes to HTML structure are done directly in SXA (or at times, in back-end code). This may be driven by the need for new renderings, or by imperatives the styling process. As wireframes and content are developed, the entire site (or portions of it) are exported to a ZIP file, where designers and front-end developers extend CSS and JavaScript and apply styling using their preferred tools. Sitecore’s Creative Exchange re-imports this zip file, detects changes to styling, and applies them to the system. The styling methodology is constrained by how the SXA framework operates (for example HTML structure and content can’t be modified, and existing class cannot be removed), and  SXA injects comments into the HTML to direct developers as appropriate.

If desired, teams can use “Creative Exchange Live” to fully automate the export/import loop. Creative Exchange can be triggered by a Continuous Integration server, integrating code changes into the main branch, and to test changes early and often.



Next: Which Way to Go?
Home: Understanding Sitecore SXA


Understanding Sitecore SXA: Prescriptive, Yet Flexible

Previous: Improved Project Cadence

Many marketers and technologists have found that their perspective on a large project can change rapidly, as the level of effort and learning curve become increasingly clear. The envisioned scope, schedule and budget are all threatened, as the imagined methodology and cadence meet reality.

SXA tackles this dilemma by providing a proven structure, framework, and methodology. The aim is to simplify processes, increase collaboration, shorten timelines, and smooth the path to success. Naturally, the maximum benefit is gained by a comprehensive adoption, but SXA can be used in an opt-in fashion, utilizing some of its features and replacing others with more conventional approaches.

By embracing all of SXA, organizations achieve the maximum benefit that they seek from a prescriptive, out-of-the-box framework. For many organizations, however, there are aspects to the architecture or build process that need a more custom approach.

Going All In

Adopting SXA to its fullest doesn’t mean giving up customization. Design customization is an essential part of an SXA build. Customizing CSS, building multiple themes, and extending the client-side structure are all common activities in an SXA build.

Page structure are actually more flexible with SXA than a typical conventional build. Content editors are able to drag-and-drop renderings, choose from more focused data sources, and adjust column widths. Also common is the use of Rendering Variants, to change the structure and appearance of components — without requiring code deployments!

SXA’s preferred project cadence solves a common pitfall. When design collides with content, it often becomes necessary to adjust, when styling designed for best casecontent collides with real world content. In the SXA model, these collisions are detected earlier, and remediated faster than with a conventional build.

In an all-out SXA implementation, the design/style/construct process is shortened, but changes from conventional expectations.

Wireframes are developed directly in Sitecore. They are exported as packages with HTML, CSS and script, organized in an orderly layered stack based on themes. Designers and front-end developers then style these wireframes, by extending and modifying the CSS and script (for the most part, without changing the HTML). Their work is imported back into Sitecore, where their theme(s) are applied to the site(s) that have already begun to take on content and structure.

This process can be iterative and branched. The export/style/import process can iterate, without necessitating radical changes to the existing work. In fact, Sitecore SXA provides “Creative Exchange Live”, that uses the gulp toolkit to bring design into a dev-ops model for continuous integration. Content editors continue to develop content and construct pages while the styling process iterates, shortening the project timeline and providing more real-world content to help refine the styling.

Multiple themes can be developed within or across sites, allowing the same components and structures to be presented differently simply by selecting from the different themes. The compounded power of reusable components with customizable themes and development-free customization, creates a force multiplier that can dramatically improves time-to-value.

Deep customization when needed

SXA ships with an impressive set of components that cover most of what the core of any site will need, and through the use of “rendering variants”, new renderings can be created without requiring coding.

Using these components out-of-the-box means (for the most part) working with the HTML as provided and styling entirely with CSS and script. An implementation can replace the HTML of all or some of these components and add new ones, but this requires requires a back-end development process more akin to a conventional build. Custom HTML is cut up and used as prototypes for c# developers to create new views, which replace default views or are registered in SXA as new components. Changes to the HTML require code deployments after a round trip through this back-end development process, which can slow concurrent content development.



Next: Changes to Roles and Processes

Understanding Sitecore SXA: Rapid Start, Smooth Execution

Previous: What is SXA?

Today’s marketing technology landscape has become a “field of dreams,” offering a dizzying array of platforms, technologies, and strategies. Marketers are eager to make their own dreams a reality, utilizing these tools to achieve the promise of true customer engagement.

Two things become quickly apparent.
  • Actually living that dream means changing business as usual.
  • Understanding how to execute is the difference between a dream and a nightmare.


Adopting a powerful technology like Sitecore can be overwhelming. A knowledgeable partner knows the landscape can guide a successful implementation, but a complex implementation can be a long and trying process.

By combining a suite of pre-built components and user-friendly tools that smooth the learning curve, SXA gets projects off to the right start and streamlines execution.

Throughout and beyond the initial project build, much of the work of modifying layout and even building new features is shifted to the Sitecore administrative interface, meaning many things that have historically relied on cumbersome code deployment processes can not be achieved with a mere publish.

Time-to-value is reduced, and concurrent team collaboration becomes a force multiplier rather than a series of tactical waves.


Understanding Sitecore SXA: Improved Project Cadence


Previous: Rapid Start, Smooth Execution

Battle-scarred professionals know the stress and cost that can go with a fully bespoke implementation. Sitecore is an outstanding platform for creating truly exceptional experiences. But implementations can be drawn out, owing to the highly linear nature if a conventional build. Nervous stakeholders don’t see progress, and the seeming vacuum created by the isolated development process begs to be filled with new ideas and changes.

A classic linear build relies on handoffs of phases of the project: from user experience to information architecture to wireframing to front-end development to back-end development to content load to UAT. Changes and fixes require looking back farther and farther in the chain, leading to regression problems and costly rework.



SXA makes the process more parallel, and allows for continual adjustment and improvement that reduces the need for gated, painstakingly detailed requirements.



After primary envisioning, Sitecore itself becomes the hub around which the project revolves. Ongoing UX and content load begins immediately, using Sitecore’s wireframe tool. As visual design progresses, front-end developers develop themes that can be applied to already-loaded content in the wireframes, which become the publishable pages. Back-end developers build new components as needed, which are available in the toolbox immediately. Most bespoke component requirements can actually be created entirely within Sitecore and styled by back-end coders, all without requiring a code deployment.

Over the life of the project, stakeholders see ongoing progress, participants stay more engaged and collaborate better, issues are identified sooner, with less regressive remediation, and best practices lead to a deliverable that can be maintained and extended by anybody familiar with the framework.


Next: Prescriptive, Yet Flexible
Home: Understanding Sitecore SXA

Tuesday, March 26, 2019

Understanding Sitecore SXA

Sitecore Experience Accelerator (SXA) can be an absolute game-changer for how projects are envisioned, executed and maintained. Understanding how SXA will affect the design/build/maintain journey is key to knowing when to adopt. Just like in an auto race, knowing when to hit the accelerator can make the difference between success and disaster.

This seven-part series will examine the what-and-why of SXA, and uncover the impact SXA can have on marketing effectiveness.

Why SXA?

If you’re facing a new implementation for the first time, or you’ve been battle-scarred from a past rocky implementations, there’s a certain allure to an out-of-the-box solution, built on best practices, containing a swath of pre-built capabilities, with a prescriptive implementation process.
Learn about SXA’s promise and what it could mean for you.
Read more...

What is SXA?

SXA is a Sitecore-supported framework, toolbox and methodology that helps projects get started faster, develop more smoothly, and deliver results reliably. Learn more about how SXA helps overcome many of the conventional hurdles faced by Sitecore project implementers.
Read more...

Rapid start, smooth execution

Adopting a powerful technology like Sitecore can be overwhelming. Implementations are rocky, solutions become unmaintainable, and the high-return “phase 2” features never come to pass. Learn how SXA’s prescriptive, best-practice methodology and user-friendly tools can smooth the learning curve, get projects off to the right start, and streamline execution.
Read more...


Flatter, faster cadence

Every Sitecore project is unique. Some large, some smaller. Some stand-alone, some multisite. Some complex, some straightforward. Languages, search, media, seo, visual design — any of these can raise the complexity bar. Learn how SXA can lower the barrier to entry, speed time to value, and enable continuous improvement.
Read more...


Prescriptive, but flexible

Digital marketers and technologists that have lived through (or are facing up to) a complex project, the unknowns of proper execution loom large. Faced with unknown complexity, the idea of a highly prescriptive methodology can be a welcome relief. That may feel confining, but SXA allows a mix-and-march approach that allows a given project to adopt all or just some of SXA’s processes. Learn how SXA allows the hard things to be hard, while keeping the simple things simple … and makes some of those hard things simpler than you thought.
Read more...


Nuances in process and responsibilities

When moving into an SXA mindset, in addition to changes in methodology and process, be prepared for roles to be redefined or at least shift a bit. Learn more about how disciplines and processes may shift when adopting SXA as an implementation framework.
Read more...


Choosing a path

Conventional build or SXA? Marketers and technologists preparing for a new site build in Sitecore have more choices than ever before. SXA offers a real alternative to the classic build approach, bringing with it both advantages and challenges.
Read more...

Sunday, June 5, 2016

Zip code data in Sitecore


The source code and documentation for this solution are available on GitHub. Download


Good old postal Zip Codes. Not a very exciting subject, but it seems like every year or two, I run into a solution that requires access to a zip code database.

There are several commercial services that provide extensive zip code data, but there is at least one free database available (https://boutell.com/zipcodes/) that includes a decent amount of data, including coordinates, city and state, and time zone.

Latitude and longitude can be useful if for example you needed to put an "approximate" pin in a map.

We recently ran into a situation where we needed to know the visitor's time zone. Sitecore GeoIP data includes zip codes, but not time zones. All I need is to wire that up to a zip code database, and I'm all set. (And before you quibble over the accuracy, don't. I know it's not perfect; I think of this as a "good enough" solution).

So I set up a solution to provide Sitecore with an API for looking up zip codes. I started with a few goals:

  • I want to be able to use this in any Sitecore solution.,
  • I don't want to use SQL. It's always administrative and deployment hassle to use custom SQL tables.
  • I don't want to impose a schema on every application that uses this. Sure, that free zip database is fine for what I need now, but others may have more detailed data they'd like to use.
  • I want to use a swappable provider so other applications can change how the data is imported, where it is stored, and/or how it is queried.
  • For my default provider, I want it to "just work". I drop the file into a folder, and my Sitecore app has access to the data (well, I did end up also needing to add a mongo connection string to the connectionstrings.config file).

I decided to use MongoDB to store the data. Once I have a connection string, I can create collections and add data with any schema I want, without bugging the SQL admins. I also added to add a caching layer. I'm probably going to access this data from rules and such, and I want it to be zippy-quick.

The data flow looks like this:


The idea is, the operational data stored in MongoDB, and accessed through a caching layer at runtime. At application start (and via an administrative interface), the data file date is compared to the last time the data was imported, and if the file is newer, it is re-imported.

Installing the module

Install the update package in your Sitecore application. When Sitecore starts, it will populate a MongoDB database with data from a provided zip code document located in App_Data. This file is sourced from https://boutell.com/zipcodes/.

Whenever this file is updated, it will be reloaded the next time Sitecore starts.

Add a connection string to your ConnectionStrings.config file with the name you'd like to use for your Mongo database. For example

<add name="zipinfo" connectionString="mongodb://localhost:27017/zipinfo" />

If you want to use a separate mongo database for each Sitecore instance sharing a common Mongo server, change the connectionString e.g.
connectionString="mongodb://localhost:27017/myapp_zipinfo"

The update package will place a copy of the data file in your App_Data folder. You can relocate this to the Sitecore data folder if you desire.

Using the module

The module exposes a "manager" static class (ZipInfo.ZipInfoManager) with static methods like Get(int zipCode) to access the data. I won't get into all the methods here (see the documentation), but there are methods for both retrieve/update and cache management operations.

The update package will also install a utility at /sitecore/admin/zipinfo.aspx that'll allow you to query the database, manage the cache, and re-import the data.

The module exposes a provider class that can be swapped out with your own provider. If you have more detailed data in a a csv file, you can simply inherit from the default provider, create your own POCO class, and override the LoadRecord method that maps fields on the csv line to the POCO. If you need a different method for loading the data rather than reading it from a csv file, override the Load method. If you don;t want to use mongo, you can replace the entire provider by creating a class that implements IZipInfoProvider. More information about the provider is available in the docs.


The source code and documentation for this solution are available on GitHub. Download





Wednesday, June 1, 2016

Content Indexing vs Site Search

I've had this conversation so many times, I thought I'd capture it here once and for all.

There is a vast difference between content indexing and site search. The following discusses these differences. This is not exhaustive; there are finer nuances that I’ll skip over in order to keep focused on the key concepts.

Content Indexing

Content indexing is the act of storing selected fields of Sitecore content items into a separate index, so that content items can be retrieved rapidly by code. Examples of this are the search box Sitecore uses for item buckets, or a custom rendering that “facets” content e.g. outputs links to every item where “Georgia” is selected in a “Home state” field.

  • Indexes are created by copying raw item data into the index, typically when the item is saved or published.
  • Content indexing is a “data-oriented” operation e.g. a lookup in an index finds an match of content in a field. 
  • A content index has no concept of pages, and does not have any ability to rank on such things as link frequency. 
  • Content Indexing is absolutely required for Sitecore to function. 
  • Sitecore implements content indexing “out of the box”, using Lucene by default, with configurable support for Solr in scaled enterprise environments. 

Site Search

Site search is the act of indexing the content of entire viewable pages, so that whole pages can be found using “free text” search. An example of this is a site visitor entering a few words in a search box and getting back a page of ranked results, akin to a Google search.

  • Indexes are created by “crawling” the site e.g. code uses http requests to pull every page of the site, storing the content in its index, and examining the links on in the page to find more pages to crawl.
  • Site search is a “free text” operation, e.g. a lookup considers all of the visible content of a page.
  • A good site search tool ranks results based on things like semantics e.g. content in <h1> tags will rank higher than body text, or linking e.g. pages with more inbound links will rank higher.
  • A site search solution is only necessary if you want visitors to be able to “free text” search the site e.g. the site has a “search box”. 
  • Sitecore does not implement free-text page search “out of the box”.

Why the distinction is important

Any given page of a Sitecore site may have visible page content derived from many content items. Therefore, out-of-the-box content indexing is not an appropriate solution for site search.

Moreover, a good “free text” search experience requires that the results be well ranked. Consider when you do a Google search. Google isn’t simply returning a flat list of every page that contains your search terms, instead, it is using highly sophisticated ranking algorithms to present the results you are most likely to want first. If you’re familiar with SEO principles, you know that there are many factors that influence rank far beyond the simple content of the page.

Of course there is some overlap. A good site search tool can also include "hard data" in the form of metadata, so that search results can be "faceted". This allows the visitor to "filter" results based on date, geography, product line, or any other "field oriented" data that you include in the page metadata.

We've already deployed Solr. Why can't we use that for site search?

In theory, there is a way to leverage a Solr index to do free text search. This is not a simple matter of “configuration”, but rather, requires extensive coding. The general idea is you build a scheduled processor that programmatically loads every page of the site (via an http request) so it can get the entirety of the content on a given page. It puts that content into a “computed field” of a Solr index. Then, custom “search box” code can search that “computed field” for occurrences of that content. There are a drawbacks to this approach:

  • It is not implemented out of the box.
  • The ranking of search is either non-existent, or at least far short of the ranking quality of a true crawler.
[edited to correct my error about Coveo]

There are “off the shelf” tools that combine the concepts of content indexing and site search.

  • Coveo is an excellent commercial product that uses a proprietary indexing mechanism, with conventional "content indexing" and also crawling. It can index both entire pages and content items. It comes with value-added tools for rapid deployment of faceted search features, and also adds some ranking capabilities, including the ability to manually tweak search ranking. It comes in on-premises, cloud, and a hobbled “free” version. It is arguably the “least effort” solution to implement, since it is very "Sitecore aware" out of the box.
  • There are lots of free and commercial solutions. For example, Arke’s SDK includes a “computed search” module. uses configured field and template types to inject page content into a Solr index. 

There are other “off the shelf” solutions that provide excellent free text search experiences that do not rely on Solr. Most of these have evolved to cloud-hosted rather than on-premises solutions. Google site search and Amazon cloud search are leaders in this space, and Coveo had a cloud edition, but there are many services available. Using one of these services would still require coding, but it would be pure “integration” coding, not an attempt to build a full blown crawler.

In the absence of an “off the shelf” solution, you could build a home-grown Solr-based crawler. It’d require significant time and effort, only to yield a pretty poor user experience due to the lack of any sophisticated ranking.


Thursday, April 14, 2016

Using ARR to enable FXM


Ever used Sitecore's Federated Experience Manager (FXM)? Effectively, it lets you use Sitecore to content manage, track, personalize and test external sites which are not hosted in Sitecore.

The motivations I often hear for using FXM are...

  • We've bought a license and plan to migrate to Sitecore later, but we want to start personalizing and gathering analytics on our site now.
  • We're moving our main site to Sitecore, but we have some related sites we just don't have time and budget to move now.
  • We want to do a demo or POC using content from a non-Sitecore site, but don’t want to re-create the content in Sitecore.

With FXM you can do that. All you need is to place a small bit of script on the external sites. Sadly, that's often not possible. Sometimes the old site is literally on a server that nobody knows how to access. Sometimes you're just doing a POC and nobody wants to edit the old site for that.

IIS's Application Request Routing (ARR) to the rescue.


IIS has features called ARR and the URL Rewrite module that amount to a reverse proxy that allows you have a “man in the middle” that can manipulate the HTML before it is returned to the browser.

We set up a IIS instance with ARR with a public-facing URL (in this example, “demo.mysite.com”), and configure ARR to do the following

  1. Take the path from the inbound request, and form a URL using the Sitecore server’s host name.
  2. Fetch the HTML from the Sitecore host.
  3. Inject the FXM beacon script into the HTML
  4. Change the URLS within the HTML for such things as images, scripts, CSS, iframes, etc, so that they will be requested from the ARR and not the Sitecore site.
  5. Strip out the “X-Frame-Settings” header (if it exists), which can interfere with FXM Experience Editor.

This results in a topology like this:



The URL structure in this example would give us a demo/POC website (“demo.mysite.com”) where we can show how a site can be tracked and manipulated with FXM. This could in theory be used for a live site by changing DNS to point www.mysite.com to the ARR, and change the hostname of the Sitecore server to something like Sitecore.mysite.com.

Setting up the Reverse Proxy

From an infrastructure perspective, setting up the proxy server is pretty simple. Install the ARR and URL Rewrite extensions, and create a new site in IIS. Set the binding up so it answers requests from the desired host (in the example above, “demo.mysite.com”). The site folder doesn’t need much; a default.htm page, and an empty web config.

The magic all happens in web.config. The URL Rewrite module is governed by rules. There are two sets, one just called “rules” which are used to route requests to the Sitecore server, and another called “outbound rules” which are used to manipulate the responses from Sitecore before they are returned to the browser. Outbound rules also allow you to define “preconditions” that allow you to restrict when an outbound rule will apply.

The IIS management console provides an interface for building up all the XML in the config file for all of this. I find that when I’m working with it, I flip between IIS and Notepad++ until I get everything just right.

The referenced articles provide good guidance for how to use the URL Rewrite module and set up rules. This example web.config could be used to implement our example.


 <?xml version="1.0" encoding="utf-8"?>  
 <configuration>  
  <system.web>  
  </system.web>  
  <system.webServer>  
   <rewrite>  
    <rules>  
     <!--  
     This rule routes requests everything to the external site.  
     The use of "HTTP_ACCEPT_ENCODING" ensures that external servers   
     will send responses in the clear (not zipped or otherwise encoded)  
     -->  
     <rule name="Route to external site" stopProcessing="true">  
      <match url="(.*)" />  
      <action type="Rewrite" url="http://www.mysite.com/{R:1}" />  
      <serverVariables>  
       <set name="HTTP_ACCEPT_ENCODING" value="" />  
      </serverVariables>  
     </rule>  
    </rules>  
    <outboundRules>  
     <!--  
     This rule converts proxied pages' urls to relative urls (so they'll be requested through the ARR server and avoid cross-domain issues)  
     -->  
     <rule name="Rewrite External Absolute Paths" preCondition="Request is for html">  
      <match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^http(s)?://www.mysite.com/(.*)" />  
      <action type="Rewrite" value="/{R:2}" />  
     </rule>  
     <!--  
     This rule removes the X_Frame_Options header, which can prevent the Experience editor from working.  
     -->  
     <rule name="Strip x-frame-options" preCondition="Request is for html" patternSyntax="ECMAScript">  
      <match serverVariable="RESPONSE_X_Frame_Options" pattern="(.+)" />  
      <action type="Rewrite" value="" />  
     </rule>  
     <!--  
     This rule removes adds "(via proxy)" to the Server header, to aid troubleshooting.  
     -->  
     <rule name="Change Server Header">  
      <match serverVariable="RESPONSE_Server" pattern="(.+)" />  
      <action type="Rewrite" value="{R:0} (via proxy)" />  
     </rule>  
     <!--  
     This rule injects the FXM script into the HTML from the external site.  
     -->  
     <rule name="Add FXM script to tb" preCondition="Request is for html" patternSyntax="ExactMatch">  
      <match filterByTags="None" pattern="&lt;/head>" />  
      <action type="Rewrite" value="&lt;script src=&quot;//sitecore.mysite.com/bundle/beacon&quot;>&lt;/script>&quot;/head>" />  
     </rule>  
     <preConditions>  
      <!--  
      This precondition allows the outbound rules to only act on html responses.  
      -->  
      <preCondition name="Request is for html">  
       <add input="{RESPONSE_CONTENT_TYPE}" pattern="text/html" />  
      </preCondition>  
     </preConditions>  
    </outboundRules>  
   </rewrite>  
  </system.webServer>  
 </configuration>  

Tuesday, February 2, 2016

Dear John...

We all go West
No I’m not writing to say I’ve left you for another CMS. But John West’s announcement today leaves me wanting to take a short detour down Memory Lane. If you came here looking for technical tidbits, I’ll be hangin’ a right back down Architecture Avenue shortly.

I had the great good fortune to work directly with John on my very first Sitecore project. It was one of the first projects to be done at scale in North America, and it was my first foray into a true enterprise-level .net CMS. When Lars Nielsen flew out to conduct our first training, John was there, both to learn and to advise. He remained tightly connected throughout the project, providing strategic advice and technical leadership (and answers to my incessant questions). John’s enthusiasm for Sitecore was infectious. His spirit of adventure set the tone for that project, and indeed for my entire Sitecore career.

John’s thought leadership has been at the bedrock of Sitecore’s growth. His quiet, unassuming tone underlies a deep passion for Sitecore. Owing to John’s example, today’s Sitecore ecosystem is infused with a sense of excitement, wonder, and a craving to learn, create and explore. His blog is a hallmark of his motivational style. John provides the signposts leading to the new and evolving capabilities of the product, while never asserting his knowledge is definitive, never assuming his observations are comprehensive, and never insisting his conclusions are absolute. Being the good teacher he is, he leaves application as an exercise for the student. And exercise we do! Many talented Sitecore professionals share valuable learnings from their Sitecore journeys. But those journeys began with John’s unspoken challenge to “Go West, young man!” (Yes, I went there.)

Over the years, as John as gone from teacher to mentor to friend, I’ve felt immense pride to be part of this dynamic community that John was so instrumental in creating. Though we have gone from speaking almost every day to interacting only sporadically, every time we see each other it seems we are picking up in mid-sentence. There has never been a “goodbye” with him, and there is not one now. Talk to you soon, friend!

(And goodbye forever, XSLT!)

Tuesday, August 4, 2015

Organizing the Language Menu's Tower of Babel

If you have a site with lots of languages, you've no doubt run into some challenges. A small -- but annoying -- one, is the ordering of the language menus. This is particularly problematic when the site has a large number of configured languages, but any given conten item may only have versions in one or two. It can be a chore slogging through the menu looking for ones that have versions.

This little tidbit allows you to re-order the language menu so that languages that actually have versions will float up to the top of that Tower of Babel.

Finding the pressure point

When I'm making changes to Sitecore's "internals" I try to be as minimally invasive as possible. Unfortunately, this solution is more like a tourniquet than a pressure point. I want to replace the code-beside for one of Sitecore's XML controls (the control that Sitecore uses for drop-down language menus in the content editor). The only way I know to do that is to replace the XML file and change the <CodeBeside> element. Anyone know a good trick to change the code-beside without replacing this file?

Worse, the decompiled code-beside doesn't seem to lend itself to a surgical strike that just changes one little part. Often, you'll find that Sitecore anticipates your need by doing things like having an overridable class fetch an object that does most of the work. You can just inherit from their class and change that one method to suit your needs.Not the case here. We're going to have to implement the whole class just to change one line of code. Such is life.

The control for this menu is located at
/Sitecore/Shell/Applications/Content Manager/Galleries/Languages/Gallery Languages.xml
What we really want to do is make a small change to the associated code-beside. First, we need to change this XML file to use our code-beside instead of Sitecore's:

    <!--<CodeBeside Type="Sitecore.Shell.Applications.ContentManager.Galleries.Languages.GalleryLanguagesForm,Sitecore.Client"/>-->  
    <CodeBeside Type="MySolution.Shell.Applications.ContentManager.Galleries.GalleryLanguagesForm,sb1"/> 

For the code-beside file, we need to copy the entire decompiled Sitecore class, although we're really just changing one line of code. So use your favorite decompiler to snag the Sitecore class (or copy the code from the end of this article), and clean up the references.

I'm going to change that one pesky line to call a new method, just to isolate the change and make it more tweakable in the future.

To fetch the list of languages to put in the menu, Sitecore just does a  GetLanguages(currentItem).


That works, but I want to take the languages for which there are actually versions, and float them to the top.


So I change their code to call my method instead of GetLanguages:

 foreach (Language language in GetLanguages(currentItem))  //currentItem.Languages)  

... and then I add the GetLanguages() method that lets me be a it more finessed about ordering:

 protected IEnumerable<Language> GetLanguages(Item {  
  return currentItem.Languages.Where(l => ItemManager.GetVersions(currentItem, l).Count > 0)  
   .Union(currentItem.Languages.Where(l => ItemManager.GetVersions(currentItem, l).Count == 0));  
 }  

And now the languages that are actually used for this item will float to the top. This might be a handy place to do other manipulation you might need for your solution, depending on the business rules for language management.

Here's the full code for the XML control and the code-beside:

 using System;  
 using System.Collections.Generic;  
 using System.Globalization;  
 using System.Linq;  
 using Sitecore;  
 using Sitecore.Configuration;  
 using Sitecore.Data;  
 using Sitecore.Data.Items;  
 using Sitecore.Data.Managers;  
 using Sitecore.Diagnostics;  
 using Sitecore.Globalization;  
 using Sitecore.Shell;  
 using Sitecore.Web;  
 using Sitecore.Web.UI.HtmlControls;  
 using Sitecore.Web.UI.Sheer;  
 using Sitecore.Web.UI.XmlControls;  
 using Control = System.Web.UI.Control;  
 namespace MySolution.Shell.Applications.ContentManager.Galleries  
 {  
   public class GalleryLanguagesForm : Sitecore.Shell.Applications.ContentManager.Galleries.GalleryForm  
   {  
     protected GalleryMenu Options;  
     protected Scrollbox Languages;  
     public GalleryLanguagesForm() : base()  
     {  
     }  
     public override void HandleMessage(Message message)  
     {  
       Assert.ArgumentNotNull((object)message, "message");  
       if (message.Name == "event:click")  
         return;  
       this.Invoke(message, true);  
     }  
     protected override void OnLoad(EventArgs e)  
     {  
       Assert.ArgumentNotNull((object)e, "e");  
       base.OnLoad(e);  
       if (Context.ClientPage.IsEvent)  
         return;  
       Item currentItem = GetCurrentItem();  
       if (currentItem == null)  
         return;  
       using (new ThreadCultureSwitcher(Context.Language.CultureInfo))  
       {  
         foreach (Language language in GetLanguages(currentItem))  //currentItem.Languages)  
         {  
           ID languageItemId = LanguageManager.GetLanguageItemId(language, currentItem.Database);  
           if (!ItemUtil.IsNull(languageItemId))  
           {  
             Item obj = currentItem.Database.GetItem(languageItemId);  
             if (obj == null || !obj.Access.CanRead() || obj.Appearance.Hidden && !UserOptions.View.ShowHiddenItems)  
               continue;  
           }  
           XmlControl xmlControl = ControlFactory.GetControl("Gallery.Languages.Option") as XmlControl;  
           Assert.IsNotNull((object)xmlControl, typeof(XmlControl));  
           Context.ClientPage.AddControl((Control)this.Languages, (Control)xmlControl);  
           Item obj1 = currentItem.Database.GetItem(currentItem.ID, language);  
           if (obj1 != null)  
           {  
             int length = obj1.Versions.GetVersionNumbers(false).Length;  
             string str1;  
             if (length != 1)  
               str1 = Translate.Text("{0} versions.", (object)length.ToString());  
             else  
               str1 = Translate.Text("1 version.");  
             string str2 = str1;  
             CultureInfo cultureInfo = language.CultureInfo;  
             xmlControl["Header"] = (object)(cultureInfo.DisplayName + " : " + cultureInfo.NativeName);  
             xmlControl["Description"] = (object)str2;  
             xmlControl["Click"] = (object)string.Format("item:load(id={0},language={1},version=0)", (object)currentItem.ID, (object)language);  
             xmlControl["ClassName"] = !language.Name.Equals(WebUtil.GetQueryString("la"), StringComparison.OrdinalIgnoreCase) ? (object)"scMenuPanelItem" : (object)"scMenuPanelItemSelected";  
           }  
         }  
       }  
       Item obj2 = Sitecore.Client.CoreDatabase.GetItem("/sitecore/content/Applications/Content Editor/Menues/Languages");  
       if (obj2 == null)  
         return;  
       this.Options.AddFromDataSource(obj2, string.Empty);  
     }  
     protected IEnumerable<Language> GetLanguages(Item currentItem)  
     {  
       return currentItem.Languages.Where(l => ItemManager.GetVersions(currentItem, l).Count > 0)  
         .Union(currentItem.Languages.Where(l => ItemManager.GetVersions(currentItem, l).Count == 0));  
     }  
     private static Item GetCurrentItem()  
     {  
       string queryString1 = WebUtil.GetQueryString("db");  
       string queryString2 = WebUtil.GetQueryString("id");  
       Language language = Language.Parse(WebUtil.GetQueryString("la"));  
       Sitecore.Data.Version version = Sitecore.Data.Version.Parse(WebUtil.GetQueryString("vs"));  
       Database database = Factory.GetDatabase(queryString1);  
       Assert.IsNotNull((object)database, queryString1);  
       return database.GetItem(queryString2, language, version);  
     }  
   }  
 }  


 <?xml version="1.0" encoding="utf-8" ?>  
 <control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense" xmlns:shell="http://www.sitecore.net/shell">  
  <Gallery.Languages>  
   <Gallery>  
    <!--<CodeBeside Type="Sitecore.Shell.Applications.ContentManager.Galleries.Languages.GalleryLanguagesForm,Sitecore.Client"/>-->  
    <CodeBeside Type="sb1.Shell.Applications.ContentManager.Galleries.GalleryLanguagesForm,sb1"/>  
    <Script>  
     window.onload = function() {  
     var activeLanguage = document.querySelector('.scMenuPanelItemSelected');  
     activeLanguage.scrollIntoView(false);  
     }  
    </Script>  
    <Stylesheet Key="GalleryLanguages">  
     .scMenuPanelItem, .scMenuPanelItem_Hover, .scMenuPanelItemSelected_Hover, .scMenuPanelItemSelected {  
     padding-left: 0;  
     padding-right: 0;  
     padding-top: 8px;  
     padding-bottom: 8px;  
     }  
     .scGalleryGrip {  
     position: absolute;  
     bottom: 1px;  
     left: 1px;  
     right: 1px;  
     height: 10px;  
     }  
     .scLanguagesGalleryMenu {  
     overflow: hidden;  
     vertical-align: top;  
     border-bottom: 12px solid transparent;  
     -moz-box-sizing: border-box;  
     box-sizing: border-box;  
     width: 100%;  
     height: 100%;  
     border-collapse: separate;  
     }  
     div#Languages img {  
     display: none;  
     }  
    </Stylesheet>  
    <Border Width="100%" Height="100%">  
     <GalleryMenu ID="Options" Class="scLanguagesGalleryMenu">  
      <MenuPanel Height="100%">  
       <Scrollbox ID="Languages" Class="scScrollbox scFixSize scFixWidthInsideGallery" style="padding-top:0 !important;" Height="100%" Width="100%" />  
      </MenuPanel>  
     </GalleryMenu>  
     <Gallery.Grip />  
    </Border>  
   </Gallery>  
  </Gallery.Languages>  
 </control>  


Wednesday, May 28, 2014

Size Doesn’t Matter: Suppressing Size Attributes in Image Tags



On a recent, massively-responsive project, our front-end developer asked us to (well, he actually threatened to hold his breath until he turned blue unless we would) remove the height and width attributes from the image tags in our Sitecore site. He’s one of the best front-end guys I've ever worked with, so instead of just dismissing this as “front-end guys will be front-end guys”, I decided to see if we could indulge him.

It makes sense, actually. Client-side code can deal manipulating height and width easily enough when rendering on different devices. But it you want the thrill of waving around the resize handle and watching all that responsiveness a’responding, then it’d be better if the height and width attributes weren't there in the first place.

By and large, there are two ways an image tag finds its way into a page generated by Sitecore. It may come from an image field, or from an embedded image in an HTML field. So we should be able to tackle this in the renderField pipeline, assuming we've been good boys and girls and used field renderers everywhere (and if you haven’t, then go to your room and think about what you've done).

The two cases (image fields and html fields) pose different challenges, so let’s look at them separately. Or if you don’t want to dig in, then you can stop reading now, download the module package or source code (zip fileGitHub, or Sitecore Marketplace) and have at it.

Image Fields

We've got images in image fields, and we’re using field renderers to generate image tags at runtime. Sitecore uses Sitecore.Pipelines.RenderField.GetImageFieldValue to do this. When we take a look under the hood, we see it in turn is using a Sitecore.Xml.Xsl.ImageRenderer. Luckily, GetImageFieldValue uses a virtual method CreateRenderer, so we can shim in our own class that inherits from GetImageFieldValue and override CreateRenderer to substitute our own handy-dandy ImageRenderer class.

Now, Sitecore's ImageRenderer class is pretty bulky, and it has more stuff that deals with dimensions than a cartographer’s workshop. I’d like to just inherit from theirs and find a good pressure point to slap down those size attributes. Taking a good look at the Render method in Sitecore’s ImageRenderer, it looks like someone anticipated our need. The last thing it does to determine the size is to call a virtual method called AdjustImageSize. All we need to do is override that and set the height and width properties to zero. The existing Sitecore code is already set up to suppress the height and width attributes if these properties are zero.

So we need two pretty lightweight classes and an override in a config file. First, the config file. We need to tell Sitecore to replace its GetImageFieldValue processor with ours.

 <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">  
  <sitecore>  
   <pipelines>  
    <renderField>  
     <processor   
      type="DimensionlessImages.Pipelines.RenderField.GetImageFieldValue,   
         DimensionlessImages"  
      patch:instead="*[@type='Sitecore.Pipelines.RenderField.GetImageFieldValue,  
         Sitecore.Kernel']"  
      />  

Then, there’s our own GetImageFieldValue processor, which inherits from Sitecore’s and overrides the CreateReplacer method.

 namespace DimensionlessImages.Pipelines.RenderField  
 {  
  using Sitecore.Xml.Xsl;  
  public class GetImageFieldValue : Sitecore.Pipelines.RenderField.GetImageFieldValue  
  {  
   protected override ImageRenderer CreateRenderer()  
   {  
    return new DimensionlessImages.ImageRenderer();  
   }   
  }  
 }  

And lastly, there’s our own ImageRenderer, which overrides the AdjustImageSize method.

 namespace DimensionlessImages  
 {  
  using Sitecore.Data.Fields;  
  public class ImageRenderer : Sitecore.Xml.Xsl.ImageRenderer  
  {  
   protected override void AdjustImageSize(ImageField imageField, float imageScale, int imageMaxWidth, int imageMaxHeight, ref int w, ref int h)  
   {  
    w = 0;  
    h = 0;  
   }  
  }  
 }  

Piece of cake, that.

HTML Fields

HTML fields are a different animal. We've got a hunk of existing html, not just some data we’ll use to form HTML at runtime.

When a media library image is inserted in the rich text editor, Sitecore adds the height and width attributes to the img tag. So between that, and the possibility that content people might edit HTML manually (bless their little programmer-wannabe hearts), we’re going to have lots of height and width attributes in our HTML fields.

We can use the HtmlAgilityPack to strip these attributes off the img tags in in the renderField pipeline. Although the HtmlAgilityPack is wicked fast, we could start to see a performance hit of there are lots of HTML fields on complex pages. It’d better to do it in a save handler or a publishItem processor, but that could present problems with the page editor or if there is already a lot of existing content. I’m seeing times of less than 0.1ms per HTML field to strip the tags at runtime, so to make this code more bullet-proof (well, ok, to let me be lazy) I’m going to do it in renderField. If your solution permits, by all means move this processing to a save handler.

Like before, we’ll start with the config changes…

 <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">  
  <sitecore>  
   <renderField>  
    <processor type="DimensionlessImages.Pipelines.RenderField.GetFieldValue, DimensionlessImages"  
      patch:instead="*[@type='Sitecore.Pipelines.RenderField.GetFieldValue, Sitecore.Kernel']"  
    />  

We’ll use a custom GetFieldValue processor. It is actually simpler than the image field case, because all we have to do is catch “rich text” fields and strip the height and width attributes.

 namespace DimensionlessImages.Pipelines.RenderField  
 {  
  using Sitecore.Pipelines.RenderField;  
  public class GetFieldValue : Sitecore.Pipelines.RenderField.GetFieldValue  
  {  
   public new void Process(RenderFieldArgs args)  
   {  
    base.Process(args);  
    if (args.FieldTypeKey == "rich text")  
    {  
     Sitecore.Diagnostics.Profiler.StartOperation("Stripping image tags from field: " + args.FieldName);  
     args.Result.FirstPart = HtmlUtil.StripDimensions(args.Result.FirstPart);  
     Sitecore.Diagnostics.Profiler.EndOperation();  
    }  
   }  
  }  
 }  

Finally, we need a helper method uses the HtmlAgilityPack to strip the dimension attributes…

 namespace DimensionlessImages  
 {  
  using System;  
  using HtmlAgilityPack;  
  public class HtmlUtil  
  {  
   public static string StripDimensions(string text)  
   {  
    if (string.IsNullOrWhiteSpace(text))  
    {  
     return text;  
    }  
    string outText = text;  
    try  
    {  
     var doc = new HtmlDocument();  
     doc.LoadHtml(outText);  
     StripAttribute(doc, "width");  
     StripAttribute(doc, "height");  
     outText = doc.DocumentNode.WriteContentTo();  
    }  
    catch (Exception)  
    {}  
    return outText;  
   }  
   private static void StripAttribute(HtmlDocument doc, string attribute)  
   {  
    // For reasons surpassing all understanding, HtmeAgilityPack returns null instead of an empty collection  
    // when the query finds no results.  
    HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes(String.Format("//img[@{0}]", attribute));  
    if (nodes == null || nodes.Count.Equals(0))  
    {  
     return;  
    }  
    foreach (HtmlNode node in nodes)  
    {  
     node.Attributes[attribute].Remove();  
    }  
   }  
  }  
 }  

- - -

This code addresses the most likely cases that emit image tags to the browser. A given solution may have other cases, like stuff being generated by custom code or copied from static files. For cases like that, we've provided the static method that custom code can use to “play ball” with the rest of this code.

Download: Module (package only) or source code (zip fileGitHub, or Sitecore Marketplace).






Friday, November 29, 2013

Sitecore Profiling and Tracing

It's been a while since I've posted. I have a couple of bigger subjects on the spike, but for now here's a quick word about Sitecore profiling and tracing.

Have you ever had to debug a problem with a Sitecore page and you wind up slogging through the log, trying to find the related error? Or have you ever wondered which of your renderings or sublayouts might be causing performance issues? Or maybe you have custom logic in a pipeline processor, and you wish there was an easy way to emit debugging or performance information?

There are a couple of underutilized features in Sitecore that can be a real time saver in situations like this: Sitecore.Diagnostics.Tracer and Sitecore.Diagnostics.Profiler. These classes are sitting right next to the much-used Sitecore.Diagnostics.Log class, but are often overlooked. Both of these classes allow you to emit information directly to the page when in debug mode. This is a far more convenient and useful way to do debugging than writing to the Sitecore log.

The Profiler class is useful for tracking the progress of the page rendering cycle, and for gathering performance data about the components and code being executed. It has the usual Info, Warning and Error static methods you are probably familiar with from the Log class, but the real gems here are the StartOperation and EndOperation methods. With them, you can gather valuable performance data about parts of the rendering process, including both large chunks of a process and nested inner pieces of the process. For example, the Arke MetaTag Manager  module emits quite a bit of information about it's internal workings to the debugger.


The information provided not only helps us understand the performance impact of this process, but also helps us troubleshoot when we suspect that a tag is not being gathered properly.

To output this information, it's a simple matter of adding a two lines of code. For example, this is the Process method used for the CustomTag pipeline processor:


public void Process(InjectMetaTagsPipelineArgs args)
{

  Sitecore.Diagnostics.Profiler.StartOperation(
    "Adding custom tag '" + GetSignatureName() + "'"
    );

  try
  {
    Assert.ArgumentNotNullOrEmpty(
      TypeSignature, 
      "Class not supplied"
      );
    args.MetaTags.Add(ReflectionUtil.GetCustomTag(TypeSignature));
  }
  catch (Exception ex)
  {
    Sitecore.Diagnostics.Tracer.Error(
      "CustomTag failed.", 
      ex
      );
    Sitecore.Diagnostics.Log.Error(
      "CustomTag failed.", 
      ex, 
      "CustomTag"
      );
  }
  finally
  {
    Sitecore.Diagnostics.Profiler.EndOperation();
  }
}


Sitecore will nest sets of profiling blocks, which makes it easy to trace through a process to see where a problem or performance hit may be.

The other handy class for "in-page" debugging is the Tracer class. Again , this class has the familiar InfoWarning and Error static methods, but the output from these methods is written to the "Trace" section of the page debugger. I use these methods for writing out error or info strings much the way I would to the Sitecore Log, but they're written to the page itself, where troubleshooting is much easier. It's always nice to just be able to turn on the debugger to see this info instead of having to search through the log file.

Again, looking at the sample code above, note that the "catch" statements include a line of code to write the error to the tracer, which would yield this:

That beats searching for the error on the log file ... or worse, throwing a "yellow screen".

Happy tracing!