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.

Tuesday, April 9, 2024

Sitecore XM Cloud Developer Certification Practice Exams: A Free Study Companion

Certification is a crucial milestone for any developer pursuing excellence and proficiency in Sitecore XM Cloud.  One of my preferred ways to learn and study is via practice exams.  However, with existing spread of Sitecore XM Cloud practice exams available online cost being between $30 and $150, the financial burden of personally preparing can be as daunting as the exam itself. 

That's why I'm excited to introduce the Sitecore XM Cloud Developer Certification Practice Exams app, a completely free resource designed to democratize the preparation process for all Sitecore developers.



Elevating Your Exam Readiness Without the Cost

The XM Cloud Certification demands a deep understanding of numerous Sitecore aspects, from XM Cloud architecture and developer workflow to security and data modeling. This exhaustive list requires serious preparation. The Sitecore XM Cloud Developer Certification Practice Exams app offer a thorough, cost-free study tool that reflects the actual exam's breadth and depth.

Tailored for Comprehensive Preparation

  • Precise Exam Simulation: The practice exams simulate the actual test with 50 questions chosen randomly, testing not just your knowledge but also your ability to perform under exam conditions.

  • Competency-Centric Learning: Dive into crucial competencies on which the exam will test you. Each practice question is sourced from Sitecore's documentation and is an opportunity to fortify your understanding of core Sitecore XM Cloud competencies.

  • Real Exam Experience: Sharpen your time management skills with a 100-minute timer that mirrors the exam's duration.



Commitment to Community and Accessibility

Access to educational resources should be barrier-free in a landscape dotted with expensive prep materials.  The Sitecore XM Cloud Developer Certification Practice Exams app was born from a blend of personal needs and a commitment to the Sitecore community. This practice exam tool is my contribution towards leveling the playing field for all aspiring XM Cloud certified developers.

I'm excited to offer this resource to the community, ensuring that everyone has the chance to study effectively and become certified without the financial strain. Start your free practice runs today and please share this tool with anyone who might benefit.


Happy learning!

Wednesday, February 14, 2024

Sitecore ADM: Resolving Stalled Tasks and Restoring Task Processing

My team is currently in the process of purging millions of historical anonymous xDB contact records and associated data using the ADM module for a client whose xDB shard database sizes have been approaching max storage capacity for the Azure tier.  Because xDB is a crucial portion of the client site's operations, our options for reducing the DB size have been somewhat limiting due to complex custom external integrations with xDB. 

In our approach, we opted to use ADM to purge historical anonymous contacts in batches. We prepare ~300k contact records per shard for each batch, which are manually retrieved via SQL query. Once we've created the temporary table in the shard DB, we prepare the data by generating a comma-delimited list of contacts and then kick off the purge process via ADM. 

When ADM populates its Tasks table, each queued record is subsequently processed by ADM and removed from the Tasks table as it completes processing that record.  The ADM task execution is a generally slow process (1 contact processed every 2-3 seconds); we closely monitor the progress with a SQL query:



With this approach (in addition to SHRINK and REINDEX operations between batches), we have seen the necessary disk size reduction of both xDB shard DBs after running a cadence of several batches.  

However, we ran into a snag in a recent batch, which resulted in the entire ADM task processing halting entirely.  The issue appeared to directly correlate with general Azure Maintenance operations, which had occurred over the weekend while the batch was mid-process.  Azure Maintenance updates typically happen without any advanced notice or warning.  Usually, Azure Maintenance operations have minimal adverse effects, but this round seemed to have caused much of the infrastructure to spiral.  We observed that the ADM tasks were no longer processing when all was said and done.  

Attempts to re-start the job via ADM kept resulting in the same error:

"[ADM] Response from xConnect did not indicate success. Status code: BadRequest, Message: {\"Message\":\"The remove task can't be started while another one is running.\"}"

Upon initial analysis, we noted that the ADM tasks table was still populated with IDs that had yet to be processed when the operation was cut off. I began dissecting the ADM binary files for clues - specifically in search of the message "The remove task can't be started while another one is running".  

I learned that the StartContactsDataRemoving method queries an IsRunning method to determine if any other tasks are in progress. If there are, it throws a BadRequest
response and returns the "The remove task can't be started while another one is running.
message" message. 



Digging deeper led me to this ClearRemoveDataSettings method - called in the StopRunningTasksAndClearStorage method.  Deeper in, there are references to a PropertiesRepository class and an object name of "RemoveDataSettings" used to store task information:


This, in turn, finally led me to a PropertyValueQuery method in a PropertiesRepositoryQueries class, which contained a SQL command used as part of the process:




We reviewed the current state of the ADM Properties table within the ADM DB and found three entries, including RemoveDataSettings:


The RemoveDataSettings record's value appeared to be a JSON representation of ADM's last ADM removal task run.  However, the JSON representation was cut off after a few hundred characters.  With this state of the present value, ADM was convinced that the task was not completed.  

Following the approach used in the code (mimicking what should occur when an ADM removal task is completed), we ran the following command:

We also entirely cleared the remaining IDs and Tasks table and re-initialized the process.  With these steps, our ADM tasks were back to processing as expected.

I hope this one helps anyone in a similar situation!