Showing posts with label powershell. Show all posts
Showing posts with label powershell. 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! ✌️

Tuesday, July 15, 2025

SPExAI GPT: SPE Script Generation, No Module Required

Earlier this year, I built SPExAI Report Builder during the 2025 Sitecore Hackathon, a natural language interface for generating Sitecore PowerShell Extensions (SPE) reports. The concept was simple: use plain English to get real, working scripts. The result? A winning module that turned heads and saved hours. 🙌

But what if your team loves the idea but doesn’t have the time, access, or appetite to install a custom module? Perhaps you just need a script now and then. Or maybe you're working in an environment with limited Sitecore customization privileges.

It would be great if you didn't need to rely on installing a module into your Sitecore instance to benefit from this technology. 


✨ Introducing SPExAI GPT

Introducing a new flavor of SPExAI; not as a Sitecore module, but as a custom GPT. It works entirely outside of Sitecore. No installs. No packages. No patch configs. Just a prompt box and your imagination.

Your prompt:

"Report of all templates (ID, Name, Path) and their usage count."

And SPExAI (the GPT version) responds with a fully working script.  Same quality, same intelligence, now universally accessible.



Simply copy the output, paste it into the Sitecore PowerShell ISE, and run it:


Results!



One benefit you get with the SPExAI GPT over the SPExAI module is that you can continue your chat, which isn't available in v1 of the SPExAI (it's all one-shot), allowing you to prompt beyond your initial prompt.  This is great for getting help with fixing errors you may encounter when using the initially generated script, or even for enhancing an existing script.

🧠 Under the Hood

The GPT version builds on the same core ideas as the Hackathon module:

  • 🔍 A system prompt trained to interpret natural-language Sitecore requests and produce accurate PowerShell reports.

  • 📚 A curated knowledgebase of common SPE scripting patterns tailored to tasks like item audits, rendering usage, field value searches, and more.

  • ⚙️ Designed to speak "Sitecore" fluently, not just ChatGPT syntax, but the nuances of $SitecoreContext, $item.Paths.FullPath, Get-Item, and all the other familiar constructs.

I've adapted the system prompt to better align with ChatGPT’s structure, ensuring cleaner completions, improved formatting, and fewer hallucinations.


🚀 Try It Yourself

If you're looking to generate SPE scripts without touching your Sitecore instance, this GPT-based version might be the way to go. Whether you're prototyping, documenting, or training a team, it removes friction and delivers fast results.

You can find the SPExAI GPT on the Chat GPT store by selecting GPTs in the left-hand panel of ChatGPT, then searching for `spexai`:



You can also access SPExAI GPT directly using this link:

If you use it, please take a moment to leave a rating. 
If you have suggestions for improving the GPT, I'm open to them! 😀

Tuesday, April 22, 2025

SPExAI Report Builder: A Winning Sitecore Hackathon Module

The results of the 2025 Sitecore Hackathon are in...

Look what I got! 😭👇

It's a really decent piece of hardware!

I'm super proud of this accomplishment, given that I was pushing into delirium territory near the end of the event, having been up for 32 hours straight and all...running on mostly caffeine and adrenaline to get it done. 

Here's a snapshot of my real-time X updates throughout the event:



Sitecore Hackathon?

It's a virtual community-driven event where teams worldwide (52 teams across 13 countries this year) compete to build the most impactful Sitecore module given a set of categories (e.g., "Best use of AI" or "Best tool for XM Cloud") within a strict 24-hour timeframe.

Typically, the event is held in late February / early March, with teams registering roughly 4-6 weeks in advance. Submissions are then judged by a panel of long-time Sitecore MVPs and community members who review each completed entry, test the functionality, and collectively pick a winner.  

Submission Requirements are clearly laid out in the GitHub repository to which each team is assigned:

Winner Benefits

  • 🗣 Name recognition across Sitecore's official channels; the winning team is announced officially at SUGCON Europe and highlighted in the MVP community.
  • 🛒 $150 Amazon Gift Card (per team member)
  • 🏆 A customized Hackathon trophy

My Past Hackathons

Wasn't my first rodeo! 🤠
Here's a rundown of my past Sitecore Hackathon participations:

The name "Sitecorepunk 2077" is a not-so-subtle reference to
the 2020 video game Cyberpunk 2077 (which, admittedly,
I've barely played, but I liked the play-on-words when I came up with it).

I kinda love that I'm a 2x Sitecore Hackathon winner now 😅


The Idea

This year, instead of multiple categories (and multiple winners), the organizer's idea prompt was simply:

"Free for all — you can create your own idea for the Hackathon solution. Show us what you got!" 

It was a huge opportunity to build without barriers, with one winning team to take it all. I've been waiting for an opportunity to bring this idea I've been mulling over in my head for months to life, and this was it. 

Problem Statement

The biggest hurdle for analyzing content and creating reports in Sitecore PowerShell Extensions (SPE) has always been the technical skill needed (PowerShell scripting/syntax + SPE-specific commandlets).  

You'd need to train up; learn how to query items using Get-Item and Get-ChildItem commands, declare an array object to store results, utilize for loops and if conditions, etc.  

And if PowerShell scripting isn't your thing, well...


In the age of generative AI though, this technical skills barrier can be dramatically lifted for non-technical Sitecore authors and admin, and/or drastically reduce the turnaround time for developers tasked with writing custom PowerShell reports.



Enter: SPExAI Report Builder

What is it?

SPExAI Report Builder is an installable Sitecore PowerShell Extensions module that allows users to describe their Sitecore report in natural language, which in turn generates a complete and reusable SPE script:

  • 🧠💻 Type your prompt
  • 📜💾 Generate a PowerShell script and save it
  • 🛠️🚀 Run it or modify it

"SPExAI" stands for Sitecore PowerShell Extension x Artificial Intelligence, which combines the power of SPE with modern LLM tech. 

Compatibility

SPExAI Report Builder works with Sitecore 10.x or later.  I tested on Sitecore 10.0, 10.3, and 10.4 during the event, but I'm pretty confident that it would also work with other versions, too.

I didn't get a chance to test this on XM Cloud, but given SPE's flexibility, it is likely compatible.


How about a quick demo?

Say you need to audit template usage across the content tree.

When activating SPExAI from the ribbon, a dialog appears where you set a title, select the root context for the report, and provide a description.

"Report of all templates (ID, Name, Path) and their usage count."



SPExAI generates this clean, complete, and reusable script, which is stored in a dedicated part of the content tree:

Running the generated script without any modifications (which, on its own, included an option to select a root context, making it easily reusable against different parts of the tree) provides an accurate result set!



Another demo!

"Report of all renderings (ID, Name, Path) and their usage count under a selected content root."

SPExAI again generates a clean, complete, and reusable script, stored again using the name provided:

Running the generated script confirms that the script has been correctly generated and provides expected results.  



What's truly amazing is that we can generate 75-100 lines of working PowerShell code in seconds.

The code is appropriately structured, cohesive, error-free, and ready to be run immediately—no developer needed!



How SPExAI Works (Under the Hood)

API Settings

Before anything runs, the module looks for a specific Sitecore item:
/sitecore/system/Modules/PowerShell/Script Library/SPExAI Report Generator/API Settings:

There are four required fields:
  • API Keyyour OpenAI secret key

  • Model – the ID of the OpenAI model to use (e.g. o3-mini-2025-01-31)

  • Knowledgebase – a markdown-formatted reference block full of Sitecore PowerShell examples, documentation, best practices, etc.  

  • System Prompt – the instruction template that tells the model exactly how to behave, respond, etc. 


The module will abort early if any of these are missing.

Model Selection

During development, I tried a few different OpenAI models. The one that gave me the most consistent, one-shot responses was o3-mini-2025-01-31.

If you want to try a different OpenAI model later, simply update the Model field with the name; no code changes are required. (Expanding beyond OpenAI to Anthropic Claude or Google Gemini is also possible as part of a potential future v2.)

The Knowledgebase

This field contains raw reference material to guide the AI's responses. Think of it as an internal code cookbook, mostly pulled from the official SPE documentation and a compiled generic collection of snippets from my private repository of PowerShell scripts.

It includes sample report formats, SPE-specific syntax, and usage patterns that the model should stick to when replicating and generating new reports.

Looks like this:

The System Prompt

This is the master instruction set. Essentially, "You are a Sitecore PowerShell assistant...you do this, this, and that..." with additional specific constraints and formatting rules.

It includes a {0} token that the Knowledgebase content replaces.

Check it out:


A good chunk of the hackathon effort was spent refining the directives that the model should abide by. With every test run, I found myself adding to the list of rules. 

Getting the model to stick to the directives was...challenging to say the least (one-shot prompting definitely has its limitations depending on the model).  

Finding the right combination of rules for the model to consider was tricky, and I'm sure both the base system prompt and the knowledge base content could use even further refinement beyond what I could get done before the deadline.  Either way, I feel like I struck a solid balance for v1.

The good news is that the module was built to easily modify the system prompt in the configuration item without touching the underlying code, hypothetically allowing you to continuously improve the final output. 

UX Flow

SPExAI provides a new button in the Sitecore Ribbon. When clicked, this button surfaces a dialog window for the user's input.  



Users fill out the Report Name, set the Report Scope (tree selector), and the Describe your Report fields.  

It takes only a few seconds for the script to be generated.  

Users are then presented with the following options:



SPExAI Code Breakdown

Here's how the pieces come together behind the scenes:

1. Load the API Settings

The script set the four field values into variables.


2. Present a dialog for user input

The user's inputs from the dialog (report name, scope, description) are stored as global variables.


3. Variable validation

Validate that variables, like the script name, are valid and don't already exist in the saved script location.

4. Invoke the custom `Invoke-OpenAIChat` function

Invoke-OpenAIChat sends a custom one-shot prompt (including merging system instructions, knowledgebase, and user input) to OpenAI’s Chat Completion API and returns the generated response.

5. Save the script to the tree

Upon successful script generation, the module saves all generated scripts under a dedicated folder:  /sitecore/system/Modules/PowerShell/Script Library/SPExAI Report Generator/Content Reports/Reports/SPExAI Generated

6. Open, Run, or Close Dialog
After saving the script item, the module presents a modal dialog with its three choices:

1. Open Script Item – jumps to the new item in the Content Editor
2. Run Report – immediately executes the report using Invoke-Script
3. Close – exits with no action



Video Demo

As part of the entry, a video demo is required. You can check it out here:



Some Final Thoughts

If you haven’t seen it, fellow long-time MVP Rodrigo Peplau compiled a list of this year's submissions - all worth checking out. 

The quality of entries this year made it hard to predict how things would shake out. Winning was unexpected, but also an absolute honor.

Bummed I couldn't be at SUGCON EU to accept the award in person, but luckily the announcement was recorded. 😀  I will cherish this screenshot for all time:


Each year I've participated (whether on a team or solo), I've come away with valuable hackathon experience and a solid module, or at least the beginnings of one, that I could share and expand on further. I've always enjoyed the satisfaction of shipping something interesting and useful to others under competitive pressure. 

If you’re considering participating next year, I highly encourage it.  About 20% of the teams are solo, but it's not for everyone.  I recommend grouping up with others, especially if it's your first Hackathon. 

It’s a great way to push yourself, learn something new, make connections, and contribute to the spirit of the Sitecore community.

Keep on hackin'! 👨‍💻

Thursday, April 17, 2025

Sitecore Container Prerequisites Script Updates



Heads up!  I’ve made some recent updates to the open source Sitecore Container Prerequisites script to keep things aligned with the latest Sitecore versions and development environments.

What’s new:

  • ✅ Added support for Sitecore 10.3.2 and 10.4.0

  • 🖥️ Improved OS compatibility checks for Windows 10 and 11

  • 📄 Refined the README with clearer instructions for installation, usage, and contributing

  • 🔗 Updated package download links and references to current installation guides

Sitecore Container Prerequisites script remains a helpful utility for preparing your Windows machine to run Sitecore containerized environments smoothly. 

Feedback and contributions are always welcome!

📍 View the GitHub repo

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, July 22, 2024

Sitecore TDS could not load all files for the project: Overcoming Long Path Restrictions in Windows

For some time, we struggled with a pesky issue in one of my client's Sitecore MVC Helix-based solutions: certain Sitecore TDS items would fail to load due to excessively long file paths. The problem varied depending on where the developer's cloned solution was located, leading to a cascade of error pop-ups in Visual Studio. Through experimentation, we landed on an effective solution to mitigate the errors.

Here’s a detailed rundown of what we tried and what ultimately worked.

The Persistent Error

The error surfaced as a series of pop-up messages in Visual Studio whenever a developer loaded the solution.

Close.  Close. Close. Close. Close.

Each pop-up corresponded to a TDS item that failed to load due to excessively long paths.   Once each pop-up was manually closed, Visual Studio would finally load the solution. 

Within the TDS console, various errors like this would be present:

The file 'C:\GITCODE\Client\Client.com\src\Foundation\SharedTemplates\tds\Client.Foundation.SharedTemplates.Master\sitecore\templates\Foundation\Client\SharedTemplates\Sales Tool\Components\Text With Image Video\_textWithImageVideo\Data\Video Thumbnail Image.item' could not be loaded.

12:07:35 PM: Sitecore TDS has finished parsing 707 files for Client.Foundation.SharedTemplates.Master.

Warning: Sitecore TDS could not load all files for the project. The files may be missing or corrupted. Please see the Sitecore TDS pane in the output window for more details.

Attempts to Resolve the Issue

1. Updating the Windows Registry

For Windows 10, we tried edited the registry to enable long paths by adding a LongPathsEnabled key:

>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem

Setting LongPathsEnabled to 1 had no effect.

2. Git Long Paths Configuration

We tried enabling long-path support in Git:

git config --system core.longpaths true
(Configures Git to support long paths for all users and repositories on the entire system, requiring administrative privileges.)

git config --global core.longpaths true
(Configures Git to support long paths for all repositories the current user uses without requiring administrative privileges.)

This approach appeared to work for Windows 11, but not Windows 10.

3. Using Directory Junctions (Symbolic Links)

Our breakthrough came from using directory junctions, which effectively shortened the path length by creating a symbolic link to the project directory. This can be accomplished using the command prompt or with PowerShell:

Creating a Directory Junction using the Command Prompt
  1. Open Command Prompt as Administrator: Press Win + R, type cmd, and press Enter.

  2. Run the mklink Command:

    • Suppose your project directory is located at C:\Users\YourUserName\GitSolutions\SitecoreSolution.

      You can create a shorter path like this:
      > mklink /J C:\ShortPath C:\Users\YourUserName\GitSolutions\SitecoreSolution

      This command creates a junction at C:\ShortPath pointing to your actual project directory.

  3. Access Your Project:

    • Open your project from the new shorter path (C:\ShortPath) in Visual Studio.
Creating a Directory Junction using PowerShell
  1. Open PowerShell (in Windows Terminal) as Administrator: Press Win + X, and select "Windows PowerShell (Admin)".

  2. Run the New-Item Cmdlet:

    • Suppose your project directory is located at C:\Users\YourUserName\GitSolutions\SitecoreSolution.

      You can create a shorter path like this:
      > New-Item -ItemType Junction -Path "C:\ShortPath" -Target "C:\Users\YourUserName\GitSolutions\SitecoreSolution"

      This command creates a junction at C:\ShortPath pointing to your actual project directory.

  3. Access Your Project:

    • You can navigate to C:\ShortPath and see that it points to your original directory. Open your solution file from the new shorter path (C:\ShortPath) in Visual Studio.

TL;DR

For Windows 10 Users: Using directory junctions proved to be the most effective solution for developers on Windows 10. It’s a non-invasive approach that doesn’t require registry edits or changes to Git configurations.

For Windows 11 Users: Enabling long paths via Git configuration has shown similar results in Windows 11 without creating a junction.
This setting seems to leverage Windows 11's improved support for long paths across more applications, including Visual Studio.

Conclusion

If you’re struggling with similar issues, I highly recommend trying directory junctions or adjusting your Git configurations based on your operating system.

Try these methods and see the difference it makes in your development process. Share your experiences or tips in the comments. 

Happy Sitecore TDS-ing!

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.

Friday, September 8, 2023

Building Better Reports: 5 Sitecore PowerShell Extensions Functions for Your Toolbelt


When navigating the Sitecore ecosystem effectively, PowerShell extensions aren't just a helpful tool; they're practically a prerequisite. As someone who has spent a substantial amount of time in this space, I have distilled a set of functions that I've found myself using time and time again. 

Join me as I unpack a curated list of five functions that have become staples in my Sitecore toolkit.

Get-IsPublished

The "Get-IsPublished" function checks if a specific item, identified by its ID, is published on a "web" database. It takes an item as input and queries for it against the "web" database using its ID. If it finds the item in the database, it returns "TRUE," indicating that it is published. If it doesn't find the item (i.e., if the item is null), it returns "FALSE", indicating that it is not published.

This is particularly useful for displaying a column in your SPE report to denote if the item is published to the web database:


Get-ItemSitecoreCELink

The "Get-ItemSitecoreLink" function generates a URL to open a specific item in the Sitecore Content Editor. It takes a Sitecore item as input and uses various properties of that item (like its ID, version number, and language) to construct a URL. This URL, when accessed, will open the Sitecore Content Editor with that specific item loaded, allowing for easy navigation directly to the editing interface for that item. 

Please note that the base of the URL ("https://yourcmurl.com/") in this function is a placeholder that you'd replace with your actual Sitecore Content Management URL.

This is useful if the report is exported to Excel or CSV as it provides the direct link to reach the item without manually traversing the tree or searching for the item by GUID.


Get-LinkFieldUrl

The "Get-LinkFieldUrl" function retrieves the URL from a Sitecore item's link field. It takes a Sitecore item as its input and utilizes a regular expression to extract and return the URL stored in a "My Link Field" field. The regular expression is designed to find and capture the URL stored as a value in a link HTML element's URL attribute.  

If the "My Link Field" is not empty and matches the pattern specified by the regular expression, the URL is retrieved and returned. If the field is empty or doesn't match the pattern, the function returns nothing, essentially returning a null value.

This function couples well for reports if you need to extract the URL out of a Sitecore Link Field value to display in your report:

Assert-HasLayout

The "Assert-HasLayout" function checks if a given Sitecore item has a "final layout" defined. It accepts a Sitecore item as its input and uses the built-in "Get-Layout" function to retrieve the final layout details of the item.  If the item has a final layout (meaning the $layout variable is not null or empty), the function returns a "TRUE" string, indicating that a layout is present.

If no layout is found for the item (meaning the $layout variable is null or empty), it returns a "FALSE" string.

Usage example


Get-FormattedDate

The "Get-FormattedDate" function takes a raw date string as its input and attempts to turn it into a more user-friendly date format.

The raw date string is expected to follow a particular "yyyyMMddTHHmmssZ" pattern ("20230908T123456Z" representing September 8, 2023, 12:34:56 PM in Coordinated Universal Time, for example) - which is precisely how Sitecore typically stored DateTime fields in the database.

The function reads this string and converts it to a date format that is more commonly used, which includes the month, day, and year (like "09/08/2023"). If, for any reason, it can't convert the input into a date (maybe because the input doesn't follow the expected pattern), it will simply return an empty string. This way, even if it receives unexpected inputs, it won't crash and will still produce a result, even if that result is just an empty string.

By passing in the raw DateTime field value into the Get-FormattedDate function, the report will convert it to a readable string value when exporting the report to Excel, CSV, or JSON.




Whether you're a seasoned Sitecore developer or just starting out, these 5 functions can become essential tools in your developer toolkit, helping you navigate the complexities of Sitecore with greater ease and efficiency ✌.

Friday, June 23, 2023

Security Bulletin SC2023-003-587441: SPE OnDoubleClick Error

Sitecore has been busy patching security vulnerabilities left and right lately.  The latest as of this post is Security Bulletin SC2023-003-587441

We applied the hotfix to a lower environment but discovered during regression testing the update had broken a key feature of Sitecore PowerShell Extensions (SPE).

After running an SPE report, double-clicking on any result item does not open a new window to the selected item as expected.  Instead, the following error was being thrown in the logs:

ERROR Application error.
Exception: System.Web.HttpUnhandledException
Message: Exception of type 'System.Web.HttpUnhandledException' was thrown.
Source: System.Web
at System.Web.UI.Page.HandleError(Exception e)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.<>c__DisplayClass285_0.<ExecuteStepImpl>b__0()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Nested Exception

Exception: Sitecore.Exceptions.AccessDeniedException
Message: Calling Spe.Client.Applications.PowerShellResultViewerList.OnDoubleClick method through reflection is not allowed.
Source: Sitecore.Kernel
at Sitecore.Reflection.MethodFilter.Filter[T](MethodInfo method)
at Sitecore.Shell.Framework.Commands.CommandManager.GetMethodCommand(String command)
at Sitecore.Web.UI.Sheer.ClientPage.Dispatch(String command)
at Sitecore.Web.UI.Sheer.ClientPage.RaiseEvent()
at Sitecore.Web.UI.Sheer.ClientPage.OnPreRender(EventArgs e)
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

The main message here:
"Calling Spe.Client.Applications.PowerShellResultViewerList.OnDoubleClick method through reflection is not allowed."

One of the more profound differences noted when comparing the original files against the hotfix files is the `Sitecore.Reflection.Filtering.config`; where we see several entries removed and many new entries related to SPE (Spe.Client.Applications.PowerShellResultViewerList.OnDoubleClick included) have been added


Upon further investigation, it was discovered that in the `OnDoubleClick` definition, an extra trailing space was present:

Removing the space restored the broken double-click functionality on the instance. 

If you find yourself in a similar situation, check this file first and determine if you have any trailing or unexpected spaces within the `methodName,` and remove them before redeploying the file. 

Hope this helps! ✌

Friday, November 4, 2022

DALL-E OpenAI Image Generator for Sitecore

On January 5, 2021, OpenAI revealed DALL-E, an artificial intelligence program that creates images from textual descriptions, on January 5, 2021. It uses a 12-billion parameter training version of the GPT-3 transformer model to interpret the natural language inputs and generate corresponding images. 

Since the initial private beta release, access to DALL-E was limited to those on a waitlist. However, about a month back, on September 28, 2022, DALL-E 2 opened to the public, and the waitlist requirement was lifted. Over the past month, millions of users have explored DALL-E's capabilities using https://labs.openai.com/.

Yesterday, on November 3, 2022, OpenAI announced the DALL-E API, which developers could start immediately consuming. 🤩


DALL·E joins GPT-3, Embeddings, and Codex in our API platform, adding a new building block that developers can use to create novel experiences and applications. All API customers can use the DALL·E API today.
As soon as I heard the news, I couldn't help but immediately explore the API myself. I was already familiar with OpenAI's API playground for text-based AI  prompts and had an existing API key ready. 

My first instinct to consume the DALL-E API was to use plain old Windows PowerShell 🤓

"A photorealistic close-up of a variety of river rocks"

"A super cute puppy"

So dope 😎 - and wildly simple!

DALL-E OpenAI Image Generator for Sitecore

AI-generated images were featured prominently in Paige O'Neill's keynote at the 2022 Sitecore Symposium in Chicago. 

There's real value in this technology. A reality where designers, marketers, and developers who need a patterned backdrop can unleash their creativity to quickly and easily generate an image without needing to license stock images is nearly here. Even in its current state, the ease and simplicity of the DALL-E API make this the perfect candidate for consuming in a CMS.

With the fresh release of the DALL-E API, I couldn't think of a better time to give it a whirl and integrate it into Sitecore. 

🥁 Introducing...the ✨ DALL-E OpenAI Image Generator for Sitecore ✨ :




The Details

Naturally, Sitecore PowerShell Extensions was the way to go. The 'Context Menu' and 'Insert Item' integration points allow users to open the prompt while targetting any 'Media Folder' - either by right-clicking, expanding Insert, and selecting 'DALL-E OpenAI Image Generator' or selecting 'DALL-E OpenAI Image Generator' from the Folder tab of the active Media Folder.
Both integration points are configured out-of-the-box to only show on items where the Template is a 'Media Folder.'
Once the prompt is toggled, the user can configure their call to the DALL-E API using the following inputs:
  1. A prompt for the DALL-E to interpret.
  2. Image sizing options - restricted to these three sizes at this time: '256x256', '512x512', 'and 1024x1024' (see https://beta.openai.com/docs/api-reference/images/create#images/create-size)
  3. The number of images to generate (DALL-E API allows an integer from 0 to 10. The module limits generation to up to 5)
  4. The base name for new Media Items created in the Media Library after images are generated from DALL-E.



The module also comes bundled with a Settings item located in the following location:
  • /sitecore/system/Modules/PowerShell/Script Library/DALL-E OpenAI Image Generator/OpenAI API Settings
  • {F102AE0D-6A5B-499B-9500-505D0E6F686F}
This item allows users to change or override some default settings - the most important being the 'OpenAI API Key' field, where users must populate their unique API key. after installing the module, You can generate an OpenAI API Key by logging in and navigating to the API Keys settings for your account: https://beta.openai.com/account/api-keys
The Setting item's Template is located in the following location:
  • /sitecore/templates/Modules/DALL-E OpenAI Image Generator
  • {169EE16E-5D44-4CC1-B475-A2DFC8B08A0B}

The Code

The complete source code is available on GitHub, along with the Sitecore package (see https://github.com/strezag/DALL-E-OpenAI-Image-Generator-for-Sitecore/releases) and installation instructions: 
https://github.com/strezag/DALL-E-OpenAI-Image-Generator-for-Sitecore

The module is totally open to any community contributions or general suggestions. Beyond that, feel free to fork a version of your own a build on top of it.

Final Thoughts

I'm really excited about this space and where I believe it's headed. As this technology continues to grow, we should expect an expansion of the DALL-E API (and others) that will almost certainly redefine how we work with digital visual assets.

Enjoy! 🤖