Showing posts with label content editor. Show all posts
Showing posts with label content editor. Show all posts

Wednesday, April 8, 2026

SitecoreAI Content Editor: Replacing Experience Editor and Preview with Working Pages Links

While working through a SitecoreAI implementation recently, I ran into a small usability issue in the good ol' Sitecore Content Editor.

The Experience Editor and Preview buttons are still sitting there in the Publish ribbon, looking as inviting as ever.

There's just one problem:

They don't work. 😄

In the SitecoreAI environment I was working with, clicking either button resulted in the following error:

Connection to your rendering host failed with an Unauthorized error.
Ensure the JSS Editing Secret is configured.

This makes sense considering the editing experience has moved on to Sitecore Pages, but it leaves Content Editor users with two very visible buttons that don't get them where they need to go.

Rather than telling users to ignore them, I decided to repurpose them.

The goal was pretty straightforward:

  • Replace Experience Editor with a SitecoreAI Page Builder button
  • Replace the existing Preview behavior with a working SitecoreAI page preview
  • Open the correct page for the currently selected Content Editor item
  • Carry the current language and version into the request
  • Support multiple sites and environments
  • Do it without building or deploying any custom .NET code

Sounds like a job for Sitecore PowerShell Extensions.


The Existing Ribbon Buttons

The Content Editor ribbon definitions live in the Core database.

The two items we're interested in are:

/sitecore/content/Applications/Content Editor/Ribbons/Chunks/Publish/Page Editor

/sitecore/content/Applications/Content Editor/Ribbons/Chunks/Publish/Preview

Rather than creating entirely new ribbon controls, I opted to reuse these existing items and replace their behavior.

Each button's Click field can execute an SPE script using item:executescript.

For the Page Builder button:

item:executescript(
  id=$ItemID,
  script={YOUR-PAGE-BUILDER-SCRIPT-ID},
  scriptDb=master
)

And for Preview:

item:executescript(
  id=$ItemID,
  script={YOUR-PREVIEW-SCRIPT-ID},
  scriptDb=master
)

Now we just need to give those buttons something useful to execute.


Creating the SPE Scripts

I created a small module underneath the Sitecore PowerShell Script Library to keep everything together.

/sitecore/system/Modules/PowerShell/Script Library/
    SitecoreAI/
        Sitecore Pages/
            Content Editor/
                Ribbon/
                    Publish/
                        Publish/
                            Page Builder
                            Preview

Both entries are standard PowerShell Script items.

For my implementation, I used the following icons:

Page Builder
/~/icon/apps/32x32/Paint.png

Preview
/~/icon/office/32x32/eye.png

Naturally, if choosing the perfect Sitecore icon becomes too difficult, I may know of a website that can help. 😄


Opening the Current Item in Sitecore Pages

The Page Builder script starts with the current Content Editor context item and builds the corresponding Sitecore Pages editor URL.

There are a few pieces of information we need along the way:

  • Current item ID
  • Current language
  • Current version
  • Site name
  • SitecoreAI tenant
  • Organization ID

In my case, the Sitecore solution contains multiple sites, so I determine the appropriate Sitecore site name based on where the selected item exists in the content tree.

Here's a sanitized version of the script:

The result is exactly what I was looking for.

Select a page in Content Editor, click SitecoreAI Page Builder, and the corresponding item opens directly in Sitecore Pages in a new browser tab.

No more hunting for the page again after switching applications.


Fixing Preview

Preview follows the same general idea, although constructing the URL requires a little more information.

In addition to the item, language, version, and Sitecore site, the script determines the route for the current item relative to the site's Home item and generates the appropriate rendering-host request.

Here's the sanitized version:

For the obvious reasons, I've replaced all tenant IDs, hosts, organization IDs, and secrets from my implementation with placeholders in the examples above.

The environment-specific SitecoreAI values can be retrieved from your Sitecore Cloud Portal / Deploy application.



Replacing the Ribbon Buttons

With both SPE scripts created, the last step is updating the original Core database ribbon items.

You can certainly update the fields manually, but since I needed to make the same change consistently between environments...PowerShell again.

The following script updates the Click, Header, and Tooltip fields on both existing ribbon items:

After updating the Core items, I rebuilt the Content Editor ribbon using the SPE utility available under:

Sitecore → PowerShell Toolbox → Development Tools → Rebuild ribbon integration points

Refresh the Content Editor and we're back in business!

Experience Editor is now SitecoreAI Page Builder, and the Preview button now launches a working preview for the selected item.


A Small Content Editor Quality-of-Life Fix

None of this changes how Sitecore Pages itself works, and it's certainly not some massive customization.

It simply makes the existing Content Editor workflow make a little more sense for users working in SitecoreAI.

If a button says Experience Editor and clicking it takes you to an error page, users are eventually going to ask why.

If we already know where they actually need to go, we might as well send them there.

And once again, SPE makes it possible to solve the problem without a custom deployment.

I hope this helps anyone else working through a similar SitecoreAI Content Editor setup!

Happy Sitecoring! ✌️

Saturday, March 22, 2025

Using Sitecore Indexes in PowerShell-Driven Multilist Datasources


If you didn't know, you can point a Sitecore field's datasource to a PowerShell script. It's a super clean way to make dynamic picklists, filtering based on the current item, tags, templates, relationships, you name it.

But if you're pulling a large set of items, you really want to use the search index.

That's where things get weird.

The Find-Item command gives you fast results… but they aren't real Sitecore items. They're search result objects; great for speed, not so great for populating a multilist. Sitecore expects actual items, and when it doesn't get them, your field ends up looking empty.

Luckly, you don't have to abandon the approach completely. The fix is actually a pretty straightforward.


Some Context

For context, you can set the datasource for a multilist to be driven via a script in SPE like this:

In the script definition itself, you can write PowerShell to obtain some set of items from the tree. The resulting list is what shows as applicable for selection on the field.

In theory, you should be able to also utilize the Find-Item commandlet to obtain a list of items from the index. In my case was necessary due the performance implications of running a Get-ChildItem against a massive subtree.

First attempt looked something like this:

So far, so good. You get back a list of items. Or do you?


The Catch

The objects in the $list variable returned by Find-Item are not Sitecore items. They're dynamic search result objects that look like items, walk like items, but won't work in your multilist unless they quack like items.

If you try to return them as-is from your script, you'll find that, even if items were found in the index, the multilist fails to render the items for selection.

The fix? Pretty simple actuallu: Transform the search results back into legit Sitecore items.

Put it all together and you're golden

Now your multilist knows what to do. The search is lightning fast thanks to the index, and authors can pick from relevant matches without sifting through the entire tree.


Why This Matters

This pattern shines when you're working with large content trees or complex tagging structures where traditional item traversal would be painfully slow. By using the index, you're offloading the heavy lifting to Solr, gaining serious performance without sacrificing editor experience.

But more importantly, it calls out a subtle, easy-to-miss SPE gotcha: not all objects returned from PowerShell helpers are Sitecore items. If you're using Find-Item, you'll almost always need a second pass to resolve those results into actual items before they'll work in a field context.

Fail to do that, and you might spend hours wondering why your multilist is coming up empty, despite the index finding exactly what you wanted.

Happy datasourcing! 🚀

Monday, May 20, 2024

Sitecore PowerShell Extensions Text-to-Speech Audio Synthesis Module

Another year, another exciting Sitecore Hackathon!  This round, I flew solo under the moniker "Sitecorepunk 2077" (a play on the critically acclaimed 2020 action role-playing video game "Cyberpunk 2077").


If you're curious how the event unfolded, I documented my progress on X (formerly Twitter) every couple of hours:













Needless to say, I was utterly exhausted and slept for 12 hours straight, following the 32 hours I had been awake.  While I didn't snag a win (congrats, team Cloud Surfers and team 451 Unavailable For Legal Reasons ), I enjoyed the experience, am proud of what I was able to output, and look forward to the next one.


Module Concept and Inspiration

The 2024 Sitecore Hackathon category I chose to work against was "Best Module for XM/XP or XM Cloud" - although the result could also fit the bill for "Best use of AI".  Inspired by the ever-increasing need for accessible content, I decided to develop a module that converts text content into spoken audio files, which are then stored remotely and saved as an MP3 links within the item's context - all from within Sitecore. Ultimately, once I landed on the idea, the goal was to provide an easy-to-use tool for generating audio versions of Sitecore content, thereby enhancing accessibility and improving user engagement for individuals with visual impairments or preferences for audio content.

Features

Here’s a breakdown of what makes the SPE Text-to-Speech Audio Synthesis Module stand out:

Lifelike Speech Synthesis from Microsoft Azure Cognitive AI Speech Services

One of the core features of this module is its ability to convert text content into lifelike speech. By transforming text into life-like speech, the module makes content more accessible to a broader audience, including those with visual impairments and individuals who prefer consuming content through audio.

The module utilizes Microsoft Azure Cognitive Services Speech Service to generate audio from selected text fields dynamically. This integration ensures high-quality, natural-sounding speech output. Whether it's a blog post, news article, or product description, every piece of content can be converted into audio, broadening its reach and enhancing user engagement.

Storage via Azure Blob Storage

To store the generated audio files, the module leverages Azure Blob Storage APIs. Once an audio file is generated and store locally in a temporary directory, it is then uploaded to a dedicated Azure Storage container. The API returns a URL to the audio file, which is then populated in the context page item’s Audio URL field. 


Interface and Custom Ribbon Button

A custom Ribbon Button on the Home tab streamlines the audio-generating process. This button triggers an interactive Sitecore PowerShell Extensions dialog where authors can configure various options, such as voice selection, field selection, and speech rate adjustment, and kick off the speech synthesis generation.


The customizable options ensure the audio output matches the intended tone and speech rate, providing a tailored listening experience.

Multi-Language Support

Recognizing the diverse needs of global users, the module supports multiple languages. For demonstration purposes and within the natural time constraints of the Hackathon, the following languages are supported in the initial implementation:

  • English (en)
  • Japanese (ja-JP)
  • German (de-DE)
  • Danish (da)

Each supported language selection has a series of Neural (lifelike, natural-sounding) voice options from Microsoft Azure Cognitive Services Speech Service (~449 neural voices to choose from). These hand-selected voices are configured to provide the best audio experience for each language. Of course, support can be expanded to include additional languages (there are 136 languages supported by Azure AI Speech Services).  


High-level Technical Breakdown

Initialization and Setup

The script sets up the necessary Azure services and local environment configurations.


User Interaction and Dialog Configuration

The script provides a dynamic interface through a custom Ribbon Button in the Sitecore Content Editor. This button, titled 'Generate Audio' or 'Regenerate Audio' based on the context item’s state, opens a dialog for configuring the audio output.  The fields and options available in the dialog are as follows:

- Field to Convert to Speech
  • Lists all Rich Text Editor (RTE) and multi-line text fields available on the item.
  • Special Case: If the 'Speech Content Override' field is populated, it appears as an additional option.

- Include Title?
  • A standalone radio button to include the item's title in the audio file.

- Voice
  • Dynamic option based on the item's language, the dialog offers preselected AI Neural voices.

- Speech Rate
  • Control the how fast the speech is spoken. 
    • Optional double value, defaulting to 1.0 if left empty.
    • Range: Between 0.5 (slow) and 2.0 (fast).

The dialog properties and user input handling are defined as follows:


Fetching and Sanitizing Text Content

The Invoke-AudioStreamFetch function handles the core functionality of fetching the text content from Sitecore, sanitizing it, and preparing it for conversion into speech.

The function checks if the title should be included and concatenates it with the main text content. It then sanitizes the text by removing HTML tags and special characters, ensuring clean input for the TTS service.


Sending Text to Azure AI for Speech Synthesis

As seen above, the sanitized text is then sent to the speech service endpoint for conversion into an audio file. The response, which contains the audio stream, is saved locally.


Uploading the Audio File to Azure Blob Storage

Once the audio file is generated, it is uploaded to Azure Blob Storage by calling the Upload-FileToAzureStorage function. This function handles the Azure Storage REST API authentication and the file upload process.


Updating Sitecore Item with Audio URL

After uploading the audio file to Azure, the script updates the Sitecore item with the URL of the audio file, ensuring that the content authors can easily access and manage the generated audio files.


Utilizing the Audio File on the Front-end

Once an item's Audio URL field has been populated, it can be used on the front-end within an HTML audio tag:




This is the simplest approach for playing the audio file, but further styling customizations are doable.


Video Demo

Part of the Hackathon Entry includes a video demo. You can check it out below:


Final Thoughts

Participating in the Sitecore Hackathon has always been an exhilarating experience for me, given the time crunch and competitiveness of the community. That night, the development of the SPE Text-to-Speech Audio Synthesis Module pushed my organizational and technical boundaries, and I'm proud of what I could accomplish in such a short timeframe. More importantly, I hope the resulting module helps highlight the importance of accessibility in content management and end-user experiences. 

If you're interested in or inspired to build your own Text-to-Speech synthesis module, the full PowerShell script and documentation are available on Github.

Thursday, July 7, 2022

Sitecore Icon Search 2022 Web App and Extensions Updates + Microsoft Edge Addon v1.0.0 Release

It's been 671 days since the last update to the Sitecore Icon Search web app and 1,278 days since my last update to the Chrome and Firefox extensions. I've made updates to squash new bugs across the Sitecore Icon Search ecosystem that have surfaced since the previous release while mitigating the extensions' delisting at the beginning of 2023. 

WebApp: Sitecore.com dependency migration. 


I recently visited sitecoreicons.com only to find that all icon images failed to load. ðŸ˜Ē

For context, by default, the app has always used www.sitecore.com to source the icon images themselves since sitecore.com happens to be a Sitecore site. Given how lazy loading is implemented in the data table, I always expected the strain on www.sitecore.com to have remained minimal.  

It turns out Sitecore has recently implemented DDOS security protection via Cloudflare. You might notice that if you go to www.sitecore.com, you'll land on a screen that looks like this before you are redirected to the site:



Due to this change, it left sitecoreicons.com imageless.   The latest updates to the Web app resolves this by hosting the icon images within the application - cutting out the dependency on www.sitecore.com completely. The browser extensions have also been updated to source icon images from sitecoreicons.com


Chrome Extension: Critical Manifest Updates to avoid looming January 2023 Deadline

The Sitecore Icon Search Chrome extension is actively used by ~500 active users. When I originally built the extension, the latest 'manifest' version (a JSON file containing information that defines the extension) was v2.  

Google has provided two critical dates as manifest v2 is deprecated and replaced entirely with v3.  

January 17, 2022: New Manifest V2 extensions will no longer be accepted by the Chrome Web Store. Developers may still push updates to existing Manifest V2 extensions, but no new Manifest V2 items may be submitted.

January 2023: The Chrome browser will no longer run Manifest V2 extensions. Developers may no longer push updates to existing Manifest V2 extensions.

This means that the Sitecore Icon Search Chome extension will no longer function after January 2023 and, in its current state, cannot be updated until upgraded. The latest release brings the extension's manifest version up to version 3 and provides code fixes for several deprecated APIs that surfaced with the upgrade. 


By default, Chrome should update to the latest version automatically. 


Firefox Add-on: Revival

Back in December 2021, I received an email from the Mozilla team regarding a policy update that I, unfortunately, hadn't found time to respond to.   This results in version 1.0.0 of the Firefox add-on being removed ðŸ˜Ē


Version 2.0.0 of the Firefox addon contains all the latest updates made for the WebApp and the Chrome extension and resolves the policy issues. I'm actively awaiting the add-on approval process to conclude and will update this portion of this blog post once the extension is available.   


UI and UX enhancements 

While I was making some updates, I at least decided to have a little fun. I've updated some fonts and scattered emojis across the app. I've also included some phrases catered to Gen-Z Sitecore developers. 😁






Microsoft Edge Addon v1.0.0 Release

While Chrome Extensions can already be added to Microsoft Edge by toggling on a feature, however, to avoid requiring Edge users to jump through hoops, I've decided to port and release the Microsoft Edge version of the browser extension. Version 1.0.0 can be downloaded on the Microsoft Add-ons library.



Thursday, April 8, 2021

Sentiment Analysis and Keyword Extraction using Sitecore PowerShell and Microsoft Cognitive Text Analytics

Sitecore Hackathon 2021

Well...wow, it actually happened...

I managed to snag a category win for the 2021 Sitecore Hackathon! 😅


This year, I unexpectedly flew solo as my team members could not attend (both due to completely understandable reasons).  Luckily for me, one of this year's categories, in particular, made me feel like I stood a chance: "Best use of Sitecore PowerShell Extensions to help Content Authors and Marketers."

YES. YES YES 1000x YES. 

Knowing that I needed to land on something fairly quickly to complete all submission requirements (a completed module with clean code, reliable installation instructions, a well-documented README.md, and a video) my evening began with a brainstorming session listing all possible routes I could take for the next 24 hours.  

I actually landed on a similar concept I posted about a couple of years back; interacting with Microsoft's Cognitive Services using PowerShell, then focusing on content translation. I knew Microsoft had continued to update their API offerings since that post, so I started digging into what was new.  I stumbled upon the Sentiment Analytics API, which seemed like an excellent use case that could satisfy the 'help Content Authors and marketers' category requirement.  

By providing the right combination of SPE user interactivity (modal dialogs, accessibility of the utility in the Ribbon, etc.), I could build a utility that analyzes content from a given field and provide a sentence-by-sentence breakdown of the content's sentiment score using AI.

After playing around with the example APIs in the browser, I decided to create my Text Analytics Cognitive Service in Azure, grab my API keys, and fiddle around with the API further in PostMan.  At that point, I felt pretty confident that I could integrate this with SPE. ðŸĪž

The Sentiment Analyzer would

  • Analyze the sentiment of field content directly in Sitecore.

  • Give Content Authors the ability to run an analysis of a given field's content, which returns an overall sentiment score and a sentence-by-sentence breakdown of each sentence's sentiment score and corresponding confidence scores.

  • The results are displayed using a Show-Result modal and rendered in an easy-to-digest format.

I built the user dialog, wrote code that generated the appropriate POST data to be passed to the sentiment API endpoint, built the functions to render the data (using emojis, of course ðŸ‘Đ‍🚀), configured a new Sitecore template and the corresponding item for API key storage then tied it all together into an SPE module that exposed the tool from the right-clicked Context Menu, and from the Ribbon.

As midnight approached, I felt that I was in decent enough shape with the Sentiment Analysis script, I could begin exploring using another API in the same Text Analytics product group. I moved forward with a second tool utilizing the API's keyphrase extraction feature without a tremendous amount of overhead; mostly endpoint changes, JSON parsing, and data rendering differences. 

The Keyword Analyzer would:

  • Analyze a field's content to extract critical keywords/phrases.

  • Give Content Authors the ability to analyze a given field's content which returns a list of extracted keywords that can then be used to manually populate a meta keywords field.

  • The results are displayed using a Show-Result modal and rendered in an easy-to-copy format. 


I got started, but a couple hours later...


Then a few hours later...

I spent most of the day (alongside juggling sick-kids priorities) polishing the scripts I had so far; resolving logic issues, error prevention, adding code comments, and overall meticulous code clean-up.

Eventually, I had a functional set of utilities. 

Buttons in the Ribbon configured in the SPE module.


Dialog when clicking either utility against an item
with a Single-Line, Multi-Line, or Rich Text field. 

Sample output of sentiment analysis

Sample output of keyword analysis

I made sure to stop by for a late morning Coffee Break. ☕


I built the final structure of the SPE module using the Module Wizard 🧙‍♂️ to configure my integration points.  The module also stores the API Settings item, so swapping in an API key would be seamless for anyone who installs the module.  


⚡ The module looked like this in the tree:




I spent the final hours of the event packaging the module/testing the installation steps before working on multiple documentation phases (using Markdown for absolutely everything in 2020 was really coming in handy).

It wasn't long before a mid-afternoon Twitter update:



The video production was probably one of the most challenging parts of this experience.  After writing a short-handed verbal script, I tried to record the entire demo in a single recording.  I used OBS Studio to record and the built-in Video Editor in Windows for post-production.  I even squeezed some personal music snippets I composed some time ago without risking Copywrite strikes on YouTube. 😂

The video submission can be viewed here:

By around 5 PM, I was done and had submitted my entry 🚀

The full Github submission can be found here, including the full source code for both scripts, the module ZIP for installation, and installation steps. 

Take it for a spin if you care to! ðŸĪđ‍♂️

I'm really humbled and proud to have been a part of the winner's circle this year.  Another big shout-out to the folks who run and judge the event, as well as a big congratulations to the other category winners!

Check out the complete 2021 Sitecore Hackathon winners announcement here: https://www.youtube.com/watch?v=YEOy7lIDZUU

I'm already looking forward to next year. 📆


Thursday, January 23, 2020

Reviving the Screenshots Ribbon Button in Sitecore using ScreenshotLayer API and PowerShell


Have you noticed that the Screenshots button has been removed in 9.3?

I guess it's not that surprising since I don't remember any point in time where it actually functioned. 

It's always gone something like this;

Click the Screenshots button:
The Screenshots button.  So familiar yet so foreign to me

Get a message stating you need to purchase the "relevant service".

Click Open

I never pass up a chance to open the Sitecore App Center
App Center Opens.  No one profits. Ever.
Great.


You may be asking, why don't the content authors just use an extension or some other tool?
Or, you know, the built-in capabilities of any Chromium-based browser.

ðŸĪ·‍♂️

The request called for consistent, full-page screenshot capabilities in Sitecore.
I thought that seemed pretty do-able.

Ever hear of ScreenshotLayer?


What's ScreenshotLayer?

It's a highly reliable REST API service that produces screenshots.
Screenshotlayer is a lightweight REST API built to deliver high quality PNG, JPEG & GIF website screenshots at unparalleled speeds and through a simple interface. 
While I was researching and proving this out, I found that using open source libraries like Freezer often returned inconsistent results.  I landed on ScreenshotsLayer given the ease of integration. Basically; feed it a URL and some parameters, and you get a high-quality screenshot back.

The service is free up to 100 screenshots per month, with a reasonably priced subscription model otherwise. https://screenshotlayer.com/product


Limitations

- CM needs to be accessible on the web without IP restrictions for API to consume.  This won't work locally.

- Added cost: Depending on the number of screenshots taken per month, you'll hit the free 100 screenshots quick.  A Basic Plan subscription - which accounts for 10,000 screenshots per month should suffice in most cases. This doesn't seem out of the ordinary considering there used to be a service you'd have to sign-up for any way.



Goal

Our goal is to add a new Content Editor Ribbon button called 'Screenshots' in the same location that the old button once sat.  If you're running Sitecore 9.2 or below, you'll want to manually remove or deny read access to the existing Screenshots button in the Core database.



The Script

The script should initially get the selected item, assert that the item has a layout, and get the item's URL.  (The Get-ItemUrl function may need to be customized further for your own needs)




User Input

Let's assume a content author should make a simple selection for their screenshot; Mobile or Desktop.  We can show a simple dialog with two buttons; Mobile or Desktop.
Simple, yet effective.

Upon selecting Mobile, we'll simply set a variable.  If we have a Page URL, we'll call our Get-Screenshot function and pass in the URL and screenshot size as parameters:



Preparing the API Query

The API expects some common parameters defined in the URL query parameters, specifically, the API key, page URL, and viewport. We can concatenate all of our options and append it to the API URL endpoint.




Consuming the API

We can now execute a WebRequest to the API and cast our result to an image stream.



Saving and Downloading the Image

With an image in the memory stream, we'll need to temporarily store the image as a physical file on the file system.  In our approach, we'll utilize the $SitecoreDataFolder variable and create a 'screenshots' folder within it.  We'll build some conditions to check for the presence of this location.  Once the image has been saved, we'll invoke the Download-File function 


Putting it All Together


SPE Integration

Creating a New Module

Right-click on the Script Library folder and select Module Wizard


We'll name our module Screenshots and select Content Editor - Ribbon as our integration point:

You'll end up with a new module folder and several items:

We'll want to delete all folders in this new module except for the Content Editor > Ribbon > Presentation > Preview.  We can add a new PowerShell Script item under the Preview folder:

First thing's first....set an icon in the appearance section (you know about sitecoreicons.com,  right? 😉)

Now, let's make sure from an integration standpoint, the Screenshots button is only displayed for items with a Layout present.  We can easily accommodate this by editing the Show Rule in the Interactive section:
The Rules Engine makes this a no-brainer
Finally, we can add our script to the Script body field of our item.  Now would be a good time to make sure the $apiKey variable has been customized before saving. 



Activating the New Module

In order for the system to pick up our new module and its corresponding integration points, we need to Sync the library.  This can be achieved by opening a new ISE session and selecting the Sync Library Content Editor Ribbon sub-option of the Rebuild All button under the Settings tab:

We should now have a Screenshots button that displays in the Presentation > Preview chunk of the ribbon whenever an item with a layout is activated. 
wo0t!

Final Result




Starter Package

If you're looking to use or expand this functionality to fit your own requirements, you can feel free to download and install the Sitecore package from: https://github.com/strezag/Sitecore.SPE.Screenshots

Happy capturing! ðŸ“ļ



Friday, January 11, 2019

Translating Text in Sitecore using Microsoft Cognitive Services and PowerShell


Microsoft Cognitive Services is an impressive set of powerful, easy to integrate APIs for developers. With AI at its core, Cognitive Services strives to solve business problems using Vision, Speech, Knowledge, Search, and Language service offerings.  Recently announced, it's now easier than ever to start using Cognitive Services with a simplified key mechanism.

Creating a Cognitive Service in Azure itself is dead simple. It take less than 5 minutes to spin up a Cognitive Service resource and you're ready to develop. The above video will help get you started.☝

So, what can we do with these services in Sitecore?

Cognitive Services Is Not That New

We've seen folks across the Sitecore community utilising this technology since its inception in 2016 - specifically the Computer Vision API. From bulk image alt tagging using Computer Vision and PowerShell and automatic image alt tag generation module, to a fully integrated Cognitive Services module from the infamous Mark Stiles.

These are really great adaptations of these APIs, but I wanted to explore these services myself.

Translator Text API

The Translator Text API is easy to integrate in your applications, websites, tools, and solutions. It allows you to add multi-language user experiences in more than 60 languages, and can be used on any hardware platform with any operating system for text-to-text language translation.
The Translator Text API is part of the Azure Cognitive Services API collection of machine learning and AI algorithms in the cloud, and is readily consumable in your development projects.
What is Translator Text API?

Being completely obsessed with PowerShell, I figured I'd give it a go.  For my demo, I want to allow content authors to right-click on a Sitecore item, select a Translate Text Fields PowerShell script, configure some option in a dialog and click Continue.



The script should create a new language version in the target language selected and translate all text fields selected.

Pricing

Before starting, it should be noted that the Free tier for translations is 2 million characters per month. 
Since we're keeping this integration simple, it's not likely we'll hit the limit.



Let's Script It

Building the dialog is pretty straightforward. Get the context item, grab the applicable languages and fields (Single-Line and Multi-Line only for simplicity and API restriction avoidance), and set the dialog options:

We'll need a function that we can call to process the item after obtaining the selections from the dialog.  We expect to call it like this:

Translate-Item -TargetItem $item -FromLang $selectedFromLanguage -ToLang $selectedToLanguage -FieldsList $fieldSelectionArray

The function should issue an authentication token from the Cognitive Service, generate a new language version for the item in the target language, and call another function to translate the original field value to the target language:

The Issue-CognitiveApiToken in the above function issues the authentication token we need to send with our request to the translation API.

We can send a request to the translation API with the issued token in a new function that will be responsible for processing the actual translation:

Putting It All Together

Now that we've got commands to interact with the Translation API, we can build out the contextual PowerShell script to provide some options for our content author.

Adding this script to one of the Context Menu (PowerShell Script Library) folders (eg. /sitecore/system/Modules/PowerShell/Script Library/SPE/Core/Platform/Content Editor/Context Menu) will allow the script to be executed contextually.


Final Result


I can see this being a good use-case for Dictionary items specifically as there are some restrictions for content length size that the API will reject.  However, this is simply a demo of what is possible here (definitely not production-ready ðŸĪŠ).



Feel free to take and use any part of the script here and build something cool!

Tuesday, July 24, 2018

Custom Sitecore Field: Google Maps Autocomplete

Maps are everywhere.  Most modern web designs often include some kind of map functionality whenever points of interest are involved.  If you've ever built a template that houses location data (stores, events, etc), you'd likely included latitude and longitude data points as Single-Line Text fields on the relevant Sitecore template.

However, this approach still forces the Content Author / Marketer to discover the latitude and longitude points based on an address themselves.

While this isn't too difficult to manage, we wanted to make eliminate this step - and make it easier for the author to ensure they're getting reliable latitude/longitude points every time without the need for any external lat/long discovery.

We've seen other examples (Google Map Location Picker Field or  this older module) exist where maps selectors are used (many of which are pretty old now!), but none that strictly use the Google Maps Autocomplete API.
If you're looking for some code on how to achieve this - you've come to the right place!

GOAL

Create a custom Sitecore Field Type integrated with Google Maps Places Autocomplete API that automatically retrieves and stores Latitude and Longitude points based on an inputted address.

Injecting Scripts to the Content Editor

In order to consume the Google Maps Autocomplete API, our page will need to render a script tag referencing the API in the Content Editor.  Luckily, Sitecore allows us to inject custom styles and scripts to the Content Editor as needed.  In this case, there are two script tags we'll need to add:
    1. The Google Maps Autocomplete API library
    2. A custom Javascript file with methods that initialize, geolocate, and fill in the Latitude and Longitude field

Our patched config entries include a new pipeline:

In our pipeline, we loop through the resource definitions set in the config and process new script tags to be added when the content editor loads:

New Template Field Type Item

Our Autocomplete Address Template Field Type is defined in the Core DB:


The Control field value is set to customfieldtypes:AutocompleteAddress. This means two things:
  1. We'll need to register customfieldtype as a new controlSource
  2. A new AutocompleteAddress class which inherits from the Sitecore.Web.UI.HtmlControls.Control class. 

Template Field Type Code and Config

The config entry to define a new control is pretty straightforward:


The corresponding control code:
In this approach, our Control code actually creates three text boxes on the fly (Address, Latitude, Longitude).  The Address text box control will act as the autocomplete which interacts with the API.

Once an address is selected from the autocomplete selections - the custom JS retrieves the address's latitude and longitude points.  These lat/long values will be mapped to the two additional text boxes, then stored as a NameValueCollection string in the raw value of the Autocomplete field type in the following string format:

Address=2104 North Clark Street, Chicago, IL, USA&Latitude=41.920385&Longitude=-87.637572

To hook the Address textbox to our custom script we're simply adding an onfocus attribute 'javascript:autoCompleteAddress.initAutocomplete' to the Address text box control.

The custom JS handles the rest of the interaction to call the API and populate the Latitude and Longitude textbox controls with the right values.


Utilizing the Field Value

When it's time to use the latitude/longitude points on the front-end, we utilize a StringExtension method to parse the necessary values.

Full source for this implementation can be found here:
https://github.com/strezag/GoogleMapsAutocompleteCustomSitecoreField

While this was built using the latest version of Sitecore (v9 Update-2 at the time of this post), we were able to verify this on a local copy of 8.2.  My guess is that this probably would work for even earlier versions (even if it's just a few tweaks).

Feel free to grab a copy and modify it to your own specs!