Last active
January 3, 2022 13:01
-
-
Save ronaldbarendse/b58bcfcdd68440ccdd316903cd5c8a03 to your computer and use it in GitHub Desktop.
Umbraco 8 - Clean-up and prune property data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Newtonsoft.Json.Linq; | |
using Umbraco.Core; | |
using Umbraco.Core.Composing; | |
using Umbraco.Core.Models; | |
using Umbraco.Core.Models.Editors; | |
using Umbraco.Core.PropertyEditors; | |
using Umbraco.Core.Scoping; | |
using Umbraco.Core.Services; | |
using Umbraco.Web; | |
using Umbraco.Web.PublishedCache; | |
using Umbraco.Web.Routing; | |
// Enable PublishCleanupComposer after setting up your baseline project | |
//[assembly: EnableComposer(typeof(PublishCleanupComposer))] | |
// Gather usage/storage data of your baseline and then ALSO enable PrunePropertyDataComposer | |
//[assembly: EnableComposer(typeof(PrunePropertyDataComposer))] | |
[Disable] | |
[ComposeBefore(typeof(RedirectTrackingComposer))] | |
public class PublishCleanupComposer : ComponentComposer<PublishCleanupComponent> | |
{ } | |
public class PublishCleanupComponent : IComponent | |
{ | |
private readonly IScopeProvider _scopeProvider; | |
private readonly IUmbracoContextFactory _umbracoContextFactory; | |
private readonly IContentService _contentService; | |
private readonly IMediaService _mediaService; | |
private readonly IPublishedSnapshotService _publishedSnapshotService; | |
public PublishCleanupComponent(IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IContentService contentService, IMediaService mediaService, IPublishedSnapshotService publishedSnapshotService) | |
{ | |
_scopeProvider = scopeProvider; | |
_umbracoContextFactory = umbracoContextFactory; | |
_contentService = contentService; | |
_mediaService = mediaService; | |
_publishedSnapshotService = publishedSnapshotService; | |
} | |
public void Initialize() | |
{ | |
using (var scope = _scopeProvider.CreateScope(autoComplete: true)) | |
using (var umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext()) | |
{ | |
// Re-publish all content | |
foreach (var rootContent in _contentService.GetRootContent()) | |
{ | |
if (rootContent.Published) | |
{ | |
_contentService.SaveAndPublishBranch(rootContent, false); | |
} | |
} | |
// Clean up | |
_contentService.EmptyRecycleBin(-1); | |
_mediaService.EmptyRecycleBin(-1); | |
for (int pageIndex = 0; ; pageIndex++) | |
{ | |
var contents = _contentService.GetPagedDescendants(-1, pageIndex, 1000, out _).ToList(); | |
if (contents.Count == 0) | |
{ | |
break; | |
} | |
foreach (var content in contents) | |
{ | |
_contentService.DeleteVersions(content.Id, DateTime.MaxValue); | |
} | |
} | |
_publishedSnapshotService.Rebuild(); | |
} | |
} | |
public void Terminate() | |
{ } | |
} | |
[Disable] | |
[ComposeBefore(typeof(PublishCleanupComposer))] | |
public class PrunePropertyDataComposer : ComponentComposer<PrunePropertyDataComponent> | |
{ } | |
public class PrunePropertyDataComponent : IComponent | |
{ | |
private readonly IUmbracoContextFactory _umbracoContextFactory; | |
private readonly IScopeProvider _scopeProvider; | |
private readonly ILocalizationService _localizationService; | |
private readonly IContentService _contentService; | |
private readonly IMediaService _mediaService; | |
private readonly PropertyEditorCollection _propertyEditors; | |
private readonly IDataTypeService _dataTypeService; | |
public PrunePropertyDataComponent(IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, ILocalizationService localizationService, IContentService contentService, IMediaService mediaService, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService) | |
{ | |
_scopeProvider = scopeProvider; | |
_umbracoContextFactory = umbracoContextFactory; | |
_localizationService = localizationService; | |
_contentService = contentService; | |
_mediaService = mediaService; | |
_propertyEditors = propertyEditors; | |
_dataTypeService = dataTypeService; | |
} | |
public void Initialize() | |
{ | |
using (var scope = _scopeProvider.CreateScope(autoComplete: true)) | |
using (var umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext()) | |
{ | |
// Prune content | |
var cultures = _localizationService.GetAllLanguages().Select(x => x.IsoCode).ToArray(); | |
for (int pageIndex = 0; ; pageIndex++) | |
{ | |
var contents = _contentService.GetPagedDescendants(-1, pageIndex, 1000, out _).ToList(); | |
if (contents.Count == 0) | |
{ | |
break; | |
} | |
contents.ForEach(x => Prune(x, cultures)); | |
// This only saves a new version: make sure to publish and cleanup old versions afterwards | |
_contentService.Save(contents); | |
} | |
// Prune media | |
for (int pageIndex = 0; ; pageIndex++) | |
{ | |
var medias = _mediaService.GetPagedDescendants(-1, pageIndex, 1000, out _).ToList(); | |
if (medias.Count == 0) | |
{ | |
break; | |
} | |
medias.ForEach(x => Prune(x, cultures)); | |
_mediaService.Save(medias); | |
} | |
} | |
} | |
private void Prune(IContentBase content, string[] cultures) | |
{ | |
foreach (var property in content.Properties) | |
{ | |
var propertyCultures = property.PropertyType.VariesByCulture() ? cultures : null; | |
Prune(content.Key, property, propertyCultures); | |
} | |
} | |
private void Prune(Guid contentKey, Property property, string[] cultures) | |
{ | |
if (!_propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out var editor)) | |
{ | |
// Editor not found, skip | |
} | |
var dataType = _dataTypeService.GetDataType(property.PropertyType.DataTypeId); | |
var valueEditor = editor.GetValueEditor(dataType.Configuration); | |
foreach (var culture in (cultures ?? new string[] { null })) | |
{ | |
var value = property.GetValue(culture); | |
var editorValue = valueEditor.ToEditor(property, _dataTypeService, culture); | |
// Handle custom transforms (done by model binding or back-office editors) | |
if (editorValue != null && valueEditor.ValueType == ValueTypes.Json) | |
{ | |
if (editorValue is string stringEditorValue) | |
{ | |
editorValue = string.IsNullOrWhiteSpace(stringEditorValue) ? null : JToken.Parse(stringEditorValue); | |
} | |
else | |
{ | |
editorValue = JToken.FromObject(editorValue); | |
} | |
} | |
else if (editorValue is string[] stringArrayEditorValue) | |
{ | |
editorValue = JArray.FromObject(stringArrayEditorValue); | |
} | |
var databaseValue = valueEditor.FromEditor(new ContentPropertyData(editorValue, dataType.Configuration) | |
{ | |
ContentKey = contentKey, | |
PropertyTypeKey = property.PropertyType.Key, | |
Files = new ContentPropertyFile[0] | |
}, value); | |
if (editor.GetTagAttribute() != null && | |
databaseValue is IEnumerable<string> tagsValue) | |
{ | |
// Handle tags property editors (this sets the value internally) | |
property.AssignTags(tagsValue, false, culture); | |
} | |
else | |
{ | |
property.SetValue(databaseValue, culture); | |
} | |
} | |
} | |
public void Terminate() | |
{ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment