Skip to content

Conversation

@patrickdemooij9
Copy link
Owner

@patrickdemooij9 patrickdemooij9 commented Dec 7, 2025

User description

First steps towards adding uSync addon for SeoToolkit


PR Type

Enhancement


Description

  • Added comprehensive uSync configuration for the SeoToolkit Umbraco site, enabling content and configuration synchronization

  • Created 40+ uSync configuration files covering content types, content nodes, media types, data types, and meta field settings

  • Defined complete content hierarchy including Home page, Blog, Articles (Podcasts, Videos, Conferences, Meetups, Community), Authors, and utility pages (Contact, Search, Error, XML Sitemap)

  • Implemented reusable composition content types for SEO controls, visibility controls, article controls, spacing properties, contact form controls, header/footer controls, and main image controls

  • Added media type configurations for Image, SVG, Audio, Video, Article documents, and File assets

  • Configured block list data types with support for rich text, images, videos, code snippets, carousels, and article displays

  • Created meta field settings for SEO metadata including OpenGraph, Twitter Card, and canonical URL support

  • Updated Visual Studio solution file to version 18 and added new SeoToolkit.Umbraco.uSync addon project

  • Added comprehensive JSON schema for uSync application settings validation


Diagram Walkthrough

flowchart LR
  A["Solution File<br/>v18 + Addons"] --> B["uSync Addon<br/>Project"]
  B --> C["Content Types<br/>Compositions & Elements"]
  B --> D["Content Nodes<br/>Pages & Articles"]
  B --> E["Media Types<br/>Assets"]
  B --> F["Data Types<br/>Block Lists"]
  B --> G["Meta Field<br/>Settings"]
  C --> H["SEO, Visibility,<br/>Article Controls"]
  D --> I["Home, Blog,<br/>Community Pages"]
  E --> J["Image, Audio,<br/>Video, SVG"]
  F --> K["Rich Text,<br/>Block Layouts"]
  G --> L["OpenGraph,<br/>Twitter Card"]
Loading

File Walkthrough

Relevant files
Configuration changes
29 files
home.config
Home page uSync content configuration                                       

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/home.config

  • Added new uSync content configuration file for the Home page (root
    level content node)
  • Defines home page structure with title "Clean Starter Kit" and
    subtitle "For Umbraco"
  • Includes block list content with article list component (3 items, no
    pagination) and 7 social icon links
  • Contains metadata properties like main image, SEO fields, and
    visibility controls
+626/-0 
podcasts-and-videos.config
Podcasts and Videos article uSync configuration                   

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/podcasts-and-videos.config

  • Added uSync content configuration for "Podcasts and Videos" article
    page
  • Includes rich content blocks with descriptions of umbraCoffee, Candid
    Contributions, and Package Manifest podcasts
  • Contains embedded video references and image captions for podcast
    artwork
  • Defines article metadata including date, author, categories, and SEO
    properties
+535/-0 
youtube-tutorials.config
YouTube Tutorials article uSync configuration                       

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/youtube-tutorials.config

  • Added uSync content configuration for "YouTube Tutorials" article page
  • Includes content blocks describing Umbraco Learning Base, Paul Seal's
    channel, and jondjones channel
  • Contains 3 embedded YouTube video references with captions for
    tutorial content
  • Defines article metadata with date, author, category, and SEO
    properties
+463/-0 
conferences.config
Conferences article uSync configuration                                   

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/conferences.config

  • Added uSync content configuration for "Conferences" article page
  • Includes content describing Umbraco conferences (Spark, Codegarden,
    DUUGFest, CODECABIN, UK Festival)
  • Contains image block and embedded video reference (Codegarden 2022
    aftermovie)
  • Defines article metadata with date, author, category, and visibility
    controls
+322/-0 
meetups.config
Meetups article uSync configuration                                           

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/meetups.config

  • Added uSync content configuration for "Meetups" article page
  • Includes content blocks with descriptions of 9 Umbraco meetup groups
    worldwide (Edinburgh, Melbourne, London, Dutch, Leeds, Sydney,
    Belgian, DK, Glasgow)
  • Contains image block showing meetup attendees
  • Defines article metadata with date, author, category, and SEO
    properties
+229/-0 
appsettings-schema.usync.json
uSync application settings JSON schema definition               

src/SeoToolkit.Umbraco.Site/appsettings-schema.usync.json

  • Added comprehensive JSON schema for uSync configuration validation
  • Defines schema for uSync settings including root folder, handler sets,
    and auto-templates configuration
  • Includes detailed property definitions for uSync behavior
    (import/export, caching, notifications, folder modes)
  • Provides schema for handler set settings and individual handler
    configurations
+397/-0 
about.config
About page uSync content configuration                                     

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/about.config

  • Added uSync content configuration for "About" page (Level 2 content
    node under Home)
  • Includes 3 content blocks describing the Clean Starter Kit, its
    purpose, and Umbraco benefits
  • Contains image block with caption about Umbraco
  • Defines page metadata with subtitle "All about this starter kit" and
    visibility controls
+220/-0 
categorylist.config
Category List content type uSync configuration                     

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/categorylist.config

  • Added uSync content type configuration for "Category List"
    (categoryList alias)
  • Defines container content type for organizing category items with blue
    tags icon
  • Allows child content type category with composition of
    visibilityControls
  • Configured as non-root, non-element type with list view enabled
+29/-0   
popular-blogs.config
uSync content configuration for Popular blogs article       

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/popular-blogs.config

  • Added new uSync content configuration file for "Popular blogs" article
    page
  • Defines article metadata including date, author, and categories
  • Contains rich text content with block list layout featuring blog
    recommendations
  • Includes SEO properties and main image configuration
+229/-0 
community.config
uSync content configuration for Community article page     

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/community.config

  • Added new uSync content configuration for "Community" article page
  • Includes article metadata with date, author, and category references
  • Contains block list content with community information and image
  • Defines visibility and SEO control properties
+229/-0 
spacingproperties.config
Content type for spacing properties composition                   

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/spacingproperties.config

  • Added new content type for spacing properties composition element
  • Defines margin and padding properties with dropdown controls
  • Organized into "Padding" tab with 8 generic properties
  • Configured as element type for reusable spacing settings
+162/-0 
join-the-umbraco-community-on-mastodon.config
uSync content configuration for Mastodon community article

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/join-the-umbraco-community-on-mastodon.config

  • Added new uSync content configuration for Mastodon community article
  • Contains article metadata with publication date and author reference
  • Includes block list content with information about Mastodon community
  • Defines SEO and visibility controls
+184/-0 
paul-seal.config
uSync content configuration for Paul Seal author profile 

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/paul-seal.config

  • Added new uSync content configuration for "Paul Seal" author page
  • Defines author profile with biographical content block
  • Includes main image and meta description for SEO
  • Configured with author content type and template
+175/-0 
seocontrols.config
Content type composition for SEO controls                               

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/seocontrols.config

  • Added new composition content type for SEO controls
  • Defines 5 generic properties for meta tags and robot directives
  • Includes meta description, keywords, name, and indexable/followable
    flags
  • Organized in "SEO" tab for reusable SEO functionality
+133/-0 
blog.config
uSync content configuration for Blog listing page               

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/blog.config

  • Added new uSync content configuration for "Blog" listing page
  • Defines article list block with pagination settings
  • Includes main image and SEO properties
  • Configured as article list content type with template
+181/-0 
error.config
uSync content configuration for error page                             

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/error.config

  • Added new uSync content configuration for error/404 page
  • Contains error message content block with fallback link
  • Configured to hide from navigation and XML sitemap
  • Includes main image and SEO control properties
+175/-0 
image.config
Media type configuration for Image assets                               

src/SeoToolkit.Umbraco.Site/uSync/v17/MediaTypes/image.config

  • Added new media type configuration for Image assets
  • Defines 6 generic properties including alt text, dimensions, and file
    info
  • Configured with image cropper editor for flexible image handling
  • Organized in "Image" tab with mandatory image file property
+116/-0 
SeoToolkit.Umbraco.sln
Solution file updates for uSync addon integration               

src/SeoToolkit.Umbraco.sln

  • Updated Visual Studio version from 17 to 18
  • Added new "Addons" solution folder
  • Added new project SeoToolkit.Umbraco.uSync under Addons folder
  • Added build configuration entries for the new uSync project
+11/-2   
contactformcontrols.config
Content type composition for contact form controls             

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/contactformcontrols.config

  • Added new composition content type for contact form controls
  • Defines 3 generic properties for form messages (success, error,
    instruction)
  • Uses rich text editor for message content
  • Organized in "Result Messages" and "Content" tabs
+89/-0   
latestarticlesrow.config
Content type element for latest articles display                 

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/latestarticlesrow.config

  • Added new element content type for displaying latest articles
  • Defines 3 generic properties for article list, page size, and
    pagination
  • Configured as element type for block list usage
  • Includes mandatory validation for article list selection
+82/-0   
BlockListMainContent.config
Data type configuration for block list main content           

src/SeoToolkit.Umbraco.Site/uSync/v17/DataTypes/BlockListMainContent.config

  • Added new data type configuration for block list main content
  • Defines 6 block types including rich text, image, video, code snippet,
    carousel, and articles
  • Includes custom labels with content preview and visibility indicators
  • Configured without block limits for flexible content composition
+47/-0   
visibilitycontrols.config
Content type composition for visibility controls                 

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/visibilitycontrols.config

  • Added new composition content type for visibility controls
  • Defines 3 generic properties for navigation and search visibility
  • Includes hide from top navigation, XML sitemap, and search options
  • Organized in "Visibility" tab for reusable visibility settings
+82/-0   
articlecontrols.config
Content type composition for article controls                       

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/articlecontrols.config

  • Added new composition content type for article controls
  • Defines 3 generic properties for article date, author, and categories
  • Uses date picker and multi-node tree picker editors
  • Organized in "Content" tab for reusable article functionality
+82/-0   
contact.config
uSync content configuration for Contact page                         

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/contact.config

  • Added new uSync content configuration for "Contact" page
  • Defines contact form with instruction, success, and error messages
  • Includes main image and SEO properties
  • Configured with contact content type and template
+91/-0   
umbracomediavectorgraphics.config
Media type configuration for SVG vector graphics                 

src/SeoToolkit.Umbraco.Site/uSync/v17/MediaTypes/umbracomediavectorgraphics.config

  • Added new media type configuration for SVG vector graphics
  • Defines 3 generic properties for file info and upload field
  • Configured with upload field editor for SVG file handling
  • Organized in "Vector Graphics" tab
+71/-0   
home.config
Content type configuration for Home page                                 

src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/home.config

  • Added new home page content type with multiple compositions
  • Includes compositions for content, footer, header, main image, and SEO
    controls
  • Defines allowed child content types for blog, authors, categories, and
    other pages
  • Configured as root-level page with home template
+42/-0   
umbracomediaarticle.config
Media type configuration for Article documents                     

src/SeoToolkit.Umbraco.Site/uSync/v17/MediaTypes/umbracomediaarticle.config

  • Added new media type configuration for Article documents
  • Defines 3 generic properties for file info and upload field
  • Configured with upload field editor for article file handling
  • Organized in "Article" tab
+71/-0   
umbracomediaaudio.config
Media type configuration for Audio files                                 

src/SeoToolkit.Umbraco.Site/uSync/v17/MediaTypes/umbracomediaaudio.config

  • Added new media type configuration for Audio files
  • Defines 3 generic properties for file info and upload field
  • Configured with upload field editor for audio file handling
  • Organized in "Audio" tab
+71/-0   
content.config
Meta field settings configuration for content                       

src/SeoToolkit.Umbraco.Site/uSync/v17/SeoToolkit/MetaFieldsSettings/content.config

  • Added new meta field settings configuration for content type
  • Defines 14 fields for SEO metadata including title, description,
    schema, and social media
  • Includes OpenGraph, Twitter Card, and canonical URL settings
  • Configured with inherited value options and default placeholders
+77/-0   
Additional files
101 files
RedirectTests.cs +10/-9   
SeoSettingSavedNotification.cs +17/-0   
ISeoSettingsRepository.cs +2/-0     
SeoSettingsRepository.cs +32/-24 
ISeoSettingsService.cs +3/-0     
SeoSettingsService.cs +14/-3   
MetaFieldsComposer.cs +1/-1     
MetaFieldSettingsSavedNotification.cs +15/-0   
IMetaFieldsSettingsRepository.cs +2/-1     
MetaFieldsSettingsDatabaseRepository.cs +10/-0   
IMetaFieldsSettingsService.cs +3/-1     
MetaFieldsSettingsService.cs +29/-10 
RedirectsController.cs +2/-2     
IRedirectsService.cs +1/-1     
RedirectCreatedByGuidEntity.cs +62/-0   
RedirectCreatedByGuidMigration.cs +67/-0   
RedirectsMigrationPlan.cs +1/-0     
Redirect.cs +1/-1     
RedirectEntity.cs +1/-1     
RedirectDeletedNotification.cs +15/-0   
RedirectSavedNotification.cs +15/-0   
RedirectsService.cs +10/-1   
IRobotsTxtService.cs +4/-0     
RobotsTxtSavedNotification.cs +15/-0   
RobotsTxtService.cs +24/-0   
IScriptManagerService.cs +1/-1     
Script.cs +0/-1     
SeoToolkit.Umbraco.Site.csproj +2/-0     
appsettings-schema.json +3/-0     
authors.config +61/-0   
categories.config +26/-0   
community_fa0pue2k.config +16/-0   
conferences_dpraan01.config +16/-0   
features.config +871/-0 
meetups_fthd5e2w.config +16/-0   
podcasts.config +16/-0   
resources.config +16/-0   
search.config +58/-0   
umbraco.config +16/-0   
videos.config +16/-0   
xmlsitemap.config +26/-0   
article.config +35/-0   
articlelist.config +36/-0   
author.config +33/-0   
authorlist.config +35/-0   
category.config +25/-0   
codesnippetrow.config +66/-0   
codesnippetrowsettings.config +28/-0   
contact.config +33/-0   
content.config +33/-0   
contentcontrols.config +50/-0   
error.config +33/-0   
footercontrols.config +50/-0   
headercontrols.config +66/-0   
hideproperty.config +50/-0   
iconlinkrow.config +66/-0   
iconlinkrowsettings.config +27/-0   
imagecarouselrow.config +50/-0   
imagecarouselrowsettings.config +28/-0   
imagerow.config +66/-0   
imagerowsettings.config +28/-0   
latestarticlesrowsettings.config +28/-0   
listpagesettings.config +33/-0   
mainimagecontrols.config +50/-0   
richtextrow.config +50/-0   
richtextrowsettings.config +28/-0   
search.config +32/-0   
videorow.config +66/-0   
videorowsettings.config +28/-0   
xmlsitemap.config +29/-0   
ApprovedColor.config +9/-0     
BlockListIconList.config +21/-0   
CheckboxList.config +9/-0     
ContentPicker.config +9/-0     
DatePicker.config +11/-0   
DatePickerWithTime.config +11/-0   
DateTimePickerWithTimeZone.config +14/-0   
Dropdown.config +11/-0   
DropdownMultiple.config +11/-0   
DropdownSpacing.config +20/-0   
ImageCropper.config +9/-0     
ImageMediaPicker.config +16/-0   
LabelBigint.config +11/-0   
LabelBytes.config +12/-0   
LabelDatetime.config +11/-0   
LabelDecimal.config +11/-0   
LabelInteger.config +11/-0   
LabelPixels.config +12/-0   
LabelString.config +11/-0   
LabelTime.config +11/-0   
ListViewContent.config +41/-0   
ListViewMedia.config +41/-0   
MNTPAuthors.config +31/-0   
MNTPCategories.config +31/-0   
MediaPicker.config +15/-0   
MediaPickerSVGImage.config +20/-0   
MemberPicker.config +9/-0     
MultiURLPicker.config +9/-0     
MultiUrlPickerSingleUrlPicker.config +13/-0   
MultipleImageMediaPicker.config +12/-0   
Additional files not shown

@qodo-code-review
Copy link

qodo-code-review bot commented Dec 7, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing Audit Logs: New critical actions (redirect save/delete) publish notifications but do not include
explicit structured audit logging capturing user, timestamp, action, and outcome.

Referred Code
    redirect.NewUrl = newUrl;
    redirect.LastUpdated = DateTime.Now;
    _redirectsRepository.Save(redirect);

    _eventAggregator.Publish(new RedirectSavedNotification(redirect));
}

public void UpdateRedirectCodes(int[] ids, int redirectCode)
{
    _redirectsRepository.UpdateRedirectCodes(ids, redirectCode);
}

public void UpdateRedirectCodes(Guid[] keys, int redirectCode)
{
    _redirectsRepository.UpdateRedirectCodes(keys, redirectCode);
}

public void Delete(int[] ids)
{
    foreach (var id in ids)
    {


 ... (clipped 18 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Migration Edge Cases: The migration renames and alters columns with raw SQL and drops the old column without
explicit try/catch or rollback handling, risking failures on different DB providers or
partial states.

Referred Code
protected override async Task MigrateAsync()
{
    var firstEntryData = GetFirstRecord("SeoToolkitRedirects", "CreatedBy");
    if (firstEntryData is not null && Guid.TryParse(firstEntryData.CreatedBy.ToString(), out Guid _))
    {
        return;
    }

    var redirects = Database.Fetch<RedirectCreatedByGuidEntity>(Sql().SelectAll().From<RedirectCreatedByGuidEntity>());
    if (DatabaseType == NPoco.DatabaseType.SQLite)
    {
        Database.Execute("ALTER TABLE SeoToolkitRedirects RENAME COLUMN CreatedBy TO CreatedBy_Old");
    }
    else
    {
        Database.Execute("exec sp_rename 'SeoToolkitRedirects.CreatedBy', 'CreatedBy_Old', 'COLUMN'");
    }

    Alter.Table("SeoToolkitRedirects").AddColumn("CreatedBy").AsGuid().Nullable().Do();
    foreach (var redirect in redirects)
    {


 ... (clipped 23 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Dec 7, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Use correct ID for user lookup
Suggestion Impact:The commit updated the query to use redirect.CreatedBy in the WHERE clause, fixing the incorrect lookup.

code diff:

-                var createdByKey = Database.Fetch<Guid?>("SELECT userKey FROM umbracoUser where id = @0", [redirect.Id]).FirstOrDefault();
+                var createdByKey = Database.Fetch<Guid?>("SELECT userKey FROM umbracoUser where id = @0", [redirect.CreatedBy]).FirstOrDefault();

In the database migration, use redirect.CreatedBy instead of redirect.Id in the
WHERE clause to correctly look up the user key and prevent data loss.

src/SeoToolkit.Umbraco.Redirects.Core/Migrations/RedirectCreatedByGuidMigration.cs [44-47]

-var createdByKey = Database.Fetch<Guid?>("SELECT userKey FROM umbracoUser where id = @0", [redirect.Id]).FirstOrDefault();
+var createdByKey = Database.Fetch<Guid?>("SELECT userKey FROM umbracoUser where id = @0", [redirect.CreatedBy]).FirstOrDefault();
 if (createdByKey is null) continue;
 
 Database.Execute("UPDATE SeoToolkitRedirects SET CreatedBy = @0 WHERE Key = @1", createdByKey, redirect.Key);

[Suggestion processed]

Suggestion importance[1-10]: 10

__

Why: This suggestion identifies a critical bug in the database migration logic where an incorrect ID is used for a lookup, which would cause data loss for the CreatedBy field.

High
Pass raw string to converter

Remove the JsonConvert.DeserializeObject call and pass the raw JSON string value
directly to the ConvertDatabaseToObject method to ensure correct data
conversion.

src/SeoToolkit.Umbraco.uSync/Serializers/MetaFieldsSerializer.cs [119-123]

 var value = fieldNode.Element("Value").ValueOrDefault<string?>(null);
 if (!string.IsNullOrWhiteSpace(value))
 {
-    fieldValue.Value = seoField.Editor.ValueConverter.ConvertDatabaseToObject(JsonConvert.DeserializeObject(value));
+    fieldValue.Value = seoField.Editor.ValueConverter.ConvertDatabaseToObject(value);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a bug where a JSON string is deserialized before being passed to a value converter that expects the raw string, which would likely cause a runtime error or data corruption during import.

High
Use correct helper for CSS

Replace SmidgeHelper.RequiresJs with SmidgeHelper.RequiresCss to correctly load
the highlight.default.min.css file and fix syntax highlighting.

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/features.config [167]

-SmidgeHelper.RequiresJs(\u0022~/clean-assets/js/highlight.default.min.css\u0022);
+SmidgeHelper.RequiresCss(\u0022~/clean-assets/css/highlight.default.min.css\u0022);

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a functional bug where a CSS file is loaded using a JavaScript helper, which would prevent code snippet syntax highlighting from working.

Medium
Fix incorrect boolean default values

In the JSON schema, change the string default values for DisableDashboard,
SummaryDashboard, and DisableNotificationSuppression to be boolean literals
(false or true).

src/SeoToolkit.Umbraco.Site/appsettings-schema.usync.json [186-210]

 "DisableDashboard": {
   "type": "boolean",
   "description": "Disable the default dashboard (so people can't accidently press the buttons).",
-  "default": "false"
+  "default": false
 },
 "SummaryDashboard": {
   "type": "boolean",
   "description": "summarize results (for when there are loads and loads of items)\n            ",
-  "default": "false"
+  "default": false
 },
 ...
 "DisableNotificationSuppression": {
   "type": "boolean",
   "description": "turns off use of the Notifications.Suppress method, so notifications\nfire after every item is imported.\n            ",
-  "default": "true"
+  "default": true
 },

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that several boolean properties in the JSON schema have their default values incorrectly set as strings, which violates the schema specification and should be corrected.

Medium
High-level
Separate library code from demo content
Suggestion Impact:The commit removed large uSync demo content/config files (home.config and seocontrols.config) from the SeoToolkit.Umbraco.Site project, aligning with the recommendation to separate demo content from the addon codebase.

code diff:

# File: src/SeoToolkit.Umbraco.Site/uSync/v17/Content/home.config
@@ -1,626 +1 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Content Key="dcf18a51-6919-4cf8-89d1-36b94ce4d963" Alias="Home" Level="1">
-  <Info>
-    <Parent Key="00000000-0000-0000-0000-000000000000"></Parent>
-    <Path>/Home</Path>
-    <Trashed>false</Trashed>
-    <ContentType>home</ContentType>
-    <CreateDate>2025-11-29T07:55:01</CreateDate>
-    <NodeName Default="Home" />
-    <SortOrder>0</SortOrder>
-    <Published Default="true" />
-    <Schedule />
-    <Template Key="907d26a5-c929-4c69-bb1d-0d0d5342d6f6">home</Template>
-  </Info>
-  <Properties>
-    <contentRows>
-      <Value><![CDATA[{
-  "contentData": [
-    {
-      "contentTypeKey": "60085a63-b77b-4509-9df4-bcb75db2755f",
-      "key": "bfc949ab-5489-4586-89f3-0c6c93f2d619",
-      "udi": null,
-      "values": [
-        {
-          "alias": "articleList",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "umb://document/31523089f64848839087ef9a0b83129f"
-        },
-        {
-          "alias": "pageSize",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "3"
-        },
-        {
-          "alias": "showPagination",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    }
-  ],
-  "settingsData": [
-    {
-      "contentTypeKey": "c56fb5b8-0b89-4206-847e-a6fecd865b84",
-      "key": "e378d8aa-9646-4bc4-ba92-3cc44fa3af5c",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        },
-        {
-          "alias": "paddingTop",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "paddingBottom",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "paddingLeft",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "paddingRight",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "marginTop",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "marginBottom",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "marginLeft",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        },
-        {
-          "alias": "marginRight",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": ""
-        }
-      ]
-    }
-  ],
-  "expose": [
-    {
-      "contentKey": "bfc949ab-5489-4586-89f3-0c6c93f2d619",
-      "culture": null,
-      "segment": null
-    }
-  ],
-  "Layout": {
-    "Umbraco.BlockList": [
-      {
-        "contentKey": "bfc949ab-5489-4586-89f3-0c6c93f2d619",
-        "contentUdi": null,
-        "settingsKey": "e378d8aa-9646-4bc4-ba92-3cc44fa3af5c",
-        "settingsUdi": null
-      }
-    ]
-  }
-}]]></Value>
-    </contentRows>
-    <isFollowable>
-      <Value><![CDATA[0]]></Value>
-    </isFollowable>
-    <isIndexable>
-      <Value><![CDATA[0]]></Value>
-    </isIndexable>
-    <mainImage>
-      <Value><![CDATA[[
-  {
-    "key": "737d0fda-9582-4cd4-9ef4-48a9f5393594",
-    "mediaKey": "5598b628-b390-4532-8bb5-dab06089e9d7",
-    "mediaTypeAlias": "Image",
-    "crops": [],
-    "focalPoint": null
-  }
-]]]></Value>
-    </mainImage>
-    <metaDescription>
-      <Value><![CDATA[]]></Value>
-    </metaDescription>
-    <metaKeywords>
-      <Value><![CDATA[]]></Value>
-    </metaKeywords>
-    <metaName>
-      <Value><![CDATA[]]></Value>
-    </metaName>
-    <socialIconLinks>
-      <Value><![CDATA[{
-  "contentData": [
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "88b3cd1e-0bd7-47fd-9081-ab4832c84241",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "1181c745-d10f-407a-8247-08f30dfe7e5c",
-              "mediaKey": "6921a6b4-3a02-4354-83bf-344074c4d00d",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "View this package repository on GitHub",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://github.com/prjseal",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "ea8a8fee-444b-461b-8b07-de267ccbe9ef",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "5bdeb205-44e0-4aea-ba6e-ff153871a6b5",
-              "mediaKey": "7639bb90-3ba4-4765-bec5-655bc3485c06",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "View this in the Umbraco Marketplace",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://marketplace.umbraco.com/package/clean",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "8fc94595-272b-475a-b4f8-0b8da018f720",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "15c2a6a4-ec0f-4913-8478-0d8733757129",
-              "mediaKey": "c08eb6e5-fbb7-4e57-b9b3-d35a9b088069",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "Follow me on twitter",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://twitter.com/codesharepaul",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "f5a40b08-daf1-412f-bc57-f768498be386",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "d549757b-95b1-4d20-89d8-51a30e6d5d6a",
-              "mediaKey": "48bc7fab-611a-410d-a9eb-1cf82d811711",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "Read my blog codeshare.co.uk",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://codeshare.co.uk",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "4342c4bf-5050-4a32-8b3e-6dbc2156afc2",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "8b0ed1d2-4d52-4410-931d-0fde4aea9081",
-              "mediaKey": "87163027-204d-44bd-a74c-c1363368d92f",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "Join the Umbraco discord server",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://discord.gg/umbraco",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "f0190005-f618-485f-a210-5d873e4c9227",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "34db3054-84f8-4a8a-8b47-e5bc188d3730",
-              "mediaKey": "fbdf1674-1658-4449-8029-0b11bf7f20ab",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "You can buy me a coffee on PayPal if you would like to",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://codeshare.co.uk/coffee",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "17db13ba-bbd9-4a44-b28f-986301156754",
-      "key": "853ba3c4-9bf1-4997-a49e-d9ebca703142",
-      "udi": null,
-      "values": [
-        {
-          "alias": "icon",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "key": "197f6b38-0249-4142-8de7-c2b10c8edeae",
-              "mediaKey": "d6e58148-8678-4bc4-8cdb-5366ae75065f",
-              "mediaTypeAlias": "umbracoMediaVectorGraphics",
-              "crops": [],
-              "focalPoint": null
-            }
-          ]
-        },
-        {
-          "alias": "link",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": [
-            {
-              "name": "Follow me on Mastodon",
-              "target": "_blank",
-              "unique": null,
-              "type": null,
-              "udi": null,
-              "url": "https://umbracocommunity.social/@CodeSharePaul",
-              "queryString": null
-            }
-          ]
-        }
-      ]
-    }
-  ],
-  "settingsData": [
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "c5fe35b5-194e-4ea2-bee5-f6baa0e0c5ad",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "ea13e2b8-f5d1-49e7-a0fa-7bfd82ac5a39",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "c96f976f-ae28-4027-a336-c5ea6b12b5f3",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "e966db03-d627-4d81-b2ca-81c98887156e",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "6e25c5da-fe50-45cb-95a7-95ef9672e426",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "67da7863-fdef-4849-9ae8-3d3edc20b7a0",
-      "udi": null,
-      "values": [
-        {
-          "alias": "hide",
-          "culture": null,
-          "editorAlias": null,
-          "segment": null,
-          "value": "0"
-        }
-      ]
-    },
-    {
-      "contentTypeKey": "84e89805-5a53-4dcf-930d-fd87c48572dd",
-      "key": "88d58039-4d47-4865-b0bc-14791b6e0881",
-      "udi": null,
-      "values": []
-    }
-  ],
-  "expose": [
-    {
-      "contentKey": "88b3cd1e-0bd7-47fd-9081-ab4832c84241",
-      "culture": null,
-      "segment": null
-    },
-    {
-      "contentKey": "ea8a8fee-444b-461b-8b07-de267ccbe9ef",
-      "culture": null,
-      "segment": null
-    },
-    {
-      "contentKey": "8fc94595-272b-475a-b4f8-0b8da018f720",
-      "culture": null,
-      "segment": null
-    },
-    {
-      "contentKey": "f5a40b08-daf1-412f-bc57-f768498be386",
-      "culture": null,
-      "segment": null
-    },
-    {
-      "contentKey": "4342c4bf-5050-4a32-8b3e-6dbc2156afc2",
-      "culture": null,
-      "segment": null
-    },
-    {
-      "contentKey": "f0190005-f618-485f-a210-5d873e4c9227",
-      "culture": null,
-      "segment": null
-    },
-    {
-      "contentKey": "853ba3c4-9bf1-4997-a49e-d9ebca703142",
-      "culture": null,
-      "segment": null
-    }
-  ],
-  "Layout": {
-    "Umbraco.BlockList": [
-      {
-        "contentKey": "88b3cd1e-0bd7-47fd-9081-ab4832c84241",
-        "contentUdi": null,
-        "settingsKey": "c5fe35b5-194e-4ea2-bee5-f6baa0e0c5ad",
-        "settingsUdi": null
-      },
-      {
-        "contentKey": "ea8a8fee-444b-461b-8b07-de267ccbe9ef",
-        "contentUdi": null,
-        "settingsKey": "ea13e2b8-f5d1-49e7-a0fa-7bfd82ac5a39",
-        "settingsUdi": null
-      },
-      {
-        "contentKey": "8fc94595-272b-475a-b4f8-0b8da018f720",
-        "contentUdi": null,
-        "settingsKey": "c96f976f-ae28-4027-a336-c5ea6b12b5f3",
-        "settingsUdi": null
-      },
-      {
-        "contentKey": "f5a40b08-daf1-412f-bc57-f768498be386",
-        "contentUdi": null,
-        "settingsKey": "e966db03-d627-4d81-b2ca-81c98887156e",
-        "settingsUdi": null
-      },
-      {
-        "contentKey": "4342c4bf-5050-4a32-8b3e-6dbc2156afc2",
-        "contentUdi": null,
-        "settingsKey": "6e25c5da-fe50-45cb-95a7-95ef9672e426",
-        "settingsUdi": null
-      },
-      {
-        "contentKey": "f0190005-f618-485f-a210-5d873e4c9227",
-        "contentUdi": null,
-        "settingsKey": "67da7863-fdef-4849-9ae8-3d3edc20b7a0",
-        "settingsUdi": null
-      },
-      {
-        "contentKey": "853ba3c4-9bf1-4997-a49e-d9ebca703142",
-        "contentUdi": null,
-        "settingsKey": "88d58039-4d47-4865-b0bc-14791b6e0881",
-        "settingsUdi": null
-      }
-    ]
-  }
-}]]></Value>
-    </socialIconLinks>
-    <subtitle>
-      <Value><![CDATA[For Umbraco]]></Value>
-    </subtitle>
-    <title>
-      <Value><![CDATA[Clean Starter Kit]]></Value>
-    </title>
-  </Properties>
-</Content>
+

# File: src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/seocontrols.config
@@ -1,133 +1 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ContentType Key="9090575e-290c-4585-91a4-b72ec30ff41f" Alias="sEOControls" Level="2">
-  <Info>
-    <Name>SEO Controls</Name>
-    <Icon>icon-settings color-pink</Icon>
-    <Thumbnail>folder.png</Thumbnail>
-    <Description></Description>
-    <AllowAtRoot>False</AllowAtRoot>
-    <ListView>00000000-0000-0000-0000-000000000000</ListView>
-    <Variations>Nothing</Variations>
-    <IsElement>false</IsElement>
-    <HistoryCleanup>
-      <PreventCleanup>False</PreventCleanup>
-      <KeepAllVersionsNewerThanDays></KeepAllVersionsNewerThanDays>
-      <KeepLatestVersionPerDayForDays></KeepLatestVersionPerDayForDays>
-    </HistoryCleanup>
-    <Folder>Compositions</Folder>
-    <Compositions />
-    <DefaultTemplate></DefaultTemplate>
-    <AllowedTemplates />
-  </Info>
-  <Structure />
-  <GenericProperties>
-    <GenericProperty>
-      <Key>5118fb9a-2251-49e7-9574-b4743fb32914</Key>
-      <Name>Is Followable</Name>
-      <Alias>isFollowable</Alias>
-      <Definition>fccec1e1-0057-48ca-b9bf-184e00758a58</Definition>
-      <Type>Umbraco.TrueFalse</Type>
-      <Mandatory>false</Mandatory>
-      <Validation></Validation>
-      <Description><![CDATA[Set this to true if you want the page to be followable by robots
 
-
-<details>
-<summary>
-<uui-icon name="icon-help-alt"></uui-icon> More information
-</summary>
-
-This sets the *follow* aspect of the [`robots` meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/name/robots)
-
-</details>]]></Description>
-      <SortOrder>25</SortOrder>
-      <Tab Alias="sEO">SEO</Tab>
-      <Variations>Nothing</Variations>
-      <MandatoryMessage></MandatoryMessage>
-      <ValidationRegExpMessage></ValidationRegExpMessage>
-      <LabelOnTop>false</LabelOnTop>
-    </GenericProperty>
-    <GenericProperty>
-      <Key>c61c9d03-0d13-4229-8f20-27cb661a32a3</Key>
-      <Name>Is Indexable</Name>
-      <Alias>isIndexable</Alias>
-      <Definition>fccec1e1-0057-48ca-b9bf-184e00758a58</Definition>
-      <Type>Umbraco.TrueFalse</Type>
-      <Mandatory>false</Mandatory>
-      <Validation></Validation>
-      <Description><![CDATA[Set this to true if you want this page to be indexable by robots
-
-<details>
-<summary>
-<uui-icon name="icon-help-alt"></uui-icon> More information
-</summary>
-
-This sets the *index* aspect of the [`robots` meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/name/robots)
-
-</details>]]></Description>
-      <SortOrder>20</SortOrder>
-      <Tab Alias="sEO">SEO</Tab>
-      <Variations>Nothing</Variations>
-      <MandatoryMessage></MandatoryMessage>
-      <ValidationRegExpMessage></ValidationRegExpMessage>
-      <LabelOnTop>false</LabelOnTop>
-    </GenericProperty>
-    <GenericProperty>
-      <Key>85878342-59e5-4827-8447-edc34a11e87e</Key>
-      <Name>Meta Description</Name>
-      <Alias>metaDescription</Alias>
-      <Definition>c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3</Definition>
-      <Type>Umbraco.TextArea</Type>
-      <Mandatory>false</Mandatory>
-      <Validation></Validation>
-      <Description><![CDATA[Enter the meta description for this page]]></Description>
-      <SortOrder>10</SortOrder>
-      <Tab Alias="sEO">SEO</Tab>
-      <Variations>Nothing</Variations>
-      <MandatoryMessage></MandatoryMessage>
-      <ValidationRegExpMessage></ValidationRegExpMessage>
-      <LabelOnTop>false</LabelOnTop>
-    </GenericProperty>
-    <GenericProperty>
-      <Key>465b40d7-a107-454d-b0de-c74d7fea3bd1</Key>
-      <Name>Meta Keywords</Name>
-      <Alias>metaKeywords</Alias>
-      <Definition>b6b73142-b9c1-4bf8-a16d-e1c23320b549</Definition>
-      <Type>Umbraco.Tags</Type>
-      <Mandatory>false</Mandatory>
-      <Validation></Validation>
-      <Description><![CDATA[Enter the keywords for this page]]></Description>
-      <SortOrder>15</SortOrder>
-      <Tab Alias="sEO">SEO</Tab>
-      <Variations>Nothing</Variations>
-      <MandatoryMessage></MandatoryMessage>
-      <ValidationRegExpMessage></ValidationRegExpMessage>
-      <LabelOnTop>false</LabelOnTop>
-    </GenericProperty>
-    <GenericProperty>
-      <Key>c901ac53-4a1d-4a05-8d06-c3188e5f0668</Key>
-      <Name>Meta Name</Name>
-      <Alias>metaName</Alias>
-      <Definition>0cc0eba1-9960-42c9-bf9b-60e150b429ae</Definition>
-      <Type>Umbraco.TextBox</Type>
-      <Mandatory>false</Mandatory>
-      <Validation></Validation>
-      <Description><![CDATA[Enter the meta name for this page]]></Description>
-      <SortOrder>5</SortOrder>
-      <Tab Alias="sEO">SEO</Tab>
-      <Variations>Nothing</Variations>
-      <MandatoryMessage></MandatoryMessage>
-      <ValidationRegExpMessage></ValidationRegExpMessage>
-      <LabelOnTop>false</LabelOnTop>
-    </GenericProperty>
-  </GenericProperties>
-  <Tabs>
-    <Tab>
-      <Key>18ae646a-c4d9-49ac-b5ea-6ffcbb45d94f</Key>
-      <Caption>SEO</Caption>
-      <Alias>sEO</Alias>
-      <Type>Tab</Type>
-      <SortOrder>25</SortOrder>
-    </Tab>
-  </Tabs>
-</ContentType>

The suggestion recommends separating the uSync addon's core logic from the
numerous uSync configuration files for the demo site. These demo files should be
moved to a distinct starter kit or example project.

Examples:

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/home.config [1-626]
<?xml version="1.0" encoding="utf-8"?>
<Content Key="dcf18a51-6919-4cf8-89d1-36b94ce4d963" Alias="Home" Level="1">
  <Info>
    <Parent Key="00000000-0000-0000-0000-000000000000"></Parent>
    <Path>/Home</Path>
    <Trashed>false</Trashed>
    <ContentType>home</ContentType>
    <CreateDate>2025-11-29T07:55:01</CreateDate>
    <NodeName Default="Home" />
    <SortOrder>0</SortOrder>

 ... (clipped 616 lines)
src/SeoToolkit.Umbraco.Site/uSync/v17/ContentTypes/seocontrols.config [1-133]

Solution Walkthrough:

Before:

// Project structure includes both the addon and demo content
src/
  SeoToolkit.Umbraco.uSync/
    // C# code for the uSync addon (serializers, handlers)
    Serializers/
      MetaFieldsSerializer.cs
      ...
    Handlers/
      MetaFieldsHandler.cs
      ...
  SeoToolkit.Umbraco.Site/
    uSync/v17/
      // Demo site content and configuration
      Content/
        home.config
        about.config
        ... (40+ files)
      ContentTypes/
        ...

After:

// Addon project is clean of demo content
src/
  SeoToolkit.Umbraco.uSync/
    // C# code for the uSync addon (serializers, handlers)
    Serializers/
      MetaFieldsSerializer.cs
      ...
    Handlers/
      MetaFieldsHandler.cs
      ...

// Demo content is in a separate project/location
examples/
  SeoToolkit.Umbraco.DemoSite/
    uSync/v17/
      // Demo site content and configuration
      Content/
        home.config
        about.config
        ...
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant architectural issue where library code is mixed with demo site content, which impacts the addon's reusability and maintainability.

Medium
Learned
best practice
Add safe null parsing guards

Guard against nulls when accessing CreatedBy and avoid ToString() on potentially
null values to prevent NullReferenceExceptions.

src/SeoToolkit.Umbraco.Redirects.Core/Migrations/RedirectCreatedByGuidMigration.cs [25-29]

 var firstEntryData = GetFirstRecord("SeoToolkitRedirects", "CreatedBy");
-if (firstEntryData is not null && Guid.TryParse(firstEntryData.CreatedBy.ToString(), out Guid _))
+var createdByObj = firstEntryData?.CreatedBy;
+if (createdByObj is string s && Guid.TryParse(s, out _))
+{
+    return;
+}
+if (createdByObj is Guid)
 {
     return;
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Use TryParse and null checks when parsing or mapping external data to prevent runtime exceptions.

Low
Security
Add noreferrer to external links

Add noreferrer to the rel attribute of the external link to improve security.

src/SeoToolkit.Umbraco.Site/uSync/v17/Content/podcasts-and-videos.config [146]

-\u003Ch2\u003E\u003Ca rel=\u0022noopener\u0022 href=\u0022https://www.youtube.com/c/umbraCoffee/\u0022 target=\u0022_blank\u0022\u003EumbraCoffee\u003C/a\u003E\u003C/h2\u003E
+\u003Ch2\u003E\u003Ca rel=\u0022noopener noreferrer\u0022 href=\u0022https://www.youtube.com/c/umbraCoffee/\u0022 target=\u0022_blank\u0022\u003EumbraCoffee\u003C/a\u003E\u003C/h2\u003E

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly points out a security best practice by adding noreferrer to an external link, which enhances security, although the existing noopener already mitigates the main risk.

Low
  • Update

@patrickdemooij9 patrickdemooij9 merged commit 47926b7 into dev/main Dec 9, 2025
@patrickdemooij9 patrickdemooij9 deleted the f/uSync branch December 9, 2025 20:19
@patrickdemooij9 patrickdemooij9 mentioned this pull request Dec 9, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants