Skip to content

Instantly share code, notes, and snippets.

@gregwiechec
Created June 17, 2024 20:57
Show Gist options
  • Save gregwiechec/3206a08b8ff15fd92c03e9383cb6039b to your computer and use it in GitHub Desktop.
Save gregwiechec/3206a08b8ff15fd92c03e9383cb6039b to your computer and use it in GitHub Desktop.
Updating language versions with property copy and publish
define([
// Dojo
"dojo", "dojo/_base/declare", "dojo/on", "dojo/when", "dijit/registry",
// dijit
"dijit/layout/_LayoutWidget", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "dijit/form/CheckBox",
//epi
"epi/_Module", "epi/dependency",
// epi shell
"epi/shell/form/Field", "epi/shell/form/formFieldRegistry", "epi/shell/widget/dialog/Dialog", "epi/shell/DialogService",
"epi/shell/MetadataTransformer",
// epi cms
"epi-cms/_ContentContextMixin", "epi-cms/contentediting/ContentViewModel", "epi-cms/contentediting/ContentActionSupport"
], function (
// Dojo
dojo, declare, on, when, dijitRegistry,
// dijit
_LayoutWidget, _TemplatedMixin, _WidgetsInTemplateMixin, CheckBox,
// epi
_Module, dependency,
// epi shell
Field, formFieldRegistry, Dialog, dialogService, MetadataTransformer,
// epi cms
_ContentContextMixin, ContentViewModel, ContentActionSupport
) {
var factory = formFieldRegistry.get(formFieldRegistry.type.field, "");
// save property values
function updateProperties(propertyName, selectedLanguages, commonDrafts) {
return new Promise((resolve) => {
var countUpdatedContent = 0;
commonDrafts.forEach((commonDraft) => {
var language = selectedLanguages.find(x => x.id === commonDraft.language);
var model = new ContentViewModel({
contentLink: commonDraft.contentLink,
enableAutoSave: false,
isInQuickEditMode: true
});
model.onContentLinkChange = function () { };
model.reload(true).then(function () {
model.setProperty(propertyName, language.value);
model.save().then(function () {
if (language.publish) {
model.changeContentStatus(ContentActionSupport.saveAction.Publish).then(() => {
countUpdatedContent++;
if (commonDrafts.length === countUpdatedContent) {
resolve();
}
});
} else {
countUpdatedContent++;
if (commonDrafts.length === countUpdatedContent) {
resolve();
}
}
});
});
});
});
}
// widget that show single language selection
var LanguageSelectionWidget = declare([_LayoutWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: `<div class="epi-form-container__section__row" style="padding-bottom: 16px;">
<label style="display: inline-block;"><span data-dojo-attach-point="checkboxNode"></span><span data-dojo-attach-point="labelNode" style="text-transform: capitalize;"></span></label>
<label style="display: inline-block; margin-left: 16px;"><span data-dojo-attach-point="publishCheckboxNode"></span><span>Publish on save</span></label>
<div class="dijitVisible" style="padding-top: 8px;">
<div data-dojo-attach-point="widgetNode"></div>
</div>
<a href="#" data-dojo-attach-point="copyValue" class="epi-visibleLink">Copy value</a>
</div>`,
buildRendering: function () {
this.inherited(arguments);
var self = this;
var checkbox = new CheckBox({
name: "checkBox-selection-" + this.language,
value: this.language,
checked: false,
onChange: (checked) => {
self._selected = checked;
self.onSelectionChanged();
}
}, this.checkboxNode);
this.own(checkbox);
var publishCheckbox = new CheckBox({
name: "checkBox-publish-" + this.language,
value: this.language,
checked: false,
onChange: (checked) => self._publish = checked
}, this.publishCheckboxNode);
this.own(publishCheckbox);
this.own(on(this.copyValue, "click", function () {
self._widget.set("value", self.propertyValue);
}));
//TODO: add editor to HTML
loadPropertyEditor(this.commonDraft, this.propertyName, this.widgetNode).then((widget) => {
self._widget = widget;
self.own(widget);
widget.startup();
});
},
_setLabelAttr: { node: "labelNode", type: "innerText" },
getSelection: function () {
return ({
id: this.language,
selected: this._selected || false,
publish: this._publish || false,
value: this._widget.get("value")
});
},
onSelectionChanged: function() {}
});
// load property editor from metadata
var metadataManager;
var metadataTransformer;
var editorFactory;
function loadPropertyEditor(commonDraft, propertyName, targetNode) {
return new Promise((resolve) => {
metadataManager = metadataManager || dependency.resolve("epi.shell.MetadataManager");
metadataManager.getMetadataForType("EPiServer.Core.ContentData", { contentLink: commonDraft.contentLink })
.then((metadata) => {
function propertyFilter(ownerMetadata, propertyMetadata) {
return propertyMetadata.name.toLowerCase() === propertyName.toLowerCase();
}
metadataTransformer = new MetadataTransformer({ propertyFilter: propertyFilter });
var useDefaultValue = true;
var componentDefinitions = metadataTransformer.toComponentDefinitions(metadata, "", useDefaultValue, false);
var editorDefinition = componentDefinitions[0].components[0];
var metadataProperty = metadata.properties.find(x => x.name.toLowerCase() === propertyName.toLowerCase());
editorFactory = editorFactory || dependency.resolve("epi.cms.contentediting.EditorFactory");
editorDefinition.wrapperType = editorFactory.defaultWrapperType;
editorDefinition.editorParams = editorDefinition.settings;
editorDefinition.metadata = metadataProperty;
// we need only editor properties
var editor = editorFactory._getWrapperAndEditor(editorDefinition);
require([metadataProperty.uiType], (editorWidgetClass) => {
try {
var editorWidget = new editorWidgetClass(editor.editorParams, targetNode);
resolve(editorWidget);
} catch (exc) {
debugger;
}
});
});
});
}
// widget used as dialog content that displays list of selections
var DialogContentWidget = declare([_LayoutWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: "<div></div>",
metadataManager: null,
_selectionWidgets: null,
addSelection: function (commonDraft, parentWidget) {
var selectionContainer = document.createElement("div");
this.domNode.appendChild(selectionContainer);
if (!this._selectionWidgets) {
this._selectionWidgets = [];
}
var self = this;
var selectionWidget = new LanguageSelectionWidget({
label: this.languages[commonDraft.language] || commonDraft.language,
language: commonDraft.language,
commonDraft: commonDraft,
propertyName: parentWidget.name,
propertyValue: parentWidget.get("value")
}, selectionContainer);
this.own(on(selectionWidget, "selectionChanged", () => self.onSelectionChanged()));
this._selectionWidgets.push(selectionWidget);
this.own(selectionWidget);
selectionWidget.startup();
},
getSelectedItems: function () {
return this._selectionWidgets.map(x => x.getSelection());
},
onSelectionChanged: function() {}
});
function selectVersions(widget, commonDrafts, languages) {
var customWidget = new DialogContentWidget({
languages: languages
});
return new Promise((resolve, reject) => {
var resolved = false;
var dialog = new Dialog({
title: "Copy to language branches",
dialogClass: "epi-dialog-landscape",
content: customWidget,
confirmActionText: "Save",
onExecute: function () {
resolve(customWidget.getSelectedItems().filter(x => x.selected));
},
onHide: function () {
if (!resolved) {
reject();
}
}
});
widget.own(dialog);
dialog.own(customWidget);
dialog.own(on(customWidget, "selectionChanged", () => {
dialog.definitionConsumer.setItemProperty(dialog._okButtonName, "disabled",
customWidget.getSelectedItems().filter(x => x.selected).length === 0);
}));
dialog.startup();
dialog.show();
dialog.definitionConsumer.setItemProperty(dialog._okButtonName, "disabled", true);
setTimeout(() => {
commonDrafts.forEach((commonDraft) => {
customWidget.addSelection(commonDraft, widget);
});
}, 100);
});
}
function onCopyPropertyValueClick(widget, parent, getCurrentContent) {
when(getCurrentContent(), function (context) {
// load content versions and select distinct languages
var contentVersionStore = dependency.resolve("epi.storeregistry").get("epi.cms.contentversion");
var languagesStore = dependency.resolve("epi.storeregistry").get("epi.cms.languagesettings");
languagesStore.get(context.contentLink).then((languageSettings) => {
contentVersionStore.query({ contentLink: context.contentLink }).then(function(versions) {
var commonDrafts = versions.filter(x => x.isCommonDraft && x.language !== context.currentLanguageBranch.languageId);
// show select versions dialog
selectVersions(widget, commonDrafts, languageSettings.languages).then((selectedLanguages) => {
// update content
var selectedIds = selectedLanguages.map(x => x.id);
commonDrafts = commonDrafts.filter(x => selectedIds.indexOf(x.language) !== -1);
updateProperties(widget.name, selectedLanguages, commonDrafts).then(() => {
dialogService.alert({
title: "Info",
description: "Language versions has been updated"
}).then(() => window.location.reload());
});
}).catch(() => {});
});
});
});
}
// create property wrapper with copy icon
function getPropertyWidgetWrapper(widget, parent, getCurrentContent, getCurrentContext) {
var wrapper = factory(widget, parent);
if (!widget.params.isLanguageSpecific) {
return wrapper;
}
// blocks are not supported
if (widget.name.indexOf(".") !== -1) {
return wrapper;
}
var languageNode = document.createElement("span");
languageNode.classList.add("dijitInline", "dijitReset", "dijitIcon", "epi-iconCopy", "epi-cursor--pointer");
languageNode.title = "Copy to other language versions";
wrapper.labelNode.appendChild(languageNode);
when(getCurrentContext()).then(function (currentContext) {
if (currentContext && currentContext.currentMode === "create" || currentContext.currentMode === "translate") {
languageNode.classList.add("dijitHidden");
}
});
wrapper.own(on(languageNode, "click", function() { onCopyPropertyValueClick(widget, parent, getCurrentContent); }));
return wrapper;
}
return declare([_Module, _ContentContextMixin], {
initialize: function () {
this.inherited(arguments);
var self = this;
var customFactory = {
type: formFieldRegistry.type.field,
hint: "",
factory: function (widget, parent) {
return getPropertyWidgetWrapper(widget, parent, self.getCurrentContent.bind(self), self.getCurrentContext.bind(self));
}
};
formFieldRegistry.add(customFactory);
}
});
});
<?xml version="1.0" encoding="utf-8"?>
<module >
<assemblies>
<!-- This adds the Alloy template assembly to the "default module" -->
<add assembly="AlloyMvcTemplates" />
</assemblies>
<clientModule initializer="alloy.initializer">
<moduleDependencies>
<add dependency="CMS" type="RunAfter" />
</moduleDependencies>
</clientModule>
<dojo>
<!-- Add a mapping from alloy to ~/ClientResources/Scripts to the dojo loader configuration -->
<paths>
<add name="alloy" path="Scripts" />
</paths>
</dojo>
</module>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment