Sunday, October 9, 2016

Build a Location Finder search using SXA

Recently I've been playing around with the new Sitecore Experience Accelerator (SXA) and wanted to share something that is possible without any coding.

Here's a preview in case you don't want to read the rest of the post.


Isn't the kitty cute? Moving on...

SXA is bundled with quite a number of search components. Let us see what is in use.
  • Location Finder - essentially a search box with label and button.
  • Search Results - just like it sounds.
  • Filter (Radius) - used to reduce the search results by geospatial comparison.
  • Map - a map using the Google Maps JavaScript API.
When you add search components to the page, they automatically interact with one another. In the event you want to have multiple searches on the same page, change the signature property on the components.

I have no need to make the search signature unique for this page so I will leave it empty. 

Another interesting part about these components is the use of the hash parameters. In this example, I changed the distance filter to 500 miles and the data was automatically populated in the url.


In the above image, the hash query contains an entry for the geolocation (g), order (o), and distance (DistanceMi).

Let's have a look at each of the search components in greater detail.

Location Finder

This control deals with accepting search criteria from the user in the form of a city, state, or zip code.

As you enter data in the field you'll see the Google Autocomplete feature activate. Selecting the city will immediately trigger the hash query to update with the new location which in turn runs a search.

As of version 1.1 there is no out of the box way to limit the results by country or city.

Note: Before adding this component to the page you'll need to create a few settings.

Distance Facet
I created a new DistanceFacet item to specify that the filter will use the unit of miles. Add a new facet under Settings -> Facets.


Location Filter
Next I created a new LocationFilter item to be used as the data source. This item makes use of the  DistanceFacet. Go ahead and set the placeholder text, label text, and finally the button text. Add a new filter under Data -> Search -> Location Filter.


Search Results

This control is responsible for rendering the queried items. We will need an item to specify the search scope.

Scope
The Scope item uses a Sitecore query to get the appropriate data. The query needs to return items that inherit from the POI template which contains fields like Latitude and Longitude. Add a new scope under Settings -> Scopes.


I used this query to get the job done. The default scope is the entire site so be sure to do this. Further down in this post you'll see how I created the POI items returned by this query.

location:{BE88BF74-BBD1-4BB0-A4ED-1E34F477F985};+custom:_templatename|poi

While you are there make sure that the Search Results component has a default data source for use when no results are found under Data -> Search -> Search Results.



Edit the component properties to make use of the scope and default result.

Filter (Radius)

This control enhances the results by filtering our the specified radius (miles or kilometers).

Radius Filter
The Radius Filter item specifies which distance facet to use. Add it under Data -> Search -> Radius Filter.

Radius Scheme
The Radius Scheme item specifies which options are available for filtering.


Note: I was not quite sure where to place the Radius Scheme item because no insert option existed so I placed it under Data -> Search -> Radius Filter. I also didn't know the Radius Scheme existed until I dug around to see what I needed to create. Looking forward to the detailed documentation for each of this features in the near future.

Map

This control renders the search results on a map with location markers. The search results need to have the Latitude and Longitude populated for this to work properly. Be sure to create a Map item under Data -> Maps before adding this control to the page.


POI Type
The POI Type item provides a way to customize the marker icon. For this demo I created a new POI type under Presentation -> POI Types, which allowed me to specify the awesome SPE custom marker icon.


POI
The POI item as mentioned before contains the Latitude and Longitude necessary for the spatial search to function. I used the POI Group item to help organize the POI items by state. Notice that the Type field uses the custom SPE POI.

Each of the cities are represented by the base POI which comes with a title, description, and image field.

Rendering Variants
The creation of new Rendering Variants for these controls is the last piece to making this work. The following should be added or updated to meet your visual needs.

For the POI rendering variant I set the Default Variant on the custom SPE POI. This should influence the presentation of the InfoWindow that appears on the map.

For the Search Results rendering variant I created a new Location Variant and applied it in the Experience Editor for the component.

I styled the rendering variant to help position the title, description, latitude, longitude, and image into the beautiful structure seen above.

Conclusion

That's pretty much all it takes to build a custom Location Finder on your website. The only code required was using SPE to siphon all of the geo data and images into Sitecore.

Additional Resources
If you haven't checked out Reinoud's post on Partial Designs and Page Designs I would highly recommend you do that today as it will give you some clue at how to build up the page in this article. Looks like there was even a refresh on the icons too!

I've chosen to use the Basic provided with SXA rather than the Wireframe theme so all my images show up.

Monday, July 13, 2015

Sitecore PowerShell Extensions Remoting v2

Let me start off by saying that the work done by Himadri here is a great example at the flexibility of SPE. Not long ago Adam posted about Remoting in SPE, it became clear that we needed to have a Windows PowerShell module that users can setup outside of Sitecore that would come with all the necessary commands to interact with SPE. In our 3.1 release we included the module, so feel free to grab that now.

In this post I would like to show some recent enhancements made in 3.2 that will make interacting with SPE even better!

The packaged Windows PowerShell module can be found on the marketplace listed as SPE Remoting. That is the preferred method for interacting with SPE outside of the Sitecore environment. If you wish to use the same code from within the browser, such as to interact with another instance of SPE, you'll find the commands under here:
master:/system/Modules/PowerShell/Script Library/Platform/Functions/Remoting2

We've included all of the documentation below in our book.
When you execute the script or import the module you'll get the following commands:
  • New-ScriptSession - This can be reused between calls to all the other commands.
  • Invoke-RemoteScript - Best option for performing any remote script execution.
  • Send-MediaItem - Remotely upload.
  • Receive-MediaItem - Remotely download.
Let's walk through a few examples at using the commands.

Create a new session object that will contain a reference to the SPE session id and the web service proxy. Next we invoke a scriptblock on the server within the session.

If you would like to download images from the media library you can do something as simple as the following:

If you would like to upload images from the filesystem to the media library you can do something like this:

Saturday, May 16, 2015

Sitecore PowerShell Extensions - System Maintenance

Here recently I was thinking about how I could perform some of the system maintenance tasks that you would have to manually run from the Sitecore Control Panel. I decided to add these scripts to the System Maintenance script module in the Sitecore PowerShell Extensions module. I hope this encourages you to spend a little more time in SPE.

// michael

Friday, April 10, 2015

Sitecore PowerShell Extensions Tip - Count Items

Today I needed a quick report to find out the number of "pages" on our site. I came up with a quick estimate using the Sitecore PowerShell Extensions module.

I hope this encourages you to spend a little more time in SPE.

// michael

Thursday, February 26, 2015

Move Workstation To New OU With PowerShell

Question came up at work today on how to move a workstation in Active Directory from one OU to another. Here's what I came up with.

I hope this helps someone!

Import-Module ActiveDirectory

$computers = "PC1","PC2" # Optionally use Get-Content -Path C:\computers.txt
$computers | ForEach-Object { 
        $computer = "$($_)$"; Get-ADComputer -Filter { SamAccountName -eq $computer } | 
        Move-ADObject -TargetPath "OU=Retired Workstations,OU=Company,DC=company,DC=corp"
    }

// michael

Tuesday, January 20, 2015

Sitecore Code Editor 1.6 Preview

Developing on the Sitecore platform has been some of the most enjoyable time in my career. For me, the excitement of discovering new aspects of  the platform and building features for a module that I'm going to just give away is well worth the late night investment. I hope those of you that use the module find it helpful. Enjoy!

Today I would like to outline the changes included in v1.6 of the Sitecore Code Editor Module. Those of you that have never seen or heard of this module, it provides an improved text editing experience using the Ace Code Editor plugin. I'll outline some of the enhancements or fixes then go through each in greater detail.

Changes:

  • Added persistent user settings for the editor.
  • Added configurable height and width for the editor window.
  • Added scrolling to the content editor window.
  • Updated with new mimetypes.
  • Fixed issue with media blobs not appearing in module packages.
  • Fixed code template share setting.
The nice thing about this release is it was heavily influenced by community requests. You may have noticed while using the code editor on most computer screens the modal window was just not quite large enough. Now the module supports storing the modal window width, height. In addition, settings that effect the text include font size, font type, and theme. 



The content window in the Content Editor now supports scrolling for large sets of text.



Mime types have been added for files with content for LESS, SCSS, Windows PowerShell, and others. These can be found in Sitecore.SharedSource.CodeEditor.config.



The Code Attachment field type was removed to correct an issue where media item blobs were not properly included in the packages. I figured out that I can add command buttons in the content editor in code rather than defining in the core database as a new field type. Thanks to John West for posting that article a few years ago :)

I've reverted back to the original Attachment system type but changed the control.


Some cleanup is required, more specificly the Code Attachment field type.

That's pretty much it. Happy coding!

References:
  • http://www.sitecore.net/Learn/Blogs/Technical-Blogs/John-West-Sitecore-Blog/Posts/2013/05/Add-Commands-to-Edit-Templates-and-Fields-in-the-Sitecore-ASPNET-CMS.aspx
// Mikey

Saturday, November 1, 2014

Sitecore PowerShell Extended with Gutters

Recently I challenged myself to find integrations with Sitecore PowerShell Extensions that have not yet been published. I saw this cool  article by ParTechIT and knew it was something I had to try. Of course I have to tell someone when I get it figured out.


After having already extended with pipelines I didn't expect this to take very long. Hopefully those reading this will learn something, decide to share it, and point out areas of improvement. Feel free to comment or make suggestions. I expect to add this to a future release of Sitecore PowerShell Extensions (SPE).

User Story:
As a spe user, I can create scripts to run when rendering gutters so that I don't have to compile the GutterRenderer.
As a spe user, the script can be configured just like any other GutterRender, so that I don't have to further complicate the setup.

Acceptance Criteria:

  • The gutter rendering scripts must reside under the following path:
    • /sitecore/system/Modules/PowerShell/Script Library/Content Editor/Gutters
  • The GutterRenderer must be configured under the following path:
    • /sitecore/content/Applications/Content Editor/Gutters
  • The example GutterRenderer must be stolen.
Some concepts you will see in this article:
  • Creating a GutterRenderer using Windows PowerShell code in SPE.
  • Configuring a GutterRenderer in Sitecore
First we begin with creating a new class in our Sitecore.SharedSource.PowerShell library. The class to create in this example is called GutterStatusRenderer.
using System;
using Cognifide.PowerShell.PowerShellIntegrations.Host;
using Cognifide.PowerShell.PowerShellIntegrations.Settings;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Shell.Applications.ContentEditor.Gutters;

namespace Sitecore.SharedSource.Gutters
{
    // Inherit from the GutterRenderer in order to override the GetIconDescriptor.
    public class GutterStatusRenderer : GutterRenderer
    {
        // We override the GetIconDescriptor so a script can be called in it's place.
        protected override GutterIconDescriptor GetIconDescriptor(Item item)
        {
            // The scriptId parameter is configured when we create a new gutter
            // here /sitecore/content/Applications/Content Editor/Gutters
            if (!Parameters.ContainsKey("scriptId")) return null;

            var scriptId = new ID(Parameters["scriptId"]);

            var db = Factory.GetDatabase("master");
            var scriptItem = db.GetItem(scriptId);

            // If a script is configured but does not exist then return.
            if (scriptItem == null) return null;

            // Create a new session for running the script.
            using (var session = new ScriptSession(ApplicationNames.Default))
            {
                var script = (scriptItem.Fields[ScriptItemFieldNames.Script] != null)
                    ? scriptItem.Fields[ScriptItemFieldNames.Script].Value
                    : String.Empty;

                // We will need the item variable in the script.
                session.SetVariable("item", item);

                try
                {
                    // Any objects written to the pipeline in the script will be returned.
                    var output = session.ExecuteScriptPart(script, false);
                    foreach (var result in output)
                    {
                        if (result.GetType() == typeof (GutterIconDescriptor))
                        {
                            return (GutterIconDescriptor) result;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex.Message, this);
                }
            }

            return null;
        }
    }
}

Second we need to create a new Gutter library and a Publication Status script. We'll come back to the content of the script later.

Third we need to create a new Gutter in the "core" database.


If you recall from the source code above, the scriptId indicates which script to call for this GutterRenderer. This will allow you to use the same GutterStatusRenderer class for all your gutter needs.

Finally we need to write our script. You'll notice that it's almost exactly what was stolen from ParTechIT, in PowerShell form.

<#
    Adapted from:
    http://www.partechit.nl/en/blog/2013/03/display-item-publication-status-in-the-sitecore-gutter
#>

# The $item variable is populated in the GutterStatusRenderer class using session.SetVariable.
if(-not $item) {
    Write-Log "The item is null."
    return $null
}
$publishingTargetsFolderId = New-Object Sitecore.Data.ID "{D9E44555-02A6-407A-B4FC-96B9026CAADD}"
$targetDatabaseFieldId = New-Object Sitecore.Data.ID "{39ECFD90-55D2-49D8-B513-99D15573DE41}"

$existsInAll = $true
$existsInOne = $false

# Find the publishing targets item folder
$publishingTargetsFolder = [Sitecore.Context]::ContentDatabase.GetItem($publishingTargetsFolderId)
if ($publishingTargetsFolder -eq $null) {
    return $null
}

# Retrieve the publishing targets database names
# Check for item existance in publishing targets
foreach($publishingTargetDatabase in $publishingTargetsFolder.GetChildren()) {
    Write-Log "Checking the $($publishingTargetDatabase[$targetDatabaseFieldId]) for the existence of $($item.ID)"
    if([Sitecore.Data.Database]::GetDatabase($publishingTargetDatabase[$targetDatabaseFieldId]).GetItem($item.ID)) {
        $existsInOne = $true
    } else {
        $existsInAll = $false
    }
}

# Return descriptor with tooltip and icon
$tooltip = [Sitecore.Globalization.Translate]::Text("This item has not yet been published")
$icon = "People/16x16/flag_red.png"

if ($existsInAll) {
    $tooltip = [Sitecore.Globalization.Translate]::Text("This item has been published to all targets")
    $icon = "People/16x16/flag_green.png"
    Write-Log "Exists in all"
} elseif ($existsInOne) {
    $tooltip = [Sitecore.Globalization.Translate]::Text("This item has been published to at least one target")
    $icon = "People/16x16/flag_yellow.png"
    Write-Log "Exists in one"
}

$gutter = New-Object Sitecore.Shell.Applications.ContentEditor.Gutters.GutterIconDescriptor
$gutter.Icon = $icon
$gutter.Tooltip = $tooltip
$gutter.Click = [String]::Format("item:publish(id={0})", $item.ID)
$gutter

Here's the final result.


That's pretty much it. Happy coding!

References:

  • http://www.partechit.nl/en/blog/2013/03/display-item-publication-status-in-the-sitecore-gutter
  • http://michaellwest.blogspot.com/2014/10/sitecore-powershell-extended-with-pipelines.html
// Mikey