Skip to content

Instantly share code, notes, and snippets.

@MattisOlsson
Last active January 30, 2019 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MattisOlsson/810ab6855a031698d8c9704f403366a9 to your computer and use it in GitHub Desktop.
Save MattisOlsson/810ab6855a031698d8c9704f403366a9 to your computer and use it in GitHub Desktop.
Custom image selector
define("site/widget/CustomContextualContentForestStoreModel", [
// dojo
"dojo/_base/array",
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/promise/all",
"dojo/when",
// epi
"epi-cms/core/ContentReference",
"epi-cms/widget/ContextualContentForestStoreModel"
],
function (
// dojo
array,
declare,
lang,
promiseAll,
when,
// epi
ContentReference,
ContextualContentForestStoreModel
) {
return declare([ContextualContentForestStoreModel], {
_getRootItems: function () {
// summary:
// Resolves the root items, including the contextual root.
// tags:
// private
var self = this,
currentStore = self.store,
// Load all ordinary roots
rootLoads = array.map(self.roots, function (item) {
return currentStore.get(item);
});
var contextRootLoad =
// REMARK: This is not a longterm solution. We should push the currentContent to the model instead of getting it from the ContentContextMixin.
// this model could be use when we do not have a editing context. E.g. an addon that just want's to pick a page/image etc.
when(promiseAll([self.getCurrentContent(), self.getCurrentContext()]), function (result) {
var currentContent = result[0],
currentContext = result.length > 1 ? result[1] : null;
var currentContentWithoutVersion = new ContentReference(currentContent.contentLink).createVersionUnspecificReference().toString();
return when(currentStore.get(currentContentWithoutVersion), function (content) {
var inCreateMode = currentContext && currentContext.currentMode === "create";
if ((inCreateMode && self.get("view") === "contentselector") || !self.canHaveContextualContent(content)) {
return null;
}
// If the current context provided a context root, load it
var assetsFolderLink = content.assetsFolderLink;
if (assetsFolderLink) {
var getContextualContent = function (localFolderLink) {
// We need to call refresh local folder here in order to set its selection on the folder tree and refresh its icon and label.
return when(currentStore.get(localFolderLink), function (contextualContent) {
// Take query in order to filters properly children that are allowed.
// So that, we will show the expanded/collapsed icon for the root node correctly.
var query = {
id: contextualContent.contentLink,
typeIdentifiers: self.typeIdentifiers,
allLanguages: self.showAllLanguages
};
return when(currentStore.query(query), function (refreshedContent) {
// Only show contextual content in the container that supports its type identifier
if (typeof self.isSupportedType === "function" && !self.isSupportedType(refreshedContent.typeIdentifier)) {
return null;
}
if (!self.isPseudoContextualRoot(refreshedContent)) {
return self.getContextualRoot(refreshedContent);
} else {
return when(self.onRefreshRoots(refreshedContent), function () {
return self._modifyContentAccess(refreshedContent, content);
});
}
});
});
};
if (assetsFolderLink === self._getPseudoContextualContent().toString()) {
return when(currentStore.refresh(currentContentWithoutVersion), function (refreshedContent) {
return getContextualContent(refreshedContent.assetsFolderLink);
});
} else {
return getContextualContent(assetsFolderLink);
}
}
return null;
});
},
//We want a return even the context is not a content item
function () {
return null;
});
// Add context root load to the ordinary root loads
rootLoads.unshift(contextRootLoad);
return when(promiseAll(rootLoads), function (rootItems) {
// When loads are done, filter the ones that resolve null
return array.filter(rootItems, Boolean);
});
}
});
});
[EditorDescriptorRegistration(TargetType = typeof(ContentReference), UIHint = UIHint.Image, EditorDescriptorBehavior = EditorDescriptorBehavior.OverrideDefault)]
public class CustomImageReferenceEditorDesciptor : ImageReferenceEditorDescriptor
{
public CustomImageReferenceEditorDesciptor()
{
ClientEditingClass = "site/widget/CustomImageSelector";
}
}
define("site/widget/CustomImageSelector", [
// Dojo
"dojo/_base/array",
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/string",
// epi
"epi/shell/widget/dialog/Dialog",
"epi/shell/TypeDescriptorManager",
"epi-cms/widget/ContentSelector",
"epi/i18n!epi/cms/nls/episerver.cms.widget.contentselector",
// custom
"site/widget/CustomImageSelectorDialog"
],
function (
// Dojo
array,
declare,
lang,
stringUtil,
// epi
Dialog,
TypeDescriptorManager,
ContentSelector,
localization,
// custom
CustomImageSelectorDialog
) {
return declare([ContentSelector], {
_getDialog: function () {
// summary:
// Create page tree dialog
// tags:
// protected
// Verifies that the dialog instance and its dom node existing or not
if (this.dialog && this.dialog.domNode) {
return this.dialog;
}
var title = localization.title;
if (this.allowedTypes && this.allowedTypes.length === 1) {
var name = TypeDescriptorManager.getResourceValue(this.allowedTypes[0], "name");
if (name) {
title = stringUtil.substitute(localization.format, [name]);
}
}
this.contentSelectorDialog = new CustomImageSelectorDialog({
canSelectOwnerContent: this.canSelectOwnerContent,
showButtons: false,
roots: this.roots,
allowedTypes: this.allowedTypes,
restrictedTypes: this.restrictedTypes,
showAllLanguages: this.showAllLanguages,
showSearchBox: this.showSearchBox,
searchArea: this.searchArea
});
this.dialog = new Dialog({
title: title,
dialogClass: "epi-dialog-portrait",
content: this.contentSelectorDialog
});
this.dialog.own(this.contentSelectorDialog);
this.connect(this.contentSelectorDialog, "onChange", "_setDialogButtonState");
this.connect(this.dialog, "onExecute", "_onDialogExecute");
this.connect(this.dialog, "onHide", "_onDialogHide");
return this.dialog;
}
});
});
define("site/widget/CustomImageSelectorDialog", [
// Dojo
"dojo/_base/array",
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/dom-geometry",
"dojo/when",
// Dijit
"dijit/layout/_LayoutWidget",
// EPi
"epi",
"epi/shell/TypeDescriptorManager",
"epi-cms/ApplicationSettings",
"epi-cms/widget/ContentTree",
"epi-cms/widget/SearchBox",
// EPi Framework
"epi/shell/widget/_ActionProviderWidget",
"epi/dependency",
// Resources
"epi/i18n!epi/cms/nls/episerver.cms.widget.contentselectordialog",
// Custom
"site/widget/CustomContextualContentForestStoreModel"
],
function (
// Dojo
array,
declare,
lang,
domGeometry,
when,
// Dijit
_LayoutWidget,
// EPi
epi,
TypeDescriptorManager,
ApplicationSettings,
ContentTree,
SearchBox,
// EPi Framework
_ActionProviderWidget,
dependency,
localization,
// Custom
CustomContextualContentForestStoreModel
) {
return declare([_LayoutWidget, _ActionProviderWidget], {
// summary:
// Content selector widget.
//
// description:
// Used for editing PropertyPage properties in fly-out editor.
//
// tags:
// internal xproduct
// _contentRef: [private] epi-cms/core/ContentReference
// Reference to the currently selected content.
_contentRef: null,
// res: [private] Object
// Resource bundle.
res: localization,
baseClass: "epi-content-selector-dialog",
// roots: [public] Array
// A list of references to the root contents.
roots: null,
// typeIdentifiers: [public] Array | String
// Type identifiers to filter.
typeIdentifiers: null,
showButtons: true,
model: null,
// allowedTypes: String[]
// An array of types that should be selectable
allowedTypes: null,
// restrictedTypes: String[]
// An array of types that not should be selectable
restrictedTypes: null,
// canSelectOwnerContent: [private] Boolean
// Indicates whether select current owner content button should be available.
canSelectOwnerContent: true,
// multiRootsMode: [public] Boolean
// Indicate that multiple roots enabled in the tree or not
multiRootsMode: false,
_typesToDisplay: null,
// showAllLanguages: Boolean
// Flags to indicate that the content tree should show all content in multiple languages or not.
showAllLanguages: false,
// selectedContentType: String
// The content type which is selected. (used when restore content for instance)
selectedContentType: null,
// showSearchBox: Boolean
// If true - a search field is displayed that allows user to find content
showSearchBox: true,
// searchArea: String
// Search area for the search box.
searchArea: null,
_searchBox: null,
buildRendering: function () {
this.inherited(arguments);
var typeIdentifiers = this._getTypesToDisplay(),
model = this.model,
roots = this.roots || (model && model.roots);
this.preventContextualContentFor = this.preventContextualContentFor || this._getSettingFromContentType();
if (!model || this.showContextualContent) {
model = new CustomContextualContentForestStoreModel({
roots: roots,
typeIdentifiers: typeIdentifiers,
notSupportContextualContents: this.preventContextualContentFor,
showAllLanguages: this.showAllLanguages
});
}
model.set("view", "contentselector");
if (this.showSearchBox && this.searchArea) {
this._searchBox = new SearchBox({
innerSearchBoxClass: "epi-search--full-width",
triggerContextChange: false,
parameters: {
allowedTypes: this.allowedTypes,
restrictedTypes: this.restrictedTypes
},
onItemAction: lang.hitch(this, function (item) {
if (item && item.metadata && this._checkAcceptance(item.metadata.typeIdentifier)) {
this.set("value", item.metadata.id);
}
})
});
this._searchBox.set("area", this.searchArea);
this._searchBox.set("searchRoots", this.roots);
var searchBox = this._searchBox;
model.getRoots(true).then(function (roots) {
searchBox.set("searchRoots", roots.join(","));
});
this.addChild(this._searchBox);
}
this.tree = new ContentTree({
roots: roots,
typeIdentifiers: typeIdentifiers,
allowManipulation: false,
model: model,
allowedTypes: lang.clone(this.allowedTypes),
restrictedTypes: lang.clone(this.restrictedTypes),
disableRestrictedTypes: true,
expandExtraNodeItems: ApplicationSettings.startPage // Set extra node items to expand when the given tree loaded
});
this.addChild(this.tree);
this.connect(this.tree, "onClick", "_onTreeNodeClick");
},
layout: function () {
this.inherited(arguments);
var treeHeight = this._contentBox.h;
//Subtract the height of the searchBox if it is created
if (this._searchBox) {
treeHeight -= domGeometry.getMarginBox(this._searchBox.domNode).h;
}
//Get the margin box for the tree and change the height
var treeMarginBox = domGeometry.getMarginBox(this.tree.domNode);
treeMarginBox.h = treeHeight;
domGeometry.setMarginBox(this.tree.domNode, treeMarginBox);
},
_getSettingFromContentType: function () {
// summary:
// Get preventContextualContentFor settings form selected content type
// tags:
// private
if (!this.selectedContentType) {
return null;
}
var contentRepositoryDescriptors = this.contentRepositoryDescriptors || dependency.resolve("epi.cms.contentRepositoryDescriptors");
var repositoryDescriptor,
matchType = function (type) {
return TypeDescriptorManager.isBaseTypeIdentifier(this.selectedContentType, type);
};
for (var index in contentRepositoryDescriptors) {
var descriptor = contentRepositoryDescriptors[index];
if (array.some(descriptor.containedTypes, matchType, this)) {
repositoryDescriptor = descriptor;
break;
}
}
return repositoryDescriptor.preventContextualContentFor;
},
_getTypesToDisplay: function () {
if (!this._typesToDisplay) {
var typesToDisplay = [];
this._getContainerTypesRecursive(this.allowedTypes, typesToDisplay);
this._typesToDisplay = typesToDisplay;
}
return this._typesToDisplay;
},
_getContainerTypesRecursive: function (types, results, checkedTypes) {
if (!checkedTypes) {
checkedTypes = [];
}
array.forEach(types, lang.hitch(this, function (type) {
// To avoid infinite recursion, check if this type is already processed
if (array.indexOf(checkedTypes, type) === -1) {
// Mark as checked
checkedTypes.push(type);
// Add the type itself if it isn't added already
if (array.indexOf(results, type) === -1) {
results.push(type);
}
// If there are any container types, process them as well
var containerTypesForType = TypeDescriptorManager.getValue(type, "containerTypes");
if (containerTypesForType) {
this._getContainerTypesRecursive(containerTypesForType, results, checkedTypes);
}
}
}));
},
_setValueAttr: function (value) {
//summary:
// Value's setter.
//
// value: String
// Value to be set.
//
// tags:
// protected
//construct ContentReference object
if (value) {
this._contentRef = value;
var contentLinkToSelect = this._contentRef === "-" ? null : this._contentRef;
if (this.multiRootsMode) {
// We need to wait until tree load top level children and then fire onLoad() event
when(this.tree.onLoadDeferred, lang.hitch(this, function () {
this.tree.selectContent(contentLinkToSelect, true);
}));
} else {
if (!epi.isEmpty(contentLinkToSelect)) {
this.tree.selectContent(contentLinkToSelect, true);
}
}
} else {
this._contentRef = null;
this._clearSelectedContent();
}
},
_getValueAttr: function () {
//summary:
// Value's getter
// tags:
// protected
this.inherited(arguments);
if (this._contentRef) {
return this._contentRef.toString();
} else {
return "";
}
},
_setAllowedTypesAttr: function (value) {
this._typesToDisplay = null;
this._set("allowedTypes", value);
},
_clearSelectedContent: function () {
// summary:
// Clear selected node(s) in tree
// tags:
// private
this.tree.set("selectedNodes", []);
this.tree.set("selectedItems", []);
},
getActions: function () {
// summary:
// Overridden from _ActionProvider to get the select current owner content action added to the containing widget
//
// returns:
// An array containing a select page action definition, if it is not a shared block
return this.canSelectOwnerContent ? [
{
name: "selectpage",
label: localization.currentpage,
settings: { type: "submit" },
action: lang.hitch(this, function () {
this._setValueOwnerContent();
})
}
]
: [];
},
_onTreeNodeClick: function (content) {
// summary:
// Handle the inner tree's content change event.
//
// content: Object
// The content object.
//
// tags:
// private
var value = content.contentLink;
if (!this._checkAcceptance(content.typeIdentifier)) {
this.tree.lastFocused.setSelected(false);
value = null;
}
this.set("value", value);
this.onChange(this.get("value"));
},
_checkAcceptance: function (typeIdentifier) {
// summary:
// Compares a type against arrays of allowed and restricted types
//
// typeIdentifier: String
// The type to check if it's accepted to use
//
// tags:
// protected
var acceptedTypes = TypeDescriptorManager.getValidAcceptedTypes([typeIdentifier], this.allowedTypes, this.restrictedTypes);
return !!acceptedTypes.length;
},
_setValueOwnerContent: function () {
this.set("value", "-");
},
onChange: function (contentLink) {
// summary:
// Fired when value is changed.
//
// contentLink:
// The content link
// tags:
// public, callback
},
focus: function () {
this.tree.focus();
}
});
});
<?xml version="1.0" encoding="utf-8"?>
<module>
<dojo>
<paths>
<add name="site" path="Scripts" />
</paths>
</dojo>
</module>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment