HCT.Sitefinity.FindAndReplace
1.3.0
dotnet add package HCT.Sitefinity.FindAndReplace --version 1.3.0
NuGet\Install-Package HCT.Sitefinity.FindAndReplace -Version 1.3.0
<PackageReference Include="HCT.Sitefinity.FindAndReplace" Version="1.3.0" />
<PackageVersion Include="HCT.Sitefinity.FindAndReplace" Version="1.3.0" />
<PackageReference Include="HCT.Sitefinity.FindAndReplace" />
paket add HCT.Sitefinity.FindAndReplace --version 1.3.0
#r "nuget: HCT.Sitefinity.FindAndReplace, 1.3.0"
#:package HCT.Sitefinity.FindAndReplace@1.3.0
#addin nuget:?package=HCT.Sitefinity.FindAndReplace&version=1.3.0
#tool nuget:?package=HCT.Sitefinity.FindAndReplace&version=1.3.0
Sitefinity Find and Replace
A comprehensive find and replace solution for Progress Sitefinity CMS, providing bulk text replacement across multiple content types through RESTful OData endpoints.
Overview
This open-source project extends Progress Sitefinity CMS with powerful find-and-replace capabilities for content editors and administrators. The solution is built as a reusable .NET Framework class library that can be easily integrated into any Sitefinity project or distributed as a NuGet package.
Features
- Multi-Content Type Support: News Items, Blog Posts, Events, and all Dynamic Content modules
- RESTful OData Integration: Leverages Sitefinity's built-in OData service
- Lifecycle Management: Full support for Sitefinity's content lifecycle (CheckOut ? Replace ? CheckIn ? Publish)
- Preview Mode: Test replacements before applying changes
- Bulk Operations: Process multiple items and fields in a single request
- Field Selection: Target specific fields or multiple fields simultaneously
- Case-Insensitive Search: Finds matches regardless of case
- Error Handling: Collects and reports errors while continuing to process remaining items
- Security: Enforces administrator-level permissions
- Reusable Library: Package once, use in multiple Sitefinity projects
Compatibility
| Component | Version |
|---|---|
| Progress Sitefinity CMS | 15.3.8500+ |
| Minimum Sitefinity Version | 12.0+ |
| .NET Framework | 4.8 |
| C# Language | 7.3+ |
| OData | Built-in Sitefinity OData v2 |
Project Structure
This solution consists of two main components:
Sitefinity.FindAndReplace/ # Reusable class library
??? Interfaces/ # Provider contracts
??? Models/ # Request/Response DTOs
??? Providers/ # Content type implementations
??? FindAndReplaceProviderRegistration.cs
SitefinityWebApp/ # Example Sitefinity web application
??? Global.asax.cs # Integration point
Installation
Option 1: NuGet Package (Recommended for Production)
Install-Package Sitefinity.FindAndReplace
Option 2: Project Reference (Development)
- Clone or download this repository
- Add the
Sitefinity.FindAndReplaceproject to your solution - Add a project reference from your Sitefinity web application:
<ItemGroup>
<ProjectReference Include="..\Sitefinity.FindAndReplace\Sitefinity.FindAndReplace.csproj">
<Name>Sitefinity.FindAndReplace</Name>
</ProjectReference>
</ItemGroup>
Option 3: Binary Reference
- Download the latest release DLL
- Copy
Sitefinity.FindAndReplace.dllto your Sitefinity project'sbinfolder - Add a reference to the DLL in your project
Quick Start
1. Register Providers
In your Sitefinity web application's Global.asax.cs:
using Sitefinity.FindAndReplace;
using System;
namespace YourSitefinityProject
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
// Register all Find and Replace providers
FindAndReplaceProviderRegistration.RegisterAll();
}
}
}
2. Build and Restart
- Build your solution
- Restart the Sitefinity application
- The OData endpoints are now available!
3. Test the Endpoints
Use Postman, Fiddler, or any HTTP client:
POST /api/default/newsitems/Default.FindAndReplace()
Content-Type: application/json
Cookie: .ASPXAUTH=your_auth_cookie
{
"Fields": ["Title", "Content"],
"Find": "2024",
"Replace": "2025",
"Provider": "OpenAccessDataProvider",
"IsPreview": true
}
Available Endpoints
After registration, these OData endpoints are automatically created:
| Content Type | Endpoint |
|---|---|
| News Items | /api/default/newsitems/Default.FindAndReplace() |
| Blog Posts | /api/default/blogposts/Default.FindAndReplace() |
| Events | /api/default/events/Default.FindAndReplace() |
| Dynamic Content | /api/default/[modulename]/Default.FindAndReplace() |
Architecture
Provider Pattern
The solution uses a provider-based architecture where each content type has a dedicated provider:
IFindAndReplaceOperationProvider (Interface)
??? FindAndReplaceProviderBase (Abstract Base Class)
??? NewsItemFindAndReplaceProvider
??? BlogPostFindAndReplaceProvider
??? EventFindAndReplaceProvider
??? DynamicContentFindAndReplaceProvider
Each provider:
- Registers for a specific CLR type
- Creates an OData endpoint automatically
- Implements type-specific field access and lifecycle management
- Inherits common functionality from the base class
Class Library Contents
Sitefinity.FindAndReplace/
??? Interfaces/
? ??? IFindAndReplaceOperationProvider.cs
??? Models/
? ??? FindAndReplaceRequest.cs
? ??? FindAndReplaceResult.cs
??? Providers/
? ??? FindAndReplaceProviderBase.cs
? ??? NewsItemFindAndReplaceProvider.cs
? ??? BlogPostFindAndReplaceProvider.cs
? ??? EventFindAndReplaceProvider.cs
? ??? DynamicContentFindAndReplaceProvider.cs
??? FindAndReplaceProviderRegistration.cs
Usage
API Request Format
Request:
{
"Fields": ["Title", "Content", "Summary"],
"Find": "old text",
"Replace": "new text",
"Provider": "OpenAccessDataProvider",
"IsPreview": false
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
Fields |
string[] | Yes | Array of field names to search/replace in |
Find |
string | Yes | Text to search for (case-insensitive) |
Replace |
string | Yes | Text to replace with (can be empty) |
Provider |
string | Yes | Data provider name (usually "OpenAccessDataProvider") |
IsPreview |
boolean | Yes | true = count only, false = perform replacement |
ContentTypeFullName |
string | No* | Required only for Dynamic Content |
*ContentTypeFullName is only required when using the DynamicContentFindAndReplaceProvider
Response:
{
"itemsAffected": 12,
"isPreview": false,
"errors": []
}
Response Fields:
| Field | Type | Description |
|---|---|---|
itemsAffected |
integer | Number of items found (preview) or updated (replace) |
isPreview |
boolean | Echoes the request's IsPreview value |
errors |
string[] | Array of error messages (empty if no errors) |
Examples
Example 1: Preview News Item Replacements
curl -X POST "https://yoursite.com/api/default/newsitems/Default.FindAndReplace()" \
-H "Content-Type: application/json" \
-H "Cookie: .ASPXAUTH=your_auth_cookie" \
-d '{
"Fields": ["Title", "Content"],
"Find": "2024",
"Replace": "2025",
"Provider": "OpenAccessDataProvider",
"IsPreview": true
}'
Response:
{
"itemsAffected": 12,
"isPreview": true,
"errors": []
}
Example 2: Replace Text in Blog Posts
POST /api/default/blogposts/Default.FindAndReplace()
{
"Fields": ["Title", "Content", "Summary"],
"Find": "old product name",
"Replace": "new product name",
"Provider": "OpenAccessDataProvider",
"IsPreview": false
}
Response:
{
"itemsAffected": 8,
"isPreview": false,
"errors": []
}
Example 3: Update Dynamic Content
POST /api/default/pressreleases/Default.FindAndReplace()
{
"Fields": ["Title", "Description", "CompanyName"],
"Find": "Acme Corp",
"Replace": "Global Industries",
"Provider": "OpenAccessDataProvider",
"ContentTypeFullName": "Telerik.Sitefinity.DynamicTypes.Model.PressReleases.PressRelease",
"IsPreview": false
}
Note: For Dynamic Content, you must include the ContentTypeFullName. Find it in:
- Sitefinity Backend ? Administration ? Module Builder ? [Your Module] ? Content Type ? Advanced
- Or inspect the OData metadata:
/api/default/[modulename]/$metadata
Supported Fields by Content Type
News Items
- Title, Content, Summary, Description
- Author, UrlName, ItemDefaultUrl
Blog Posts
- Title, Content, Summary, Description
- UrlName
Events
- Title, Content, Summary, Description
- Location, ContactName, ContactEmail, ContactPhone
Dynamic Content
- Any string or Lstring field defined in the module
How It Works
Operation Modes
1. Preview Mode (IsPreview: true)
- Queries all live (published) items
- Checks each item for matching text
- Returns count of items that would be affected
- Makes no changes to content
- Use this to verify scope before replacing
2. Replace Mode (IsPreview: false)
- Queries all live items
- For each matching item:
- Get Master - Retrieve the master version
- Check Out - Create temporary editable version
- Replace - Modify text in specified fields
- Check In - Save temporary version as new master
- Publish - Make changes live
- Notify Workflows - Trigger any configured workflows
- Returns count of successfully updated items
- Collects errors for failed items
Text Replacement
- Method: Regex-based replacement with case-insensitive matching
- Pattern: Escapes special regex characters in find text
- Matching: Finds "test", "TEST", "Test", "TeSt", etc.
- Replacement: Replaces all matches with the exact replacement text
Example:
Find: "test"
Original: "This is a TEST example with Test and test"
Replace: "demo"
Result: "This is a demo example with demo and demo"
Security
Required Permissions
- Administrator Access: Must have unrestricted access to Sitefinity
- Check:
ClaimsManager.IsUnrestricted() == true - Error: Returns
Unauthorized access: User does not have required permissions.
Authentication
Use one of these methods:
Cookie Authentication (Browser/Postman):
- Log in to Sitefinity backend
- Copy
.ASPXAUTHcookie - Include in API requests
Token Authentication (Programmatic):
- Obtain bearer token from Sitefinity
- Include in
Authorizationheader
Testing Strategy
Recommended Testing Flow
Start with Preview
{ "Fields": ["Title"], "Find": "test", "Replace": "new", "Provider": "OpenAccessDataProvider", "IsPreview": true }Verify Count - Check
itemsAffectedmatches expectationsTest on Subset - Create test content and verify results
Execute Full Replace
{ "Fields": ["Title", "Content"], "Find": "test", "Replace": "new", "Provider": "OpenAccessDataProvider", "IsPreview": false }Verify Results - Check count, errors, and content in Sitefinity
Performance Considerations
| Operation | Approximate Time |
|---|---|
| Direct property access | ~1-5 microseconds per field |
| Reflection access | ~100-200 microseconds per field |
| Lifecycle operations | ~50-100ms per item |
| Average item update | ~100-200ms per item |
Recommendations:
- Always test with preview mode first
- Process large datasets during off-peak hours
- Monitor memory usage for 1000+ items
- Consider batching for 10,000+ items
Limitations
- Live Items Only: Only processes published (live) content
- Administrator Required: Must have unrestricted access
- Query Limit: Maximum 1,000,000 items per request (Sitefinity OData limit)
- Case-Insensitive Only: Cannot preserve original casing during replacement
- Text Fields Only: Supports Lstring and string field types only
- Single-Threaded: Processes items sequentially (not parallelized)
Extensibility
Adding Support for New Content Types
- Create a Provider Class:
using Sitefinity.FindAndReplace.Providers;
using Telerik.Sitefinity.Libraries.Model;
namespace YourNamespace
{
public class ImageFindAndReplaceProvider : FindAndReplaceProviderBase
{
public override IEnumerable<OperationData> GetOperations(Type clrType)
{
if (clrType == typeof(Image))
{
yield return new OperationData
{
Name = "Default.FindAndReplace",
// ... implementation
};
}
}
protected override List<object> GetItemsInternal(...)
{
// Query logic for Images
}
protected override bool ReplaceAndSaveItem(...)
{
// Replace logic for Images
}
}
}
- Register Your Provider:
ObjectFactory.Container.RegisterType(
typeof(IOperationProvider),
typeof(ImageFindAndReplaceProvider),
"ImageFindAndReplace",
new TransientLifetimeManager()
);
- Restart - The new endpoint is automatically available at:
POST /api/default/images/Default.FindAndReplace()
Error Handling
Common Errors
| Error | Cause | Solution |
|---|---|---|
Unauthorized access |
Not administrator | Log in as administrator |
Request cannot be null |
Empty request body | Provide valid JSON body |
At least one field must be specified |
Empty Fields array | Add at least one field name |
Find text is required |
Empty Find parameter | Provide search text |
Provider is required |
Empty Provider parameter | Set to "OpenAccessDataProvider" |
Property not found |
Invalid field name | Check field name spelling/case |
Cannot check out locked item |
Item locked by another user | Unlock item or wait |
Error Collection Strategy
The implementation uses a continue-on-error strategy:
- Errors are collected in a list
- Processing continues for remaining items
- Final response includes all errors
- Allows partial success scenarios
Example:
{
"itemsAffected": 45,
"isPreview": false,
"errors": [
"Error processing NewsItem (ID: abc123): Item is locked",
"Error processing NewsItem (ID: def456): Invalid field 'CustomField'"
]
}
45 items were successfully updated, 2 failed with reported errors
Use Cases
1. Annual Content Updates
Replace year references across all content
2. Rebranding
Update company name across blog posts
3. URL Updates
Fix internal links or references
4. Contact Information Updates
Update contact details in events
5. Compliance & Legal
Remove or replace specific terms
Troubleshooting
Endpoint Not Found (404)
Solutions:
- Verify
FindAndReplaceProviderRegistration.RegisterAll()is called inApplication_Start - Restart the application
- Check Sitefinity OData is enabled: Administration ? Settings ? Advanced ? WebServices ? OData
- Verify URL format:
/api/default/[plural]/Default.FindAndReplace()
Unauthorized (401)
Solutions:
- Log in as administrator
- Verify user has unrestricted permissions
- Check authentication cookie/token is included
No Items Affected
Solutions:
- Verify find text exactly matches content
- Check field names are correct (case-sensitive)
- Ensure items are published (live)
- Verify correct provider name
Best Practices
Before Running Replace Operations
- ? Always preview first - Use
IsPreview: trueto verify scope - ? Backup your database - Create a backup before bulk operations
- ? Test on staging - Verify behavior in non-production environment
- ? Start small - Test with one field before multiple fields
After Replace Operations
- ? Verify content - Spot-check items in Sitefinity backend
- ? Check frontend - Ensure pages display correctly
- ? Test workflows - Verify any triggered workflows complete
Dependencies
Core Sitefinity Packages
<package id="Progress.Sitefinity.Core" version="15.3.8500" />
<package id="Telerik.Sitefinity.Content" version="15.3.8500" />
<package id="Telerik.Sitefinity.Mvc" version="15.3.8500" />
<package id="Newtonsoft.Json" version="13.0.1" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" />
See the class library's packages.config for the complete list.
Contributing
Contributions are welcome! This is an open-source project.
How to Contribute
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Contribution Ideas
- Add support for additional content types (Images, Documents, Videos)
- Implement pagination for large datasets
- Add regex pattern support for advanced find/replace
- Create PowerShell/CLI wrapper for automation
- Add unit tests and integration tests
- Improve performance with parallel processing
- Add audit logging for compliance
Documentation
- Class Library Documentation: See
Sitefinity.FindAndReplace/README.md - Migration Guide: See
Sitefinity.FindAndReplace/MIGRATION_GUIDE.md - Sitefinity Resources: Sitefinity Documentation
License
This project is open source and available for use in your Sitefinity projects.
Support
Community Support
- Issues: Report bugs or request features via GitHub Issues
- Discussions: Ask questions and share ideas via GitHub Discussions
- Pull Requests: Contribute improvements and fixes
Sitefinity Resources
Acknowledgments
- Built for Progress Sitefinity CMS
- Uses Telerik Data Access ORM
- Leverages Unity IoC Container
- Follows OData v2 Protocol
Version History
- v1.0 - Initial release as reusable class library with News, Blog, Event, and Dynamic Content support
FAQ
Q: Can I use this on Sitefinity Cloud?
A: Yes, as long as you have deployment access and administrator permissions.
Q: Does this work with multilingual content?
A: Yes, Lstring fields support multiple languages and all will be searched/replaced.
Q: Will this trigger workflows?
A: Yes, the MessageWorkflow() method notifies configured workflows after publishing.
Q: Can I undo replacements?
A: No built-in undo. Always preview first and maintain database backups.
Q: Does this work with scheduled/draft content?
A: No, only published (live) content is processed.
Q: Can I use this library in multiple Sitefinity projects?
A: Yes! That's the primary benefit of the class library approach. Reference it from multiple projects or install via NuGet.
Built with ?? for the Sitefinity Community
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net48 is compatible. net481 was computed. |
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|
Initial replace