-
-
Save gregwiechec/a63abc58b2419be26c6d3baabc1671e8 to your computer and use it in GitHub Desktop.
Update referenced content
This file contains 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.Collections.Generic; | |
using System.Linq; | |
using EPiServer; | |
using EPiServer.Cms.Shell.UI.Rest.Internal; | |
using EPiServer.Core; | |
using EPiServer.Core.Html.StringParsing; | |
using EPiServer.Data.Entity; | |
using EPiServer.DataAccess; | |
using EPiServer.Security; | |
using EPiServer.Shell.Services.Rest; | |
using EPiServer.SpecializedProperties; | |
using EPiServer.Web; | |
using Microsoft.AspNetCore.Mvc; | |
namespace AlloyTemplates.Business; | |
[RestStore("content-reference-update")] | |
public class ContentReferenceUpdateStore : RestControllerBase | |
{ | |
private readonly ReferencedContentResolver _referencedContentResolver; | |
private readonly IContentRepository _contentRepository; | |
public ContentReferenceUpdateStore( | |
ReferencedContentResolver referencedContentResolver, | |
IContentRepository contentRepository) | |
{ | |
_referencedContentResolver = referencedContentResolver; | |
_contentRepository = contentRepository; | |
} | |
[HttpPost] | |
public ActionResult Post([FromBody] UpdateReferenceModel model) | |
{ | |
if (model == null) | |
{ | |
return BadRequest(); | |
} | |
var references = _referencedContentResolver.GetReferenceList(new ContentReference(model.SourceReference)); | |
if (!string.IsNullOrWhiteSpace(model.SourceContentLinkToReplace)) | |
{ | |
var sourceContentReference = new ContentReference(model.SourceContentLinkToReplace); | |
references = references.Where(x => x.ContentLink == sourceContentReference).ToList(); | |
} | |
foreach (var reference in references) | |
{ | |
var updated = false; | |
var content = _contentRepository.Get<IContent>(reference.ContentLink); | |
if (content is IReadOnly) | |
{ | |
content = (content as IReadOnly).CreateWritableClone() as IContent; | |
} | |
for (var index = 0; index < content.Property.Count; index++) | |
{ | |
var propertyData = content.Property[index]; | |
if (TryUpdateProperty(propertyData, new ContentReference(model.SourceReference), | |
new ContentReference(model.TargetReference))) | |
{ | |
updated = true; | |
} | |
} | |
if (updated) | |
{ | |
_contentRepository.Save(content, SaveAction.ForceCurrentVersion, AccessLevel.NoAccess); | |
} | |
} | |
return Ok(); | |
} | |
private bool TryUpdateProperty(PropertyData propertyData, ContentReference fromReference, ContentReference toReference) | |
{ | |
if (propertyData.Value == null) | |
{ | |
return false; | |
} | |
// | |
// ContentArea | |
// | |
if (propertyData.Value is ContentArea) | |
{ | |
var contentArea = propertyData.Value as ContentArea; | |
if (contentArea.Items == null) | |
{ | |
return false; | |
} | |
if (contentArea.Items.Any(x => x.ContentLink == fromReference.ToReferenceWithoutVersion())) | |
{ | |
for (var index = 0; index < contentArea.Items.Count; index++) | |
{ | |
var contentAreaItem = contentArea.Items[index]; | |
if (contentAreaItem.ContentLink == fromReference) | |
{ | |
contentAreaItem.ContentLink = toReference; | |
} | |
} | |
return true; | |
} | |
} | |
// | |
// ContentReference | |
// | |
if (propertyData.Value is ContentReference) | |
{ | |
if (((ContentReference)propertyData.Value) == fromReference) | |
{ | |
propertyData.Value = toReference; | |
return true; | |
} | |
} | |
// | |
// ContentReference list | |
// | |
if (propertyData.Value is IEnumerable<ContentReference>) | |
{ | |
var references = (propertyData.Value as IEnumerable<ContentReference>).ToList(); | |
if (references.Any(x => x == fromReference)) | |
{ | |
propertyData.Value = references.Select(x => (x == fromReference) ? toReference : x).ToList(); | |
return true; | |
} | |
} | |
if (propertyData.Value is LinkItemCollection || | |
propertyData.Value is XhtmlString || | |
propertyData.Value is LinkItem) | |
{ | |
var hasLink = false; | |
var content = _contentRepository.Get<IContent>(fromReference); | |
var currentVirtualPath = | |
PermanentLinkUtility.GetPermanentLinkVirtualPath(content.ContentGuid, ".aspx"); | |
var newContent = _contentRepository.Get<IContent>(toReference); | |
var newVirtualPath = PermanentLinkUtility.GetPermanentLinkVirtualPath(newContent.ContentGuid, ".aspx"); | |
// | |
// LinkItemCollection | |
// | |
if (propertyData.Value is LinkItemCollection) | |
{ | |
var updatedList = new List<LinkItem>(); | |
var linkItems = (LinkItemCollection)propertyData.Value; | |
for (var index = 0; index < linkItems.Count; index++) | |
{ | |
var linkItem = linkItems[index]; | |
if (linkItem.Href == currentVirtualPath) | |
{ | |
linkItem.Href = newVirtualPath; | |
linkItem.Text = newContent.Name; | |
hasLink = true; | |
} | |
updatedList.Add(linkItem); | |
} | |
if (hasLink) | |
{ | |
propertyData.Value = new LinkItemCollection(updatedList); | |
return true; | |
} | |
return false; | |
} | |
// | |
// LinkItem | |
// | |
if (propertyData.Value is LinkItem) | |
{ | |
var linkItem = (LinkItem)propertyData.Value; | |
if (linkItem.Href == currentVirtualPath) | |
{ | |
var linkItemCopy = new LinkItem | |
{ | |
Href = newVirtualPath, | |
Text = newContent.Name, | |
Target = linkItem.Target, | |
Title = linkItem.Title | |
}; | |
linkItemCopy.Attributes.Clear(); | |
foreach (var attr in linkItem.Attributes) | |
{ | |
linkItemCopy.Attributes.Add(attr.Key, attr.Value); | |
} | |
linkItemCopy.Attributes["href"] = newVirtualPath; | |
propertyData.Value = linkItemCopy; | |
return true; | |
} | |
return false; | |
} | |
// | |
// XhtmlString | |
// | |
if (propertyData.Value is XhtmlString) | |
{ | |
var xhtml = (XhtmlString)propertyData.Value; | |
var str = xhtml.ToHtmlString(); | |
foreach (var stringFragment in xhtml.Fragments) | |
{ | |
if (stringFragment is UrlFragment) | |
{ | |
var urlFragment = (UrlFragment)stringFragment; | |
if (urlFragment.Url == currentVirtualPath) | |
{ | |
str = str.Replace(urlFragment.Url.Replace("~", ""), newVirtualPath.Replace("~", "")); | |
hasLink = true; | |
} | |
} | |
} | |
if (hasLink) | |
{ | |
propertyData.Value = new XhtmlString(str); | |
return true; | |
} | |
return false; | |
} | |
} | |
return false; | |
} | |
} | |
public class UpdateReferenceModel | |
{ | |
public string SourceReference { get; set; } | |
public string SourceContentLinkToReplace { get; set; } | |
public string TargetReference { get; set; } | |
} |
This file contains 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
define([// Dojo | |
"dojo", | |
"dojo/_base/declare", | |
"dojo/on", | |
"dijit/registry", | |
//CMS | |
"epi/_Module", | |
"epi/dependency", | |
"epi/routes", | |
"epi/shell/store/JsonRest", | |
"epi/shell/widget/dialog/Dialog", | |
"epi-cms/widget/ContentReferences", | |
"epi-cms/widget/ContentSelector", | |
"epi/i18n!epi/cms/nls/episerver.shared.action" | |
], | |
function ( | |
// Dojo | |
dojo, | |
declare, | |
on, | |
dijitRegistry, | |
//CMS | |
_Module, | |
dependency, | |
routes, | |
JsonRest, | |
Dialog, | |
ContentReferences, | |
ContentSelector, | |
resources | |
) { | |
// last selected link cleared when opening references dialog | |
var lastSelectedLink = null; | |
// create dialog that allows to replace links | |
function createDialog(container) { | |
var contentSelector = new ContentSelector({ | |
roots: [1] | |
}); | |
var dialog = new Dialog({ | |
dialogClass: "epi-dialog--wide", | |
defaultActionsVisible: true, | |
confirmActionText: "Change", | |
content: contentSelector, | |
title: "Replace content with another content", | |
focusActionsOnLoad: true | |
}); | |
container.own(dialog); | |
dialog.own(contentSelector); | |
on.once(dialog, "show", function (value) { | |
// set epiStringList class to have width 100% for property | |
contentSelector.displayNode.classList.add("epiStringList"); | |
if (lastSelectedLink) { | |
contentSelector.set("value", lastSelectedLink); | |
} | |
}); | |
var resolved = false; | |
return new Promise((resolve) => { | |
on.once(dialog, "execute", function () { | |
resolved = true; | |
resolve(contentSelector.get("value")); | |
}); | |
on.once(dialog, "hide", function () { | |
if (!resolved) { | |
resolve(""); | |
} | |
}); | |
dialog.show(); | |
}); | |
} | |
function replaceReferences(selectedContentLink, container, sourceContentLink) { | |
createDialog(container).then((contentLink) => { | |
if (!contentLink) { | |
return; | |
} | |
lastSelectedLink = contentLink; | |
var updateParameters = { | |
sourceReference: sourceContentLink, | |
targetReference: contentLink | |
}; | |
// when empty then run Replace All | |
if (selectedContentLink) { | |
updateParameters.sourceContentLinkToReplace = selectedContentLink; | |
} | |
var registry = dependency.resolve("epi.storeregistry"); | |
var store = registry.get("app.updatereferences"); | |
store.add(updateParameters).then(function () { | |
container.fetchData(); | |
}); | |
}); | |
} | |
function patchStartup() { | |
var originalStartup = ContentReferences.prototype.startup; | |
ContentReferences.prototype.startup = function () { | |
lastSelectedLink = null; | |
var result = originalStartup.apply(this, arguments); | |
var self = this; | |
this.grid.on(".dgrid-column-uri a.link-replace:click", function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
var selectedContentLink = Object.keys(self.grid.selection)[0]; | |
var sourceContentLink = self.model.contentItems[0].contentLink; | |
replaceReferences(selectedContentLink, self, sourceContentLink); | |
}); | |
this.grid.columns.uri.className = "epi-width20" | |
return result; | |
} | |
ContentReferences.prototype.startup.nom = "startup"; | |
} | |
// do not change context when click on Replace link | |
function patchChangeContext() { | |
var originalChangeContext = ContentReferences.prototype._onChangeContext; | |
ContentReferences.prototype._onChangeContext = function (e) { | |
if (e.target.classList.contains("link-replace")) { | |
return; | |
} | |
return originalChangeContext.apply(this, arguments); | |
} | |
ContentReferences.prototype._onChangeContext.nom = "_onChangeContext"; | |
} | |
// add Replace link to grid | |
function patchGetLinkTemplate() { | |
var orignalGetTemplateLink = ContentReferences.prototype._getLinkTemplate; | |
ContentReferences.prototype._getLinkTemplate = function () { | |
var result = orignalGetTemplateLink.apply(this, arguments); | |
var replaceLink = document.createElement("a"); | |
replaceLink.classList.add("epi-visibleLink"); | |
replaceLink.classList.add("link-replace"); | |
replaceLink.innerHTML = resources.replace; | |
replaceLink.title = "Replace usage with another link"; | |
replaceLink.style.marginLeft = "8px"; | |
return "<div>" + result + replaceLink.outerHTML + "</div>"; | |
} | |
ContentReferences.prototype._getLinkTemplate.nom = "_getLinkTemplate"; | |
} | |
// add additional dialog button that allows to replace all references | |
function patchDialog() { | |
var origianlGetActions = Dialog.prototype.getActions; | |
Dialog.prototype.getActions = function () { | |
var self = this; | |
var result = origianlGetActions.apply(this, arguments); | |
if (this.dialogClass === "epi-dialog-contentReferences") { | |
var replaceButton = { | |
name: "Replace All", | |
label: "Replace All", | |
title: null, | |
action: function() { | |
// find grid widget | |
var contentReferencesWidget = dijitRegistry.getEnclosingWidget(self.containerNode).getChildren()[0]; | |
var selectedContentLink = contentReferencesWidget.contentItems[0].contentLink; | |
replaceReferences(null, contentReferencesWidget, selectedContentLink); | |
} | |
} | |
return [result[0], replaceButton, result[1]]; | |
} | |
return result; | |
} | |
Dialog.prototype.getActions.nom = "getActions"; | |
} | |
// initialize store for updating references | |
function initializeStore() { | |
var registry = dependency.resolve("epi.storeregistry"); | |
registry.add("app.updatereferences", | |
new JsonRest({ | |
target: routes.getRestPath({ moduleArea: "app", storeName: "content-reference-update" }) | |
}) | |
); | |
} | |
return declare([_Module], { | |
// summary: Module initializer for the default module. | |
initialize: function () { | |
this.inherited(arguments); | |
initializeStore(); | |
patchStartup(); | |
patchChangeContext(); | |
patchGetLinkTemplate(); | |
patchDialog(); | |
} | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment