Merge branch 'v8/dev' into v8/contrib
This commit is contained in:
@@ -134,15 +134,16 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
// publishing = deal with edit and published values
|
||||
foreach (var propertyValue in property.Values)
|
||||
{
|
||||
var isInvariantValue = propertyValue.Culture == null;
|
||||
var isCultureValue = propertyValue.Culture != null && propertyValue.Segment == null;
|
||||
var isInvariantValue = propertyValue.Culture == null && propertyValue.Segment == null;
|
||||
var isCultureValue = propertyValue.Culture != null;
|
||||
var isSegmentValue = propertyValue.Segment != null;
|
||||
|
||||
// deal with published value
|
||||
if (propertyValue.PublishedValue != null && publishedVersionId > 0)
|
||||
if ((propertyValue.PublishedValue != null || isSegmentValue) && publishedVersionId > 0)
|
||||
propertyDataDtos.Add(BuildDto(publishedVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.PublishedValue));
|
||||
|
||||
// deal with edit value
|
||||
if (propertyValue.EditedValue != null)
|
||||
if (propertyValue.EditedValue != null || isSegmentValue)
|
||||
propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue));
|
||||
|
||||
// property.Values will contain ALL of it's values, both variant and invariant which will be populated if the
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Umbraco.Tests.Web
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image"), "headerImage", null); //invariant property
|
||||
|
||||
Assert.AreEqual("_Properties.headerImage.invariant", ms.Keys.First());
|
||||
Assert.AreEqual("_Properties.headerImage.invariant.null", ms.Keys.First());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -73,9 +73,57 @@ namespace Umbraco.Tests.Web
|
||||
var localizationService = new Mock<ILocalizationService>();
|
||||
localizationService.Setup(x => x.GetDefaultLanguageIsoCode()).Returns("en-US");
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image"), "headerImage", "en-US"); //invariant property
|
||||
ms.AddPropertyError(new ValidationResult("no header image"), "headerImage", "en-US"); //variant property
|
||||
|
||||
Assert.AreEqual("_Properties.headerImage.en-US", ms.Keys.First());
|
||||
Assert.AreEqual("_Properties.headerImage.en-US.null", ms.Keys.First());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Add_Invariant_Segment_Property_Error()
|
||||
{
|
||||
var ms = new ModelStateDictionary();
|
||||
var localizationService = new Mock<ILocalizationService>();
|
||||
localizationService.Setup(x => x.GetDefaultLanguageIsoCode()).Returns("en-US");
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image"), "headerImage", null, "mySegment"); //invariant/segment property
|
||||
|
||||
Assert.AreEqual("_Properties.headerImage.invariant.mySegment", ms.Keys.First());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Add_Variant_Segment_Property_Error()
|
||||
{
|
||||
var ms = new ModelStateDictionary();
|
||||
var localizationService = new Mock<ILocalizationService>();
|
||||
localizationService.Setup(x => x.GetDefaultLanguageIsoCode()).Returns("en-US");
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image"), "headerImage", "en-US", "mySegment"); //variant/segment property
|
||||
|
||||
Assert.AreEqual("_Properties.headerImage.en-US.mySegment", ms.Keys.First());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Add_Invariant_Segment_Field_Property_Error()
|
||||
{
|
||||
var ms = new ModelStateDictionary();
|
||||
var localizationService = new Mock<ILocalizationService>();
|
||||
localizationService.Setup(x => x.GetDefaultLanguageIsoCode()).Returns("en-US");
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image", new[] { "myField" }), "headerImage", null, "mySegment"); //invariant/segment property
|
||||
|
||||
Assert.AreEqual("_Properties.headerImage.invariant.mySegment.myField", ms.Keys.First());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Add_Variant_Segment_Field_Property_Error()
|
||||
{
|
||||
var ms = new ModelStateDictionary();
|
||||
var localizationService = new Mock<ILocalizationService>();
|
||||
localizationService.Setup(x => x.GetDefaultLanguageIsoCode()).Returns("en-US");
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image", new[] { "myField" }), "headerImage", "en-US", "mySegment"); //variant/segment property
|
||||
|
||||
Assert.AreEqual("_Properties.headerImage.en-US.mySegment.myField", ms.Keys.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
-28
@@ -31,7 +31,7 @@
|
||||
$scope.page.hideActionsMenu = infiniteMode ? true : false;
|
||||
$scope.page.hideChangeVariant = false;
|
||||
$scope.allowOpen = true;
|
||||
$scope.app = null;
|
||||
$scope.activeApp = null;
|
||||
|
||||
//initializes any watches
|
||||
function startWatches(content) {
|
||||
@@ -74,31 +74,23 @@
|
||||
var isAppPresent = false;
|
||||
|
||||
// on first init, we dont have any apps. but if we are re-initializing, we do, but ...
|
||||
if ($scope.app) {
|
||||
if ($scope.activeApp) {
|
||||
|
||||
// lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.)
|
||||
_.forEach(content.apps, function (app) {
|
||||
if (app === $scope.app) {
|
||||
if (app.alias === $scope.activeApp.alias) {
|
||||
isAppPresent = true;
|
||||
$scope.appChanged(app);
|
||||
}
|
||||
});
|
||||
|
||||
// if we did reload our DocType, but still have the same app we will try to find it by the alias.
|
||||
if (isAppPresent === false) {
|
||||
_.forEach(content.apps, function (app) {
|
||||
if (app.alias === $scope.app.alias) {
|
||||
isAppPresent = true;
|
||||
app.active = true;
|
||||
$scope.appChanged(app);
|
||||
}
|
||||
});
|
||||
// active app does not exist anymore.
|
||||
$scope.activeApp = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// if we still dont have a app, lets show the first one:
|
||||
if (isAppPresent === false && content.apps.length) {
|
||||
content.apps[0].active = true;
|
||||
if ($scope.activeApp === null && content.apps.length) {
|
||||
$scope.appChanged(content.apps[0]);
|
||||
}
|
||||
// otherwise make sure the save options are up to date with the current content state
|
||||
@@ -151,8 +143,8 @@
|
||||
}
|
||||
|
||||
/** Returns true if the content item varies by culture */
|
||||
function isContentCultureVariant() {
|
||||
return $scope.content.variants.length > 1;
|
||||
function hasVariants(content) {
|
||||
return content.variants.length > 1;
|
||||
}
|
||||
|
||||
function reload() {
|
||||
@@ -215,6 +207,13 @@
|
||||
}));
|
||||
}
|
||||
|
||||
function appendRuntimeData() {
|
||||
$scope.content.variants.forEach((variant) => {
|
||||
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This does the content loading and initializes everything, called on first load
|
||||
*/
|
||||
@@ -226,6 +225,7 @@
|
||||
|
||||
$scope.content = data;
|
||||
|
||||
appendRuntimeData();
|
||||
init();
|
||||
|
||||
syncTreeNode($scope.content, $scope.content.path, true);
|
||||
@@ -251,6 +251,7 @@
|
||||
|
||||
$scope.content = data;
|
||||
|
||||
appendRuntimeData();
|
||||
init();
|
||||
startWatches($scope.content);
|
||||
|
||||
@@ -274,7 +275,7 @@
|
||||
$scope.page.saveButtonStyle = content.trashed || content.isElement || content.isBlueprint ? "primary" : "info";
|
||||
// only create the save/publish/preview buttons if the
|
||||
// content app is "Conent"
|
||||
if ($scope.app && $scope.app.alias !== "umbContent" && $scope.app.alias !== "umbInfo" && $scope.app.alias !== "umbListView") {
|
||||
if ($scope.activeApp && $scope.activeApp.alias !== "umbContent" && $scope.activeApp.alias !== "umbInfo" && $scope.activeApp.alias !== "umbListView") {
|
||||
$scope.defaultButton = null;
|
||||
$scope.subButtons = null;
|
||||
$scope.page.showSaveButton = false;
|
||||
@@ -589,7 +590,7 @@
|
||||
|
||||
$scope.sendToPublish = function () {
|
||||
clearNotifications($scope.content);
|
||||
if (isContentCultureVariant()) {
|
||||
if (hasVariants($scope.content)) {
|
||||
//before we launch the dialog we want to execute all client side validations first
|
||||
if (formHelper.submitForm({ scope: $scope, action: "publish" })) {
|
||||
|
||||
@@ -649,7 +650,7 @@
|
||||
|
||||
$scope.saveAndPublish = function () {
|
||||
clearNotifications($scope.content);
|
||||
if (isContentCultureVariant()) {
|
||||
if (hasVariants($scope.content)) {
|
||||
//before we launch the dialog we want to execute all client side validations first
|
||||
if (formHelper.submitForm({ scope: $scope, action: "publish" })) {
|
||||
var dialog = {
|
||||
@@ -711,7 +712,7 @@
|
||||
$scope.save = function () {
|
||||
clearNotifications($scope.content);
|
||||
// TODO: Add "..." to save button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant
|
||||
if (isContentCultureVariant()) {
|
||||
if (hasVariants($scope.content)) {
|
||||
//before we launch the dialog we want to execute all client side validations first
|
||||
if (formHelper.submitForm({ scope: $scope, action: "openSaveDialog" })) {
|
||||
|
||||
@@ -776,7 +777,7 @@
|
||||
clearNotifications($scope.content);
|
||||
//before we launch the dialog we want to execute all client side validations first
|
||||
if (formHelper.submitForm({ scope: $scope, action: "schedule" })) {
|
||||
if (!isContentCultureVariant()) {
|
||||
if (!hasVariants($scope.content)) {
|
||||
//ensure the flags are set
|
||||
$scope.content.variants[0].save = true;
|
||||
}
|
||||
@@ -813,7 +814,7 @@
|
||||
}, function (err) {
|
||||
clearDirtyState($scope.content.variants);
|
||||
//if this is invariant, show the notification errors, else they'll be shown inline with the variant
|
||||
if (!isContentCultureVariant()) {
|
||||
if (!hasVariants($scope.content)) {
|
||||
formHelper.showNotifications(err.data);
|
||||
}
|
||||
model.submitButtonState = "error";
|
||||
@@ -840,7 +841,7 @@
|
||||
//before we launch the dialog we want to execute all client side validations first
|
||||
if (formHelper.submitForm({ scope: $scope, action: "publishDescendants" })) {
|
||||
|
||||
if (!isContentCultureVariant()) {
|
||||
if (!hasVariants($scope.content)) {
|
||||
//ensure the flags are set
|
||||
$scope.content.variants[0].save = true;
|
||||
$scope.content.variants[0].publish = true;
|
||||
@@ -873,7 +874,7 @@
|
||||
}, function (err) {
|
||||
clearDirtyState($scope.content.variants);
|
||||
//if this is invariant, show the notification errors, else they'll be shown inline with the variant
|
||||
if (!isContentCultureVariant()) {
|
||||
if (!hasVariants($scope.content)) {
|
||||
formHelper.showNotifications(err.data);
|
||||
}
|
||||
model.submitButtonState = "error";
|
||||
@@ -963,11 +964,18 @@
|
||||
* Call back when a content app changes
|
||||
* @param {any} app
|
||||
*/
|
||||
$scope.appChanged = function (app) {
|
||||
$scope.appChanged = function (activeApp) {
|
||||
|
||||
$scope.app = app;
|
||||
$scope.activeApp = activeApp;
|
||||
|
||||
_.forEach($scope.content.apps, function (app) {
|
||||
app.active = false;
|
||||
if (app.alias === $scope.activeApp.alias) {
|
||||
app.active = true;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$broadcast("editors.apps.appChanged", { app: app });
|
||||
$scope.$broadcast("editors.apps.appChanged", { app: activeApp });
|
||||
|
||||
createButtons($scope.content);
|
||||
|
||||
@@ -1029,6 +1037,7 @@
|
||||
getMethod: "&",
|
||||
getScaffoldMethod: "&?",
|
||||
culture: "=?",
|
||||
segment: "=?",
|
||||
infiniteModel: "=?"
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -323,7 +323,7 @@
|
||||
// find the urls for the currently selected language
|
||||
if (scope.node.variants.length > 1) {
|
||||
// nodes with variants
|
||||
scope.currentUrls = _.filter(scope.node.urls, (url) => scope.currentVariant.language.culture === url.culture);
|
||||
scope.currentUrls = _.filter(scope.node.urls, (url) => (scope.currentVariant.language && scope.currentVariant.language.culture === url.culture));
|
||||
} else {
|
||||
// invariant nodes
|
||||
scope.currentUrls = scope.node.urls;
|
||||
|
||||
+30
-14
@@ -4,7 +4,7 @@
|
||||
/** This directive is used to render out the current variant tabs and properties and exposes an API for other directives to consume */
|
||||
function tabbedContentDirective($timeout) {
|
||||
|
||||
function link($scope, $element, $attrs) {
|
||||
function link($scope, $element) {
|
||||
|
||||
var appRootNode = $element[0];
|
||||
|
||||
@@ -115,21 +115,18 @@
|
||||
|
||||
}
|
||||
|
||||
function controller($scope, $element, $attrs) {
|
||||
|
||||
function controller($scope) {
|
||||
|
||||
//expose the property/methods for other directives to use
|
||||
this.content = $scope.content;
|
||||
this.activeVariant = _.find(this.content.variants, variant => {
|
||||
return variant.active;
|
||||
});
|
||||
|
||||
$scope.activeVariant = this.activeVariant;
|
||||
|
||||
$scope.defaultVariant = _.find(this.content.variants, variant => {
|
||||
return variant.language.isDefault;
|
||||
});
|
||||
|
||||
|
||||
if($scope.contentNodeModel) {
|
||||
$scope.defaultVariant = _.find($scope.contentNodeModel.variants, variant => {
|
||||
// defaultVariant will never have segment. Wether it has a language or not depends on the setup.
|
||||
return !variant.segment && ((variant.language && variant.language.isDefault) || (!variant.language));
|
||||
});
|
||||
}
|
||||
|
||||
$scope.unlockInvariantValue = function(property) {
|
||||
property.unlockInvariantValue = !property.unlockInvariantValue;
|
||||
};
|
||||
@@ -141,6 +138,24 @@
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$scope.propertyEditorDisabled = function (property) {
|
||||
if (property.unlockInvariantValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentLanguage = $scope.content.language;
|
||||
|
||||
var canEditCulture = !contentLanguage ||
|
||||
// If the property culture equals the content culture it can be edited
|
||||
property.culture === contentLanguage.culture ||
|
||||
// A culture-invariant property can only be edited by the default language variant
|
||||
(property.culture == null && contentLanguage.isDefault);
|
||||
|
||||
var canEditSegment = property.segment === $scope.content.segment;
|
||||
|
||||
return !canEditCulture || !canEditSegment;
|
||||
}
|
||||
}
|
||||
|
||||
var directive = {
|
||||
@@ -150,7 +165,8 @@
|
||||
controller: controller,
|
||||
link: link,
|
||||
scope: {
|
||||
content: "="
|
||||
content: "=", // in this context the content is the variant model.
|
||||
contentNodeModel: "=?" //contentNodeModel is the content model for the node,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+24
-15
@@ -12,7 +12,6 @@
|
||||
editor: "<",
|
||||
editorIndex: "<",
|
||||
editorCount: "<",
|
||||
openVariants: "<",
|
||||
onCloseSplitView: "&",
|
||||
onSelectVariant: "&",
|
||||
onOpenSplitView: "&",
|
||||
@@ -25,7 +24,7 @@
|
||||
controller: umbVariantContentController
|
||||
};
|
||||
|
||||
function umbVariantContentController($scope, $element, $location) {
|
||||
function umbVariantContentController($scope) {
|
||||
|
||||
var unsubscribe = [];
|
||||
|
||||
@@ -42,13 +41,14 @@
|
||||
vm.showBackButton = showBackButton;
|
||||
|
||||
function onInit() {
|
||||
// disable the name field if the active content app is not "Content"
|
||||
vm.nameDisabled = false;
|
||||
angular.forEach(vm.editor.content.apps, function(app){
|
||||
if(app.active && app.alias !== "umbContent" && app.alias !== "umbInfo" && app.alias !== "umbListView") {
|
||||
vm.nameDisabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Make copy of apps, so we can have a variant specific model for the App. (needed for validation etc.)
|
||||
vm.editor.variantApps = Utilities.copy(vm.content.apps);
|
||||
|
||||
var activeApp = vm.content.apps.find((app) => app.active);
|
||||
|
||||
onAppChanged(activeApp);
|
||||
|
||||
}
|
||||
|
||||
function showBackButton() {
|
||||
@@ -94,14 +94,23 @@
|
||||
}
|
||||
|
||||
$scope.$on("editors.apps.appChanged", function($event, $args) {
|
||||
var app = $args.app;
|
||||
// disable the name field if the active content app is not "Content" or "Info"
|
||||
vm.nameDisabled = false;
|
||||
if(app && app.alias !== "umbContent" && app.alias !== "umbInfo" && app.alias !== "umbListView") {
|
||||
vm.nameDisabled = true;
|
||||
}
|
||||
var activeApp = $args.app;
|
||||
|
||||
// sync varaintApps active with new active.
|
||||
_.forEach(vm.editor.variantApps, function (app) {
|
||||
app.active = (app.alias === activeApp.alias);
|
||||
});
|
||||
|
||||
onAppChanged(activeApp);
|
||||
});
|
||||
|
||||
function onAppChanged(activeApp) {
|
||||
|
||||
// disable the name field if the active content app is not "Content" or "Info"
|
||||
vm.nameDisabled = (activeApp && activeApp.alias !== "umbContent" && activeApp.alias !== "umbInfo" && activeApp.alias !== "umbListView");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to proxy a callback
|
||||
* @param {any} item
|
||||
|
||||
+77
-175
@@ -8,8 +8,9 @@
|
||||
templateUrl: 'views/components/content/umb-variant-content-editors.html',
|
||||
bindings: {
|
||||
page: "<",
|
||||
content: "<", // TODO: Not sure if this should be = since we are changing the 'active' property of a variant
|
||||
content: "<",
|
||||
culture: "<",
|
||||
segment: "<",
|
||||
onSelectApp: "&?",
|
||||
onSelectAppAnchor: "&?",
|
||||
onBack: "&?",
|
||||
@@ -19,12 +20,11 @@
|
||||
controller: umbVariantContentEditorsController
|
||||
};
|
||||
|
||||
function umbVariantContentEditorsController($scope, $location, $timeout) {
|
||||
function umbVariantContentEditorsController($scope, $location, contentEditingHelper) {
|
||||
|
||||
var prevContentDateUpdated = null;
|
||||
|
||||
var vm = this;
|
||||
var activeAppAlias = null;
|
||||
|
||||
vm.$onInit = onInit;
|
||||
vm.$onChanges = onChanges;
|
||||
@@ -39,13 +39,11 @@
|
||||
|
||||
//Used to track how many content views there are (for split view there will be 2, it could support more in theory)
|
||||
vm.editors = [];
|
||||
//Used to track the open variants across the split views
|
||||
vm.openVariants = [];
|
||||
|
||||
/** Called when the component initializes */
|
||||
function onInit() {
|
||||
prevContentDateUpdated = Utilities.copy(vm.content.updateDate);
|
||||
setActiveCulture();
|
||||
setActiveVariant();
|
||||
}
|
||||
|
||||
/** Called when the component has linked all elements, this is when the form controller is available */
|
||||
@@ -60,14 +58,16 @@
|
||||
function onChanges(changes) {
|
||||
|
||||
if (changes.culture && !changes.culture.isFirstChange() && changes.culture.currentValue !== changes.culture.previousValue) {
|
||||
setActiveCulture();
|
||||
setActiveVariant();
|
||||
} else if (changes.segment && !changes.segment.isFirstChange() && changes.segment.currentValue !== changes.segment.previousValue) {
|
||||
setActiveVariant();
|
||||
}
|
||||
}
|
||||
|
||||
/** Allows us to deep watch whatever we want - executes on every digest cycle */
|
||||
function doCheck() {
|
||||
if (!angular.equals(vm.content.updateDate, prevContentDateUpdated)) {
|
||||
setActiveCulture();
|
||||
setActiveVariant();
|
||||
prevContentDateUpdated = Utilities.copy(vm.content.updateDate);
|
||||
}
|
||||
}
|
||||
@@ -79,37 +79,32 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the active variant based on the current culture (query string)
|
||||
* Set the active variant based on the current culture or segment (query string)
|
||||
*/
|
||||
function setActiveCulture() {
|
||||
function setActiveVariant() {
|
||||
// set the active variant
|
||||
var activeVariant = null;
|
||||
_.each(vm.content.variants, function (v) {
|
||||
if (v.language && v.language.culture === vm.culture) {
|
||||
v.active = true;
|
||||
if ((vm.culture === "invariant" || v.language && v.language.culture === vm.culture) && v.segment === vm.segment) {
|
||||
activeVariant = v;
|
||||
}
|
||||
else {
|
||||
v.active = false;
|
||||
}
|
||||
});
|
||||
if (!activeVariant) {
|
||||
// Set the first variant to active if we can't find it.
|
||||
// If the content item is invariant, then only one item exists in the array.
|
||||
vm.content.variants[0].active = true;
|
||||
activeVariant = vm.content.variants[0];
|
||||
}
|
||||
|
||||
insertVariantEditor(0, initVariant(activeVariant, 0));
|
||||
insertVariantEditor(0, activeVariant);
|
||||
|
||||
if (vm.editors.length > 1) {
|
||||
//now re-sync any other editor content (i.e. if split view is open)
|
||||
for (var s = 1; s < vm.editors.length; s++) {
|
||||
//get the variant from the scope model
|
||||
var variant = _.find(vm.content.variants, function (v) {
|
||||
return v.language.culture === vm.editors[s].content.language.culture;
|
||||
return (!v.language || v.language.culture === vm.editors[s].content.language.culture) && v.segment === vm.editors[s].content.segment;
|
||||
});
|
||||
vm.editors[s].content = initVariant(variant, s);
|
||||
vm.editors[s].content = variant;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,157 +117,84 @@
|
||||
*/
|
||||
function insertVariantEditor(index, variant) {
|
||||
|
||||
if (vm.editors[index]) {
|
||||
if (vm.editors[index].content === variant) {
|
||||
// This variant is already the content of the editor in this index.
|
||||
return;
|
||||
}
|
||||
vm.editors[index].content.active = false;
|
||||
}
|
||||
variant.active = true;
|
||||
|
||||
var variantCulture = variant.language ? variant.language.culture : "invariant";
|
||||
var variantSegment = variant.segment;
|
||||
|
||||
//check if the culture at the index is the same, if it's null an editor will be added
|
||||
var currentCulture = vm.editors.length === 0 || vm.editors.length <= index ? null : vm.editors[index].culture;
|
||||
var currentCulture = index < vm.editors.length ? vm.editors[index].culture : null;
|
||||
var currentSegment = index < vm.editors.length ? vm.editors[index].segment : null;
|
||||
|
||||
// if index not already exists or if the culture or segment isnt identical then we do a replacement.
|
||||
if (index >= vm.editors.length || currentCulture !== variantCulture || currentSegment !== variantSegment) {
|
||||
|
||||
if (currentCulture !== variantCulture) {
|
||||
//Not the current culture which means we need to modify the array.
|
||||
//Not the current culture or segment which means we need to modify the array.
|
||||
//NOTE: It is not good enough to just replace the `content` object at a given index in the array
|
||||
// since that would mean that directives are not re-initialized.
|
||||
vm.editors.splice(index, 1, {
|
||||
compositeId: variant.compositeId,
|
||||
content: variant,
|
||||
//used for "track-by" ng-repeat
|
||||
culture: variantCulture
|
||||
culture: variantCulture,
|
||||
segment: variantSegment
|
||||
});
|
||||
}
|
||||
else {
|
||||
//replace the editor for the same culture
|
||||
//replace the content of the editor, since the culture and segment is the same.
|
||||
vm.editors[index].content = variant;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function initVariant(variant, editorIndex) {
|
||||
//The model that is assigned to the editor contains the current content variant along
|
||||
//with a copy of the contentApps. This is required because each editor renders it's own
|
||||
//header and content apps section and the content apps contains the view for editing content itself
|
||||
//and we need to assign a view model to the subView so that it is scoped to the current
|
||||
//editor so that split views work.
|
||||
|
||||
//copy the apps from the main model if not assigned yet to the variant
|
||||
if (!variant.apps) {
|
||||
variant.apps = Utilities.copy(vm.content.apps);
|
||||
}
|
||||
|
||||
//if this is a variant has a culture/language than we need to assign the language drop down info
|
||||
if (variant.language) {
|
||||
//if the variant list that defines the header drop down isn't assigned to the variant then assign it now
|
||||
if (!variant.variants) {
|
||||
variant.variants = _.map(vm.content.variants,
|
||||
function (v) {
|
||||
return _.pick(v, "active", "language", "state");
|
||||
});
|
||||
}
|
||||
else {
|
||||
//merge the scope variants on top of the header variants collection (handy when needing to refresh)
|
||||
angular.extend(variant.variants,
|
||||
_.map(vm.content.variants,
|
||||
function (v) {
|
||||
return _.pick(v, "active", "language", "state");
|
||||
}));
|
||||
}
|
||||
|
||||
//ensure the current culture is set as the active one
|
||||
for (var i = 0; i < variant.variants.length; i++) {
|
||||
if (variant.variants[i].language.culture === variant.language.culture) {
|
||||
variant.variants[i].active = true;
|
||||
}
|
||||
else {
|
||||
variant.variants[i].active = false;
|
||||
}
|
||||
}
|
||||
|
||||
// keep track of the open variants across the different split views
|
||||
// push the first variant then update the variant index based on the editor index
|
||||
if (vm.openVariants && vm.openVariants.length === 0) {
|
||||
vm.openVariants.push(variant.language.culture);
|
||||
} else {
|
||||
vm.openVariants[editorIndex] = variant.language.culture;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//then assign the variant to a view model to the content app
|
||||
var contentApp = _.find(variant.apps, function (a) {
|
||||
return a.alias === "umbContent";
|
||||
});
|
||||
|
||||
if (contentApp) {
|
||||
//The view model for the content app is simply the index of the variant being edited
|
||||
var variantIndex = vm.content.variants.indexOf(variant);
|
||||
contentApp.viewModel = variantIndex;
|
||||
}
|
||||
|
||||
// make sure the same app it set to active in the new variant
|
||||
if (activeAppAlias) {
|
||||
angular.forEach(variant.apps, function (app) {
|
||||
app.active = false;
|
||||
if (app.alias === activeAppAlias) {
|
||||
app.active = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new editor to the editors array to show content in a split view
|
||||
* @param {any} selectedVariant
|
||||
*/
|
||||
function openSplitView(selectedVariant) {
|
||||
var selectedCulture = selectedVariant.language.culture;
|
||||
// enforce content contentApp in splitview.
|
||||
var contentApp = vm.content.apps.find((app) => app.alias === "umbContent");
|
||||
if(contentApp) {
|
||||
selectApp(contentApp);
|
||||
}
|
||||
|
||||
insertVariantEditor(vm.editors.length, selectedVariant);
|
||||
|
||||
splitViewChanged();
|
||||
|
||||
}
|
||||
|
||||
$scope.$on("editors.content.splitViewRequest", function(event, args) {requestSplitView(args);});
|
||||
vm.requestSplitView = requestSplitView;
|
||||
function requestSplitView(args) {
|
||||
var culture = args.culture;
|
||||
var segment = args.segment;
|
||||
|
||||
//Find the whole variant model based on the culture that was chosen
|
||||
var variant = _.find(vm.content.variants, function (v) {
|
||||
return v.language.culture === selectedCulture;
|
||||
return (!v.language || v.language.culture === culture) && v.segment === segment;
|
||||
});
|
||||
|
||||
insertVariantEditor(vm.editors.length, initVariant(variant, vm.editors.length));
|
||||
|
||||
//only the content app can be selected since no other apps are shown, and because we copy all of these apps
|
||||
//to the "editors" we need to update this across all editors
|
||||
for (var e = 0; e < vm.editors.length; e++) {
|
||||
var editor = vm.editors[e];
|
||||
for (var i = 0; i < editor.content.apps.length; i++) {
|
||||
var app = editor.content.apps[i];
|
||||
if (app.alias === "umbContent") {
|
||||
app.active = true;
|
||||
// tell the world that the app has changed (but do it only once)
|
||||
if (e === 0) {
|
||||
selectApp(app);
|
||||
}
|
||||
}
|
||||
else {
|
||||
app.active = false;
|
||||
}
|
||||
}
|
||||
if (variant != null) {
|
||||
openSplitView(variant);
|
||||
}
|
||||
|
||||
// TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
|
||||
editor.collapsed = true;
|
||||
editor.loading = true;
|
||||
$timeout(function () {
|
||||
editor.collapsed = false;
|
||||
editor.loading = false;
|
||||
splitViewChanged();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/** Closes the split view */
|
||||
function closeSplitView(editorIndex) {
|
||||
// TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
|
||||
var editor = vm.editors[editorIndex];
|
||||
editor.loading = true;
|
||||
editor.collapsed = true;
|
||||
$timeout(function () {
|
||||
vm.editors.splice(editorIndex, 1);
|
||||
//remove variant from open variants
|
||||
vm.openVariants.splice(editorIndex, 1);
|
||||
//update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)
|
||||
$location.search("cculture", vm.openVariants[0]);
|
||||
splitViewChanged();
|
||||
}, 400);
|
||||
vm.editors.splice(editorIndex, 1);
|
||||
editor.content.active = false;
|
||||
|
||||
//update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)
|
||||
|
||||
$location.search({"cculture": vm.editors[0].content.language ? vm.editors[0].content.language.culture : null, "csegment": vm.editors[0].content.segment});
|
||||
splitViewChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,38 +204,26 @@
|
||||
*/
|
||||
function selectVariant(variant, editorIndex) {
|
||||
|
||||
// prevent variants already open in a split view to be opened
|
||||
if (vm.openVariants.indexOf(variant.language.culture) !== -1) {
|
||||
var variantCulture = variant.language ? variant.language.culture : "invariant";
|
||||
var variantSegment = variant.segment || null;
|
||||
|
||||
// Check if we already have this editor open, if so, do nothing.
|
||||
if (vm.editors.find((editor) => (!editor.content.language || editor.content.language.culture === variantCulture) && editor.content.segment === variantSegment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//if the editor index is zero, then update the query string to track the lang selection, otherwise if it's part
|
||||
//of a 2nd split view editor then update the model directly.
|
||||
if (editorIndex === 0) {
|
||||
//If we've made it this far, then update the query string.
|
||||
//The editor will respond to this query string changing.
|
||||
$location.search("cculture", variant.language.culture);
|
||||
$location.search("cculture", variantCulture).search("csegment", variantSegment);
|
||||
}
|
||||
else {
|
||||
|
||||
//Update the 'active' variant for this editor
|
||||
var editor = vm.editors[editorIndex];
|
||||
//set all variant drop down items as inactive for this editor and then set the selected one as active
|
||||
for (var i = 0; i < editor.content.variants.length; i++) {
|
||||
editor.content.variants[i].active = false;
|
||||
}
|
||||
variant.active = true;
|
||||
|
||||
//get the variant content model and initialize the editor with that
|
||||
var contentVariant = _.find(vm.content.variants,
|
||||
function (v) {
|
||||
return v.language.culture === variant.language.culture;
|
||||
});
|
||||
editor.content = initVariant(contentVariant, editorIndex);
|
||||
|
||||
//update the editors collection
|
||||
insertVariantEditor(editorIndex, contentVariant);
|
||||
|
||||
insertVariantEditor(editorIndex, variant);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,25 +232,17 @@
|
||||
* @param {any} app This is the model of the selected app
|
||||
*/
|
||||
function selectApp(app) {
|
||||
if (vm.onSelectApp) {
|
||||
vm.onSelectApp({ "app": app });
|
||||
if(vm.onSelectApp) {
|
||||
vm.onSelectApp({"app": app});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function selectAppAnchor(app, anchor) {
|
||||
if (vm.onSelectAppAnchor) {
|
||||
vm.onSelectAppAnchor({ "app": app, "anchor": anchor });
|
||||
if(vm.onSelectAppAnchor) {
|
||||
vm.onSelectAppAnchor({"app": app, "anchor": anchor});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$scope.$on("editors.apps.appChanged", function ($event, $args) {
|
||||
var app = $args.app;
|
||||
if (app && app.alias) {
|
||||
activeAppAlias = app.alias;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').component('umbVariantContentEditors', umbVariantContentEditors);
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ Use this directive to generate a list of breadcrumbs.
|
||||
|
||||
var path = scope.pathTo(ancestor);
|
||||
$location.path(path);
|
||||
navigationService.clearSearch(["cculture"]);
|
||||
navigationService.clearSearch(["cculture", "csegment"]);
|
||||
}
|
||||
|
||||
scope.pathTo = function (ancestor) {
|
||||
|
||||
+166
-153
@@ -2,11 +2,10 @@
|
||||
'use strict';
|
||||
|
||||
function EditorContentHeader(serverValidationManager, localizationService, editorState) {
|
||||
function link(scope) {
|
||||
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
var unsubscribe = [];
|
||||
|
||||
|
||||
if (!scope.serverValidationNameField) {
|
||||
scope.serverValidationNameField = "Name";
|
||||
}
|
||||
@@ -14,19 +13,20 @@
|
||||
scope.serverValidationAliasField = "Alias";
|
||||
}
|
||||
|
||||
scope.isNew = scope.content.state == "NotCreated";
|
||||
|
||||
localizationService.localizeMany([
|
||||
scope.isNew ? "visuallyHiddenTexts_createItem" : "visuallyHiddenTexts_edit",
|
||||
"visuallyHiddenTexts_name",
|
||||
scope.isNew ? "general_new" : "general_edit"]
|
||||
).then(function (data) {
|
||||
scope.isNew = scope.editor.content.state == "NotCreated";
|
||||
|
||||
localizationService.localizeMany(
|
||||
[
|
||||
scope.isNew ? "placeholders_a11yCreateItem" : "placeholders_a11yEdit",
|
||||
"placeholders_a11yName",
|
||||
scope.isNew ? "general_new" : "general_edit"
|
||||
]
|
||||
).then(function (data) {
|
||||
scope.a11yMessage = data[0];
|
||||
scope.a11yName = data[1];
|
||||
var title = data[2] + ": ";
|
||||
if (!scope.isNew) {
|
||||
scope.a11yMessage += " " + scope.content.name;
|
||||
scope.a11yMessage += " " + scope.editor.content.name;
|
||||
title += scope.content.name;
|
||||
} else {
|
||||
var name = editorState.current.contentTypeName;
|
||||
@@ -34,199 +34,212 @@
|
||||
scope.a11yName = name + " " + scope.a11yName;
|
||||
title += name;
|
||||
}
|
||||
|
||||
|
||||
scope.$emit("$changeTitle", title);
|
||||
});
|
||||
scope.vm = {};
|
||||
scope.vm.hasVariants = false;
|
||||
scope.vm.hasSubVariants = false;
|
||||
scope.vm.hasCulture = false;
|
||||
scope.vm.hasSegments = false;
|
||||
scope.vm.dropdownOpen = false;
|
||||
scope.vm.currentVariant = "";
|
||||
scope.vm.variantsWithError = [];
|
||||
scope.vm.defaultVariant = null;
|
||||
|
||||
scope.vm.errorsOnOtherVariants = false;// indicating wether to show that other variants, than the current, have errors.
|
||||
|
||||
function updateVaraintErrors() {
|
||||
scope.content.variants.forEach( function (variant) {
|
||||
variant.hasError = scope.variantHasError(variant);
|
||||
|
||||
});
|
||||
checkErrorsOnOtherVariants();
|
||||
}
|
||||
|
||||
function checkErrorsOnOtherVariants() {
|
||||
var check = false;
|
||||
angular.forEach(scope.content.variants, function (variant) {
|
||||
if (scope.openVariants.indexOf(variant.language.culture) === -1 && scope.variantHasError(variant.language.culture)) {
|
||||
scope.content.variants.forEach( function (variant) {
|
||||
if (variant.active !== true && variant.hasError) {
|
||||
check = true;
|
||||
}
|
||||
});
|
||||
scope.vm.errorsOnOtherVariants = check;
|
||||
}
|
||||
|
||||
function onVariantValidation(valid, errors, allErrors, culture, segment) {
|
||||
|
||||
function onCultureValidation(valid, errors, allErrors, culture) {
|
||||
var index = scope.vm.variantsWithError.indexOf(culture);
|
||||
if (valid === true) {
|
||||
// only want to react to property errors:
|
||||
if(errors.findIndex(error => {return error.propertyAlias !== null;}) === -1) {
|
||||
// we dont have any errors for properties, meaning we will back out.
|
||||
return;
|
||||
}
|
||||
|
||||
// If error coming back is invariant, we will assign the error to the default variant by picking the defaultVariant language.
|
||||
if(culture === "invariant") {
|
||||
culture = scope.vm.defaultVariant.language.culture;
|
||||
}
|
||||
|
||||
var index = scope.vm.variantsWithError.findIndex((item) => item.culture === culture && item.segment === segment)
|
||||
if(valid === true) {
|
||||
if (index !== -1) {
|
||||
scope.vm.variantsWithError.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
if (index === -1) {
|
||||
scope.vm.variantsWithError.push(culture);
|
||||
scope.vm.variantsWithError.push({"culture": culture, "segment": segment});
|
||||
}
|
||||
}
|
||||
checkErrorsOnOtherVariants();
|
||||
scope.$evalAsync(updateVaraintErrors);
|
||||
}
|
||||
|
||||
|
||||
function onInit() {
|
||||
|
||||
// find default.
|
||||
angular.forEach(scope.content.variants, function (variant) {
|
||||
if (variant.language.isDefault) {
|
||||
|
||||
// find default + check if we have variants.
|
||||
scope.content.variants.forEach( function (variant) {
|
||||
if (variant.language !== null && variant.language.isDefault) {
|
||||
scope.vm.defaultVariant = variant;
|
||||
}
|
||||
});
|
||||
|
||||
setCurrentVariant();
|
||||
|
||||
angular.forEach(scope.content.apps, (app) => {
|
||||
if (app.alias === "umbContent") {
|
||||
app.anchors = scope.content.tabs;
|
||||
if (variant.language !== null) {
|
||||
scope.vm.hasCulture = true;
|
||||
}
|
||||
if (variant.segment !== null) {
|
||||
scope.vm.hasSegments = true;
|
||||
}
|
||||
});
|
||||
|
||||
scope.vm.hasVariants = (scope.vm.hasCulture || scope.vm.hasSegments);
|
||||
scope.vm.hasSubVariants = (scope.vm.hasCulture && scope.vm.hasSegments);
|
||||
|
||||
angular.forEach(scope.content.variants, function (variant) {
|
||||
unsubscribe.push(serverValidationManager.subscribe(null, variant.language.culture, null, onCultureValidation));
|
||||
});
|
||||
updateVaraintErrors();
|
||||
|
||||
unsubscribe.push(serverValidationManager.subscribe(null, null, null, onCultureValidation));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
function setCurrentVariant() {
|
||||
angular.forEach(scope.content.variants, function (variant) {
|
||||
if (variant.active) {
|
||||
scope.vm.currentVariant = variant;
|
||||
checkErrorsOnOtherVariants();
|
||||
scope.vm.variantMenu = [];
|
||||
if (scope.vm.hasCulture) {
|
||||
scope.content.variants.forEach( (v) => {
|
||||
if (v.language !== null && v.segment === null) {
|
||||
var variantMenuEntry = {
|
||||
key: String.CreateGuid(),
|
||||
open: v.language && v.language.culture === scope.editor.culture,
|
||||
variant: v,
|
||||
subVariants: scope.content.variants.filter( (subVariant) => subVariant.language.culture === v.language.culture && subVariant.segment !== null)
|
||||
};
|
||||
scope.vm.variantMenu.push(variantMenuEntry);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
scope.content.variants.forEach( (v) => {
|
||||
scope.vm.variantMenu.push({
|
||||
key: String.CreateGuid(),
|
||||
variant: v
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scope.editor.variantApps.forEach( (app) => {
|
||||
if (app.alias === "umbContent") {
|
||||
app.anchors = scope.editor.content.tabs;
|
||||
}
|
||||
});
|
||||
|
||||
scope.goBack = function () {
|
||||
if (scope.onBack) {
|
||||
scope.onBack();
|
||||
scope.content.variants.forEach( function (variant) {
|
||||
|
||||
// if we are looking for the variant with default language then we also want to check for invariant variant.
|
||||
if (variant.language && variant.language.culture === scope.vm.defaultVariant.language.culture && variant.segment === null) {
|
||||
unsubscribe.push(serverValidationManager.subscribe(null, "invariant", null, onVariantValidation, null));
|
||||
}
|
||||
unsubscribe.push(serverValidationManager.subscribe(null, variant.language !== null ? variant.language.culture : null, null, onVariantValidation, variant.segment));
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
scope.selectVariant = function (event, variant) {
|
||||
scope.goBack = function () {
|
||||
if (scope.onBack) {
|
||||
scope.onBack();
|
||||
}
|
||||
};
|
||||
|
||||
if (scope.onSelectVariant) {
|
||||
scope.vm.dropdownOpen = false;
|
||||
scope.onSelectVariant({ "variant": variant });
|
||||
scope.selectVariant = function (event, variant) {
|
||||
|
||||
if (scope.onSelectVariant) {
|
||||
scope.vm.dropdownOpen = false;
|
||||
scope.onSelectVariant({ "variant": variant });
|
||||
}
|
||||
};
|
||||
|
||||
scope.selectNavigationItem = function(item) {
|
||||
if(scope.onSelectNavigationItem) {
|
||||
scope.onSelectNavigationItem({"item": item});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scope.selectNavigationItem = function (item) {
|
||||
if (scope.onSelectNavigationItem) {
|
||||
scope.onSelectNavigationItem({ "item": item });
|
||||
scope.selectAnchorItem = function(item, anchor) {
|
||||
if(scope.onSelectAnchorItem) {
|
||||
scope.onSelectAnchorItem({"item": item, "anchor": anchor});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope.selectAnchorItem = function (item, anchor) {
|
||||
if (scope.onSelectAnchorItem) {
|
||||
scope.onSelectAnchorItem({ "item": item, "anchor": anchor });
|
||||
}
|
||||
}
|
||||
scope.closeSplitView = function () {
|
||||
if (scope.onCloseSplitView) {
|
||||
scope.onCloseSplitView();
|
||||
}
|
||||
};
|
||||
|
||||
scope.closeSplitView = function () {
|
||||
if (scope.onCloseSplitView) {
|
||||
scope.onCloseSplitView();
|
||||
}
|
||||
};
|
||||
|
||||
scope.openInSplitView = function (event, variant) {
|
||||
if (scope.onOpenInSplitView) {
|
||||
scope.vm.dropdownOpen = false;
|
||||
scope.onOpenInSplitView({ "variant": variant });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* keep track of open variants - this is used to prevent the same variant to be open in more than one split view
|
||||
* @param {any} culture
|
||||
*/
|
||||
scope.variantIsOpen = function (culture) {
|
||||
return (scope.openVariants.indexOf(culture) !== -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a variant has a error, used to display errors in variant switcher.
|
||||
* @param {any} culture
|
||||
*/
|
||||
scope.variantHasError = function (culture) {
|
||||
// if we are looking for the default language we also want to check for invariant.
|
||||
if (culture === scope.vm.defaultVariant.language.culture) {
|
||||
if (scope.vm.variantsWithError.indexOf("invariant") !== -1) {
|
||||
scope.openInSplitView = function (event, variant) {
|
||||
if (scope.onOpenInSplitView) {
|
||||
scope.vm.dropdownOpen = false;
|
||||
scope.onOpenInSplitView({ "variant": variant });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether a variant has a error, used to display errors in variant switcher.
|
||||
* @param {any} culture
|
||||
*/
|
||||
scope.variantHasError = function(variant) {
|
||||
if(scope.vm.variantsWithError.find((item) => (!variant.language || item.culture === variant.language.culture) && item.segment === variant.segment) !== undefined) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (scope.vm.variantsWithError.indexOf(culture) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onInit();
|
||||
|
||||
//watch for the active culture changing, if it changes, update the current variant
|
||||
if (scope.content.variants) {
|
||||
scope.$watch(function () {
|
||||
for (var i = 0; i < scope.content.variants.length; i++) {
|
||||
var v = scope.content.variants[i];
|
||||
if (v.active) {
|
||||
return v.language.culture;
|
||||
}
|
||||
}
|
||||
return scope.vm.currentVariant.language.culture; //should never get here
|
||||
}, function (newValue, oldValue) {
|
||||
if (newValue !== scope.vm.currentVariant.language.culture) {
|
||||
setCurrentVariant();
|
||||
onInit();
|
||||
|
||||
scope.$on('$destroy', function () {
|
||||
for (var u in unsubscribe) {
|
||||
unsubscribe[u]();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scope.$on('$destroy', function () {
|
||||
for (var u in unsubscribe) {
|
||||
unsubscribe[u]();
|
||||
}
|
||||
});
|
||||
|
||||
var directive = {
|
||||
transclude: true,
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/editor/umb-editor-content-header.html',
|
||||
scope: {
|
||||
name: "=",
|
||||
nameDisabled: "<?",
|
||||
menu: "=",
|
||||
hideActionsMenu: "<?",
|
||||
content: "=",
|
||||
editor: "=",
|
||||
hideChangeVariant: "<?",
|
||||
onSelectNavigationItem: "&?",
|
||||
onSelectAnchorItem: "&?",
|
||||
showBackButton: "<?",
|
||||
onBack: "&?",
|
||||
splitViewOpen: "=?",
|
||||
onOpenInSplitView: "&?",
|
||||
onCloseSplitView: "&?",
|
||||
onSelectVariant: "&?",
|
||||
serverValidationNameField: "@?",
|
||||
serverValidationAliasField: "@?"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
|
||||
var directive = {
|
||||
transclude: true,
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/editor/umb-editor-content-header.html',
|
||||
scope: {
|
||||
name: "=",
|
||||
nameDisabled: "<?",
|
||||
menu: "=",
|
||||
hideActionsMenu: "<?",
|
||||
content: "=",
|
||||
openVariants: "<",
|
||||
hideChangeVariant: "<?",
|
||||
onSelectNavigationItem: "&?",
|
||||
onSelectAnchorItem: "&?",
|
||||
showBackButton: "<?",
|
||||
onBack: "&?",
|
||||
splitViewOpen: "=?",
|
||||
onOpenInSplitView: "&?",
|
||||
onCloseSplitView: "&?",
|
||||
onSelectVariant: "&?",
|
||||
serverValidationNameField: "@?",
|
||||
serverValidationAliasField: "@?"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbEditorContentHeader', EditorContentHeader);
|
||||
|
||||
}) ();
|
||||
})();
|
||||
|
||||
+2
-1
@@ -6,7 +6,7 @@
|
||||
**/
|
||||
function EditorSubViewDirective() {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
function link(scope) {
|
||||
//The model can contain: view, viewModel, name, alias, icon
|
||||
|
||||
if (!scope.model.view) {
|
||||
@@ -20,6 +20,7 @@
|
||||
templateUrl: 'views/components/editor/umb-editor-sub-view.html',
|
||||
scope: {
|
||||
model: "=",
|
||||
variantContent: "=?",
|
||||
content: "="
|
||||
},
|
||||
link: link
|
||||
|
||||
+2
-2
@@ -39,7 +39,7 @@ angular.module("umbraco.directives")
|
||||
|
||||
function stringToJson(text) {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
return Utilities.fromJson(text);
|
||||
} catch (err) {
|
||||
setInvalid();
|
||||
return text;
|
||||
@@ -55,7 +55,7 @@ angular.module("umbraco.directives")
|
||||
function isValidJson(model) {
|
||||
var flag = true;
|
||||
try {
|
||||
JSON.parse(model)
|
||||
Utilities.fromJson(model)
|
||||
} catch (err) {
|
||||
flag = false;
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ angular.module("umbraco.directives")
|
||||
//Support one or more attribute properties to update
|
||||
var keys = attrs.localize.split(',');
|
||||
|
||||
keys.forEach((value, key) => {
|
||||
Utilities.forEach(keys, (value, key) => {
|
||||
var attr = element.attr(value);
|
||||
|
||||
if (attr) {
|
||||
|
||||
+2
@@ -530,6 +530,7 @@
|
||||
contentType: scope.contentType,
|
||||
contentTypeName: scope.model.name,
|
||||
contentTypeAllowCultureVariant: scope.model.allowCultureVariant,
|
||||
contentTypeAllowSegmentVariant: scope.model.allowSegmentVariant,
|
||||
view: "views/common/infiniteeditors/propertysettings/propertysettings.html",
|
||||
size: "small",
|
||||
submit: function (model) {
|
||||
@@ -557,6 +558,7 @@
|
||||
property.isSensitiveData = propertyModel.isSensitiveData;
|
||||
property.isSensitiveValue = propertyModel.isSensitiveValue;
|
||||
property.allowCultureVariant = propertyModel.allowCultureVariant;
|
||||
property.allowSegmentVariant = propertyModel.allowSegmentVariant;
|
||||
|
||||
// update existing data types
|
||||
if (model.updateSameDataTypes) {
|
||||
|
||||
+2
-2
@@ -158,9 +158,9 @@ When this combination is hit an overview is opened with shortcuts based on the m
|
||||
};
|
||||
|
||||
function onInit() {
|
||||
scope.model.forEach(shortcutGroup => {
|
||||
Utilities.forEach(scope.model, shortcutGroup => {
|
||||
|
||||
shortcutGroup.shortcuts.forEach(shortcut => {
|
||||
Utilities.forEach(shortcutGroup.shortcuts, shortcut => {
|
||||
shortcut.platformKeys = [];
|
||||
|
||||
// get shortcut keys for mac
|
||||
|
||||
+26
-19
@@ -38,7 +38,8 @@ function valPropertyMsg(serverValidationManager, localizationService) {
|
||||
var currentProperty = umbPropCtrl.property;
|
||||
scope.currentProperty = currentProperty;
|
||||
|
||||
var currentCulture = currentProperty.culture;
|
||||
var currentCulture = currentProperty.culture;
|
||||
var currentSegment = currentProperty.segment;
|
||||
|
||||
// validation object won't exist when editor loads outside the content form (ie in settings section when modifying a content type)
|
||||
var isMandatory = currentProperty.validation ? currentProperty.validation.mandatory : undefined;
|
||||
@@ -54,7 +55,7 @@ function valPropertyMsg(serverValidationManager, localizationService) {
|
||||
var currentVariant = umbVariantCtrl.editor.content;
|
||||
|
||||
// Lets check if we have variants and we are on the default language then ...
|
||||
if (umbVariantCtrl.content.variants.length > 1 && !currentVariant.language.isDefault && !currentCulture && !currentProperty.unlockInvariantValue) {
|
||||
if (umbVariantCtrl.content.variants.length > 1 && (!currentVariant.language || !currentVariant.language.isDefault) && !currentCulture && !currentSegment && !currentProperty.unlockInvariantValue) {
|
||||
//This property is locked cause its a invariant property shown on a non-default language.
|
||||
//Therefor do not validate this field.
|
||||
return;
|
||||
@@ -70,7 +71,7 @@ function valPropertyMsg(serverValidationManager, localizationService) {
|
||||
//this can be null if no property was assigned
|
||||
if (scope.currentProperty) {
|
||||
//first try to get the error msg from the server collection
|
||||
var err = serverValidationManager.getPropertyError(scope.currentProperty.alias, null, "");
|
||||
var err = serverValidationManager.getPropertyError(scope.currentProperty.alias, null, "", null);
|
||||
//if there's an error message use it
|
||||
if (err && err.errorMsg) {
|
||||
return err.errorMsg;
|
||||
@@ -221,25 +222,31 @@ function valPropertyMsg(serverValidationManager, localizationService) {
|
||||
// the correct field validation in their property editors.
|
||||
|
||||
if (scope.currentProperty) { //this can be null if no property was assigned
|
||||
|
||||
function serverValidationManagerCallback(isValid, propertyErrors, allErrors) {
|
||||
hasError = !isValid;
|
||||
if (hasError) {
|
||||
//set the error message to the server message
|
||||
scope.errorMsg = propertyErrors[0].errorMsg;
|
||||
//flag that the current validator is invalid
|
||||
formCtrl.$setValidity('valPropertyMsg', false);
|
||||
startWatch();
|
||||
}
|
||||
else {
|
||||
scope.errorMsg = "";
|
||||
//flag that the current validator is valid
|
||||
formCtrl.$setValidity('valPropertyMsg', true);
|
||||
stopWatch();
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribe.push(serverValidationManager.subscribe(scope.currentProperty.alias,
|
||||
currentCulture,
|
||||
"",
|
||||
function(isValid, propertyErrors, allErrors) {
|
||||
hasError = !isValid;
|
||||
if (hasError) {
|
||||
//set the error message to the server message
|
||||
scope.errorMsg = propertyErrors[0].errorMsg;
|
||||
//flag that the current validator is invalid
|
||||
formCtrl.$setValidity('valPropertyMsg', false);
|
||||
startWatch();
|
||||
}
|
||||
else {
|
||||
scope.errorMsg = "";
|
||||
//flag that the current validator is valid
|
||||
formCtrl.$setValidity('valPropertyMsg', true);
|
||||
stopWatch();
|
||||
}
|
||||
}));
|
||||
serverValidationManagerCallback,
|
||||
currentSegment
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ function valServer(serverValidationManager) {
|
||||
|
||||
var currentProperty = umbPropCtrl.property;
|
||||
var currentCulture = currentProperty.culture;
|
||||
var currentSegment = currentProperty.segment;
|
||||
|
||||
if (umbVariantCtrl) {
|
||||
//if we are inside of an umbVariantContent directive
|
||||
@@ -31,7 +32,7 @@ function valServer(serverValidationManager) {
|
||||
var currentVariant = umbVariantCtrl.editor.content;
|
||||
|
||||
// Lets check if we have variants and we are on the default language then ...
|
||||
if (umbVariantCtrl.content.variants.length > 1 && !currentVariant.language.isDefault && !currentCulture && !currentProperty.unlockInvariantValue) {
|
||||
if (umbVariantCtrl.content.variants.length > 1 && (!currentVariant.language || !currentVariant.language.isDefault) && !currentCulture && !currentSegment && !currentProperty.unlockInvariantValue) {
|
||||
//This property is locked cause its a invariant property shown on a non-default language.
|
||||
//Therefor do not validate this field.
|
||||
return;
|
||||
@@ -75,7 +76,7 @@ function valServer(serverValidationManager) {
|
||||
if (modelCtrl.$invalid) {
|
||||
modelCtrl.$setValidity('valServer', true);
|
||||
//clear the server validation entry
|
||||
serverValidationManager.removePropertyError(currentProperty.alias, currentCulture, fieldName);
|
||||
serverValidationManager.removePropertyError(currentProperty.alias, currentCulture, fieldName, currentSegment);
|
||||
stopWatch();
|
||||
}
|
||||
}, true);
|
||||
@@ -90,23 +91,26 @@ function valServer(serverValidationManager) {
|
||||
}
|
||||
|
||||
//subscribe to the server validation changes
|
||||
function serverValidationManagerCallback(isValid, propertyErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
modelCtrl.$setValidity('valServer', false);
|
||||
//assign an error msg property to the current validator
|
||||
modelCtrl.errorMsg = propertyErrors[0].errorMsg;
|
||||
startWatch();
|
||||
}
|
||||
else {
|
||||
modelCtrl.$setValidity('valServer', true);
|
||||
//reset the error message
|
||||
modelCtrl.errorMsg = "";
|
||||
stopWatch();
|
||||
}
|
||||
}
|
||||
unsubscribe.push(serverValidationManager.subscribe(currentProperty.alias,
|
||||
currentCulture,
|
||||
fieldName,
|
||||
function(isValid, propertyErrors, allErrors) {
|
||||
if (!isValid) {
|
||||
modelCtrl.$setValidity('valServer', false);
|
||||
//assign an error msg property to the current validator
|
||||
modelCtrl.errorMsg = propertyErrors[0].errorMsg;
|
||||
startWatch();
|
||||
}
|
||||
else {
|
||||
modelCtrl.$setValidity('valServer', true);
|
||||
//reset the error message
|
||||
modelCtrl.errorMsg = "";
|
||||
stopWatch();
|
||||
}
|
||||
}));
|
||||
serverValidationManagerCallback,
|
||||
currentSegment)
|
||||
);
|
||||
|
||||
scope.$on('$destroy', function () {
|
||||
stopWatch();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name umbraco.filters.filter:CMS_joinArray
|
||||
* @name umbraco.filters.filter:umbCmsJoinArray
|
||||
* @namespace umbCmsJoinArray
|
||||
*
|
||||
* param {array} array of string or objects, if an object use the third argument to specify which prop to list.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name umbraco.filters.filter:umbCmsTitleCase
|
||||
* @namespace umbCmsTitleCase
|
||||
*
|
||||
* param {string} the text turned into title case.
|
||||
*
|
||||
* @description
|
||||
* Transforms text to title case. Capitalizes the first letter of each word, and transforms the rest of the word to lower case.
|
||||
*
|
||||
*/
|
||||
angular.module("umbraco.filters").filter('umbCmsTitleCase', function() {
|
||||
return function (str) {
|
||||
return str.replace(
|
||||
/\w\S*/g,
|
||||
txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -24,6 +24,7 @@
|
||||
if ($routeParams) {
|
||||
// it's an API request, add the current client culture as a header value
|
||||
config.headers["X-UMB-CULTURE"] = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;
|
||||
config.headers["X-UMB-SEGMENT"] = $routeParams.csegment ? $routeParams.csegment : null;
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
@@ -90,7 +90,7 @@ angular.module('umbraco.mocks').
|
||||
name: "1 column layout",
|
||||
sections: [
|
||||
{
|
||||
grid: 12,
|
||||
grid: 12
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -98,7 +98,7 @@ angular.module('umbraco.mocks').
|
||||
name: "2 column layout",
|
||||
sections: [
|
||||
{
|
||||
grid: 4,
|
||||
grid: 4
|
||||
},
|
||||
{
|
||||
grid: 8
|
||||
@@ -139,7 +139,7 @@ angular.module('umbraco.mocks').
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -698,7 +698,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
// /belle/#/content/edit/9876 (where 9876 is the new id)
|
||||
|
||||
//clear the query strings
|
||||
navigationService.clearSearch(["cculture"]);
|
||||
navigationService.clearSearch(["cculture", "csegment"]);
|
||||
if (softRedirect) {
|
||||
navigationService.setSoftRedirect();
|
||||
}
|
||||
|
||||
@@ -159,9 +159,16 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
//the alias in model state can be in dot notation which indicates
|
||||
// * the first part is the content property alias
|
||||
// * the second part is the field to which the valiation msg is associated with
|
||||
//There will always be at least 3 parts for content properties since all model errors for properties are prefixed with "_Properties"
|
||||
//There will always be at least 4 parts for content properties since all model errors for properties are prefixed with "_Properties"
|
||||
//If it is not prefixed with "_Properties" that means the error is for a field of the object directly.
|
||||
|
||||
// Example: "_Properties.headerImage.en-US.mySegment.myField"
|
||||
// * it's for a property since it has a _Properties prefix
|
||||
// * it's for the headerImage property type
|
||||
// * it's for the en-US culture
|
||||
// * it's for the mySegment segment
|
||||
// * it's for the myField html field (optional)
|
||||
|
||||
var parts = e.split(".");
|
||||
|
||||
//Check if this is for content properties - specific to content/media/member editors because those are special
|
||||
@@ -179,16 +186,23 @@ function formHelper(angularHelper, serverValidationManager, notificationsService
|
||||
}
|
||||
}
|
||||
|
||||
//if it contains 3 '.' then we will wire it up to a property's html field
|
||||
var segment = null;
|
||||
if (parts.length > 3) {
|
||||
//add an error with a reference to the field for which the validation belongs too
|
||||
serverValidationManager.addPropertyError(propertyAlias, culture, parts[3], modelState[e][0]);
|
||||
segment = parts[3];
|
||||
//special check in case the string is formatted this way
|
||||
if (segment === "null") {
|
||||
segment = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//add a generic error for the property, no reference to a specific html field
|
||||
serverValidationManager.addPropertyError(propertyAlias, culture, "", modelState[e][0]);
|
||||
|
||||
var htmlFieldReference = "";
|
||||
if (parts.length > 4) {
|
||||
htmlFieldReference = parts[4] || "";
|
||||
}
|
||||
|
||||
// add a generic error for the property
|
||||
serverValidationManager.addPropertyError(propertyAlias, culture, htmlFieldReference, modelState[e][0], segment);
|
||||
|
||||
} else {
|
||||
|
||||
//Everthing else is just a 'Field'... the field name could contain any level of 'parts' though, for example:
|
||||
|
||||
@@ -29,7 +29,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
|
||||
|
||||
|
||||
//A list of query strings defined that when changed will not cause a reload of the route
|
||||
var nonRoutingQueryStrings = ["mculture", "cculture", "lq", "sr"];
|
||||
var nonRoutingQueryStrings = ["mculture", "cculture", "csegment", "lq", "sr"];
|
||||
var retainedQueryStrings = ["mculture"];
|
||||
|
||||
function setMode(mode) {
|
||||
|
||||
@@ -13,13 +13,14 @@ function serverValidationManager($timeout) {
|
||||
var callbacks = [];
|
||||
|
||||
/** calls the callback specified with the errors specified, used internally */
|
||||
function executeCallback(self, errorsForCallback, callback, culture) {
|
||||
function executeCallback(self, errorsForCallback, callback, culture, segment) {
|
||||
|
||||
callback.apply(self, [
|
||||
false, // pass in a value indicating it is invalid
|
||||
errorsForCallback, // pass in the errors for this item
|
||||
self.items, // pass in all errors in total
|
||||
culture // pass the culture that we are listing for.
|
||||
culture, // pass the culture that we are listing for.
|
||||
segment // pass the segment that we are listing for.
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -35,7 +36,8 @@ function serverValidationManager($timeout) {
|
||||
});
|
||||
}
|
||||
|
||||
function getPropertyErrors(self, propertyAlias, culture, fieldName) {
|
||||
|
||||
function getPropertyErrors(self, propertyAlias, culture, segment, fieldName) {
|
||||
if (!Utilities.isString(propertyAlias)) {
|
||||
throw "propertyAlias must be a string";
|
||||
}
|
||||
@@ -46,22 +48,28 @@ function serverValidationManager($timeout) {
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
//find all errors for this property
|
||||
return _.filter(self.items, function (item) {
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
}
|
||||
|
||||
function getCultureErrors(self, culture) {
|
||||
function getVariantErrors(self, culture, segment) {
|
||||
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
//find all errors for this property
|
||||
return _.filter(self.items, function (item) {
|
||||
return (item.culture === culture);
|
||||
return (item.culture === culture && item.segment === segment);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,21 +79,21 @@ function serverValidationManager($timeout) {
|
||||
//its a field error callback
|
||||
var fieldErrors = getFieldErrors(self, callbacks[cb].fieldName);
|
||||
if (fieldErrors.length > 0) {
|
||||
executeCallback(self, fieldErrors, callbacks[cb].callback, callbacks[cb].culture);
|
||||
executeCallback(self, fieldErrors, callbacks[cb].callback, callbacks[cb].culture, callbacks[cb].segment);
|
||||
}
|
||||
}
|
||||
else if (callbacks[cb].propertyAlias != null) {
|
||||
//its a property error
|
||||
var propErrors = getPropertyErrors(self, callbacks[cb].propertyAlias, callbacks[cb].culture, callbacks[cb].fieldName);
|
||||
var propErrors = getPropertyErrors(self, callbacks[cb].propertyAlias, callbacks[cb].culture, callbacks[cb].segment, callbacks[cb].fieldName);
|
||||
if (propErrors.length > 0) {
|
||||
executeCallback(self, propErrors, callbacks[cb].callback, callbacks[cb].culture);
|
||||
executeCallback(self, propErrors, callbacks[cb].callback, callbacks[cb].culture, callbacks[cb].segment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//its a culture error
|
||||
var cultureErrors = getCultureErrors(self, callbacks[cb].culture);
|
||||
if (cultureErrors.length > 0) {
|
||||
executeCallback(self, cultureErrors, callbacks[cb].callback, callbacks[cb].culture);
|
||||
//its a variant error
|
||||
var variantErrors = getVariantErrors(self, callbacks[cb].culture, callbacks[cb].segment);
|
||||
if (variantErrors.length > 0) {
|
||||
executeCallback(self, variantErrors, callbacks[cb].callback, callbacks[cb].culture, callbacks[cb].segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,20 +158,27 @@ function serverValidationManager($timeout) {
|
||||
* field alias to listen for.
|
||||
* If propertyAlias is null, then this subscription is for a field property (not a user defined property).
|
||||
*/
|
||||
subscribe: function (propertyAlias, culture, fieldName, callback) {
|
||||
subscribe: function (propertyAlias, culture, fieldName, callback, segment) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var id = String.CreateGuid();
|
||||
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
if (propertyAlias === null) {
|
||||
callbacks.push({
|
||||
propertyAlias: null,
|
||||
culture: culture,
|
||||
segment: segment,
|
||||
fieldName: fieldName,
|
||||
callback: callback,
|
||||
id: id
|
||||
@@ -175,6 +190,7 @@ function serverValidationManager($timeout) {
|
||||
callbacks.push({
|
||||
propertyAlias: propertyAlias,
|
||||
culture: culture,
|
||||
segment: segment,
|
||||
fieldName: fieldName,
|
||||
callback: callback,
|
||||
id: id
|
||||
@@ -199,25 +215,29 @@ function serverValidationManager($timeout) {
|
||||
* @param {} fieldName
|
||||
* @returns {}
|
||||
*/
|
||||
unsubscribe: function (propertyAlias, culture, fieldName) {
|
||||
unsubscribe: function (propertyAlias, culture, fieldName, segment) {
|
||||
|
||||
//normalize culture to null
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
if (propertyAlias === null) {
|
||||
|
||||
//remove all callbacks for the content field
|
||||
callbacks = _.reject(callbacks, function (item) {
|
||||
return item.propertyAlias === null && item.culture === culture && item.fieldName === fieldName;
|
||||
return item.propertyAlias === null && item.culture === culture && item.segment === segment && item.fieldName === fieldName;
|
||||
});
|
||||
|
||||
}
|
||||
else if (propertyAlias !== undefined) {
|
||||
//remove all callbacks for the content property
|
||||
callbacks = _.reject(callbacks, function (item) {
|
||||
return item.propertyAlias === propertyAlias && item.culture === culture &&
|
||||
return item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment &&
|
||||
(item.fieldName === fieldName ||
|
||||
((item.fieldName === undefined || item.fieldName === "") && (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
@@ -236,16 +256,20 @@ function serverValidationManager($timeout) {
|
||||
* This will always return any callbacks registered for just the property (i.e. field name is empty) and for ones with an
|
||||
* explicit field name set.
|
||||
*/
|
||||
getPropertyCallbacks: function (propertyAlias, culture, fieldName) {
|
||||
getPropertyCallbacks: function (propertyAlias, culture, fieldName, segment) {
|
||||
|
||||
//normalize culture to null
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
var found = _.filter(callbacks, function (item) {
|
||||
//returns any callback that have been registered directly against the field and for only the property
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (item.fieldName === undefined || item.fieldName === "")));
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (item.fieldName === undefined || item.fieldName === "")));
|
||||
});
|
||||
return found;
|
||||
},
|
||||
@@ -262,7 +286,7 @@ function serverValidationManager($timeout) {
|
||||
getFieldCallbacks: function (fieldName) {
|
||||
var found = _.filter(callbacks, function (item) {
|
||||
//returns any callback that have been registered directly against the field
|
||||
return (item.propertyAlias === null && item.culture === "invariant" && item.fieldName === fieldName);
|
||||
return (item.propertyAlias === null && item.culture === "invariant" && item.segment === null && item.fieldName === fieldName);
|
||||
});
|
||||
return found;
|
||||
},
|
||||
@@ -274,12 +298,29 @@ function serverValidationManager($timeout) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Gets all callbacks that has been registered using the subscribe method for the culture.
|
||||
* Gets all callbacks that has been registered using the subscribe method for the culture. Not including segments.
|
||||
*/
|
||||
getCultureCallbacks: function (culture) {
|
||||
var found = _.filter(callbacks, function (item) {
|
||||
//returns any callback that have been registered directly/ONLY against the culture
|
||||
return (item.culture === culture && item.propertyAlias === null && item.fieldName === null);
|
||||
return (item.culture === culture && item.segment === null && item.propertyAlias === null && item.fieldName === null);
|
||||
});
|
||||
return found;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name getVariantCallbacks
|
||||
* @methodOf umbraco.services.serverValidationManager
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Gets all callbacks that has been registered using the subscribe method for the culture and segment.
|
||||
*/
|
||||
getVariantCallbacks: function (culture, segment) {
|
||||
var found = _.filter(callbacks, function (item) {
|
||||
//returns any callback that have been registered directly against the given culture and given segment.
|
||||
return (item.culture === culture && item.segment === segment && item.propertyAlias === null && item.fieldName === null);
|
||||
});
|
||||
return found;
|
||||
},
|
||||
@@ -303,6 +344,7 @@ function serverValidationManager($timeout) {
|
||||
this.items.push({
|
||||
propertyAlias: null,
|
||||
culture: "invariant",
|
||||
segment: null,
|
||||
fieldName: fieldName,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
@@ -314,7 +356,7 @@ function serverValidationManager($timeout) {
|
||||
var cbs = this.getFieldCallbacks(fieldName);
|
||||
//call each callback for this error
|
||||
for (var cb in cbs) {
|
||||
executeCallback(this, errorsForCallback, cbs[cb].callback, null);
|
||||
executeCallback(this, errorsForCallback, cbs[cb].callback, null, null);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -327,7 +369,7 @@ function serverValidationManager($timeout) {
|
||||
* @description
|
||||
* Adds an error message for the content property
|
||||
*/
|
||||
addPropertyError: function (propertyAlias, culture, fieldName, errorMsg) {
|
||||
addPropertyError: function (propertyAlias, culture, fieldName, errorMsg, segment) {
|
||||
if (!propertyAlias) {
|
||||
return;
|
||||
}
|
||||
@@ -336,31 +378,36 @@ function serverValidationManager($timeout) {
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
//only add the item if it doesn't exist
|
||||
if (!this.hasPropertyError(propertyAlias, culture, fieldName)) {
|
||||
if (!this.hasPropertyError(propertyAlias, culture, fieldName, segment)) {
|
||||
this.items.push({
|
||||
propertyAlias: propertyAlias,
|
||||
culture: culture,
|
||||
segment: segment,
|
||||
fieldName: fieldName,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
}
|
||||
|
||||
//find all errors for this item
|
||||
var errorsForCallback = getPropertyErrors(this, propertyAlias, culture, fieldName);
|
||||
var errorsForCallback = getPropertyErrors(this, propertyAlias, culture, segment, fieldName);
|
||||
//we should now call all of the call backs registered for this error
|
||||
var cbs = this.getPropertyCallbacks(propertyAlias, culture, fieldName);
|
||||
var cbs = this.getPropertyCallbacks(propertyAlias, culture, fieldName, segment);
|
||||
//call each callback for this error
|
||||
for (var cb in cbs) {
|
||||
executeCallback(this, errorsForCallback, cbs[cb].callback, culture);
|
||||
executeCallback(this, errorsForCallback, cbs[cb].callback, culture, segment);
|
||||
}
|
||||
|
||||
//execute culture specific callbacks here too when a propery error is added
|
||||
var cultureCbs = this.getCultureCallbacks(culture);
|
||||
//execute variant specific callbacks here too when a propery error is added
|
||||
var variantCbs = this.getVariantCallbacks(culture, segment);
|
||||
//call each callback for this error
|
||||
for (var cb in cultureCbs) {
|
||||
executeCallback(this, errorsForCallback, cultureCbs[cb].callback, culture);
|
||||
for (var cb in variantCbs) {
|
||||
executeCallback(this, errorsForCallback, variantCbs[cb].callback, culture, segment);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -373,7 +420,7 @@ function serverValidationManager($timeout) {
|
||||
* @description
|
||||
* Removes an error message for the content property
|
||||
*/
|
||||
removePropertyError: function (propertyAlias, culture, fieldName) {
|
||||
removePropertyError: function (propertyAlias, culture, fieldName, segment) {
|
||||
|
||||
if (!propertyAlias) {
|
||||
return;
|
||||
@@ -383,10 +430,14 @@ function serverValidationManager($timeout) {
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
//remove the item
|
||||
this.items = _.reject(this.items, function (item) {
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -405,7 +456,10 @@ function serverValidationManager($timeout) {
|
||||
callbacks[cb].callback.apply(this, [
|
||||
true, //pass in a value indicating it is VALID
|
||||
[], //pass in empty collection
|
||||
[]]); //pass in empty collection
|
||||
[],
|
||||
null,
|
||||
null]
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -431,16 +485,20 @@ function serverValidationManager($timeout) {
|
||||
* @description
|
||||
* Gets the error message for the content property
|
||||
*/
|
||||
getPropertyError: function (propertyAlias, culture, fieldName) {
|
||||
getPropertyError: function (propertyAlias, culture, fieldName, segment) {
|
||||
|
||||
//normalize culture to null
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
var err = _.find(this.items, function (item) {
|
||||
//return true if the property alias matches and if an empty field name is specified or the field name matches
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
return err;
|
||||
},
|
||||
@@ -457,7 +515,7 @@ function serverValidationManager($timeout) {
|
||||
getFieldError: function (fieldName) {
|
||||
var err = _.find(this.items, function (item) {
|
||||
//return true if the property alias matches and if an empty field name is specified or the field name matches
|
||||
return (item.propertyAlias === null && item.culture === "invariant" && item.fieldName === fieldName);
|
||||
return (item.propertyAlias === null && item.culture === "invariant" && item.segment === null && item.fieldName === fieldName);
|
||||
});
|
||||
return err;
|
||||
},
|
||||
@@ -471,16 +529,20 @@ function serverValidationManager($timeout) {
|
||||
* @description
|
||||
* Checks if the content property + culture + field name combo has an error
|
||||
*/
|
||||
hasPropertyError: function (propertyAlias, culture, fieldName) {
|
||||
hasPropertyError: function (propertyAlias, culture, fieldName, segment) {
|
||||
|
||||
//normalize culture to null
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
var err = _.find(this.items, function (item) {
|
||||
//return true if the property alias matches and if an empty field name is specified or the field name matches
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
return (item.propertyAlias === propertyAlias && item.culture === culture && item.segment === segment && (item.fieldName === fieldName || (fieldName === undefined || fieldName === "")));
|
||||
});
|
||||
return err ? true : false;
|
||||
},
|
||||
@@ -497,12 +559,11 @@ function serverValidationManager($timeout) {
|
||||
hasFieldError: function (fieldName) {
|
||||
var err = _.find(this.items, function (item) {
|
||||
//return true if the property alias matches and if an empty field name is specified or the field name matches
|
||||
return (item.propertyAlias === null && item.culture === "invariant" && item.fieldName === fieldName);
|
||||
return (item.propertyAlias === null && item.culture === "invariant" && item.segment === null && item.fieldName === fieldName);
|
||||
});
|
||||
return err ? true : false;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name hasCultureError
|
||||
@@ -513,14 +574,40 @@ function serverValidationManager($timeout) {
|
||||
* Checks if the given culture has an error
|
||||
*/
|
||||
hasCultureError: function (culture) {
|
||||
|
||||
//normalize culture to null
|
||||
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
|
||||
var err = _.find(this.items, function (item) {
|
||||
return (item.culture === culture && item.segment === null);
|
||||
});
|
||||
return err ? true : false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name hasVariantError
|
||||
* @methodOf umbraco.services.serverValidationManager
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if the given culture has an error
|
||||
*/
|
||||
hasVariantError: function (culture, segment) {
|
||||
|
||||
//normalize culture to "invariant"
|
||||
if (!culture) {
|
||||
culture = "invariant";
|
||||
}
|
||||
//normalize segment to null
|
||||
if (!segment) {
|
||||
segment = null;
|
||||
}
|
||||
|
||||
var err = _.find(this.items, function (item) {
|
||||
return item.culture === culture;
|
||||
return (item.culture === culture && item.segment === segment);
|
||||
});
|
||||
return err ? true : false;
|
||||
},
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
function registerAllTours() {
|
||||
tours = [];
|
||||
return tourResource.getTours().then(function (tourFiles) {
|
||||
tourFiles.forEach(tourFile => {
|
||||
Utilities.forEach(tourFiles, tourFile => {
|
||||
|
||||
tourFile.tours.forEach(newTour => {
|
||||
Utilities.forEach(tourFile.tours, newTour => {
|
||||
validateTour(newTour);
|
||||
validateTourRegistration(newTour);
|
||||
tours.push(newTour);
|
||||
@@ -251,7 +251,7 @@
|
||||
*/
|
||||
function validateTourRegistration(tour) {
|
||||
// check for existing tours with the same alias
|
||||
tours.forEach(existingTour => {
|
||||
Utilities.forEach(tours, existingTour => {
|
||||
if (existingTour.alias === tour.alias) {
|
||||
throw "A tour with the alias " + tour.alias + " is already registered";
|
||||
}
|
||||
@@ -267,17 +267,17 @@
|
||||
var deferred = $q.defer();
|
||||
currentUserResource.getTours().then(function (storedTours) {
|
||||
|
||||
storedTours.forEach(storedTour => {
|
||||
Utilities.forEach(storedTours, storedTour => {
|
||||
|
||||
if (storedTour.completed === true) {
|
||||
tours.forEach(tour => {
|
||||
Utilities.forEach(tours, tour => {
|
||||
if (storedTour.alias === tour.alias) {
|
||||
tour.completed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (storedTour.disabled === true) {
|
||||
tours.forEach(tour => {
|
||||
Utilities.forEach(tours, tour => {
|
||||
if (storedTour.alias === tour.alias) {
|
||||
tour.disabled = true;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
var saveModel = _.pick(displayModel,
|
||||
'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes',
|
||||
'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed',
|
||||
'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'isElement');
|
||||
'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'allowSegmentVariant', 'isElement');
|
||||
|
||||
// TODO: Map these
|
||||
saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; });
|
||||
@@ -83,7 +83,7 @@
|
||||
});
|
||||
|
||||
var saveProperties = _.map(realProperties, function (p) {
|
||||
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData', 'allowCultureVariant');
|
||||
var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData', 'allowCultureVariant', 'allowSegmentVariant');
|
||||
return saveProperty;
|
||||
});
|
||||
|
||||
@@ -367,6 +367,7 @@
|
||||
name: v.name || "", //if its null/empty,we must pass up an empty string else we get json converter errors
|
||||
properties: getContentProperties(v.tabs),
|
||||
culture: v.language ? v.language.culture : null,
|
||||
segment: v.segment,
|
||||
publish: v.publish,
|
||||
save: v.save,
|
||||
releaseDate: v.releaseDate,
|
||||
@@ -393,38 +394,59 @@
|
||||
*/
|
||||
formatContentGetData: function(displayModel) {
|
||||
|
||||
//We need to check for invariant properties among the variant variants.
|
||||
//When we detect this, we want to make sure that the property object instance is the
|
||||
//same reference object between all variants instead of a copy (which it will be when
|
||||
//return from the JSON structure).
|
||||
// We need to check for invariant properties among the variant variants,
|
||||
// as the value of an invariant property is shared between different variants.
|
||||
// A property can be culture invariant, segment invariant, or both.
|
||||
// When we detect this, we want to make sure that the property object instance is the
|
||||
// same reference object between all variants instead of a copy (which it will be when
|
||||
// return from the JSON structure).
|
||||
|
||||
if (displayModel.variants && displayModel.variants.length > 1) {
|
||||
// Collect all invariant properties from the variants that are either the
|
||||
// default language variant or the default segment variant.
|
||||
var defaultVariants = _.filter(displayModel.variants, function (variant) {
|
||||
var isDefaultLanguage = variant.language && variant.language.isDefault;
|
||||
var isDefaultSegment = variant.segment == null;
|
||||
|
||||
var invariantProperties = [];
|
||||
|
||||
//collect all invariant properties on the first first variant
|
||||
var firstVariant = displayModel.variants[0];
|
||||
_.each(firstVariant.tabs, function(tab, tabIndex) {
|
||||
_.each(tab.properties, function (property, propIndex) {
|
||||
//in theory if there's more than 1 variant, that means they would all have a language
|
||||
//but we'll do our safety checks anyways here
|
||||
if (firstVariant.language && !property.culture) {
|
||||
invariantProperties.push({
|
||||
tabIndex: tabIndex,
|
||||
propIndex: propIndex,
|
||||
property: property
|
||||
});
|
||||
}
|
||||
});
|
||||
return isDefaultLanguage || isDefaultSegment;
|
||||
});
|
||||
|
||||
if (defaultVariants.length > 0) {
|
||||
_.each(defaultVariants, function (defaultVariant) {
|
||||
var invariantProps = [];
|
||||
|
||||
//now assign this same invariant property instance to the same index of the other variants property array
|
||||
for (var j = 1; j < displayModel.variants.length; j++) {
|
||||
var variant = displayModel.variants[j];
|
||||
_.each(defaultVariant.tabs, function (tab, tabIndex) {
|
||||
_.each(tab.properties, function (property, propIndex) {
|
||||
// culture == null -> property is culture invariant
|
||||
// segment == null -> property is *possibly* segment invariant
|
||||
if (!property.culture || !property.segment) {
|
||||
invariantProps.push({
|
||||
tabIndex: tabIndex,
|
||||
propIndex: propIndex,
|
||||
property: property
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
_.each(invariantProperties, function (invProp) {
|
||||
variant.tabs[invProp.tabIndex].properties[invProp.propIndex] = invProp.property;
|
||||
var otherVariants = _.filter(displayModel.variants, function (variant) {
|
||||
return variant !== defaultVariant;
|
||||
});
|
||||
|
||||
// now assign this same invariant property instance to the same index of the other variants property array
|
||||
_.each(otherVariants, function (variant) {
|
||||
_.each(invariantProps, function (invProp) {
|
||||
var tab = variant.tabs[invProp.tabIndex];
|
||||
var prop = tab.properties[invProp.propIndex];
|
||||
|
||||
var inheritsCulture = prop.culture === invProp.property.culture && prop.segment == null && invProp.property.segment == null;
|
||||
var inheritsSegment = prop.segment === invProp.property.segment && !prop.culture;
|
||||
|
||||
if (inheritsCulture || inheritsSegment) {
|
||||
tab.properties[invProp.propIndex] = invProp.property;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ function umbSessionStorage($window) {
|
||||
return {
|
||||
|
||||
get: function (key) {
|
||||
return JSON.parse(storage["umb_" + key]);
|
||||
return Utilities.fromJson(storage["umb_" + key]);
|
||||
},
|
||||
|
||||
set: function (key, value) {
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
@import "components/overlays.less";
|
||||
@import "components/card.less";
|
||||
@import "components/editor/umb-editor.less";
|
||||
@import "components/editor/umb-variant-switcher.less";
|
||||
@import "components/umb-sub-views.less";
|
||||
@import "components/umb-editor-navigation.less";
|
||||
@import "components/umb-editor-navigation-item.less";
|
||||
@@ -137,6 +138,7 @@
|
||||
@import "components/tooltip/umb-tooltip-list.less";
|
||||
@import "components/overlays/umb-overlay-backdrop.less";
|
||||
@import "components/overlays/umb-itempicker.less";
|
||||
@import "components/overlays/umb-variant-selector-overlay";
|
||||
@import "components/umb-grid.less";
|
||||
@import "components/umb-empty-state.less";
|
||||
@import "components/umb-property-editor.less";
|
||||
|
||||
@@ -2,20 +2,28 @@
|
||||
Library of card related compoents, like the right-hand icon list on the grid "cards"
|
||||
*/
|
||||
|
||||
.umb-card{
|
||||
.umb-card {
|
||||
position: relative;
|
||||
padding: 5px 10px 5px 10px;
|
||||
background: @white;
|
||||
width: 100%;
|
||||
|
||||
.title{padding: 12px; color: @gray-3; border-bottom: 1px solid @gray-8; font-weight: 400; font-size: 16px; text-transform: none; margin: 0 -10px 10px -10px;}
|
||||
.title {
|
||||
padding: 12px;
|
||||
color: @gray-3;
|
||||
border-bottom: 1px solid @gray-8;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
text-transform: none;
|
||||
margin: 0 -10px 10px -10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.umb-card-thumb{
|
||||
.umb-card-thumb {
|
||||
text-align: center;
|
||||
|
||||
i{
|
||||
i {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
line-height: 40px;
|
||||
@@ -25,18 +33,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
.umb-card-content{
|
||||
.item-title{color: @blackLight; font-weight: 400; border: none; font-size: 16px; text-transform: none; margin-bottom: 3px;}
|
||||
p{color: @gray-3; margin-bottom: 1px;}
|
||||
.umb-card-content {
|
||||
.item-title {
|
||||
color: @blackLight;
|
||||
font-weight: 400;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
text-transform: none;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
p {
|
||||
color: @gray-3;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-card-actions{
|
||||
.umb-card-actions {
|
||||
padding-top: 10px;
|
||||
border-top: @gray-10 1px solid;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.umb-card-icons{
|
||||
.umb-card-icons {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
@@ -45,7 +63,7 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.umb-card-icons.vertical{
|
||||
.umb-card-icons.vertical {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
@@ -53,19 +71,19 @@
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.umb-card-icons li{
|
||||
.umb-card-icons li {
|
||||
display: inline-block;
|
||||
margin: 0 2px 0 2px;
|
||||
}
|
||||
|
||||
.umb-card-icons.vertical li{
|
||||
.umb-card-icons.vertical li {
|
||||
float: right;
|
||||
display: block;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
//card iocn list
|
||||
.umb-card-list{
|
||||
.umb-card-list {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -81,7 +99,7 @@
|
||||
|
||||
|
||||
//Card icon grid for picking items off a card
|
||||
.umb-card-grid{
|
||||
.umb-card-grid {
|
||||
padding: 0;
|
||||
margin: 0 auto;
|
||||
list-style: none;
|
||||
@@ -101,14 +119,24 @@
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.umb-card-grid.-six-in-row li {
|
||||
flex: 0 0 25%;
|
||||
max-width: 117px;
|
||||
}
|
||||
|
||||
.umb-card-grid.-four-in-row li {
|
||||
flex: 0 0 25%;
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
.umb-card-grid.-three-in-row li {
|
||||
flex: 0 0 33.33%;
|
||||
max-width:33.33%;
|
||||
flex: 0 0 33.333%;
|
||||
max-width:33.333%;
|
||||
|
||||
i {
|
||||
font-size: 36px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-card-grid .umb-card-grid-item {
|
||||
@@ -117,7 +145,7 @@
|
||||
width: 100%;
|
||||
//height: 100%;
|
||||
padding-top: 100%;
|
||||
border-radius: 3px;
|
||||
border-radius: @baseBorderRadius * 2;
|
||||
transition: background-color 120ms;
|
||||
|
||||
> span {
|
||||
@@ -141,6 +169,41 @@
|
||||
color: @ui-option-type-hover;
|
||||
}
|
||||
|
||||
.umb-card-grid .umb-card-grid-item-slot {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-top: 100%;
|
||||
border-radius: @baseBorderRadius * 2;
|
||||
|
||||
box-sizing: border-box;
|
||||
transition: background-color 120ms;
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: @ui-option-hover;
|
||||
> span {
|
||||
color:@ui-action-discreet-type-hover;
|
||||
border-color:@ui-action-discreet-border-hover;
|
||||
}
|
||||
}
|
||||
|
||||
> span {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
background-color: transparent;
|
||||
border:1.5px dashed @ui-action-discreet-border;
|
||||
border-radius: @baseBorderRadius * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.umb-card-grid a {
|
||||
color: @ui-option-type;
|
||||
text-decoration: none;
|
||||
@@ -149,6 +212,7 @@
|
||||
.umb-card-grid i {
|
||||
font-size: 30px;
|
||||
line-height: 20px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
@@ -164,7 +228,7 @@
|
||||
|
||||
|
||||
//Round icon-like button - this should be somewhere else
|
||||
.umb-btn-round{
|
||||
.umb-btn-round {
|
||||
padding: 4px 6px 4px 6px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
@@ -174,7 +238,8 @@
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.umb-btn-round:hover, .umb-btn-round:hover *{
|
||||
.umb-btn-round:hover,
|
||||
.umb-btn-round:hover * {
|
||||
background: @blueDark !important;
|
||||
color: @white !important;
|
||||
border-color: @blueDark !important;
|
||||
|
||||
@@ -162,161 +162,6 @@ a.umb-editor-header__close-split-view:hover {
|
||||
}
|
||||
}
|
||||
|
||||
/* variant switcher */
|
||||
.umb-variant-switcher__toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
margin: 1px 1px;
|
||||
right: 0;
|
||||
height: 30px;
|
||||
text-decoration: none !important;
|
||||
font-size: 13px;
|
||||
color: @ui-action-discreet-type;
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
max-width: 50%;
|
||||
white-space: nowrap;
|
||||
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
button.umb-variant-switcher__toggle {
|
||||
transition: color 0.2s ease-in-out;
|
||||
&:hover {
|
||||
//background-color: @gray-10;
|
||||
color: @ui-action-discreet-type-hover;
|
||||
.umb-variant-switcher__expand {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
&.--error {
|
||||
&::before {
|
||||
content: '!';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: @errorBackground;
|
||||
color: @errorText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__expand {
|
||||
color: @ui-action-discreet-type;
|
||||
margin-top: 3px;
|
||||
margin-left: 5px;
|
||||
margin-right: -5px;
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid @gray-9;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--current {
|
||||
color: @ui-light-active-type;
|
||||
}
|
||||
.umb-variant-switcher__item.--current .umb-variant-switcher__name-wrapper {
|
||||
border-left: 4px solid @ui-active;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--not-allowed:not(.--current) .umb-variant-switcher__name-wrapper:hover {
|
||||
//background-color: @white !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:hover .umb-variant-switcher__split-view {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--error {
|
||||
.umb-variant-switcher__name {
|
||||
color: @red;
|
||||
&::after {
|
||||
content: '!';
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5px;
|
||||
top: -3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 7px;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: @errorBackground;
|
||||
color: @errorText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name-wrapper {
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
padding-top: 6px !important;
|
||||
padding-bottom: 6px !important;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-left: 2px solid transparent;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__state {
|
||||
font-size: 13px;
|
||||
color: @gray-4;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__split-view {
|
||||
font-size: 13px;
|
||||
display: none;
|
||||
padding: 16px 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: @white;
|
||||
|
||||
&:hover {
|
||||
background-color: @ui-option-hover;
|
||||
color: @ui-option-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
// container
|
||||
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
/* variant switcher */
|
||||
.umb-variant-switcher__toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
margin: 1px 1px;
|
||||
right: 0;
|
||||
height: 30px;
|
||||
text-decoration: none !important;
|
||||
font-size: 13px;
|
||||
color: @ui-action-discreet-type;
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
max-width: 50%;
|
||||
white-space: nowrap;
|
||||
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
button.umb-variant-switcher__toggle {
|
||||
transition: color 0.2s ease-in-out;
|
||||
&:hover {
|
||||
//background-color: @gray-10;
|
||||
color: @ui-action-discreet-type-hover;
|
||||
.umb-variant-switcher__expand {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
&.--error {
|
||||
&::before {
|
||||
content: '!';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: @errorBackground;
|
||||
color: @errorText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__expand {
|
||||
color: @ui-action-discreet-type;
|
||||
margin-top: 3px;
|
||||
margin-left: 5px;
|
||||
margin-right: -5px;
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
.umb-variant-switcher {
|
||||
min-width: 100%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
margin-top: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid @gray-9;
|
||||
position: relative;
|
||||
.umb-variant-switcher__name-wrapper:hover {
|
||||
.umb-variant-switcher__name {
|
||||
color: @blueMid;
|
||||
}
|
||||
.umb-variant-switcher__state {
|
||||
color: @blueMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
.umb-variant-switcher__item.--state-notCreated:not(.--active) {
|
||||
.umb-variant-switcher__name-wrapper::before {
|
||||
content: "+";
|
||||
display: block;
|
||||
float: left;
|
||||
font-size: 15px;
|
||||
font-weight: 900;
|
||||
padding: 8px 16px 8px 6px;
|
||||
color: @gray-5;
|
||||
}
|
||||
.umb-variant-switcher__item-expand-button + .umb-variant-switcher__name-wrapper::before {
|
||||
padding: 8px 16px 8px 20px;
|
||||
}
|
||||
.umb-variant-switcher__name {
|
||||
color: @gray-5;
|
||||
}
|
||||
.umb-variant-switcher__state {
|
||||
color: @gray-6;
|
||||
}
|
||||
.umb-variant-switcher__name-wrapper::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
border: 1px dashed @gray-9;
|
||||
top: 7px;
|
||||
bottom: 7px;
|
||||
left: 7px;
|
||||
right: 7px;
|
||||
border-radius: 3px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name-wrapper:hover {
|
||||
&::before {
|
||||
color: @blueMid;
|
||||
}
|
||||
.umb-variant-switcher__name {
|
||||
color: @blueMid;
|
||||
}
|
||||
.umb-variant-switcher__state {
|
||||
color: @blueMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
.umb-variant-switcher__item.--state-draft {
|
||||
.umb-variant-switcher__name {
|
||||
color: @gray-5;
|
||||
}
|
||||
&:hover {
|
||||
.umb-variant-switcher__name {
|
||||
color: @blueMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
.umb-variant-switcher.--has-sub-variants {
|
||||
.umb-variant-switcher__item {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item-expand-button {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
flex: 0;
|
||||
align-self: stretch;
|
||||
|
||||
padding-left: 22px !important;
|
||||
padding-right: 14px !important;
|
||||
|
||||
font-size: 12px;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--current {
|
||||
//color: @ui-light-active-type;
|
||||
//background-color: @pinkExtraLight;
|
||||
.umb-variant-switcher__name {
|
||||
//color: @ui-light-active-type;
|
||||
font-weight: 700;
|
||||
}
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-radius: 0 4px 4px 0;
|
||||
background-color: @ui-active-border;
|
||||
width: 4px;
|
||||
top:8px;
|
||||
bottom: 8px;
|
||||
left:0;
|
||||
z-index:1;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--active:not(.--current) .umb-variant-switcher__name-wrapper:hover {
|
||||
//background-color: @white !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:focus .umb-variant-switcher__split-view,
|
||||
.umb-variant-switcher__item:focus-within .umb-variant-switcher__split-view,
|
||||
.umb-variant-switcher__item:hover .umb-variant-switcher__split-view,
|
||||
.umb-variant-switcher__split-view:focus {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--error {
|
||||
.umb-variant-switcher__name {
|
||||
color: @red;
|
||||
&::after {
|
||||
content: '!';
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5px;
|
||||
top: -3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 7px;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: @errorBackground;
|
||||
color: @errorText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name-wrapper {
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
.dropdown-menu>li {
|
||||
> .umb-variant-switcher__name-wrapper {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
> .umb-variant-switcher__item-expand-button + .umb-variant-switcher__name-wrapper {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.umb-variant-switcher__name {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__state {
|
||||
font-size: 12px;
|
||||
color: @gray-4;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__split-view {
|
||||
font-size: 12px;
|
||||
display: none;
|
||||
padding: 20px 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: @white;
|
||||
|
||||
&:hover {
|
||||
background-color: @ui-option-hover;
|
||||
color: @ui-option-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.umb-variant-switcher__sub-variants {
|
||||
|
||||
position: relative;
|
||||
border-bottom: 1px solid @gray-9;
|
||||
background-color: @gray-13;
|
||||
/*
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0px;
|
||||
left: 20px;
|
||||
width: 4px;
|
||||
bottom: 14px;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
background-color: @gray-8;
|
||||
}
|
||||
*/
|
||||
.umb-variant-switcher__item {
|
||||
border-bottom-color: @gray-10;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item.--state-notCreated:not(.--active) {
|
||||
.umb-variant-switcher__name-wrapper::after {
|
||||
left: 55px;// overwrite left to achieve same indentation on the dashed border as language.
|
||||
}
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name-wrapper {
|
||||
|
||||
margin-left: 48px;
|
||||
padding-left: 20px;
|
||||
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&:hover {
|
||||
color: @ui-option-type-hover;
|
||||
background-color: @ui-option-hover;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name {
|
||||
//margin-right: 20px;
|
||||
}
|
||||
.umb-variant-switcher__state {
|
||||
//flex: 0 0 200px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -24,16 +24,19 @@
|
||||
margin-top: 0;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
padding: 20px 20px 0;
|
||||
padding: 30px 30px 0;
|
||||
}
|
||||
|
||||
.umb-overlay__section-header {
|
||||
width: 100%;
|
||||
margin-top:30px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h5 {
|
||||
display: inline;
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
@@ -66,7 +69,7 @@
|
||||
flex-shrink: 1;
|
||||
flex-basis: auto;
|
||||
position: relative;
|
||||
padding: 30px;
|
||||
padding: 20px 30px;
|
||||
background: @white;
|
||||
max-height: calc(100vh - 170px);
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
.umb-itempicker .form-search {
|
||||
margin-top:10px;
|
||||
}
|
||||
.umb-card-grid {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
.umb-variant-selector-overlay {
|
||||
|
||||
|
||||
.umb-variant-selector-entry {
|
||||
.umb-form-check {
|
||||
.umb-form-check__symbol {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.umb-variant-selector-entry__title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
.__secondarytitle {
|
||||
font-weight: normal;
|
||||
color: @gray-5;
|
||||
}
|
||||
}
|
||||
.umb-variant-selector-entry__description {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: @gray-4;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -178,11 +178,11 @@
|
||||
}
|
||||
|
||||
// Validation
|
||||
.umb-sub-views-nav-item__action.-has-error,
|
||||
.show-validation .umb-sub-views-nav-item__action.-has-error,
|
||||
.show-validation .umb-sub-views-nav-item > a.-has-error {
|
||||
color: @red;
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
background-color: @red;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
}
|
||||
|
||||
.umb-group-builder__group.-placeholder {
|
||||
width:100%;
|
||||
min-height: 86px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -136,9 +137,10 @@ input.umb-group-builder__group-title-input:disabled:hover {
|
||||
}
|
||||
|
||||
.umb-group-builder__group-add-property {
|
||||
min-height: 46px;
|
||||
margin-right: 45px;
|
||||
|
||||
width: calc(100% - 315px);
|
||||
margin-left: 270px;
|
||||
min-height: 46px;
|
||||
border-radius: 3px;
|
||||
|
||||
display: flex;
|
||||
@@ -344,8 +346,9 @@ input.umb-group-builder__group-title-input:disabled:hover {
|
||||
|
||||
.umb-group-builder__property-actions {
|
||||
flex: 0 0 44px;
|
||||
text-align: right;
|
||||
margin-top: 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.umb-group-builder__property-action {
|
||||
@@ -473,7 +476,7 @@ input.umb-group-builder__group-sort-value {
|
||||
font-weight: bold;
|
||||
resize: none;
|
||||
line-height: 1.5em;
|
||||
padding-left: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
@@ -498,45 +501,82 @@ input.umb-group-builder__group-sort-value {
|
||||
text-decoration: none;
|
||||
color: @ui-action-type-hover;
|
||||
border-color: @ui-action-border-hover;
|
||||
background-color: @ui-action-discreet-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
.editor-wrapper {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.editor-icon-wrapper {
|
||||
border: 1px solid @gray-8;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
border-radius: 5px;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
.editor {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: stretch;
|
||||
|
||||
.icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
min-height: 80px;
|
||||
|
||||
border: 1px solid @gray-9;
|
||||
color: @ui-action-discreet-type;
|
||||
border-radius: @baseBorderRadius;
|
||||
|
||||
}
|
||||
|
||||
.editor-info {
|
||||
flex: 1 0 auto;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: calc(100% - 48px);
|
||||
min-height: 80px;
|
||||
color: @ui-action-discreet-type;
|
||||
|
||||
&:hover {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
background-color: @ui-action-discreet-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-icon-wrapper {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
flex: 0 0 60px;
|
||||
padding-left: 10px;
|
||||
|
||||
.icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-details {
|
||||
flex: 1 1 auto;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.editor-name {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.editor-details {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
|
||||
.editor-name {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.editor-editor {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
.editor-editor {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-settings-icon {
|
||||
font-size: 18px;
|
||||
margin-top: 8px;
|
||||
.editor-remove-icon {
|
||||
flex: 0 0 48px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
|
||||
min-height: 80px;
|
||||
color: @ui-action-discreet-type;
|
||||
&:hover {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
background-color: @ui-action-discreet-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,6 +584,11 @@ input.umb-group-builder__group-sort-value {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.editor-description {
|
||||
margin-top: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.editor-description,
|
||||
.editor-validation-pattern {
|
||||
min-width: 100%;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
.umb-list--condensed {
|
||||
.umb-list-item {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-list-item {
|
||||
border-bottom: 1px solid @gray-9;
|
||||
border-bottom: 1px solid @gray-11;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
display: flex;
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.umb-list-item:hover,
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
.umb-validation-label {
|
||||
position: absolute;
|
||||
top: 27px;
|
||||
min-width: 100px;
|
||||
max-width: 200px;
|
||||
padding: 1px 5px;
|
||||
z-index: 1;
|
||||
top: 28px;
|
||||
min-width: 80px;
|
||||
max-width: 260px;
|
||||
padding: 2px 6px;
|
||||
background: @red;
|
||||
color: @white;
|
||||
font-size: 11px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5em;
|
||||
border-radius: @baseBorderRadius;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.umb-validation-label:after {
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
}
|
||||
|
||||
.date-wrapper-mini--checkbox{
|
||||
margin: 0 0 0 26px;
|
||||
margin: 0 0 0 28px;
|
||||
}
|
||||
|
||||
.date-wrapper-mini__date {
|
||||
@@ -62,6 +62,10 @@
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
.flatpickr-input > button:first-of-type {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.date-wrapper-mini__date .flatpickr-input > a {
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
@sand-5: #F3ECE8;// added 2019
|
||||
@sand-6: #F6F1EF;// added 2019
|
||||
@sand-7: #F9F7F5;// added 2019
|
||||
@sand-8: #fbfaf9;// added 2019
|
||||
@sand-9: #fdfcfc;// added 2019
|
||||
|
||||
|
||||
// Additional Icon Colours
|
||||
|
||||
@@ -495,7 +495,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
|
||||
//execute them sequentially
|
||||
|
||||
// set selected language to active
|
||||
$scope.languages.forEach(language => {
|
||||
Utilities.forEach($scope.languages, language => {
|
||||
language.active = false;
|
||||
});
|
||||
|
||||
|
||||
@@ -94,6 +94,26 @@
|
||||
return JSON.stringify(obj, toJsonReplacer, pretty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to angular.fromJson
|
||||
*/
|
||||
const fromJson = (val) => {
|
||||
if (!isString(val)) {
|
||||
return val;
|
||||
}
|
||||
return JSON.parse(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not equivalent to angular.forEach. But like the angularJS method this does not fail on null or undefined.
|
||||
*/
|
||||
const forEach = (obj, iterator) => {
|
||||
if (obj) {
|
||||
return obj.forEach(iterator);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
let _utilities = {
|
||||
noop: noop,
|
||||
copy: copy,
|
||||
@@ -106,10 +126,12 @@
|
||||
isString: isString,
|
||||
isNumber: isNumber,
|
||||
isObject: isObject,
|
||||
toJson: toJson
|
||||
fromJson: fromJson,
|
||||
toJson: toJson,
|
||||
forEach: forEach
|
||||
};
|
||||
|
||||
if (typeof (window.Utilities) === 'undefined') {
|
||||
window.Utilities = _utilities;
|
||||
}
|
||||
})(window);
|
||||
})(window);
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.Editors.DataTypeConfigurationPickerController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The controller for the content type editor data type configuration picker dialog
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function DataTypeConfigurationPicker($scope, $filter, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.configs = [];
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
vm.newDataType = newDataType;
|
||||
vm.pickDataType = pickDataType;
|
||||
vm.close = close;
|
||||
|
||||
function activate() {
|
||||
setTitle();
|
||||
load();
|
||||
}
|
||||
|
||||
function setTitle() {
|
||||
if (!$scope.model.title) {
|
||||
localizationService.localize("defaultdialogs_selectEditorConfiguration")
|
||||
.then(function(data){
|
||||
$scope.model.title = data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function load() {
|
||||
|
||||
dataTypeResource.getGroupedDataTypes().then(function(configs) {
|
||||
|
||||
var filteredConfigs = [];
|
||||
|
||||
_.each(configs, function(configGroup) {
|
||||
for(var i = 0; i<configGroup.length; i++) {
|
||||
if (configGroup[i].alias === $scope.model.editor.alias) {
|
||||
filteredConfigs.push(configGroup[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
vm.configs = filteredConfigs;
|
||||
vm.loading = false;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function newDataType() {
|
||||
|
||||
var dataTypeSettings = {
|
||||
propertyEditor: $scope.model.editor,
|
||||
property: $scope.model.property,
|
||||
contentTypeName: $scope.model.contentTypeName,
|
||||
create: true,
|
||||
view: "views/common/infiniteeditors/datatypesettings/datatypesettings.html",
|
||||
submit: function(model) {
|
||||
contentTypeResource.getPropertyTypeScaffold(model.dataType.id).then(function(propertyType) {
|
||||
$scope.model.submit(model.dataType, propertyType, true);
|
||||
editorService.close();
|
||||
});
|
||||
},
|
||||
close: function() {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
|
||||
editorService.open(dataTypeSettings);
|
||||
|
||||
}
|
||||
|
||||
function pickDataType(selectedConfig) {
|
||||
|
||||
selectedConfig.loading = true;
|
||||
dataTypeResource.getById(selectedConfig.id).then(function(dataType) {
|
||||
contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) {
|
||||
selectedConfig.loading = false;
|
||||
$scope.model.submit(dataType, propertyType, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function close() {
|
||||
if($scope.model.close) {
|
||||
$scope.model.close();
|
||||
}
|
||||
}
|
||||
|
||||
activate();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DataTypeConfigurationPickerController", DataTypeConfigurationPicker);
|
||||
|
||||
})();
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<div ng-controller="Umbraco.Editors.DataTypeConfigurationPickerController as vm">
|
||||
<umb-editor-view data-element="editor-data-type-picker">
|
||||
|
||||
<form novalidate name="DataTypeConfigurationPickerForm" val-form-manager>
|
||||
|
||||
<umb-editor-header
|
||||
name="model.title"
|
||||
name-locked="true"
|
||||
hide-alias="true"
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
|
||||
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
|
||||
|
||||
<div ng-if="!vm.loading">
|
||||
|
||||
<ul class="umb-card-grid -three-in-row">
|
||||
<li ng-repeat="dataTypeConfig in vm.configs | orderBy:'name'"
|
||||
data-element="datatypeconfig-{{dataTypeConfig.name}}"
|
||||
ng-click="vm.pickDataType(dataTypeConfig)">
|
||||
<div ng-if="dataTypeConfig.loading" class="umb-card-grid-item__loading">
|
||||
<div class="umb-button__progress"></div>
|
||||
</div>
|
||||
<a class="umb-card-grid-item" href="" title="{{ dataTypeConfig.name }}">
|
||||
<span>
|
||||
<i class="{{ dataTypeConfig.icon }}" ng-class="{'icon-autofill': dataTypeConfig.icon == null}"></i>
|
||||
{{ dataTypeConfig.name }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="umb-card-grid -three-in-row">
|
||||
<li ng-click="vm.newDataType()">
|
||||
<a class="umb-card-grid-item-slot" href="" title="Create a new configuration of {{ model.dataType.name }}">
|
||||
<span>
|
||||
<i class="icon icon-add"></i>
|
||||
Create new
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
shortcut="esc"
|
||||
action="vm.close()">
|
||||
</umb-button>
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
|
||||
</form>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</div>
|
||||
+90
-130
@@ -13,30 +13,23 @@
|
||||
function DataTypePicker($scope, $filter, dataTypeResource, contentTypeResource, localizationService, editorService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
|
||||
vm.showDataTypes = true;
|
||||
vm.dataTypes = [];
|
||||
vm.loading = true;
|
||||
vm.loadingConfigs = false;
|
||||
vm.searchTerm = "";
|
||||
vm.showTabs = false;
|
||||
vm.tabsLoaded = 0;
|
||||
vm.typesAndEditors = [];
|
||||
vm.userConfigured = [];
|
||||
vm.loading = false;
|
||||
vm.tabs = [];
|
||||
vm.labels = {};
|
||||
|
||||
vm.onTabChange = onTabChange;
|
||||
vm.filterItems = filterItems;
|
||||
vm.showDetailsOverlay = showDetailsOverlay;
|
||||
vm.hideDetailsOverlay = hideDetailsOverlay;
|
||||
vm.pickEditor = pickEditor;
|
||||
vm.searchResult = null;
|
||||
|
||||
vm.viewOptionsForEditor = viewOptionsForEditor;
|
||||
vm.pickDataType = pickDataType;
|
||||
vm.pickEditor = pickEditor;
|
||||
vm.close = close;
|
||||
vm.searchTermChanged = searchTermChanged;
|
||||
|
||||
function activate() {
|
||||
setTitle();
|
||||
loadTabs();
|
||||
getGroupedDataTypes();
|
||||
getGroupedPropertyEditors();
|
||||
|
||||
loadTypes();
|
||||
}
|
||||
|
||||
function setTitle() {
|
||||
@@ -44,128 +37,104 @@
|
||||
localizationService.localize("defaultdialogs_selectEditor")
|
||||
.then(function(data){
|
||||
$scope.model.title = data;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function loadTabs() {
|
||||
|
||||
var labels = ["contentTypeEditor_availableEditors", "contentTypeEditor_reuse"];
|
||||
|
||||
localizationService.localizeMany(labels)
|
||||
.then(function(data){
|
||||
vm.labels.availableDataTypes = data[0];
|
||||
vm.labels.reuse = data[1];
|
||||
|
||||
vm.tabs = [{
|
||||
active: true,
|
||||
id: 1,
|
||||
label: vm.labels.availableDataTypes,
|
||||
alias: "Default",
|
||||
typesAndEditors: []
|
||||
}, {
|
||||
active: false,
|
||||
id: 2,
|
||||
label: vm.labels.reuse,
|
||||
alias: "Reuse",
|
||||
userConfigured: []
|
||||
}];
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function getGroupedPropertyEditors() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
dataTypeResource.getGroupedPropertyEditors().then(function(data) {
|
||||
vm.tabs[0].typesAndEditors = data;
|
||||
vm.typesAndEditors = data;
|
||||
vm.tabsLoaded = vm.tabsLoaded + 1;
|
||||
checkIfTabContentIsLoaded();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function getGroupedDataTypes() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
dataTypeResource.getGroupedDataTypes().then(function(data) {
|
||||
vm.tabs[1].userConfigured = data;
|
||||
vm.userConfigured = data;
|
||||
vm.tabsLoaded = vm.tabsLoaded + 1;
|
||||
checkIfTabContentIsLoaded();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function checkIfTabContentIsLoaded() {
|
||||
if (vm.tabsLoaded === 2) {
|
||||
|
||||
function loadTypes() {
|
||||
|
||||
dataTypeResource.getGroupedPropertyEditors().then(function(dataTypes) {
|
||||
vm.dataTypes = dataTypes;
|
||||
vm.loading = false;
|
||||
vm.showTabs = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onTabChange(selectedTab) {
|
||||
vm.tabs.forEach(function(tab) {
|
||||
tab.active = false;
|
||||
});
|
||||
selectedTab.active = true;
|
||||
|
||||
}
|
||||
|
||||
function loadConfigurations() {
|
||||
|
||||
vm.loading = true;
|
||||
vm.loadingConfigs = true;
|
||||
|
||||
dataTypeResource.getGroupedDataTypes().then(function(configs) {
|
||||
vm.configs = configs;
|
||||
vm.loading = false;
|
||||
performeSearch();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function filterItems() {
|
||||
// clear item details
|
||||
$scope.model.itemDetails = null;
|
||||
|
||||
if (vm.searchTerm) {
|
||||
vm.showTabs = false;
|
||||
|
||||
var regex = new RegExp(vm.searchTerm, "i");
|
||||
|
||||
var userConfigured = filterCollection(vm.userConfigured, regex),
|
||||
typesAndEditors = filterCollection(vm.typesAndEditors, regex);
|
||||
|
||||
var totalResults = _.reduce(_.pluck(_.union(userConfigured, typesAndEditors), 'count'), (m, n) => m + n, 0);
|
||||
|
||||
vm.filterResult = {
|
||||
userConfigured: userConfigured,
|
||||
typesAndEditors: typesAndEditors,
|
||||
totalResults: totalResults
|
||||
};
|
||||
|
||||
|
||||
function searchTermChanged() {
|
||||
|
||||
vm.showDataTypes = (vm.searchTerm === "");
|
||||
|
||||
if(vm.loadingConfigs !== true) {
|
||||
loadConfigurations()
|
||||
} else {
|
||||
vm.filterResult = null;
|
||||
vm.showTabs = true;
|
||||
performeSearch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function performeSearch() {
|
||||
|
||||
if (vm.searchTerm) {
|
||||
if (vm.configs) {
|
||||
|
||||
var regex = new RegExp(vm.searchTerm, "i");
|
||||
vm.searchResult = {
|
||||
configs: filterCollection(vm.configs, regex),
|
||||
dataTypes: filterCollection(vm.dataTypes, regex)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
vm.searchResult = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function filterCollection(collection, regex) {
|
||||
return _.map(_.keys(collection), function (key) {
|
||||
|
||||
var filteredDataTypes = $filter('filter')(collection[key], function (dataType) {
|
||||
return regex.test(dataType.name) || regex.test(dataType.alias);
|
||||
});
|
||||
|
||||
return {
|
||||
group: key,
|
||||
count: filteredDataTypes.length,
|
||||
dataTypes: filteredDataTypes
|
||||
entries: $filter('filter')(collection[key], function (dataType) {
|
||||
return regex.test(dataType.name) || regex.test(dataType.alias);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showDetailsOverlay(property) {
|
||||
|
||||
function viewOptionsForEditor(editor) {
|
||||
|
||||
var dataTypeConfigurationPicker = {
|
||||
editor: editor,
|
||||
property: $scope.model.property,
|
||||
contentTypeName: $scope.model.contentTypeName,
|
||||
view: "views/common/infiniteeditors/datatypeconfigurationpicker/datatypeconfigurationpicker.html",
|
||||
size: "small",
|
||||
submit: function(dataType, propertyType, isNew) {
|
||||
submit(dataType, propertyType, isNew);
|
||||
editorService.close();
|
||||
},
|
||||
close: function() {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
|
||||
var propertyDetails = {};
|
||||
propertyDetails.icon = property.icon;
|
||||
propertyDetails.title = property.name;
|
||||
editorService.open(dataTypeConfigurationPicker);
|
||||
|
||||
$scope.model.itemDetails = propertyDetails;
|
||||
}
|
||||
|
||||
function hideDetailsOverlay() {
|
||||
$scope.model.itemDetails = null;
|
||||
function pickDataType(selectedDataType) {
|
||||
selectedDataType.loading = true;
|
||||
dataTypeResource.getById(selectedDataType.id).then(function(dataType) {
|
||||
contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) {
|
||||
selectedDataType.loading = false;
|
||||
submit(dataType, propertyType, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pickEditor(propertyEditor) {
|
||||
@@ -188,16 +157,7 @@
|
||||
};
|
||||
|
||||
editorService.open(dataTypeSettings);
|
||||
}
|
||||
|
||||
function pickDataType(selectedDataType) {
|
||||
selectedDataType.loading = true;
|
||||
dataTypeResource.getById(selectedDataType.id).then(function(dataType) {
|
||||
contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) {
|
||||
selectedDataType.loading = false;
|
||||
submit(dataType, propertyType, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function submit(dataType, propertyType, isNew) {
|
||||
@@ -213,9 +173,9 @@
|
||||
|
||||
$scope.model.submit($scope.model);
|
||||
}
|
||||
|
||||
|
||||
function close() {
|
||||
if ($scope.model.close) {
|
||||
if($scope.model.close) {
|
||||
$scope.model.close();
|
||||
}
|
||||
}
|
||||
|
||||
+59
-105
@@ -15,127 +15,81 @@
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
|
||||
<!-- FILTER -->
|
||||
<div class="umb-control-group -no-border">
|
||||
<div class="form-search">
|
||||
<i class="icon-search"></i>
|
||||
<input type="text"
|
||||
style="width: 100%"
|
||||
ng-change="vm.filterItems()"
|
||||
ng-model="vm.searchTerm"
|
||||
class="umb-search-field search-query search-input input-block-level"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_filter"
|
||||
umb-auto-focus
|
||||
no-dirty-check />
|
||||
style="width: 100%"
|
||||
ng-change="vm.searchTermChanged()"
|
||||
ng-model="vm.searchTerm"
|
||||
class="umb-search-field search-query search-input input-block-level"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_search"
|
||||
umb-auto-focus
|
||||
no-dirty-check />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
|
||||
<umb-load-indicator ng-if="vm.loading === true"></umb-load-indicator>
|
||||
|
||||
<!-- TABS -->
|
||||
<div ng-if="vm.showTabs">
|
||||
<umb-tabs-nav
|
||||
ng-if="vm.tabs"
|
||||
tabs="vm.tabs"
|
||||
on-tab-change="vm.onTabChange(tab)">
|
||||
</umb-tabs-nav>
|
||||
<umb-tab-content ng-repeat="tab in vm.tabs" tab="tab" ng-if="tab.active">
|
||||
<div ng-if="tab.alias==='Default'">
|
||||
<div ng-repeat="(key,value) in tab.typesAndEditors">
|
||||
<h5>{{key}}</h5>
|
||||
<ul class="umb-card-grid -four-in-row" ng-mouseleave="vm.hideDetailsOverlay()">
|
||||
<li ng-repeat="systemDataType in value | orderBy:'name'"
|
||||
data-element="editor-{{systemDataType.name}}"
|
||||
ng-mouseover="vm.showDetailsOverlay(systemDataType)"
|
||||
ng-click="vm.pickEditor(systemDataType)"
|
||||
class="cursor-pointer">
|
||||
<span class="umb-card-grid-item" title="{{ systemDataType.name }}">
|
||||
<span>
|
||||
<i class="{{ systemDataType.icon }}" ng-class="{'icon-autofill': systemDataType.icon == null}"></i>
|
||||
{{ systemDataType.name }}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="tab.alias==='Reuse'">
|
||||
<div ng-repeat="(key,value) in tab.userConfigured">
|
||||
<h5>{{key}}</h5>
|
||||
<ul class="umb-card-grid -four-in-row" ng-mouseleave="vm.hideDetailsOverlay()">
|
||||
<li ng-repeat="dataType in value | orderBy:'name'"
|
||||
data-element="editor-{{dataType.name}}"
|
||||
ng-mouseover="vm.showDetailsOverlay(dataType)"
|
||||
ng-click="vm.pickDataType(dataType)"
|
||||
class="cursor-pointer">
|
||||
<div ng-if="dataType.loading" class="umb-card-grid-item__loading">
|
||||
<div class="umb-button__progress"></div>
|
||||
</div>
|
||||
<span class="umb-card-grid-item" title="{{ dataType.name }}">
|
||||
<span>
|
||||
<i class="{{ dataType.icon }}" ng-class="{'icon-autofill': dataType.icon == null}"></i>
|
||||
{{ dataType.name }}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</umb-tab-content>
|
||||
<div ng-if="vm.loading === false && vm.showDataTypes === true">
|
||||
<div ng-repeat="(key,value) in vm.dataTypes">
|
||||
<h5>{{key | umbCmsTitleCase}}</h5>
|
||||
<ul class="umb-card-grid -six-in-row">
|
||||
<li ng-repeat="dataType in value | orderBy:'name'"
|
||||
data-element="datatype-{{dataType.name}}"
|
||||
ng-click="vm.viewOptionsForEditor(dataType)">
|
||||
<a class="umb-card-grid-item" href="" title="{{ dataType.name }}">
|
||||
<span>
|
||||
<i class="{{ dataType.icon }}" ng-class="{'icon-autofill': dataType.icon == null}"></i>
|
||||
{{ dataType.name }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FILTER RESULTS -->
|
||||
<div ng-if="vm.filterResult">
|
||||
<div ng-if="vm.filterResult.totalResults > 0">
|
||||
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_reuse"></localize></h5>
|
||||
<div ng-repeat="result in vm.filterResult.userConfigured">
|
||||
<div ng-if="result.dataTypes.length > 0">
|
||||
<h5>{{result.group}}</h5>
|
||||
<ul class="umb-card-grid -four-in-row" ng-mouseleave="vm.hideDetailsOverlay()">
|
||||
<li ng-repeat="dataType in result.dataTypes | orderBy:'name'"
|
||||
ng-mouseover="vm.showDetailsOverlay(dataType)"
|
||||
ng-click="vm.pickDataType(dataType)"
|
||||
class="cursor-pointer">
|
||||
<div ng-if="dataType.loading" class="umb-card-grid-item__loading">
|
||||
<div class="umb-button__progress"></div>
|
||||
</div>
|
||||
<span class="umb-card-grid-item" title="{{dataType.name}}">
|
||||
<span>
|
||||
<i class="{{dataType.icon}}" ng-class="{'icon-autofill': dataType.icon == null}"></i>
|
||||
{{dataType.name}}
|
||||
</span>
|
||||
<!-- SEARCH RESULTS -->
|
||||
<div ng-if="vm.loading === false && vm.showDataTypes === false && vm.searchResult !== null">
|
||||
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_searchResultSettings"></localize></h5>
|
||||
<div ng-repeat="result in vm.searchResult.configs">
|
||||
<div ng-if="result.entries.length > 0">
|
||||
<h5>{{result.group | umbCmsTitleCase}}</h5>
|
||||
<ul class="umb-card-grid -six-in-row">
|
||||
<li ng-repeat="dataType in result.entries | orderBy:'name'"
|
||||
ng-mouseover="vm.showDetailsOverlay(dataType)"
|
||||
ng-click="vm.pickDataType(dataType)">
|
||||
<div ng-if="dataType.loading" class="umb-card-grid-item__loading">
|
||||
<div class="umb-button__progress"></div>
|
||||
</div>
|
||||
<a class="umb-card-grid-item" href="" title="{{ dataType.name }}">
|
||||
<span>
|
||||
<i class="{{ dataType.icon }}" ng-class="{'icon-autofill': dataType.icon == null}"></i>
|
||||
{{ dataType.name }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="vm.filterResult.totalResults > 0">
|
||||
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_availableEditors"></localize></h5>
|
||||
<div ng-repeat="result in vm.filterResult.typesAndEditors">
|
||||
<div ng-if="result.dataTypes.length > 0">
|
||||
<h5>{{result.group}}</h5>
|
||||
<ul class="umb-card-grid -four-in-row" ng-mouseleave="vm.hideDetailsOverlay()">
|
||||
<li ng-repeat="systemDataType in result.dataTypes | orderBy:'name'"
|
||||
ng-mouseover="vm.showDetailsOverlay(systemDataType)"
|
||||
ng-click="vm.pickEditor(systemDataType)"
|
||||
class="cursor-pointer">
|
||||
<span class="umb-card-grid-item" title="{{systemDataType.name}}">
|
||||
<span>
|
||||
<i class="{{systemDataType.icon}}" ng-class="{'icon-autofill': systemDataType.icon == null}"></i>
|
||||
{{systemDataType.name}}
|
||||
</span>
|
||||
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_searchResultEditors"></localize></h5>
|
||||
<div ng-repeat="result in vm.searchResult.dataTypes">
|
||||
<div ng-if="result.entries.length > 0">
|
||||
<h5>{{result.group | umbCmsTitleCase}}</h5>
|
||||
<ul class="umb-card-grid -six-in-row">
|
||||
<li ng-repeat="dataType in result.entries | orderBy:'name'"
|
||||
ng-click="vm.pickEditor(dataType)">
|
||||
<a class="umb-card-grid-item" href="" title="{{ dataType.name }}">
|
||||
<span>
|
||||
<i class="{{ dataType.icon }}" ng-class="{'icon-autofill': dataType.icon == null}"></i>
|
||||
{{ dataType.name }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-empty-state position="center" ng-if="vm.filterResult.totalResults === 0">
|
||||
<localize key="general_searchNoResult"></localize>
|
||||
</umb-empty-state>
|
||||
</div>
|
||||
|
||||
</umb-box-content>
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
<umb-box ng-if="!vm.loadingDataType">
|
||||
<umb-box-content>
|
||||
|
||||
<div class="umb-control-group" ng-if="vm.dataType.id !== 0">
|
||||
<div class="control-group umb-control-group" ng-if="vm.dataType.id !== 0">
|
||||
<i class="icon-alert red"></i>
|
||||
<strong class="red"><localize key="contentTypeEditor_allDocumentTypes"></localize></strong> using this editor will get updated with the new settings.
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi
|
||||
//detect if it is a json string
|
||||
if (val.detectIsJson()) {
|
||||
try {
|
||||
//Parse it to json
|
||||
prop.value = JSON.parse(val);
|
||||
//Parse it from json
|
||||
prop.value = Utilities.fromJson(val);
|
||||
}
|
||||
catch (e) {
|
||||
// not json
|
||||
|
||||
+6
-1
@@ -30,6 +30,7 @@
|
||||
vm.close = close;
|
||||
|
||||
vm.toggleAllowCultureVariants = toggleAllowCultureVariants;
|
||||
vm.toggleAllowSegmentVariants = toggleAllowSegmentVariants;
|
||||
vm.toggleValidation = toggleValidation;
|
||||
vm.toggleShowOnMemberProfile = toggleShowOnMemberProfile;
|
||||
vm.toggleMemberCanEdit = toggleMemberCanEdit;
|
||||
@@ -114,7 +115,7 @@
|
||||
property: $scope.model.property,
|
||||
contentTypeName: $scope.model.contentTypeName,
|
||||
view: "views/common/infiniteeditors/datatypepicker/datatypepicker.html",
|
||||
size: "small",
|
||||
size: "medium",
|
||||
submit: function(model) {
|
||||
|
||||
$scope.model.updateSameDataTypes = model.updateSameDataTypes;
|
||||
@@ -248,6 +249,10 @@
|
||||
$scope.model.property.allowCultureVariant = toggleValue($scope.model.property.allowCultureVariant);
|
||||
}
|
||||
|
||||
function toggleAllowSegmentVariants() {
|
||||
$scope.model.property.allowSegmentVariant = toggleValue($scope.model.property.allowSegmentVariant);
|
||||
}
|
||||
|
||||
function toggleValidation() {
|
||||
$scope.model.property.validation.mandatory = toggleValue($scope.model.property.validation.mandatory);
|
||||
}
|
||||
|
||||
+59
-42
@@ -18,8 +18,8 @@
|
||||
|
||||
<div class="content-type-editor-dialog edit-property-settings">
|
||||
|
||||
<div class="umb-control-group" ng-if="!model.property.locked">
|
||||
<div class="control-group">
|
||||
<div class="umb-control-group -no-border" ng-if="!model.property.locked">
|
||||
<div class="control-group -no-margin">
|
||||
<textarea class="editor-label"
|
||||
data-element="property-name"
|
||||
name="propertyLabel"
|
||||
@@ -36,12 +36,12 @@
|
||||
<span class="umb-validation-label" ng-message="required">Required label</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group -no-margin">
|
||||
<div class="control-group -no-margin" style="margin-top:2px;">
|
||||
<umb-generate-alias enable-lock="true" alias-from="model.property.label" alias="model.property.alias"></umb-generate-alias>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group control-group">
|
||||
|
||||
<div class="umb-control-group control-group -no-border">
|
||||
<textarea data-element="property-description"
|
||||
class="editor-description"
|
||||
ng-model="model.property.description"
|
||||
@@ -51,35 +51,42 @@
|
||||
umb-auto-resize>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="editor-wrapper umb-control-group control-group" ng-model="model.property.editor" val-require-component ng-if="!model.property.locked">
|
||||
|
||||
|
||||
<a data-element="editor-add" href="" ng-if="!model.property.editor" class="editor-placeholder" hotkey="alt+shift+e" ng-click="vm.openDataTypePicker(model.property)">
|
||||
<localize key="shortcuts_addEditor"></localize>
|
||||
</a>
|
||||
|
||||
<div class="editor clearfix" ng-if="model.property.editor">
|
||||
|
||||
<a href="" class="editor-icon-wrapper" ng-click="vm.openDataTypePicker(model.property)">
|
||||
<i class="icon {{ model.property.dataTypeIcon }}" ng-class="{'icon-autofill': model.property.dataTypeIcon == null}"></i>
|
||||
</a>
|
||||
|
||||
<div class="editor-details">
|
||||
<a href="" class="editor-name" ng-click="vm.openDataTypePicker(model.property)">{{ model.property.dataTypeName }}</a>
|
||||
<a href="" class="editor-editor" ng-click="vm.openDataTypePicker(model.property)">{{ model.property.editor }}</a>
|
||||
<localize key="defaultdialogs_selectEditor"></localize>
|
||||
<div ng-messages="model.property.editor" show-validation-on-submit>
|
||||
<span class="umb-validation-label" ng-message="required">Required editor</span>
|
||||
</div>
|
||||
|
||||
<a href class="editor-settings-icon pull-right"
|
||||
ng-click="vm.openDataTypeSettings(model.property)"
|
||||
hotkey="alt+shift+d"
|
||||
ng-if="model.property.editor">
|
||||
<i class="icon icon-settings"></i>
|
||||
</a>
|
||||
|
||||
</a>
|
||||
|
||||
<div class="editor clearfix" ng-if="model.property.editor">
|
||||
|
||||
<button class="btn-reset editor-info" ng-click="vm.openDataTypeSettings(model.property)">
|
||||
<div class="editor-icon-wrapper">
|
||||
<i class="icon {{ model.property.dataTypeIcon }}" ng-class="{'icon-autofill': model.property.dataTypeIcon == null}"></i>
|
||||
</div>
|
||||
|
||||
<div class="editor-details">
|
||||
<span class="editor-name">{{ model.property.dataTypeName }}</span>
|
||||
<span class="editor-editor">{{ model.property.editor }}</span>
|
||||
</div>
|
||||
|
||||
</button>
|
||||
|
||||
<button class="editor-remove-icon btn-reset pull-right"
|
||||
ng-click="vm.openDataTypePicker(model.property)"
|
||||
hotkey="alt+shift+d"
|
||||
localize="title"
|
||||
title="@actions_changeDataType"
|
||||
>
|
||||
<i class="icon icon-wrong"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="umb-control-group clearfix" ng-if="!model.property.locked">
|
||||
|
||||
<h5><localize key="validation_validation"></localize></h5>
|
||||
@@ -132,19 +139,29 @@
|
||||
ng-keypress="vm.submitOnEnter($event)" />
|
||||
|
||||
</div>
|
||||
<div class="umb-control-group clearfix" ng-if="model.contentType === 'documentType' && model.contentTypeAllowCultureVariant">
|
||||
|
||||
<h5><localize key="contentTypeEditor_variantsHeading" /></h5>
|
||||
|
||||
<umb-toggle data-element="permissions-allow-culture-variant"
|
||||
checked="model.property.allowCultureVariant"
|
||||
on-click="vm.toggleAllowCultureVariants()"
|
||||
>
|
||||
</umb-toggle>
|
||||
<div class="umb-control-group clearfix -no-border" ng-if="model.contentType === 'documentType' && model.contentTypeAllowCultureVariant">
|
||||
|
||||
<h5><localize key="contentTypeEditor_cultureVariantHeading" /></h5>
|
||||
<umb-toggle data-element="permissions-allow-culture-variant"
|
||||
checked="model.property.allowCultureVariant"
|
||||
on-click="vm.toggleAllowCultureVariants()"
|
||||
>
|
||||
</umb-toggle>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group clearfix" ng-if="model.contentType === 'memberType'">
|
||||
<div class="umb-control-group clearfix" ng-if="model.contentType === 'documentType' && model.contentTypeAllowSegmentVariant">
|
||||
|
||||
<h5><localize key="contentTypeEditor_segmentVariantHeading" /></h5>
|
||||
|
||||
<umb-toggle data-element="permissions-allow-segment-variant"
|
||||
checked="model.property.allowSegmentVariant"
|
||||
on-click="vm.toggleAllowSegmentVariants()"
|
||||
>
|
||||
</umb-toggle>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group clearfix -no-border" ng-if="model.contentType === 'memberType'">
|
||||
|
||||
<h5><localize key="general_options"></localize></h5>
|
||||
|
||||
@@ -177,7 +194,7 @@
|
||||
checked="model.property.isSensitiveData"
|
||||
on-click="vm.toggleIsSensitiveData()">
|
||||
</umb-toggle>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +224,7 @@
|
||||
label-key="general_submit"
|
||||
action="vm.submit(model)">
|
||||
</umb-button>
|
||||
|
||||
|
||||
</umb-editor-footer-content-right>
|
||||
|
||||
</umb-editor-footer>
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
|
||||
<umb-load-indicator
|
||||
ng-if="vm.loading">
|
||||
</umb-load-indicator>
|
||||
@@ -21,21 +21,21 @@
|
||||
|
||||
<div ng-if="model.node.variants.length > 1">
|
||||
<h5><localize key="general_language"></localize></h5>
|
||||
<select
|
||||
<select
|
||||
class="input-block-level"
|
||||
ng-model="vm.selectedLanguage"
|
||||
ng-options="variant as variant.language.name for variant in model.node.variants track by variant.language.culture"
|
||||
ng-options="variant as variant.displayName for variant in model.node.variants track by variant.language.culture"
|
||||
ng-change="vm.changeLanguage(vm.selectedLanguage)">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<h5><localize key="rollback_currentVersion"></localize></h5>
|
||||
<p>{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})</p>
|
||||
|
||||
|
||||
<h5><localize key="rollback_rollbackTo"></localize></h5>
|
||||
<select
|
||||
<select
|
||||
class="input-block-level"
|
||||
ng-model="vm.selectedVersion"
|
||||
ng-options="version.displayValue for version in vm.previousVersions track by version.versionId"
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<h5>Changes</h5>
|
||||
<small style="margin-bottom: 15px; display: block;"><localize key="rollback_diffHelp"></localize></small>
|
||||
|
||||
|
||||
<table class="table table-condensed table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -73,7 +73,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</umb-box-content>
|
||||
|
||||
+1
-1
@@ -189,7 +189,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
|
||||
if ($scope.model.filter.startsWith("{")) {
|
||||
$scope.model.filterAdvanced = true;
|
||||
//convert to object
|
||||
$scope.model.filter = JSON.parse($scope.model.filter);
|
||||
$scope.model.filter = Utilities.fromJson($scope.model.filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
page="page"
|
||||
content="content"
|
||||
culture="culture"
|
||||
segment="segment"
|
||||
on-select-app="appChanged(app)"
|
||||
on-select-app-anchor="appAnchorChanged(app, anchor)"
|
||||
on-back="onBack()"
|
||||
@@ -61,9 +62,9 @@
|
||||
shortcut="ctrl+s"
|
||||
add-ellipsis="{{page.saveButtonEllipsis}}">
|
||||
</umb-button>
|
||||
|
||||
<umb-button-group
|
||||
ng-if="defaultButton && !content.trashed && !content.isElement"
|
||||
|
||||
<umb-button-group
|
||||
ng-if="defaultButton && !content.trashed && !content.isElement"
|
||||
button-style="success"
|
||||
default-button="defaultButton"
|
||||
sub-buttons="subButtons"
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<umb-box-header
|
||||
title="{{historyLabel}}">
|
||||
|
||||
|
||||
<umb-button
|
||||
ng-hide="node.trashed"
|
||||
type="button"
|
||||
@@ -64,7 +64,7 @@
|
||||
<umb-load-indicator ng-show="loadingAuditTrail"></umb-load-indicator>
|
||||
|
||||
<div ng-show="auditTrail.length === 0" style="padding: 10px;">
|
||||
<umb-empty-state
|
||||
<umb-empty-state
|
||||
position="center"
|
||||
size="small">
|
||||
<localize key="content_noChanges"></localize>
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
<div class="history-item__break">
|
||||
<div class="history-item__avatar">
|
||||
<umb-avatar
|
||||
<umb-avatar
|
||||
color="secondary"
|
||||
size="xs"
|
||||
name="{{item.userName}}"
|
||||
@@ -105,7 +105,7 @@
|
||||
<localize key="auditTrails_{{ item.logType | lowercase }}" tokens="[item.parameters]">{{ item.comment }}</localize>
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -126,7 +126,7 @@
|
||||
</div>
|
||||
|
||||
<div class="umb-package-details__sidebar">
|
||||
|
||||
|
||||
<umb-box data-element="node-info-general">
|
||||
<umb-box-header title-key="general_general"></umb-box-header>
|
||||
<umb-box-content class="block-form">
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
data-element="property-{{property.alias}}"
|
||||
ng-repeat="property in group.properties track by property.alias"
|
||||
property="property"
|
||||
show-inherit="content.variants.length > 1 && !property.culture && !activeVariant.language.isDefault"
|
||||
inherits-from="defaultVariant.language.name">
|
||||
show-inherit="propertyEditorDisabled(property)"
|
||||
inherits-from="defaultVariant.displayName">
|
||||
|
||||
<div ng-class="{'o-40 cursor-not-allowed': content.variants.length > 1 && !activeVariant.language.isDefault && !property.culture && !property.unlockInvariantValue}">
|
||||
<div ng-class="{'o-40 cursor-not-allowed': propertyEditorDisabled(property) }">
|
||||
<umb-property-editor
|
||||
model="property"
|
||||
preview="content.variants.length > 1 && !activeVariant.language.isDefault && !property.culture && !property.unlockInvariantValue">
|
||||
preview="propertyEditorDisabled(property)">
|
||||
</umb-property-editor>
|
||||
</div>
|
||||
|
||||
|
||||
+1
-3
@@ -1,7 +1,6 @@
|
||||
<div class="umb-split-views">
|
||||
<div class="umb-split-view"
|
||||
ng-repeat="editor in vm.editors track by editor.culture"
|
||||
ng-class="{'umb-split-view--collapsed': editor.collapsed}">
|
||||
ng-repeat="editor in vm.editors track by editor.compositeId">
|
||||
|
||||
<umb-variant-content
|
||||
page="vm.page"
|
||||
@@ -9,7 +8,6 @@
|
||||
editor="editor"
|
||||
editor-index="$index"
|
||||
editor-count="vm.editors.length"
|
||||
open-variants="vm.openVariants"
|
||||
on-open-split-view="vm.openSplitView(variant)"
|
||||
on-close-split-view="vm.closeSplitView($index)"
|
||||
on-select-variant="vm.selectVariant(variant, $index)"
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
|
||||
<div class="umb-split-view__content" ng-show="!vm.editor.loading">
|
||||
|
||||
<ng-form name="contentHeaderForm" ng-if="vm.editor.content.apps.length > 0">
|
||||
<ng-form name="contentHeaderForm" ng-if="vm.content.apps.length > 0">
|
||||
<umb-editor-content-header
|
||||
menu="vm.page.menu"
|
||||
hide-menu="vm.page.hideActionsMenu"
|
||||
name="vm.editor.content.name"
|
||||
name-disabled="vm.nameDisabled"
|
||||
content="vm.editor.content"
|
||||
editor="vm.editor"
|
||||
content="vm.content"
|
||||
on-select-navigation-item="vm.selectApp(item)"
|
||||
on-select-anchor-item="vm.selectAppAnchor(item, anchor)"
|
||||
open-variants="vm.openVariants"
|
||||
hide-change-variant="vm.page.hideChangeVariant"
|
||||
show-back-button="vm.showBackButton()"
|
||||
on-back="vm.onBack()"
|
||||
@@ -26,7 +26,7 @@
|
||||
</umb-editor-content-header>
|
||||
</ng-form>
|
||||
|
||||
<umb-editor-container ng-if="vm.editor.content.apps.length > 0">
|
||||
<umb-editor-container ng-if="vm.content.apps.length > 0">
|
||||
|
||||
<!-- Deleted Context Message Bar (Displayed when viewing node in recycle bin) -->
|
||||
<div ng-show="vm.content.trashed" class="umb-editor--trashed-message">
|
||||
@@ -34,14 +34,14 @@
|
||||
</div>
|
||||
|
||||
<div class="umb-editor-sub-views">
|
||||
<div ng-repeat="app in vm.editor.content.apps track by app.alias">
|
||||
<umb-editor-sub-view model="app" content="vm.content" />
|
||||
<div ng-repeat="app in vm.editor.variantApps track by app.alias">
|
||||
<umb-editor-sub-view model="app" content="vm.content" variant-content="vm.editor.content"/>
|
||||
</div>
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-empty-state
|
||||
ng-if="vm.editor.content.apps.length === 0"
|
||||
ng-if="vm.content.apps.length === 0"
|
||||
position="center">
|
||||
<localize key="content_noProperties"></localize>
|
||||
</umb-empty-state>
|
||||
|
||||
+39
-13
@@ -36,26 +36,52 @@
|
||||
required
|
||||
aria-required="true"
|
||||
aria-invalid="{{contentForm.headerNameForm.headerName.$invalid ? true : false}}"
|
||||
autocomplete="off" maxlength="255" />
|
||||
autocomplete="off"
|
||||
maxlength="255" />
|
||||
</ng-form>
|
||||
|
||||
<button type="button" ng-if="content.variants.length > 0 && hideChangeVariant !== true" class="umb-variant-switcher__toggle umb-outline" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen" ng-class="{'--error': vm.errorsOnOtherVariants}">
|
||||
<span>{{vm.currentVariant.language.name}}</span>
|
||||
<button type="button" ng-if="vm.hasVariants === true && hideChangeVariant !== true" class="umb-variant-switcher__toggle umb-outline" ng-click="vm.dropdownOpen = !vm.dropdownOpen" ng-class="{'--error': vm.errorsOnOtherVariants}">
|
||||
<span ng-bind="editor.content.displayName"></span>
|
||||
<ins class="umb-variant-switcher__expand" ng-class="{'icon-navigation-down': !vm.dropdownOpen, 'icon-navigation-up': vm.dropdownOpen}"> </ins>
|
||||
</button>
|
||||
|
||||
<span ng-if="hideChangeVariant" class="umb-variant-switcher__toggle">
|
||||
<span>{{vm.currentVariant.language.name}}</span>
|
||||
<span ng-if="vm.hasVariants === true && hideChangeVariant" class="umb-variant-switcher__toggle">
|
||||
<span ng-bind="editor.content.displayName"></span>
|
||||
</span>
|
||||
|
||||
<umb-dropdown ng-if="vm.dropdownOpen" style="min-width: 100%; max-height: 250px; overflow-y: auto; margin-top: 5px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
|
||||
<umb-dropdown-item class="umb-variant-switcher__item" ng-class="{'--current': variant.active, '--not-allowed': variantIsOpen(variant.language.culture), '--error': variantHasError(variant.language.culture)}" ng-repeat="variant in content.variants">
|
||||
<button class="umb-variant-switcher__name-wrapper umb-outline umb-outline--thin" ng-click="selectVariant($event, variant)" prevent-default>
|
||||
<span class="umb-variant-switcher__name">{{variant.language.name}}</span>
|
||||
<umb-variant-state variant="variant" class="umb-variant-switcher__state"></umb-variant-state>
|
||||
<umb-dropdown ng-if="vm.dropdownOpen" class="umb-variant-switcher" ng-class="{'--has-sub-variants': vm.hasSubVariants === true}" on-close="vm.dropdownOpen = false" umb-keyboard-list>
|
||||
<umb-dropdown-item
|
||||
ng-repeat-start="entry in vm.variantMenu track by entry.key"
|
||||
class="umb-variant-switcher__item"
|
||||
ng-class="{'--current': entry.variant === editor.content, '--active': entry.variant.active && vm.dropdownOpen, '--error': entry.variant.active !== true && entry.variant.hasError, '--state-notCreated':entry.variant.state==='NotCreated' && entry.variant.name == null, '--state-draft':entry.variant.state==='Draft' || (entry.variant.state==='NotCreated' && entry.variant.name != null)}"
|
||||
>
|
||||
<button type="button" ng-if="entry.subVariants && entry.subVariants.length > 0" class="umb-variant-switcher__item-expand-button umb-outline" ng-click="entry.open = !entry.open">
|
||||
<i class="icon icon-navigation-down" ng-if="entry.open"></i>
|
||||
<i class="icon icon-navigation-right" ng-if="!entry.open"></i>
|
||||
</button>
|
||||
<div ng-if="splitViewOpen !== true && !variant.active" class="umb-variant-switcher__split-view" ng-click="openInSplitView($event, variant)">Open in split view</div>
|
||||
<button type="button" class="umb-variant-switcher__name-wrapper umb-outline" ng-click="selectVariant($event, entry.variant)" prevent-default>
|
||||
<span class="umb-variant-switcher__name" ng-bind="entry.variant.displayName"></span>
|
||||
<umb-variant-state variant="entry.variant" class="umb-variant-switcher__state"></umb-variant-state>
|
||||
</button>
|
||||
<div ng-if="splitViewOpen !== true && !entry.variant.active" class="umb-variant-switcher__split-view umb-outline" ng-click="openInSplitView($event, entry.variant)">Open in split view</div>
|
||||
</umb-dropdown-item>
|
||||
<div
|
||||
ng-repeat-end
|
||||
ng-if="entry.open === true && entry.subVariants && entry.subVariants.length > 0"
|
||||
class="umb-variant-switcher__sub-variants umb-outline"
|
||||
>
|
||||
<umb-dropdown-item
|
||||
ng-repeat="subVariant in entry.subVariants track by $index"
|
||||
class="umb-variant-switcher__item"
|
||||
ng-class="{'--current': subVariant === editor.content, '--active': subVariant.active && vm.dropdownOpen, '--error': subVariant.active !== true && subVariant.hasError, '--state-notCreated':subVariant.state==='NotCreated', '--state-draft':subVariant.state==='Draft'}"
|
||||
>
|
||||
<button type="button" class="umb-variant-switcher__name-wrapper umb-outline" ng-click="selectVariant($event, subVariant)" prevent-default>
|
||||
<span class="umb-variant-switcher__name" ng-bind="subVariant.segment"></span>
|
||||
<umb-variant-state variant="subVariant" class="umb-variant-switcher__state"></umb-variant-state>
|
||||
</button>
|
||||
<div ng-if="splitViewOpen !== true && !subVariant.active" class="umb-variant-switcher__split-view umb-outline" ng-click="openInSplitView($event, subVariant)">Open in split view</div>
|
||||
</umb-dropdown-item>
|
||||
</div>
|
||||
</umb-dropdown>
|
||||
|
||||
</div>
|
||||
@@ -70,10 +96,10 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div ng-if="content.apps && splitViewOpen !== true">
|
||||
<div ng-if="editor.variantApps && splitViewOpen !== true">
|
||||
<umb-editor-navigation
|
||||
data-element="editor-sub-views"
|
||||
navigation="content.apps"
|
||||
navigation="editor.variantApps"
|
||||
on-select="selectNavigationItem(item)"
|
||||
on-anchor-select="selectAnchorItem(item, anchor)">
|
||||
</umb-editor-navigation>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div data-element="overlay" class="umb-overlay umb-overlay-{{position}} umb-overlay--{{size}}" on-outside-click="outSideClick()" role="dialog" aria-labelledby="umb-overlay-title" aria-describedby="umb-overlay-description">
|
||||
<ng-form class="umb-overlay__form" name="overlayForm" novalidate val-form-manager>
|
||||
<div data-element="overlay-header" class="umb-overlay-header">
|
||||
<div data-element="overlay-header" class="umb-overlay-header" ng-show="!model.hideHeader">
|
||||
<h1 class="umb-overlay__title" id="umb-overlay-title">{{model.title}}</h1>
|
||||
<p class="umb-overlay__subtitle" id="umb-overlay-description" ng-if="model.subtitle">{{model.subtitle}}</p>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
alias="compositions"
|
||||
ng-if="compositions !== false"
|
||||
type="button"
|
||||
button-style="action"
|
||||
button-style="outline"
|
||||
label-key="contentTypeEditor_compositions"
|
||||
icon="icon-merge"
|
||||
action="openCompositionsDialog()"
|
||||
@@ -22,7 +22,7 @@
|
||||
alias="reorder"
|
||||
ng-if="sorting !== false"
|
||||
type="button"
|
||||
button-style="action"
|
||||
button-style="outline"
|
||||
label-key="{{sortingButtonKey}}"
|
||||
icon="icon-navigation"
|
||||
action="toggleSortingMode();"
|
||||
@@ -37,16 +37,16 @@
|
||||
<localize key="contentTypeEditor_noGroups"></localize>
|
||||
</div>
|
||||
|
||||
<a ng-if="!sortingMode" hotkey="alt+shift+p" ng-click="addPropertyToActiveGroup()"></a>
|
||||
<a ng-if="!sortingMode" hotkey="alt+shift+p" ng-click="addPropertyToActiveGroup()"></a>
|
||||
|
||||
<ul class="umb-group-builder__groups" ui-sortable="sortableOptionsGroup" ng-model="model.groups">
|
||||
|
||||
<li ng-repeat="tab in model.groups" ng-class="{'umb-group-builder__group-sortable': sortingMode}" data-element="group-{{tab.name}}">
|
||||
|
||||
<!-- TAB INIT STATE -->
|
||||
<a href="" class="umb-group-builder__group -placeholder" hotkey="alt+shift+g" ng-click="addGroup(tab)" ng-if="tab.tabState=='init' && !sortingMode" data-element="group-add">
|
||||
<button href="" class="umb-group-builder__group -placeholder" hotkey="alt+shift+g" ng-click="addGroup(tab)" ng-if="tab.tabState=='init' && !sortingMode" data-element="group-add">
|
||||
<localize key="contentTypeEditor_addGroup"></localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
<!-- TAB ACTIVE OR INACTIVE STATE -->
|
||||
<div class="umb-group-builder__group" ng-if="tab.tabState !== 'init'" ng-class="{'-active':tab.tabState=='active', '-inherited': tab.inherited, 'umb-group-builder__group-handle -sortable': sortingMode && !tab.inherited}" tabindex="0" ng-focus="activateGroup(tab)">
|
||||
@@ -117,7 +117,7 @@
|
||||
<li data-element="property-{{property.alias}}" ng-class="{'umb-group-builder__property-sortable': sortingMode && !property.inherited}" ng-repeat="property in tab.properties">
|
||||
|
||||
<!-- Add new property -->
|
||||
<a href=""
|
||||
<button href=""
|
||||
data-element="property-add"
|
||||
class="umb-group-builder__group-add-property"
|
||||
ng-if="property.propertyState=='init' && !sortingMode"
|
||||
@@ -125,7 +125,7 @@
|
||||
ng-focus="activateGroup(tab)"
|
||||
focus-when="{{property.focus}}">
|
||||
<localize key="contentTypeEditor_addProperty"></localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
<div class="umb-group-builder__property" ng-if="property.propertyState!=='init'" ng-class="{'-active': property.dialogIsOpen, '-active': property.propertyState=='active', '-inherited': property.inherited, '-locked': property.locked, 'umb-group-builder__property-handle -sortable': sortingMode && !property.inherited, '-sortable-locked': sortingMode && property.inherited}">
|
||||
|
||||
@@ -215,7 +215,12 @@
|
||||
|
||||
<div class="umb-group-builder__property-tag -white" ng-if="property.allowCultureVariant">
|
||||
<i class="icon-shuffle umb-group-builder__property-tag-icon"></i>
|
||||
<localize key="contentTypeEditor_variantsHeading"></localize>
|
||||
<localize key="contentTypeEditor_cultureVariantLabel"></localize>
|
||||
</div>
|
||||
|
||||
<div class="umb-group-builder__property-tag -white" ng-if="property.allowSegmentVariant">
|
||||
<i class="icon-shuffle umb-group-builder__property-tag-icon"></i>
|
||||
<localize key="contentTypeEditor_segmentVariantLabel"></localize>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -7,35 +7,62 @@
|
||||
//if we make the viewModel the variant itself, we end up with a circular reference in the models which isn't ideal
|
||||
// (i.e. variant.apps[contentApp].viewModel = variant)
|
||||
//so instead since we already have access to the content, we can just get the variant directly by the index.
|
||||
|
||||
var unbindLanguageWatcher = function() {};
|
||||
var unbindSegmentWatcher = function() {};
|
||||
var timeout = null;
|
||||
|
||||
var vm = this;
|
||||
vm.loading = true;
|
||||
|
||||
function onInit() {
|
||||
//get the variant by index (see notes above)
|
||||
vm.content = $scope.content.variants[$scope.model.viewModel];
|
||||
|
||||
serverValidationManager.notify();
|
||||
vm.loading = false;
|
||||
timeout = null;// ensure timeout is set to null, so we know that its not running anymore.
|
||||
|
||||
//if this variant has a culture/language assigned, then we need to watch it since it will change
|
||||
//if the language drop down changes and we need to re-init
|
||||
if (vm.content.language) {
|
||||
$scope.$watch(function () {
|
||||
return vm.content.language.culture;
|
||||
if ($scope.variantContent) {
|
||||
if ($scope.variantContent.language) {
|
||||
unbindLanguageWatcher = $scope.$watch(function () {
|
||||
return $scope.variantContent.language.culture;
|
||||
}, function (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
requestUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unbindSegmentWatcher = $scope.$watch(function () {
|
||||
return $scope.variantContent.segment;
|
||||
}, function (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
vm.loading = true;
|
||||
|
||||
// TODO: Can we minimize the flicker?
|
||||
$timeout(function () {
|
||||
onInit();
|
||||
}, 100);
|
||||
requestUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function requestUpdate() {
|
||||
if (timeout === null) {
|
||||
vm.loading = true;
|
||||
|
||||
// TODO: Can we minimize the flicker?
|
||||
timeout = $timeout(function () {
|
||||
onInit();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
onInit();
|
||||
|
||||
$scope.$on("$destroy", function() {
|
||||
unbindLanguageWatcher();
|
||||
unbindSegmentWatcher();
|
||||
$timeout.cancel(timeout);
|
||||
});
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Content.Apps.ContentController", ContentAppContentController);
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
<umb-tabbed-content
|
||||
ng-if="!vm.loading"
|
||||
content="vm.content">
|
||||
content-node-model="content"
|
||||
content="variantContent">
|
||||
</umb-tabbed-content>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -64,6 +64,9 @@ function contentCreateController($scope,
|
||||
language as what is selected in the tree */
|
||||
.search("cculture", mainCulture)
|
||||
/* when we create a new node we must make sure that any previously
|
||||
opened segments is reset */
|
||||
.search("csegment", null)
|
||||
/* when we create a new node we must make sure that any previously
|
||||
used blueprint is reset */
|
||||
.search("blueprintId", null);
|
||||
close();
|
||||
|
||||
@@ -28,6 +28,7 @@ function ContentEditController($scope, $routeParams, contentResource) {
|
||||
$scope.isNew = infiniteMode ? $scope.model.create : $routeParams.create;
|
||||
//load the default culture selected in the main tree if any
|
||||
$scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;
|
||||
$scope.segment = $routeParams.csegment ? $routeParams.csegment : null;
|
||||
|
||||
//Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered.
|
||||
//This is so we can listen to changes on the cculture parameter since that will not cause a route change
|
||||
@@ -36,6 +37,7 @@ function ContentEditController($scope, $routeParams, contentResource) {
|
||||
//will not cause a route change and so we can update the isNew and contentId flags accordingly.
|
||||
$scope.$on('$routeUpdate', function (event, next) {
|
||||
$scope.culture = next.params.cculture ? next.params.cculture : $routeParams.mculture;
|
||||
$scope.segment = next.params.csegment ? next.params.csegment : null;
|
||||
$scope.isNew = next.params.create === "true";
|
||||
$scope.contentId = infiniteMode ? $scope.model.id : $routeParams.id;
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
tree-alias="content"
|
||||
is-new="isNew"
|
||||
culture="culture"
|
||||
segment="segment"
|
||||
infinite-model="model">
|
||||
</content-editor>
|
||||
</div>
|
||||
|
||||
@@ -5,193 +5,171 @@
|
||||
|
||||
var vm = this;
|
||||
vm.loading = true;
|
||||
vm.hasPristineVariants = false;
|
||||
vm.isNew = true;
|
||||
|
||||
vm.changeSelection = changeSelection;
|
||||
vm.dirtyVariantFilter = dirtyVariantFilter;
|
||||
vm.pristineVariantFilter = pristineVariantFilter;
|
||||
|
||||
/** Returns true if publishing is possible based on if there are un-published mandatory languages */
|
||||
/** Returns true if publish meets the requirements of mandatory languages */
|
||||
function canPublish() {
|
||||
|
||||
var possible = false;
|
||||
var hasSomethingToPublish = false;
|
||||
|
||||
for (var i = 0; i < vm.variants.length; i++) {
|
||||
var variant = vm.variants[i];
|
||||
var state = canVariantPublish(variant);
|
||||
if (state === true) {
|
||||
possible = true;
|
||||
}
|
||||
if (state === false) {
|
||||
|
||||
// if varaint is mandatory and not already published:
|
||||
if (variant.publish === false && notPublishedMandatoryFilter(variant)) {
|
||||
return false;
|
||||
}
|
||||
if (variant.publish === true) {
|
||||
hasSomethingToPublish = true;
|
||||
}
|
||||
|
||||
}
|
||||
return possible;
|
||||
}
|
||||
|
||||
/** Returns true if publishing is possible based on if the variant is a un-published mandatory language */
|
||||
function canVariantPublish(variant) {
|
||||
|
||||
//if this variant will show up in the publish-able list
|
||||
var publishable = dirtyVariantFilter(variant);
|
||||
var published = !(variant.state === "NotCreated" || variant.state === "Draft");
|
||||
|
||||
// is this variant mandatory:
|
||||
if (variant.language.isMandatory && !published && !variant.publish) {
|
||||
//if a mandatory variant isn't published or set to be published
|
||||
//then we cannot continue
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// is this variant selected for publish:
|
||||
if (variant.publish === true) {
|
||||
return publishable;
|
||||
}
|
||||
|
||||
return null;
|
||||
return hasSomethingToPublish;
|
||||
}
|
||||
|
||||
function changeSelection(variant) {
|
||||
|
||||
// update submit button state:
|
||||
$scope.model.disableSubmitButton = !canPublish();
|
||||
//need to set the Save state to true if publish is true
|
||||
//need to set the Save state to same as publish.
|
||||
variant.save = variant.publish;
|
||||
|
||||
variant.willPublish = canVariantPublish(variant);
|
||||
}
|
||||
|
||||
function dirtyVariantFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * the active one
|
||||
// * it's editor is in a $dirty state
|
||||
// * it has pending saves
|
||||
// * it is unpublished
|
||||
// * it is in NotCreated state
|
||||
return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges" || variant.state === "NotCreated");
|
||||
}
|
||||
|
||||
function hasAnyData(variant) {
|
||||
function hasAnyDataFilter(variant) {
|
||||
|
||||
if (variant.name == null || variant.name.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = variant.isDirty != null;
|
||||
|
||||
if(result) return true;
|
||||
if(variant.isDirty === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var t=0; t < variant.tabs.length; t++){
|
||||
for (var p=0; p < variant.tabs[t].properties.length; p++){
|
||||
|
||||
var property = variant.tabs[t].properties[p];
|
||||
|
||||
if(property.culture == null) continue;
|
||||
|
||||
result = result || (property.value != null && property.value.length > 0);
|
||||
|
||||
if(result) return true;
|
||||
if (property.value != null && property.value.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
function pristineVariantFilter(variant) {
|
||||
return !(dirtyVariantFilter(variant));
|
||||
function dirtyVariantFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * it's editor is in a $dirty state
|
||||
// * it has pending saves
|
||||
// * it is unpublished
|
||||
return (variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
|
||||
}
|
||||
|
||||
function publishableVariantFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * variant is active
|
||||
// * it's editor is in a $dirty state
|
||||
// * it has pending saves
|
||||
// * it is unpublished
|
||||
return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
|
||||
}
|
||||
|
||||
function notPublishedMandatoryFilter(variant) {
|
||||
return variant.state !== "Published" && isMandatoryFilter(variant);
|
||||
}
|
||||
function isMandatoryFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * has a mandatory language
|
||||
// * without having a segment, segments cant be mandatory at current state of code.
|
||||
return (variant.language && variant.language.isMandatory === true && variant.segment == null);
|
||||
}
|
||||
function notPublishableButMandatoryFilter(variant) {
|
||||
//determine a variant is needed, but not already a choice.
|
||||
// * publishable — aka. displayed as a publish option.
|
||||
// * published — its already published and everything is then fine.
|
||||
// * mandatory — this is needed, and thats why we highlight it.
|
||||
return !publishableVariantFilter(variant) && variant.state !== "Published" && variant.isMandatory === true;
|
||||
}
|
||||
|
||||
function onInit() {
|
||||
|
||||
|
||||
|
||||
vm.variants = $scope.model.variants;
|
||||
|
||||
if (!$scope.model.title) {
|
||||
localizationService.localize("content_readyToPublish").then(function (value) {
|
||||
$scope.model.title = value;
|
||||
});
|
||||
}
|
||||
_.each(vm.variants, (variant) => {
|
||||
|
||||
// reset to not be published
|
||||
variant.publish = false;
|
||||
variant.save = false;
|
||||
|
||||
variant.isMandatory = isMandatoryFilter(variant);
|
||||
|
||||
vm.hasPristineVariants = false;
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
if(variant.state === "NotCreated") {
|
||||
vm.isNew = true;
|
||||
}
|
||||
// If we have a variant thats not in the state of NotCreated, then we know we have adata and its not a new content node.
|
||||
if(variant.state !== "NotCreated") {
|
||||
vm.isNew = false;
|
||||
}
|
||||
);
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
|
||||
// reset to not be published
|
||||
variant.publish = false;
|
||||
variant.save = false;
|
||||
|
||||
//check for pristine variants
|
||||
if (!vm.hasPristineVariants) {
|
||||
vm.hasPristineVariants = pristineVariantFilter(variant);
|
||||
}
|
||||
|
||||
// If the variant havent been created jet.
|
||||
if(variant.state === "NotCreated") {
|
||||
// If the variant is mandatory, then set the variant to be published.
|
||||
if (variant.language.isMandatory === true) {
|
||||
variant.publish = true;
|
||||
variant.save = true;
|
||||
}
|
||||
}
|
||||
|
||||
variant.canPublish = dirtyVariantFilter(variant);
|
||||
|
||||
// if we have data on this variant.
|
||||
if(variant.canPublish && hasAnyData(variant)) {
|
||||
// and if some varaints havent been saved before, or they dont have a publishing date set, then we set it for publishing.
|
||||
if(vm.isNew || variant.publishDate == null){
|
||||
variant.publish = true;
|
||||
variant.save = true;
|
||||
}
|
||||
}
|
||||
|
||||
variant.willPublish = canVariantPublish(variant);
|
||||
}
|
||||
);
|
||||
|
||||
if (vm.variants.length !== 0) {
|
||||
//now sort it so that the current one is at the top
|
||||
vm.variants = _.sortBy(vm.variants, function (v) {
|
||||
return v.active ? 0 : 1;
|
||||
});
|
||||
|
||||
var active = _.find(vm.variants, function (v) {
|
||||
return v.active;
|
||||
});
|
||||
|
||||
if (active) {
|
||||
//ensure that the current one is selected
|
||||
active.publish = true;
|
||||
active.save = true;
|
||||
}
|
||||
|
||||
$scope.model.disableSubmitButton = !canPublish();
|
||||
|
||||
} else {
|
||||
//disable Publish button if we have nothing to publish, should not happen
|
||||
$scope.model.disableSubmitButton = true;
|
||||
}
|
||||
|
||||
var labelKey = vm.isNew ? "content_languagesToPublishForFirstTime" : "content_languagesToPublish";
|
||||
|
||||
localizationService.localize(labelKey).then(function (value) {
|
||||
vm.headline = value;
|
||||
vm.loading = false;
|
||||
});
|
||||
|
||||
_.each(vm.variants, (variant) => {
|
||||
|
||||
// if this is a new node and we have data on this variant.
|
||||
if(vm.isNew === true && hasAnyDataFilter(variant)) {
|
||||
variant.save = true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
vm.availableVariants = vm.variants.filter(publishableVariantFilter);
|
||||
vm.missingMandatoryVariants = vm.variants.filter(notPublishableButMandatoryFilter);
|
||||
|
||||
// if any active varaiant that is available for publish, we set it to be published:
|
||||
_.each(vm.availableVariants, (v) => {
|
||||
if(v.active) {
|
||||
v.save = v.publish = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (vm.availableVariants.length !== 0) {
|
||||
vm.availableVariants.sort(function (a, b) {
|
||||
if (a.language && b.language) {
|
||||
if (a.language.name > b.language.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.language.name < b.language.name) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.segment && b.segment) {
|
||||
if (a.segment > b.segment) {
|
||||
return -1;
|
||||
}
|
||||
if (a.segment < b.segment) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.model.disableSubmitButton = !canPublish();
|
||||
|
||||
if (vm.missingMandatoryVariants.length > 0) {
|
||||
localizationService.localize("content_notReadyToPublish").then(function (value) {
|
||||
$scope.model.title = value;
|
||||
vm.loading = false;
|
||||
});
|
||||
} else {
|
||||
if (!$scope.model.title) {
|
||||
localizationService.localize("content_readyToPublish").then(function (value) {
|
||||
$scope.model.title = value;
|
||||
vm.loading = false;
|
||||
});
|
||||
} else {
|
||||
vm.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +1,83 @@
|
||||
<div ng-controller="Umbraco.Overlays.PublishController as vm">
|
||||
|
||||
<div class="mb3">
|
||||
<p>{{vm.headline}}</p>
|
||||
</div>
|
||||
<div ng-controller="Umbraco.Overlays.PublishController as vm" class="umb-variant-selector-overlay">
|
||||
|
||||
<div ng-if="vm.loading" style="min-height: 50px; position: relative;">
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading">
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && vm.missingMandatoryVariants.length === 0">
|
||||
|
||||
<div class="mb3">
|
||||
<p><localize key="content_variantsToPublish"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list-item"
|
||||
ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId">
|
||||
ng-repeat="variant in vm.availableVariants track by variant.compositeId">
|
||||
<ng-form name="publishVariantSelectorForm">
|
||||
<div ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
|
||||
<umb-checkbox input-id="{{variant.htmlId}}"
|
||||
name="publishVariantSelector"
|
||||
model="variant.publish"
|
||||
on-change="vm.changeSelection(variant)"
|
||||
disabled="(variant.canPublish === false)"
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
text="{{ variant.language.name }}"
|
||||
/>
|
||||
<div>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.language.isMandatory"> - </span>
|
||||
<span ng-if="variant.language.isMandatory" ng-class="{'text-error': (variant.language.isMandatory && variant.willPublish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.isMandatory"> - </span>
|
||||
<span ng-if="variant.isMandatory" ng-class="{'text-error': (variant.publish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
</umb-checkbox>
|
||||
|
||||
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
<br />
|
||||
<br/>
|
||||
<div class="mb3" ng-if="vm.isNew === true">
|
||||
<p><localize key="content_variantsWillBeSaved"></localize></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && vm.hasPristineVariants">
|
||||
<div class="bold mb3">
|
||||
<p><localize key="content_publishedLanguages"></localize></p>
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && vm.missingMandatoryVariants.length !== 0">
|
||||
<div class="mb3">
|
||||
<p><localize key="content_publishRequiresVariants"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.pristineVariantFilter">
|
||||
<div>
|
||||
<div style="margin-bottom: 2px;">
|
||||
<span>{{variant.language.name}}</span>
|
||||
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
|
||||
</div>
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.missingMandatoryVariants track by variant.compositeId">
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.isMandatory"> - </span>
|
||||
<span ng-if="variant.isMandatory" ng-class="{'text-error': (variant.publish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
|
||||
<div ng-if="!(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state class="umb-list-item__description" variant="variant"></umb-variant-state>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="notification in variant.notifications">
|
||||
<div class="umb-list-item__description text-success">{{notification.message}}</div>
|
||||
</div>
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+41
-13
@@ -5,12 +5,16 @@
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.includeUnpublished = false;
|
||||
|
||||
vm.changeSelection = changeSelection;
|
||||
vm.toggleIncludeUnpublished = toggleIncludeUnpublished;
|
||||
|
||||
|
||||
function onInit() {
|
||||
|
||||
vm.includeUnpublished = false;
|
||||
vm.variants = $scope.model.variants;
|
||||
vm.displayVariants = vm.variants.slice(0);// shallow copy, we dont want to share the array-object(because we will be performing a sort method) but each entry should be shared (because we need validation and notifications).
|
||||
vm.labels = {};
|
||||
|
||||
if (!$scope.model.title) {
|
||||
@@ -19,17 +23,30 @@
|
||||
});
|
||||
}
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
variant.compositeId = (variant.language ? variant.language.culture : "inv") + "_" + (variant.segment ? variant.segment : "");
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
});
|
||||
_.each(vm.variants, function (variant) {
|
||||
variant.isMandatory = isMandatoryFilter(variant);
|
||||
});
|
||||
|
||||
if (vm.variants.length > 1) {
|
||||
|
||||
//now sort it so that the current one is at the top
|
||||
vm.variants = _.sortBy(vm.variants, function (v) {
|
||||
return v.active ? 0 : 1;
|
||||
vm.displayVariants.sort(function (a, b) {
|
||||
if (a.language && b.language) {
|
||||
if (a.language.name > b.language.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.language.name < b.language.name) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.segment && b.segment) {
|
||||
if (a.segment > b.segment) {
|
||||
return -1;
|
||||
}
|
||||
if (a.segment < b.segment) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var active = _.find(vm.variants, function (v) {
|
||||
@@ -48,14 +65,17 @@
|
||||
// localize help text for invariant content
|
||||
vm.labels.help = {
|
||||
"key": "content_publishDescendantsHelp",
|
||||
"tokens": []
|
||||
"tokens": [vm.variants[0].name]
|
||||
};
|
||||
// add the node name as a token so it will show up in the translated text
|
||||
vm.labels.help.tokens.push(vm.variants[0].name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function toggleIncludeUnpublished() {
|
||||
console.log("toggleIncludeUnpublished")
|
||||
vm.includeUnpublished = !vm.includeUnpublished;
|
||||
}
|
||||
|
||||
/** Returns true if publishing is possible based on if there are un-published mandatory languages */
|
||||
function canPublish() {
|
||||
var selected = [];
|
||||
@@ -64,7 +84,7 @@
|
||||
|
||||
var published = !(variant.state === "NotCreated" || variant.state === "Draft");
|
||||
|
||||
if (variant.language.isMandatory && !published && !variant.publish) {
|
||||
if (variant.segment == null && variant.language && variant.language.isMandatory && !published && !variant.publish) {
|
||||
//if a mandatory variant isn't published
|
||||
//and not flagged for saving
|
||||
//then we cannot continue
|
||||
@@ -86,6 +106,14 @@
|
||||
variant.save = variant.publish;
|
||||
}
|
||||
|
||||
|
||||
function isMandatoryFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * has a mandatory language
|
||||
// * without having a segment, segments cant be mandatory at current state of code.
|
||||
return (variant.language && variant.language.isMandatory === true && variant.segment == null);
|
||||
}
|
||||
|
||||
//when this dialog is closed, reset all 'publish' flags
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var i = 0; i < vm.variants.length; i++) {
|
||||
|
||||
@@ -1,67 +1,69 @@
|
||||
<div ng-controller="Umbraco.Overlays.PublishDescendantsController as vm">
|
||||
<div ng-controller="Umbraco.Overlays.PublishDescendantsController as vm" class="umb-variant-selector-overlay">
|
||||
|
||||
<div ng-if="vm.variants.length === 1">
|
||||
<div ng-if="vm.displayVariants.length === 1">
|
||||
<div class="mb3">
|
||||
<p><localize key="{{vm.labels.help.key}}" tokens="vm.labels.help.tokens"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="flex mb3">
|
||||
<umb-checkbox
|
||||
model="model.includeUnpublished"
|
||||
text="Include drafts: also publish unpublished content items."
|
||||
label-key="content_includeUnpublished"
|
||||
<umb-toggle
|
||||
checked="vm.includeUnpublished"
|
||||
on-click="vm.toggleIncludeUnpublished()"
|
||||
class="mr2"
|
||||
/>
|
||||
<localize key="content_includeUnpublished"></localize>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.variants.length > 1">
|
||||
<div ng-if="vm.displayVariants.length > 1">
|
||||
|
||||
<div class="mb3">
|
||||
<p><localize key="content_publishDescendantsWithVariantsHelp"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="flex mb3">
|
||||
<umb-checkbox
|
||||
model="model.includeUnpublished"
|
||||
text="Include drafts: also publish unpublished content items."
|
||||
label-key="content_includeUnpublished"
|
||||
<umb-toggle
|
||||
checked="vm.includeUnpublished"
|
||||
on-click="vm.toggleIncludeUnpublished()"
|
||||
class="mr2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="bold mb1">
|
||||
<localize key="treeHeaders_languages"></localize>
|
||||
<localize key="content_includeUnpublished"></localize>
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed">
|
||||
|
||||
<div class="umb-list-item umb-list--condensed" ng-repeat="variant in vm.variants">
|
||||
<div class="umb-list-item umb-list--condensed" ng-repeat="variant in vm.displayVariants track by variant.compositeId">
|
||||
<ng-form name="publishVariantSelectorForm">
|
||||
<div ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
|
||||
<umb-checkbox
|
||||
<umb-checkbox input-id="{{variant.htmlId}}"
|
||||
name="publishVariantSelector"
|
||||
model="variant.publish"
|
||||
on-change="vm.changeSelection(variant)"
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
text="{{variant.language.name}}"/>
|
||||
|
||||
<div>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<span class="db umb-list-item__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
|
||||
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.isMandatory"> - </span>
|
||||
<span ng-if="variant.isMandatory" ng-class="{'text-error': (variant.publish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
</umb-checkbox>
|
||||
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
</div>
|
||||
</ng-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
vm.isNew = true;
|
||||
|
||||
vm.changeSelection = changeSelection;
|
||||
vm.dirtyVariantFilter = dirtyVariantFilter;
|
||||
vm.pristineVariantFilter = pristineVariantFilter;
|
||||
|
||||
function changeSelection(variant) {
|
||||
var firstSelected = _.find(vm.variants, function (v) {
|
||||
@@ -19,16 +17,18 @@
|
||||
$scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected
|
||||
}
|
||||
|
||||
function dirtyVariantFilter(variant) {
|
||||
function saveableVariantFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as save-able) if it's
|
||||
// * the active one
|
||||
// * it's editor is in a $dirty state
|
||||
// * it is in NotCreated state
|
||||
return (variant.active || variant.isDirty);
|
||||
}
|
||||
|
||||
function pristineVariantFilter(variant) {
|
||||
return !(dirtyVariantFilter(variant));
|
||||
function isMandatoryFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * has a mandatory language
|
||||
// * without having a segment, segments cant be mandatory at current state of code.
|
||||
return (variant.language && variant.language.isMandatory === true && variant.segment == null);
|
||||
}
|
||||
|
||||
function hasAnyData(variant) {
|
||||
@@ -57,6 +57,7 @@
|
||||
|
||||
function onInit() {
|
||||
vm.variants = $scope.model.variants;
|
||||
vm.availableVariants = vm.variants.filter(saveableVariantFilter);
|
||||
|
||||
if(!$scope.model.title) {
|
||||
localizationService.localize("content_readyToSave").then(function(value){
|
||||
@@ -64,10 +65,15 @@
|
||||
});
|
||||
}
|
||||
|
||||
vm.hasPristineVariants = false;
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
|
||||
//reset state:
|
||||
variant.save = false;
|
||||
variant.publish = false;
|
||||
|
||||
variant.isMandatory = isMandatoryFilter(variant);
|
||||
|
||||
if(variant.state !== "NotCreated"){
|
||||
vm.isNew = false;
|
||||
}
|
||||
@@ -75,34 +81,40 @@
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
|
||||
//check for pristine variants
|
||||
if (!vm.hasPristineVariants) {
|
||||
vm.hasPristineVariants = pristineVariantFilter(variant);
|
||||
}
|
||||
|
||||
if(vm.isNew && hasAnyData(variant)){
|
||||
variant.save = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (vm.variants.length !== 0) {
|
||||
//now sort it so that the current one is at the top
|
||||
vm.variants = _.sortBy(vm.variants, function (v) {
|
||||
return v.active ? 0 : 1;
|
||||
|
||||
_.find(vm.variants, function (v) {
|
||||
if(v.active) {
|
||||
//ensure that the current one is selected
|
||||
v.save = true;
|
||||
}
|
||||
});
|
||||
|
||||
var active = _.find(vm.variants, function (v) {
|
||||
return v.active;
|
||||
vm.availableVariants.sort(function (a, b) {
|
||||
if (a.language && b.language) {
|
||||
if (a.language.name > b.language.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.language.name < b.language.name) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.segment && b.segment) {
|
||||
if (a.segment > b.segment) {
|
||||
return -1;
|
||||
}
|
||||
if (a.segment < b.segment) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (active) {
|
||||
//ensure that the current one is selected
|
||||
active.save = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
//disable save button if we have nothing to save
|
||||
$scope.model.disableSubmitButton = true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div ng-controller="Umbraco.Overlays.SaveContentController as vm">
|
||||
<div ng-controller="Umbraco.Overlays.SaveContentController as vm" class="umb-variant-selector-overlay">
|
||||
|
||||
<div ng-if="vm.loading" style="min-height: 50px; position: relative;">
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
@@ -7,17 +7,16 @@
|
||||
<div ng-if="!vm.loading">
|
||||
<div class="mb3">
|
||||
<p>
|
||||
<localize ng-if="!vm.isNew" key="content_languagesToSave"></localize>
|
||||
<localize ng-if="vm.isNew" key="content_languagesToSaveForFirstTime"></localize>
|
||||
<localize key="content_variantsToSave"></localize>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed">
|
||||
|
||||
<div class="umb-list-item"
|
||||
ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId">
|
||||
ng-repeat="variant in vm.availableVariants track by variant.compositeId">
|
||||
<ng-form name="saveVariantSelectorForm">
|
||||
<div ng-class="{'umb-list-item--error': saveVariantSelectorForm.saveVariantSelector.$invalid}">
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': saveVariantSelectorForm.saveVariantSelector.$invalid}">
|
||||
|
||||
<umb-checkbox
|
||||
input-id="{{variant.htmlId}}"
|
||||
@@ -25,52 +24,33 @@
|
||||
model="variant.save"
|
||||
on-change="vm.changeSelection(variant)"
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
text="{{variant.language.name}}"
|
||||
/>
|
||||
>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-if="!saveVariantSelectorForm.saveVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.isMandatory"> - </span>
|
||||
<span ng-if="variant.isMandatory" ng-class="{'text-error': (variant.publish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-messages="saveVariantSelectorForm.saveVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="text-error" ng-message="valServerField">{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
</umb-checkbox>
|
||||
|
||||
<div>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox" ng-if="!saveVariantSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
|
||||
<span class="db" ng-messages="saveVariantSelectorForm.saveVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox text-error" ng-message="valServerField">{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="vm.hasPristineVariants">
|
||||
<div class="bold mb3">
|
||||
<p>
|
||||
<localize ng-if="!vm.isNew" key="content_unmodifiedLanguages"></localize>
|
||||
<localize ng-if="vm.isNew" key="content_untouchedLanguagesForFirstTime"></localize>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.pristineVariantFilter">
|
||||
<div>
|
||||
<div style="margin-bottom: 2px;">
|
||||
<span>{{ variant.language.name }}</span>
|
||||
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
|
||||
</div>
|
||||
|
||||
<div ng-if="!(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state class="umb-list-item__description" variant="variant"></umb-variant-state>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="notification in variant.notifications">
|
||||
<div class="umb-list-item__description text-success">{{notification.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
vm.clearPublishDate = clearPublishDate;
|
||||
vm.clearUnpublishDate = clearUnpublishDate;
|
||||
vm.dirtyVariantFilter = dirtyVariantFilter;
|
||||
vm.pristineVariantFilter = pristineVariantFilter;
|
||||
vm.changeSelection = changeSelection;
|
||||
|
||||
vm.firstSelectedDates = {};
|
||||
@@ -24,7 +23,7 @@
|
||||
function onInit() {
|
||||
|
||||
vm.variants = $scope.model.variants;
|
||||
vm.hasPristineVariants = false;
|
||||
vm.displayVariants = vm.variants.slice(0);// shallow copy, we dont want to share the array-object(because we will be performing a sort method) but each entry should be shared (because we need validation and notifications).
|
||||
|
||||
for (let i = 0; i < vm.variants.length; i++) {
|
||||
origDates.push({
|
||||
@@ -38,36 +37,42 @@
|
||||
$scope.model.title = value;
|
||||
});
|
||||
}
|
||||
|
||||
_.each(vm.variants, function (variant) {
|
||||
variant.isMandatory = isMandatoryFilter(variant);
|
||||
|
||||
});
|
||||
|
||||
// Check for variants: if a node is invariant it will still have the default language in variants
|
||||
// so we have to check for length > 1
|
||||
if (vm.variants.length > 1) {
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
|
||||
//check for pristine variants
|
||||
if (!vm.hasPristineVariants) {
|
||||
vm.hasPristineVariants = pristineVariantFilter(variant);
|
||||
vm.displayVariants.sort(function (a, b) {
|
||||
if (a.language && b.language) {
|
||||
if (a.language.name > b.language.name) {
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
|
||||
//now sort it so that the current one is at the top
|
||||
vm.variants = _.sortBy(vm.variants, function (v) {
|
||||
return v.active ? 0 : 1;
|
||||
if (a.language.name < b.language.name) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.segment && b.segment) {
|
||||
if (a.segment > b.segment) {
|
||||
return -1;
|
||||
}
|
||||
if (a.segment < b.segment) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var active = _.find(vm.variants, function (v) {
|
||||
return v.active;
|
||||
_.each(vm.variants, function (v) {
|
||||
if (v.active) {
|
||||
v.save = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (active) {
|
||||
//ensure that the current one is selected
|
||||
active.save = true;
|
||||
}
|
||||
|
||||
|
||||
$scope.model.disableSubmitButton = !canSchedule();
|
||||
|
||||
}
|
||||
@@ -127,6 +132,7 @@
|
||||
* @param {any} type publish or unpublish
|
||||
*/
|
||||
function datePickerChange(variant, dateStr, type) {
|
||||
console.log("datePickerChange", variant, dateStr, type)
|
||||
if (type === 'publish') {
|
||||
setPublishDate(variant, dateStr);
|
||||
} else if (type === 'unpublish') {
|
||||
@@ -241,6 +247,7 @@
|
||||
* @param {any} variant
|
||||
*/
|
||||
function clearPublishDate(variant) {
|
||||
console.log("clearPublishDate", variant, variant.releaseDate)
|
||||
if(variant && variant.releaseDate) {
|
||||
variant.releaseDate = null;
|
||||
// we don't have a publish date anymore so we can clear the min date for unpublish
|
||||
@@ -256,6 +263,7 @@
|
||||
* @param {any} variant
|
||||
*/
|
||||
function clearUnpublishDate(variant) {
|
||||
console.log("clearUnpublishDate", variant)
|
||||
if(variant && variant.expireDate) {
|
||||
variant.expireDate = null;
|
||||
// we don't have a unpublish date anymore so we can clear the max date for publish
|
||||
@@ -297,8 +305,11 @@
|
||||
return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges" || variant.state === "NotCreated");
|
||||
}
|
||||
|
||||
function pristineVariantFilter(variant) {
|
||||
return !(dirtyVariantFilter(variant));
|
||||
function isMandatoryFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * has a mandatory language
|
||||
// * without having a segment, segments cant be mandatory at current state of code.
|
||||
return (variant.language && variant.language.isMandatory === true && variant.segment == null);
|
||||
}
|
||||
|
||||
/** Returns true if publishing is possible based on if there are un-published mandatory languages */
|
||||
@@ -321,7 +332,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
var isMandatory = variant.language && variant.language.isMandatory;
|
||||
var isMandatory = variant.segment == null && variant.language && variant.language.isMandatory;
|
||||
|
||||
//if this variant will show up in the publish-able list
|
||||
var publishable = dirtyVariantFilter(variant);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div ng-controller="Umbraco.Overlays.ScheduleContentController as vm">
|
||||
<div ng-controller="Umbraco.Overlays.ScheduleContentController as vm" class="umb-variant-selector-overlay">
|
||||
|
||||
<!-- invariant nodes -->
|
||||
<div ng-if="vm.variants.length === 1">
|
||||
@@ -84,28 +84,39 @@
|
||||
|
||||
<div class="umb-list umb-list--condensed">
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants">
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.displayVariants track by variant.compositeId">
|
||||
<ng-form name="scheduleSelectorForm" style="width:100%;">
|
||||
<div ng-class="{'umb-list-item--error': scheduleSelectorForm.saveVariantReleaseDate.$invalid}">
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': scheduleSelectorForm.saveVariantReleaseDate.$invalid}">
|
||||
|
||||
<umb-checkbox
|
||||
input-id="{{'saveVariantSelector_' + variant.language.culture}}"
|
||||
input-id="{{variant.htmlId}}"
|
||||
name="saveVariantSelector"
|
||||
model="variant.save"
|
||||
on-change="vm.changeSelection(variant)"
|
||||
text="{{variant.language.name}}"
|
||||
/>
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description"
|
||||
ng-if="!scheduleSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-show="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
</umb-checkbox>
|
||||
|
||||
<div>
|
||||
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox"
|
||||
ng-if="!scheduleSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-show="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
|
||||
|
||||
<div class="date-wrapper-mini date-wrapper-mini--checkbox">
|
||||
<div class="date-wrapper-mini__date" ng-if="vm.dirtyVariantFilter(variant) && (variant.releaseDate || variant.save)">
|
||||
<div class="date-wrapper-mini__date" ng-if="(variant.releaseDate || variant.save)">
|
||||
|
||||
<div style="font-size: 13px; margin-right: 5px;">Publish:<em ng-show="!variant.save"> {{variant.releaseDateFormatted}}</em></div>
|
||||
|
||||
@@ -117,7 +128,7 @@
|
||||
on-open="vm.datePickerShow(variant, 'publish')"
|
||||
on-close="vm.datePickerClose(variant, 'publish')">
|
||||
<div>
|
||||
<button ng-show="variant.releaseDate" class="btn umb-button--xxs" style="outline: none;">
|
||||
<button ng-show="variant.releaseDate" class="btn umb-button--xxs btn-outline umb-outline">
|
||||
{{variant.releaseDateFormatted}}
|
||||
</button>
|
||||
|
||||
@@ -126,9 +137,9 @@
|
||||
</a>
|
||||
</div>
|
||||
</umb-date-time-picker>
|
||||
<a ng-show="variant.releaseDate" ng-click="vm.clearPublishDate(variant)" class="btn umb-button--xxs dropdown-toggle umb-button-group__toggle" style="margin-left: -2px;">
|
||||
<button ng-show="variant.releaseDate" ng-click="vm.clearPublishDate(variant)" class="btn umb-button--xxs dropdown-toggle umb-button-group__toggle btn-outline umb-outline" style="margin-left: -2px;">
|
||||
<span class="icon icon-wrong"></span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -143,7 +154,7 @@
|
||||
on-open="vm.datePickerShow(variant, 'unpublish')"
|
||||
on-close="vm.datePickerClose(variant, 'unpublish')">
|
||||
<div>
|
||||
<button ng-show="variant.expireDate" class="btn umb-button--xxs" style="outline: none;">
|
||||
<button ng-show="variant.expireDate" class="btn umb-button--xxs btn-outline umb-outline">
|
||||
{{variant.expireDateFormatted}}
|
||||
</button>
|
||||
|
||||
@@ -152,7 +163,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</umb-date-time-picker>
|
||||
<a ng-show="variant.expireDate" ng-click="vm.clearUnpublishDate(variant)" class="btn umb-button--xxs dropdown-toggle umb-button-group__toggle" style="margin-left: -2px;">
|
||||
<a ng-show="variant.expireDate" ng-click="vm.clearUnpublishDate(variant)" class="btn umb-button--xxs dropdown-toggle umb-button-group__toggle btn-outline umb-outline" style="margin-left: -2px;">
|
||||
<span class="icon icon-wrong"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
var vm = this;
|
||||
vm.loading = true;
|
||||
|
||||
vm.modifiedVariantFilter = modifiedVariantFilter;
|
||||
vm.unmodifiedVariantFilter = unmodifiedVariantFilter;
|
||||
vm.changeSelection = changeSelection;
|
||||
|
||||
function onInit() {
|
||||
@@ -21,28 +19,40 @@
|
||||
});
|
||||
}
|
||||
|
||||
_.each(vm.variants, function (variant) {
|
||||
variant.isMandatory = isMandatoryFilter(variant);
|
||||
});
|
||||
|
||||
vm.availableVariants = vm.variants.filter(publishableVariantFilter);
|
||||
|
||||
if (vm.availableVariants.length !== 0) {
|
||||
|
||||
if (vm.variants.length !== 0) {
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
});
|
||||
|
||||
//now sort it so that the current one is at the top
|
||||
vm.variants = _.sortBy(vm.variants, function (v) {
|
||||
return v.active ? 0 : 1;
|
||||
vm.availableVariants = vm.availableVariants.sort(function (a, b) {
|
||||
if (a.language && b.language) {
|
||||
if (a.language.name > b.language.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.language.name < b.language.name) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.segment && b.segment) {
|
||||
if (a.segment > b.segment) {
|
||||
return -1;
|
||||
}
|
||||
if (a.segment < b.segment) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var active = _.find(vm.variants, function (v) {
|
||||
return v.active;
|
||||
_.each(vm.availableVariants, function (v) {
|
||||
if(v.active) {
|
||||
v.save = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (active) {
|
||||
//ensure that the current one is selected
|
||||
active.save = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
//disable save button if we have nothing to save
|
||||
$scope.model.disableSubmitButton = true;
|
||||
@@ -59,20 +69,20 @@
|
||||
$scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected
|
||||
}
|
||||
|
||||
function modifiedVariantFilter(variant) {
|
||||
//determine a variant is 'modified' (meaning it will show up as able to send for approval)
|
||||
// * it's editor is in a $dirty state
|
||||
// * it is in Draft state
|
||||
// * it is published with pending changes
|
||||
return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
|
||||
function isMandatoryFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * has a mandatory language
|
||||
// * without having a segment, segments cant be mandatory at current state of code.
|
||||
return (variant.language && variant.language.isMandatory === true && variant.segment == null);
|
||||
}
|
||||
|
||||
function unmodifiedVariantFilter(variant) {
|
||||
//determine a variant is 'unmodified' (meaning it will NOT show up as able to send for approval)
|
||||
function publishableVariantFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * variant is active
|
||||
// * it's editor is in a $dirty state
|
||||
// * it has been published
|
||||
// * it is not created for that specific language
|
||||
return (variant.state === "Published" && !variant.isDirty && !variant.active || variant.state === "NotCreated" && !variant.isDirty && !variant.active);
|
||||
// * it has pending saves
|
||||
// * it is unpublished
|
||||
return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
|
||||
}
|
||||
|
||||
//when this dialog is closed, reset all 'save' flags
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div ng-controller="Umbraco.Overlays.SendToPublishController as vm">
|
||||
<div ng-controller="Umbraco.Overlays.SendToPublishController as vm" class="umb-variant-selector-overlay">
|
||||
|
||||
<div class="mb3">
|
||||
<p><localize key="content_languagesToSendForApproval"></localize></p>
|
||||
@@ -10,31 +10,36 @@
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading">
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.modifiedVariantFilter">
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.availableVariants track by variant.compositeId">
|
||||
<ng-form name="publishVariantSelectorForm">
|
||||
<div ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid}">
|
||||
|
||||
<umb-checkbox
|
||||
input-id="{{variant.htmlId}}"
|
||||
<umb-checkbox input-id="{{variant.htmlId}}"
|
||||
name="publishVariantSelector"
|
||||
model="variant.save"
|
||||
model="variant.publish"
|
||||
on-change="vm.changeSelection(variant)"
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
text="{{ variant.language.name }}"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
<span ng-if="variant.isMandatory"> - </span>
|
||||
<span ng-if="variant.isMandatory" ng-class="{'text-error': (variant.publish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
|
||||
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
</umb-checkbox>
|
||||
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
</div>
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
@@ -42,20 +47,4 @@
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && (vm.variants | filter:vm.unmodifiedVariantFilter).length > 0">
|
||||
<div class="bold mb3">
|
||||
<p><localize key="content_unmodifiedLanguages"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.unmodifiedVariantFilter">
|
||||
<div>
|
||||
<div style="margin-bottom: 2px;">
|
||||
<span>{{ variant.language.name }}</span>
|
||||
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
|
||||
</div>
|
||||
<umb-variant-state class="umb-list-item__description" variant="variant"></umb-variant-state>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
var autoSelectedVariants = [];
|
||||
|
||||
vm.changeSelection = changeSelection;
|
||||
vm.publishedVariantFilter = publishedVariantFilter;
|
||||
vm.unpublishedVariantFilter = unpublishedVariantFilter;
|
||||
|
||||
function onInit() {
|
||||
|
||||
vm.variants = $scope.model.variants;
|
||||
vm.unpublishableVariants = vm.variants.filter(publishedVariantFilter)
|
||||
|
||||
// set dialog title
|
||||
if (!$scope.model.title) {
|
||||
@@ -21,24 +20,38 @@
|
||||
});
|
||||
}
|
||||
|
||||
_.each(vm.variants,
|
||||
function (variant) {
|
||||
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
|
||||
variant.htmlId = "_content_variant_" + variant.compositeId;
|
||||
});
|
||||
_.each(vm.variants, function (variant) {
|
||||
variant.isMandatory = isMandatoryFilter(variant);
|
||||
});
|
||||
|
||||
// node has variants
|
||||
if (vm.variants.length !== 1) {
|
||||
//now sort it so that the current one is at the top
|
||||
vm.variants = _.sortBy(vm.variants, function (v) {
|
||||
return v.active ? 0 : 1;
|
||||
|
||||
vm.unpublishableVariants.sort(function (a, b) {
|
||||
if (a.language && b.language) {
|
||||
if (a.language.name > b.language.name) {
|
||||
return -1;
|
||||
}
|
||||
if (a.language.name < b.language.name) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.segment && b.segment) {
|
||||
if (a.segment > b.segment) {
|
||||
return -1;
|
||||
}
|
||||
if (a.segment < b.segment) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
var active = _.find(vm.variants, function (v) {
|
||||
return v.active;
|
||||
});
|
||||
|
||||
if (active) {
|
||||
if (active && publishedVariantFilter(active)) {
|
||||
//ensure that the current one is selected
|
||||
active.save = true;
|
||||
}
|
||||
@@ -51,21 +64,15 @@
|
||||
|
||||
function changeSelection(selectedVariant) {
|
||||
|
||||
// disable submit button if nothing is selected
|
||||
var firstSelected = _.find(vm.variants, function (v) {
|
||||
return v.save;
|
||||
});
|
||||
$scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected
|
||||
|
||||
// if a mandatory variant is selected we want to select all other variants
|
||||
// if a mandatory variant is selected we want to select all other variants, we cant have anything published if a mandatory variants gets unpublished.
|
||||
// and disable selection for the others
|
||||
if(selectedVariant.save && selectedVariant.language.isMandatory) {
|
||||
if(selectedVariant.save && selectedVariant.segment == null && selectedVariant.language && selectedVariant.language.isMandatory) {
|
||||
|
||||
angular.forEach(vm.variants, function(variant){
|
||||
if(!variant.save && publishedVariantFilter(variant)) {
|
||||
vm.variants.forEach(function(variant) {
|
||||
if(!variant.save) {
|
||||
// keep track of the variants we automaically select
|
||||
// so we can remove the selection again
|
||||
autoSelectedVariants.push(variant.language.culture);
|
||||
autoSelectedVariants.push(variant);
|
||||
variant.save = true;
|
||||
}
|
||||
variant.disabled = true;
|
||||
@@ -79,12 +86,12 @@
|
||||
// if a mandatory variant is deselected we want to deselet all the variants
|
||||
// that was automatically selected so it goes back to the state before the mandatory language was selected.
|
||||
// We also want to enable all checkboxes again
|
||||
if(!selectedVariant.save && selectedVariant.language.isMandatory) {
|
||||
if(!selectedVariant.save && selectedVariant.segment == null && selectedVariant.language && selectedVariant.language.isMandatory) {
|
||||
|
||||
angular.forEach(vm.variants, function(variant){
|
||||
vm.variants.forEach( function(variant){
|
||||
|
||||
// check if variant was auto selected, then deselect
|
||||
if(_.contains(autoSelectedVariants, variant.language.culture)) {
|
||||
if(_.contains(autoSelectedVariants, variant)) {
|
||||
variant.save = false;
|
||||
};
|
||||
|
||||
@@ -93,6 +100,19 @@
|
||||
autoSelectedVariants = [];
|
||||
}
|
||||
|
||||
// disable submit button if nothing is selected
|
||||
var firstSelected = _.find(vm.variants, function (v) {
|
||||
return v.save;
|
||||
});
|
||||
$scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected
|
||||
|
||||
}
|
||||
|
||||
function isMandatoryFilter(variant) {
|
||||
//determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
|
||||
// * has a mandatory language
|
||||
// * without having a segment, segments cant be mandatory at current state of code.
|
||||
return (variant.language && variant.language.isMandatory === true && variant.segment == null);
|
||||
}
|
||||
|
||||
function publishedVariantFilter(variant) {
|
||||
@@ -102,13 +122,6 @@
|
||||
return (variant.state === "Published" || variant.state === "PublishedPendingChanges");
|
||||
}
|
||||
|
||||
function unpublishedVariantFilter(variant) {
|
||||
//determine a variant is 'modified' (meaning it will NOT show up as able to unpublish)
|
||||
// * it's editor is in a $dirty state
|
||||
// * it is published with pending changes
|
||||
return (variant.state !== "Published" && variant.state !== "PublishedPendingChanges");
|
||||
}
|
||||
|
||||
//when this dialog is closed, remove all unpublish and disabled flags
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var i = 0; i < vm.variants.length; i++) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div ng-controller="Umbraco.Overlays.UnpublishController as vm">
|
||||
<div ng-controller="Umbraco.Overlays.UnpublishController as vm" class="umb-variant-selector-overlay">
|
||||
|
||||
<!-- Single language -->
|
||||
<div ng-if="vm.variants.length === 1">
|
||||
@@ -10,12 +10,12 @@
|
||||
<div style="margin-bottom: 15px;">
|
||||
<p><localize key="content_languagesToUnpublish"></localize></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="umb-list umb-list--condensed">
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.publishedVariantFilter">
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.unpublishableVariants track by variant.compositeId">
|
||||
<ng-form name="unpublishVariantSelectorForm">
|
||||
<div>
|
||||
<div class="umb-variant-selector-entry" ng-class="{'umb-list-item--error': unpublishVariantSelectorForm.unpublishVariantSelector.$invalid}">
|
||||
|
||||
<umb-checkbox
|
||||
input-id="{{variant.htmlId}}"
|
||||
@@ -24,41 +24,35 @@
|
||||
on-change="vm.changeSelection(variant)"
|
||||
disabled="variant.disabled"
|
||||
server-validation-field="{{variant.htmlId}}"
|
||||
text="{{ variant.language.name }}"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<span class="db umb-list-item__description umb-list-item__description--checkbox">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
|
||||
>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="!(variant.segment && variant.language)">
|
||||
<span ng-bind="variant.displayName"></span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
</div>
|
||||
<span class="umb-variant-selector-entry__title" ng-if="variant.segment && variant.language">
|
||||
<span ng-bind="variant.segment"></span>
|
||||
<span class="__secondarytitle"> — {{variant.language.name}}</span>
|
||||
<strong ng-if="variant.isMandatory" class="umb-control-required">*</strong>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
|
||||
<umb-variant-state variant="variant"></umb-variant-state>
|
||||
<span ng-if="variant.isMandatory"> - </span>
|
||||
<span ng-if="variant.isMandatory" ng-class="{'text-error': (variant.publish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
|
||||
</span>
|
||||
<span class="umb-variant-selector-entry__description" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
|
||||
<span class="text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
|
||||
</span>
|
||||
</umb-checkbox>
|
||||
|
||||
<umb-variant-notification-list notifications="variant.notifications"></umb-variant-notification-list>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && (vm.variants | filter:vm.unpublishedVariantFilter).length > 0">
|
||||
<div class="bold mb3">
|
||||
<p><localize key="content_unpublishedLanguages"></localize></p>
|
||||
</div>
|
||||
|
||||
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.unpublishedVariantFilter">
|
||||
<div>
|
||||
<div style="margin-bottom: 2px;">
|
||||
<span>{{ variant.language.name }}</span>
|
||||
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<umb-variant-state class="umb-list-item__description" variant="variant"></umb-variant-state>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -44,6 +44,7 @@ function ContentBlueprintEditController($scope, $routeParams, contentResource) {
|
||||
|
||||
//load the default culture selected in the main tree if any
|
||||
$scope.culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;
|
||||
$scope.segment = $routeParams.csegment ? $routeParams.csegment : null;
|
||||
|
||||
//Bind to $routeUpdate which will execute anytime a location changes but the route is not triggered.
|
||||
//This is so we can listen to changes on the cculture parameter since that will not cause a route change
|
||||
@@ -52,6 +53,7 @@ function ContentBlueprintEditController($scope, $routeParams, contentResource) {
|
||||
//will not cause a route change and so we can update the isNew and contentId flags accordingly.
|
||||
$scope.$on('$routeUpdate', function (event, next) {
|
||||
$scope.culture = next.params.cculture ? next.params.cculture : $routeParams.mculture;
|
||||
$scope.segment = next.params.csegment ? next.params.csegment : null;
|
||||
$scope.isNew = $routeParams.id === "-1";
|
||||
$scope.contentId = $routeParams.id;
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
get-scaffold-method="getScaffoldMethod"
|
||||
is-new="isNew"
|
||||
tree-alias="contentblueprints"
|
||||
culture="culture">
|
||||
culture="culture"
|
||||
segment="segment">
|
||||
</content-editor>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
var totalInfo = 0;
|
||||
|
||||
// count total number of statusses
|
||||
group.checks.forEach(check => {
|
||||
Utilities.forEach(group.checks, check => {
|
||||
|
||||
if (check.status) {
|
||||
check.status.forEach(status => {
|
||||
@@ -95,22 +95,24 @@
|
||||
group.checkCounter = 0;
|
||||
group.loading = true;
|
||||
|
||||
checks.forEach(check => {
|
||||
check.loading = true;
|
||||
if (checks) {
|
||||
checks.forEach(check => {
|
||||
check.loading = true;
|
||||
|
||||
healthCheckResource.getStatus(check.id)
|
||||
.then(function (response) {
|
||||
check.status = response;
|
||||
group.checkCounter = group.checkCounter + 1;
|
||||
check.loading = false;
|
||||
healthCheckResource.getStatus(check.id)
|
||||
.then(function (response) {
|
||||
check.status = response;
|
||||
group.checkCounter = group.checkCounter + 1;
|
||||
check.loading = false;
|
||||
|
||||
// when all checks are done, set global group result
|
||||
if (group.checkCounter === checks.length) {
|
||||
setGroupGlobalResultType(group);
|
||||
group.loading = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
// when all checks are done, set global group result
|
||||
if (group.checkCounter === checks.length) {
|
||||
setGroupGlobalResultType(group);
|
||||
group.loading = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function openGroup(group) {
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"shortcuts_navigateSections",
|
||||
"shortcuts_addGroup",
|
||||
"shortcuts_addProperty",
|
||||
"shortcuts_addEditor",
|
||||
"defaultdialogs_selectEditor",
|
||||
"shortcuts_editDataType",
|
||||
"shortcuts_toggleListView",
|
||||
"shortcuts_toggleAllowAsRoot",
|
||||
|
||||
+6
@@ -20,12 +20,14 @@
|
||||
vm.selectedChildren = [];
|
||||
|
||||
vm.overlayTitle = "";
|
||||
vm.showAllowSegmentationOption = Umbraco.Sys.ServerVariables.umbracoSettings.showAllowSegmentationForDocumentTypes || false;
|
||||
|
||||
vm.addChild = addChild;
|
||||
vm.removeChild = removeChild;
|
||||
vm.sortChildren = sortChildren;
|
||||
vm.toggleAllowAsRoot = toggleAllowAsRoot;
|
||||
vm.toggleAllowCultureVariants = toggleAllowCultureVariants;
|
||||
vm.toggleAllowSegmentVariants = toggleAllowSegmentVariants;
|
||||
vm.canToggleIsElement = false;
|
||||
vm.toggleIsElement = toggleIsElement;
|
||||
|
||||
@@ -110,6 +112,10 @@
|
||||
$scope.model.allowCultureVariant = $scope.model.allowCultureVariant ? false : true;
|
||||
}
|
||||
|
||||
function toggleAllowSegmentVariants() {
|
||||
$scope.model.allowSegmentVariant = $scope.model.allowSegmentVariant ? false : true;
|
||||
}
|
||||
|
||||
function toggleIsElement() {
|
||||
$scope.model.isElement = $scope.model.isElement ? false : true;
|
||||
}
|
||||
|
||||
+19
-2
@@ -43,8 +43,8 @@
|
||||
<div class="sub-view-columns">
|
||||
|
||||
<div class="sub-view-column-left">
|
||||
<h5><localize key="contentTypeEditor_variantsHeading" /></h5>
|
||||
<small><localize key="contentTypeEditor_variantsDescription" /></small>
|
||||
<h5><localize key="contentTypeEditor_cultureVariantHeading" /></h5>
|
||||
<small><localize key="contentTypeEditor_cultureVariantDescription" /></small>
|
||||
</div>
|
||||
|
||||
<div class="sub-view-column-right">
|
||||
@@ -57,6 +57,23 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sub-view-columns" ng-if="vm.showAllowSegmentationOption">
|
||||
|
||||
<div class="sub-view-column-left">
|
||||
<h5><localize key="contentTypeEditor_segmentVariantHeading" /></h5>
|
||||
<small><localize key="contentTypeEditor_segmentVariantDescription" /></small>
|
||||
</div>
|
||||
|
||||
<div class="sub-view-column-right">
|
||||
<umb-toggle data-element="permissions-allow-culture-variant"
|
||||
checked="model.allowSegmentVariant"
|
||||
on-click="vm.toggleAllowSegmentVariants()">
|
||||
</umb-toggle>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="sub-view-columns">
|
||||
|
||||
<div class="sub-view-column-left">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<umb-editor-sub-header>
|
||||
|
||||
<umb-editor-sub-header-content-left>
|
||||
<umb-button button-style="action"
|
||||
<umb-button button-style="outline"
|
||||
type="button"
|
||||
action="vm.addLanguage()"
|
||||
label-key="languages_addLanguage">
|
||||
|
||||
@@ -47,13 +47,18 @@
|
||||
|
||||
<div class="editor clearfix" ng-if="model.parameter.editor">
|
||||
|
||||
<a href="" class="editor-icon-wrapper" ng-click="vm.openMacroParameterPicker(model.parameter)">
|
||||
<i class="icon {{model.parameter.dataTypeIcon}}" ng-class="{'icon-autofill': model.parameter.dataTypeIcon == null}"></i>
|
||||
</a>
|
||||
|
||||
<div class="editor-details">
|
||||
<a href="" class="editor-name" ng-click="vm.openMacroParameterPicker(model.parameter)">{{model.parameter.dataTypeName}}</a>
|
||||
<a href="" class="editor-editor" ng-click="vm.openMacroParameterPicker(model.parameter)">{{model.parameter.editor}}</a>
|
||||
<button class="btn-reset editor-info" ng-click="vm.openMacroParameterPicker(model.parameter)">
|
||||
|
||||
<div class="editor-icon-wrapper">
|
||||
<i class="icon {{model.parameter.dataTypeIcon}}" ng-class="{'icon-autofill': model.parameter.dataTypeIcon == null}"></i>
|
||||
</div>
|
||||
|
||||
<div class="editor-details">
|
||||
<a href="" class="editor-name">{{model.parameter.dataTypeName}}</a>
|
||||
<a href="" class="editor-editor">{{model.parameter.editor}}</a>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
+3
-1
@@ -208,7 +208,8 @@
|
||||
});
|
||||
});
|
||||
|
||||
vm.overlayMenu.title = vm.overlayMenu.pasteItems.length > 0 ? labels.grid_addElement : labels.content_createEmpty;
|
||||
vm.overlayMenu.title = labels.grid_addElement;
|
||||
vm.overlayMenu.hideHeader = vm.overlayMenu.pasteItems.length > 0;
|
||||
|
||||
vm.overlayMenu.clickClearPaste = function ($event) {
|
||||
$event.stopPropagation();
|
||||
@@ -216,6 +217,7 @@
|
||||
clipboardService.clearEntriesOfType("elementType", contentTypeAliases);
|
||||
clipboardService.clearEntriesOfType("elementTypeArray", contentTypeAliases);
|
||||
vm.overlayMenu.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually.
|
||||
vm.overlayMenu.hideHeader = false;
|
||||
};
|
||||
|
||||
if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) {
|
||||
|
||||
@@ -121,13 +121,15 @@ describe('contentEditingHelper tests', function () {
|
||||
|
||||
//act
|
||||
//note the null, that's because culture is null
|
||||
formHelper.handleServerValidation({ "_Properties.bodyText.null.value": ["Required"] });
|
||||
formHelper.handleServerValidation({ "_Properties.bodyText.null.null.value": ["Required"] });
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.items.length).toBe(1);
|
||||
expect(serverValidationManager.items[0].fieldName).toBe("value");
|
||||
expect(serverValidationManager.items[0].errorMsg).toBe("Required");
|
||||
expect(serverValidationManager.items[0].propertyAlias).toBe("bodyText");
|
||||
expect(serverValidationManager.items[0].culture).toBe("invariant");
|
||||
expect(serverValidationManager.items[0].segment).toBeNull();
|
||||
});
|
||||
|
||||
it('adds a multiple property and field level server validation errors when they are invalid', function () {
|
||||
@@ -142,7 +144,7 @@ describe('contentEditingHelper tests', function () {
|
||||
"Name": ["Required"],
|
||||
"UpdateDate": ["Invalid date"],
|
||||
//note the null, that's because culture is null
|
||||
"_Properties.bodyText.null.value": ["Required field"],
|
||||
"_Properties.bodyText.en-US.mySegment.value": ["Required field"],
|
||||
"_Properties.textarea": ["Invalid format"]
|
||||
});
|
||||
|
||||
@@ -157,6 +159,8 @@ describe('contentEditingHelper tests', function () {
|
||||
expect(serverValidationManager.items[2].fieldName).toBe("value");
|
||||
expect(serverValidationManager.items[2].errorMsg).toBe("Required field");
|
||||
expect(serverValidationManager.items[2].propertyAlias).toBe("bodyText");
|
||||
expect(serverValidationManager.items[2].culture).toBe("en-US");
|
||||
expect(serverValidationManager.items[2].segment).toBe("mySegment");
|
||||
expect(serverValidationManager.items[3].fieldName).toBe("");
|
||||
expect(serverValidationManager.items[3].errorMsg).toBe("Invalid format");
|
||||
expect(serverValidationManager.items[3].propertyAlias).toBe("textarea");
|
||||
|
||||
+142
-44
@@ -61,12 +61,12 @@
|
||||
it('can retrieve property validation errors for a sub field', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2");
|
||||
|
||||
//act
|
||||
var err1 = serverValidationManager.getPropertyError("myProperty", null, "value1");
|
||||
var err2 = serverValidationManager.getPropertyError("myProperty", null, "value2");
|
||||
var err1 = serverValidationManager.getPropertyError("myProperty", null, "value1", null);
|
||||
var err2 = serverValidationManager.getPropertyError("myProperty", null, "value2", null);
|
||||
|
||||
//assert
|
||||
expect(err1).not.toBeUndefined();
|
||||
@@ -85,14 +85,14 @@
|
||||
it('can retrieve property validation errors for a sub field for culture', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 2", null);
|
||||
|
||||
//act
|
||||
var err1 = serverValidationManager.getPropertyError("myProperty", "en-US", "value1");
|
||||
var err1NotFound = serverValidationManager.getPropertyError("myProperty", null, "value2");
|
||||
var err2 = serverValidationManager.getPropertyError("myProperty", "fr-FR", "value2");
|
||||
var err2NotFound = serverValidationManager.getPropertyError("myProperty", null, "value2");
|
||||
var err1 = serverValidationManager.getPropertyError("myProperty", "en-US", "value1", null);
|
||||
var err1NotFound = serverValidationManager.getPropertyError("myProperty", null, "value1", null);
|
||||
var err2 = serverValidationManager.getPropertyError("myProperty", "fr-FR", "value2", null);
|
||||
var err2NotFound = serverValidationManager.getPropertyError("myProperty", null, "value2", null);
|
||||
|
||||
|
||||
//assert
|
||||
@@ -111,12 +111,77 @@
|
||||
expect(err2.errorMsg).toEqual("Another value 2");
|
||||
expect(err2.culture).toEqual("fr-FR");
|
||||
});
|
||||
|
||||
it('can retrieve property validation errors for a sub field for segments', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", "segment1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", "segment2");
|
||||
|
||||
//act
|
||||
var err1 = serverValidationManager.getPropertyError("myProperty", null, "value1", "segment1");
|
||||
var err1NotFound = serverValidationManager.getPropertyError("myProperty", null, "value1", null);
|
||||
var err2 = serverValidationManager.getPropertyError("myProperty", null, "value2", "segment2");
|
||||
var err2NotFound = serverValidationManager.getPropertyError("myProperty", null, "value2", null);
|
||||
|
||||
|
||||
//assert
|
||||
expect(err1NotFound).toBeUndefined();
|
||||
expect(err2NotFound).toBeUndefined();
|
||||
|
||||
expect(err1).not.toBeUndefined();
|
||||
expect(err1.propertyAlias).toEqual("myProperty");
|
||||
expect(err1.fieldName).toEqual("value1");
|
||||
expect(err1.errorMsg).toEqual("Some value 1");
|
||||
expect(err1.segment).toEqual("segment1");
|
||||
|
||||
expect(err2).not.toBeUndefined();
|
||||
expect(err2.propertyAlias).toEqual("myProperty");
|
||||
expect(err2.fieldName).toEqual("value2");
|
||||
expect(err2.errorMsg).toEqual("Another value 2");
|
||||
expect(err2.segment).toEqual("segment2");
|
||||
});
|
||||
|
||||
|
||||
it('can retrieve property validation errors for a sub field for culture with segments', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 1", "segment1");
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 2", "segment2");
|
||||
|
||||
//act
|
||||
var err1 = serverValidationManager.getPropertyError("myProperty", "en-US", "value1", "segment1");
|
||||
expect(serverValidationManager.getPropertyError("myProperty", null, "value1", null)).toBeUndefined();
|
||||
expect(serverValidationManager.getPropertyError("myProperty", "en-US", "value1", null)).toBeUndefined();
|
||||
expect(serverValidationManager.getPropertyError("myProperty", null, "value1", "segment1")).toBeUndefined();
|
||||
var err2 = serverValidationManager.getPropertyError("myProperty", "fr-FR", "value2", "segment2");
|
||||
expect(serverValidationManager.getPropertyError("myProperty", null, "value2", null)).toBeUndefined();
|
||||
expect(serverValidationManager.getPropertyError("myProperty", "fr-FR", "value2", null)).toBeUndefined();
|
||||
expect(serverValidationManager.getPropertyError("myProperty", null, "value2", "segment2")).toBeUndefined();
|
||||
|
||||
|
||||
//assert
|
||||
|
||||
expect(err1).not.toBeUndefined();
|
||||
expect(err1.propertyAlias).toEqual("myProperty");
|
||||
expect(err1.fieldName).toEqual("value1");
|
||||
expect(err1.errorMsg).toEqual("Some value 1");
|
||||
expect(err1.culture).toEqual("en-US");
|
||||
expect(err1.segment).toEqual("segment1");
|
||||
|
||||
expect(err2).not.toBeUndefined();
|
||||
expect(err2.propertyAlias).toEqual("myProperty");
|
||||
expect(err2.fieldName).toEqual("value2");
|
||||
expect(err2.errorMsg).toEqual("Another value 2");
|
||||
expect(err2.culture).toEqual("fr-FR");
|
||||
expect(err2.segment).toEqual("segment2");
|
||||
});
|
||||
|
||||
it('can add a property errors with multiple sub fields and it the first will be retreived with only the property alias', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", null);
|
||||
|
||||
//act
|
||||
var err = serverValidationManager.getPropertyError("myProperty");
|
||||
@@ -132,10 +197,10 @@
|
||||
it('will return null for a non-existing property error', function () {
|
||||
|
||||
//arrage
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value", "Required");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value", "Required", null);
|
||||
|
||||
//act
|
||||
var err = serverValidationManager.getPropertyError("DoesntExist", null, "value");
|
||||
var err = serverValidationManager.getPropertyError("DoesntExist", null, "value", null);
|
||||
|
||||
//assert
|
||||
expect(err).toBeUndefined();
|
||||
@@ -145,15 +210,15 @@
|
||||
it('detects if a property error exists', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", null);
|
||||
|
||||
//act
|
||||
var err1 = serverValidationManager.hasPropertyError("myProperty");
|
||||
var err2 = serverValidationManager.hasPropertyError("myProperty", null, "value1");
|
||||
var err3 = serverValidationManager.hasPropertyError("myProperty", null, "value2");
|
||||
var err2 = serverValidationManager.hasPropertyError("myProperty", null, "value1", null);
|
||||
var err3 = serverValidationManager.hasPropertyError("myProperty", null, "value2", null);
|
||||
var err4 = serverValidationManager.hasPropertyError("notFound");
|
||||
var err5 = serverValidationManager.hasPropertyError("myProperty", null, "notFound");
|
||||
var err5 = serverValidationManager.hasPropertyError("myProperty", null, "notFound", null);
|
||||
|
||||
//assert
|
||||
expect(err1).toBe(true);
|
||||
@@ -167,15 +232,15 @@
|
||||
it('can remove a property error with a sub field specified', function () {
|
||||
|
||||
//arrage
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", null);
|
||||
|
||||
//act
|
||||
serverValidationManager.removePropertyError("myProperty", null, "value1");
|
||||
serverValidationManager.removePropertyError("myProperty", null, "value1", null);
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value1")).toBe(false);
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value2")).toBe(true);
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value1", null)).toBe(false);
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value2", null)).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
@@ -189,8 +254,8 @@
|
||||
serverValidationManager.removePropertyError("myProperty");
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value1")).toBe(false);
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value2")).toBe(false);
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value1", null)).toBe(false);
|
||||
expect(serverValidationManager.hasPropertyError("myProperty", null, "value2", null)).toBe(false);
|
||||
|
||||
});
|
||||
|
||||
@@ -201,10 +266,10 @@
|
||||
it('can retrieve culture validation errors', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 3");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 2", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 3", null);
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.hasCultureError(null)).toBe(true);
|
||||
@@ -216,6 +281,39 @@
|
||||
|
||||
});
|
||||
|
||||
describe('managing variant validation errors', function () {
|
||||
|
||||
it('can retrieve variant validation errors', function () {
|
||||
|
||||
//arrange
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 2", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 3", null);
|
||||
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", "MySegment");
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 2", "MySegment");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Another value 2", "MySegment");
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "value2", "Another value 3", "MySegment");
|
||||
|
||||
//assert
|
||||
expect(serverValidationManager.hasVariantError(null, null)).toBe(true);
|
||||
expect(serverValidationManager.hasVariantError("en-US", null)).toBe(true);
|
||||
expect(serverValidationManager.hasVariantError("fr-FR", null)).toBe(true);
|
||||
|
||||
expect(serverValidationManager.hasVariantError(null, "MySegment")).toBe(true);
|
||||
expect(serverValidationManager.hasVariantError("en-US", "MySegment")).toBe(true);
|
||||
expect(serverValidationManager.hasVariantError("fr-FR", "MySegment")).toBe(true);
|
||||
|
||||
expect(serverValidationManager.hasVariantError("es-ES", null)).toBe(false);
|
||||
expect(serverValidationManager.hasVariantError("es-ES", "MySegment")).toBe(false);
|
||||
expect(serverValidationManager.hasVariantError("fr-FR", "MySegmentNotRight")).toBe(false);
|
||||
expect(serverValidationManager.hasVariantError(null, "MySegmentNotRight")).toBe(false);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('validation error subscriptions', function() {
|
||||
|
||||
it('can subscribe to a field error', function() {
|
||||
@@ -228,11 +326,11 @@
|
||||
propertyErrors: propertyErrors,
|
||||
allErrors: allErrors
|
||||
};
|
||||
});
|
||||
}, null);
|
||||
|
||||
//act
|
||||
serverValidationManager.addFieldError("Name", "Required");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
|
||||
//assert
|
||||
expect(args).not.toBeUndefined();
|
||||
@@ -249,8 +347,8 @@
|
||||
};
|
||||
var cb2 = function () {
|
||||
};
|
||||
serverValidationManager.subscribe(null, null, "Name", cb1);
|
||||
serverValidationManager.subscribe(null, null, "Title", cb2);
|
||||
serverValidationManager.subscribe(null, null, "Name", cb1, null);
|
||||
serverValidationManager.subscribe(null, null, "Title", cb2, null);
|
||||
|
||||
//act
|
||||
serverValidationManager.addFieldError("Name", "Required");
|
||||
@@ -284,7 +382,7 @@
|
||||
propertyErrors: propertyErrors,
|
||||
allErrors: allErrors
|
||||
};
|
||||
});
|
||||
}, null);
|
||||
|
||||
serverValidationManager.subscribe("myProperty", null, "", function (isValid, propertyErrors, allErrors) {
|
||||
numCalled++;
|
||||
@@ -293,12 +391,12 @@
|
||||
propertyErrors: propertyErrors,
|
||||
allErrors: allErrors
|
||||
};
|
||||
});
|
||||
}, null);
|
||||
|
||||
//act
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Some value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "", "Some value 3");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value2", "Some value 2", null);
|
||||
serverValidationManager.addPropertyError("myProperty", null, "", "Some value 3", null);
|
||||
|
||||
//assert
|
||||
expect(args1).not.toBeUndefined();
|
||||
@@ -335,7 +433,7 @@
|
||||
propertyErrors: propertyErrors,
|
||||
allErrors: allErrors
|
||||
};
|
||||
});
|
||||
}, null);
|
||||
|
||||
serverValidationManager.subscribe(null, "es-ES", null, function (isValid, propertyErrors, allErrors) {
|
||||
numCalled++;
|
||||
@@ -344,13 +442,13 @@
|
||||
propertyErrors: propertyErrors,
|
||||
allErrors: allErrors
|
||||
};
|
||||
});
|
||||
}, null);
|
||||
|
||||
//act
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 1");
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value2", "Some value 2");
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "", "Some value 3");
|
||||
serverValidationManager.addPropertyError("myProperty", null, "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value1", "Some value 1", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "en-US", "value2", "Some value 2", null);
|
||||
serverValidationManager.addPropertyError("myProperty", "fr-FR", "", "Some value 3", null);
|
||||
|
||||
//assert
|
||||
expect(args1).not.toBeUndefined();
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
(function () {
|
||||
describe("Utilities", function () {
|
||||
describe("fromJson", function () {
|
||||
it("should deserialize json as object", function () {
|
||||
expect(Utilities.fromJson('{"a":1,"b":2}')).toEqual({ a: 1, b: 2 });
|
||||
});
|
||||
it("should return object as object", function () {
|
||||
expect(Utilities.fromJson({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 });
|
||||
});
|
||||
}),
|
||||
describe("toJson", function () {
|
||||
it("should delegate to JSON.stringify", function () {
|
||||
var spy = spyOn(JSON, "stringify").and.callThrough();
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
<key alias="assignDomain">Tilføj domæne</key>
|
||||
<key alias="auditTrail">Revisionsspor</key>
|
||||
<key alias="browse">Gennemse elementer</key>
|
||||
<key alias="changeDocType">Skift dokumenttype</key>
|
||||
<key alias="changeDocType">Skift Dokument Type</key>
|
||||
<key alias="changeDataType">Skift Input Type</key>
|
||||
<key alias="copy">Kopier</key>
|
||||
<key alias="create">Opret</key>
|
||||
<key alias="export">Eksportér</key>
|
||||
@@ -479,6 +480,7 @@
|
||||
<key alias="unLinkYour">Fjern link fra dit</key>
|
||||
<key alias="account">konto</key>
|
||||
<key alias="selectEditor">Vælg editor</key>
|
||||
<key alias="selectEditorConfiguration">Vælg konfiguration</key>
|
||||
<key alias="selectSnippet">Vælg snippet</key>
|
||||
<key alias="variantdeletewarning">Dette vil slette noden og alle dets sprog. Hvis du kun vil slette et sprog, så afpublicér det i stedet.</key>
|
||||
</area>
|
||||
@@ -780,6 +782,7 @@
|
||||
<key alias="generalHeader">Generelt</key>
|
||||
<key alias="editorHeader">Editor</key>
|
||||
<key alias="toggleAllowCultureVariants">Skift tillad sprogvarianter</key>
|
||||
<key alias="toggleAllowSegmentVariants">Skift tillad segmentering</key>
|
||||
</area>
|
||||
<area alias="graphicheadline">
|
||||
<key alias="backgroundcolor">Baggrundsfarve</key>
|
||||
@@ -1378,9 +1381,11 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="compositionInUse">Indholdstypen bliver brugt i en komposition og kan derfor ikke blive anvendt som komposition</key>
|
||||
<key alias="noAvailableCompositions">Der er ingen indholdstyper tilgængelige at bruge som komposition</key>
|
||||
<key alias="compositionRemoveWarning">Når du fjerner en komposition vil alle associerede indholdsdata blive slettet. Når først dokumenttypen er gemt, er der ingen vej tilbage.</key>
|
||||
<key alias="availableEditors">Tilgængelige editors</key>
|
||||
<key alias="availableEditors">Opret ny indstilling</key>
|
||||
<key alias="reuse">Genbrug</key>
|
||||
<key alias="editorSettings">Editor indstillinger</key>
|
||||
<key alias="editorSettings">Input indstillinger</key>
|
||||
<key alias="searchResultSettings">Tilgængelige indstillinger</key>
|
||||
<key alias="searchResultEditors">Opret ny indstilling</key>
|
||||
<key alias="configuration">Konfiguration</key>
|
||||
<key alias="yesDelete">Ja, slet</key>
|
||||
<key alias="movedUnderneath">blev flyttet til</key>
|
||||
@@ -1406,9 +1411,16 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="tabHasNoSortOrder">fane har ingen sorteringsrækkefølge</key>
|
||||
<key alias="compositionUsageHeading">Hvor er denne komposition brugt?</key>
|
||||
<key alias="compositionUsageSpecification">Denne komposition brugt i kompositionen af de følgende indholdstyper:</key>
|
||||
<key alias="variantsHeading">Tillad sprogvariation</key>
|
||||
<key alias="variantsHeading">Tillad variationer</key>
|
||||
<key alias="cultureVariantHeading">Tillad sprogvariation</key>
|
||||
<key alias="segmentVariantHeading">Tillad segmentering</key>
|
||||
<key alias="cultureVariantLabel">Tillader sprogvariationer</key>
|
||||
<key alias="segmentVariantLabel">Tillader segmentering</key>
|
||||
<key alias="variantsDescription">Tillad at redaktører kan oprette indhold af denne type på flere sprog.</key>
|
||||
<key alias="cultureVariantDescription">Tillad at redaktører kan oprette dette indhold på flere sprog.</key>
|
||||
<key alias="segmentVariantDescription">Tillad at redaktører kan oprette flere udgaver af denne type indhold.</key>
|
||||
<key alias="allowVaryByCulture">Tillad sprogvariation</key>
|
||||
<key alias="allowVaryBySegment">Tillad segmentering</key>
|
||||
<key alias="elementType">Element-type</key>
|
||||
<key alias="elementHeading">Er en Element-type</key>
|
||||
<key alias="elementDescription">En Element-type er tiltænkt brug i f.eks. Nested Content, ikke i indholdstræet.</key>
|
||||
|
||||
@@ -250,8 +250,8 @@
|
||||
<key alias="published">Published</key>
|
||||
<key alias="publishedPendingChanges">Published (pending changes)</key>
|
||||
<key alias="publishStatus">Publication Status</key>
|
||||
<key alias="publishDescendantsHelp"><![CDATA[Click <em>Publish with descendants</em> to publish <strong>%0%</strong> and all content items underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="publishDescendantsWithVariantsHelp"><![CDATA[Click <em>Publish with descendants</em> to publish <strong>the selected languages</strong> and the same languages of content items underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="publishDescendantsHelp"><![CDATA[Publish <strong>%0%</strong> and all content items underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="publishDescendantsWithVariantsHelp"><![CDATA[Publish variants and variants of same type underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="releaseDate">Publish at</key>
|
||||
<key alias="unpublishDate">Unpublish at</key>
|
||||
<key alias="removeDate">Clear Date</key>
|
||||
@@ -288,7 +288,7 @@
|
||||
<key alias="addTextBox">Add another text box</key>
|
||||
<key alias="removeTextBox">Remove this text box</key>
|
||||
<key alias="contentRoot">Content root</key>
|
||||
<key alias="includeUnpublished">Include drafts: also publish unpublished content items.</key>
|
||||
<key alias="includeUnpublished">Include drafts and unpublished content items.</key>
|
||||
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
|
||||
<key alias="isSensitiveValue_short">This value is hidden.</key>
|
||||
<key alias="languagesToPublishForFirstTime">What languages would you like to publish? All languages with content are saved!</key>
|
||||
@@ -302,7 +302,17 @@
|
||||
<key alias="unpublishedLanguages">Unpublished Languages</key>
|
||||
<key alias="unmodifiedLanguages">Unmodified Languages</key>
|
||||
<key alias="untouchedLanguagesForFirstTime">These languages haven't been created</key>
|
||||
<key alias="readyToPublish">Ready to Publish?</key>
|
||||
|
||||
<key alias="variantsWillBeSaved">All new variants will be saved.</key>
|
||||
<key alias="variantsToPublish">Which variants you would like to publish?</key>
|
||||
<key alias="variantsToSave">Choose which variants to be saved.</key>
|
||||
<key alias="variantsToSendForApproval">Pick variants to send for approval.</key>
|
||||
<key alias="variantsToSchedule">Set scheduled publishing...</key>
|
||||
<key alias="variantsToUnpublish">Select the variants to unpublish. Unpublishing a mandatory language will unpublish all variants.</key>
|
||||
<key alias="publishRequiresVariants">The following variants is required for publishing to take place:</key>
|
||||
|
||||
<key alias="notReadyToPublish">We are not ready to Publish</key>
|
||||
<key alias="readyToPublish">Ready to publish?</key>
|
||||
<key alias="readyToSave">Ready to Save?</key>
|
||||
<key alias="sendForApproval">Send for approval</key>
|
||||
<key alias="schedulePublishHelp">Select the date and time to publish and/or unpublish the content item.</key>
|
||||
@@ -796,6 +806,7 @@
|
||||
<key alias="generalHeader">General</key>
|
||||
<key alias="editorHeader">Editor</key>
|
||||
<key alias="toggleAllowCultureVariants">Toggle allow culture variants</key>
|
||||
<key alias="toggleAllowSegmentVariants">Toggle allow segmentation</key>
|
||||
</area>
|
||||
<area alias="graphicheadline">
|
||||
<key alias="backgroundcolor">Background colour</key>
|
||||
@@ -1646,9 +1657,16 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="tabHasNoSortOrder">tab has no sort order</key>
|
||||
<key alias="compositionUsageHeading">Where is this composition used?</key>
|
||||
<key alias="compositionUsageSpecification">This composition is currently used in the composition of the following content types:</key>
|
||||
<key alias="variantsHeading">Allow varying by culture</key>
|
||||
<key alias="variantsHeading">Allow variations</key>
|
||||
<key alias="cultureVariantHeading">Allow vary by culture</key>
|
||||
<key alias="segmentVariantHeading">Allow segmentation</key>
|
||||
<key alias="cultureVariantLabel">Vary by culture</key>
|
||||
<key alias="segmentVariantLabel">Vary by segments</key>
|
||||
<key alias="variantsDescription">Allow editors to create content of this type in different languages.</key>
|
||||
<key alias="cultureVariantDescription">Allow editors to create content of different languages.</key>
|
||||
<key alias="segmentVariantDescription">Allow editors to create segments of this content.</key>
|
||||
<key alias="allowVaryByCulture">Allow varying by culture</key>
|
||||
<key alias="allowVaryBySegment">Allow segmentation</key>
|
||||
<key alias="elementType">Element type</key>
|
||||
<key alias="elementHeading">Is an Element type</key>
|
||||
<key alias="elementDescription">An Element type is meant to be used for instance in Nested Content, and not in the tree.</key>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<key alias="auditTrail">Audit Trail</key>
|
||||
<key alias="browse">Browse Node</key>
|
||||
<key alias="changeDocType">Change Document Type</key>
|
||||
<key alias="changeDataType">Change Data Type</key>
|
||||
<key alias="copy">Copy</key>
|
||||
<key alias="create">Create</key>
|
||||
<key alias="export">Export</key>
|
||||
@@ -253,8 +254,8 @@
|
||||
<key alias="published">Published</key>
|
||||
<key alias="publishedPendingChanges">Published (pending changes)</key>>
|
||||
<key alias="publishStatus">Publication Status</key>
|
||||
<key alias="publishDescendantsHelp"><![CDATA[Click <em>Publish with descendants</em> to publish <strong>%0%</strong> and all content items underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="publishDescendantsWithVariantsHelp"><![CDATA[Click <em>Publish with descendants</em> to publish <strong>the selected languages</strong> and the same languages of content items underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="publishDescendantsHelp"><![CDATA[Publish <strong>%0%</strong> and all content items underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="publishDescendantsWithVariantsHelp"><![CDATA[Publish variants and variants of same type underneath and thereby making their content publicly available.]]></key>
|
||||
<key alias="releaseDate">Publish at</key>
|
||||
<key alias="unpublishDate">Unpublish at</key>
|
||||
<key alias="removeDate">Clear Date</key>
|
||||
@@ -292,7 +293,7 @@
|
||||
<key alias="addTextBox">Add another text box</key>
|
||||
<key alias="removeTextBox">Remove this text box</key>
|
||||
<key alias="contentRoot">Content root</key>
|
||||
<key alias="includeUnpublished">Include drafts: also publish unpublished content items.</key>
|
||||
<key alias="includeUnpublished">Include drafts and unpublished content items.</key>
|
||||
<key alias="isSensitiveValue">This value is hidden. If you need access to view this value please contact your website administrator.</key>
|
||||
<key alias="isSensitiveValue_short">This value is hidden.</key>
|
||||
<key alias="languagesToPublishForFirstTime">What languages would you like to publish? All languages with content are saved!</key>
|
||||
@@ -306,7 +307,17 @@
|
||||
<key alias="unpublishedLanguages">Unpublished Languages</key>
|
||||
<key alias="unmodifiedLanguages">Unmodified Languages</key>
|
||||
<key alias="untouchedLanguagesForFirstTime">These languages haven't been created</key>
|
||||
<key alias="readyToPublish">Ready to Publish?</key>
|
||||
|
||||
<key alias="variantsWillBeSaved">All new variants will be saved.</key>
|
||||
<key alias="variantsToPublish">Which variants you would like to publish?</key>
|
||||
<key alias="variantsToSave">Choose which variants to be saved.</key>
|
||||
<key alias="variantsToSendForApproval">Pick variants to send for approval.</key>
|
||||
<key alias="variantsToSchedule">Set scheduled publishing...</key>
|
||||
<key alias="variantsToUnpublish">Select the variants to unpublish. Unpublishing a mandatory language will unpublish all variants.</key>
|
||||
<key alias="publishRequiresVariants">The following variants is required for publishing to take place:</key>
|
||||
|
||||
<key alias="notReadyToPublish">We are not ready to Publish</key>
|
||||
<key alias="readyToPublish">Ready to publish?</key>
|
||||
<key alias="readyToSave">Ready to Save?</key>
|
||||
<key alias="sendForApproval">Send for approval</key>
|
||||
<key alias="schedulePublishHelp">Select the date and time to publish and/or unpublish the content item.</key>
|
||||
@@ -493,6 +504,7 @@
|
||||
<key alias="unLinkYour">Un-link your</key>
|
||||
<key alias="account">account</key>
|
||||
<key alias="selectEditor">Select editor</key>
|
||||
<key alias="selectEditorConfiguration">Select configuration</key>
|
||||
<key alias="selectSnippet">Select snippet</key>
|
||||
<key alias="variantdeletewarning">This will delete the node and all its languages. If you only want to delete one language, you should unpublish the node in that language instead.</key>
|
||||
</area>
|
||||
@@ -797,6 +809,7 @@
|
||||
<key alias="generalHeader">General</key>
|
||||
<key alias="editorHeader">Editor</key>
|
||||
<key alias="toggleAllowCultureVariants">Toggle allow culture variants</key>
|
||||
<key alias="toggleAllowSegmentVariants">Toggle allow segmentation</key>
|
||||
</area>
|
||||
<area alias="graphicheadline">
|
||||
<key alias="backgroundcolor">Background color</key>
|
||||
@@ -1631,6 +1644,8 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="availableEditors">Create new</key>
|
||||
<key alias="reuse">Use existing</key>
|
||||
<key alias="editorSettings">Editor settings</key>
|
||||
<key alias="searchResultSettings">Available configurations</key>
|
||||
<key alias="searchResultEditors">Create a new configuration</key>
|
||||
<key alias="configuration">Configuration</key>
|
||||
<key alias="yesDelete">Yes, delete</key>
|
||||
<key alias="movedUnderneath">was moved underneath</key>
|
||||
@@ -1656,9 +1671,16 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="tabHasNoSortOrder">tab has no sort order</key>
|
||||
<key alias="compositionUsageHeading">Where is this composition used?</key>
|
||||
<key alias="compositionUsageSpecification">This composition is currently used in the composition of the following content types:</key>
|
||||
<key alias="variantsHeading">Allow varying by culture</key>
|
||||
<key alias="variantsHeading">Allow variations</key>
|
||||
<key alias="cultureVariantHeading">Allow vary by culture</key>
|
||||
<key alias="segmentVariantHeading">Allow segmentation</key>
|
||||
<key alias="cultureVariantLabel">Vary by culture</key>
|
||||
<key alias="segmentVariantLabel">Vary by segments</key>
|
||||
<key alias="variantsDescription">Allow editors to create content of this type in different languages.</key>
|
||||
<key alias="cultureVariantDescription">Allow editors to create content of different languages.</key>
|
||||
<key alias="segmentVariantDescription">Allow editors to create segments of this content.</key>
|
||||
<key alias="allowVaryByCulture">Allow varying by culture</key>
|
||||
<key alias="allowVaryBySegment">Allow segmentation</key>
|
||||
<key alias="elementType">Element type</key>
|
||||
<key alias="elementHeading">Is an element type</key>
|
||||
<key alias="elementDescription">An element type is meant to be used for instance in Nested Content, and not in the tree.</key>
|
||||
|
||||
@@ -349,6 +349,7 @@ namespace Umbraco.Web.Editors
|
||||
{"loginBackgroundImage", Current.Configs.Settings().Content.LoginBackgroundImage},
|
||||
{"showUserInvite", EmailSender.CanSendRequiredEmail},
|
||||
{"canSendRequiredEmail", EmailSender.CanSendRequiredEmail},
|
||||
{"showAllowSegmentationForDocumentTypes", false},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -897,7 +897,13 @@ namespace Umbraco.Web.Editors
|
||||
if (variantCount > 1)
|
||||
{
|
||||
var cultureErrors = ModelState.GetCulturesWithErrors(Services.LocalizationService, cultureForInvariantErrors);
|
||||
foreach (var c in contentItem.Variants.Where(x => x.Save && !cultureErrors.Contains(x.Culture)).Select(x => x.Culture).ToArray())
|
||||
|
||||
var savedWithoutErrors = contentItem.Variants
|
||||
.Where(x => x.Save && !cultureErrors.Contains(x.Culture) && x.Culture != null)
|
||||
.Select(x => x.Culture)
|
||||
.ToArray();
|
||||
|
||||
foreach (var c in savedWithoutErrors)
|
||||
{
|
||||
AddSuccessNotification(notifications, c,
|
||||
Services.TextService.Localize("speechBubbles/editContentSavedHeader"),
|
||||
@@ -1874,7 +1880,8 @@ namespace Umbraco.Web.Editors
|
||||
? variant.PropertyCollectionDto
|
||||
: new ContentPropertyCollectionDto
|
||||
{
|
||||
Properties = variant.PropertyCollectionDto.Properties.Where(x => !x.Culture.IsNullOrWhiteSpace())
|
||||
Properties = variant.PropertyCollectionDto.Properties.Where(
|
||||
x => !x.Culture.IsNullOrWhiteSpace() || !x.Segment.IsNullOrWhiteSpace())
|
||||
};
|
||||
|
||||
//for each variant, map the property values
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace Umbraco.Web.Editors.Filters
|
||||
r.ErrorMessage = property.ValidationRegExpMessage;
|
||||
}
|
||||
|
||||
modelState.AddPropertyError(r, property.Alias, property.Culture);
|
||||
modelState.AddPropertyError(r, property.Alias, property.Culture, property.Segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +49,15 @@ namespace Umbraco.Web
|
||||
/// <param name="propertyAlias"></param>
|
||||
/// <param name="culture">The culture for the property, if the property is invariant than this is empty</param>
|
||||
internal static void AddPropertyError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState,
|
||||
ValidationResult result, string propertyAlias, string culture = "")
|
||||
ValidationResult result, string propertyAlias, string culture = "", string segment = "")
|
||||
{
|
||||
if (culture == null)
|
||||
culture = "";
|
||||
modelState.AddValidationError(result, "_Properties", propertyAlias,
|
||||
//if the culture is null, we'll add the term 'invariant' as part of the key
|
||||
culture.IsNullOrWhiteSpace() ? "invariant" : culture);
|
||||
culture.IsNullOrWhiteSpace() ? "invariant" : culture,
|
||||
// if the segment is null, we'll add the term 'null' as part of the key
|
||||
segment.IsNullOrWhiteSpace() ? "null" : segment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -69,6 +69,9 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "allowCultureVariant")]
|
||||
public bool AllowCultureVariant { get; set; }
|
||||
|
||||
[DataMember(Name = "allowSegmentVariant")]
|
||||
public bool AllowSegmentVariant { get; set; }
|
||||
|
||||
//Tabs
|
||||
[DataMember(Name = "groups")]
|
||||
public IEnumerable<PropertyGroupBasic<TPropertyType>> Groups { get; set; }
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "name", IsRequired = true)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "displayName")]
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the tabs containing display properties
|
||||
/// </summary>
|
||||
|
||||
@@ -20,9 +20,12 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "defaultTemplate")]
|
||||
public EntityBasic DefaultTemplate { get; set; }
|
||||
|
||||
|
||||
[DataMember(Name = "allowCultureVariant")]
|
||||
public bool AllowCultureVariant { get; set; }
|
||||
|
||||
[DataMember(Name = "allowSegmentVariant")]
|
||||
public bool AllowSegmentVariant { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,5 +54,8 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "allowCultureVariant")]
|
||||
public bool AllowCultureVariant { get; set; }
|
||||
|
||||
[DataMember(Name = "allowSegmentVariant")]
|
||||
public bool AllowSegmentVariant { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user