Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB3970-membership-providers
# Conflicts: # src/Umbraco.Web/Editors/Filters/MemberSaveModelValidator.cs # src/Umbraco.Web/Editors/Filters/MemberSaveValidationAttribute.cs
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* @restrict E
|
||||
**/
|
||||
angular.module("umbraco.directives")
|
||||
.directive('umbProperty', function (umbPropEditorHelper, userService) {
|
||||
.directive('umbProperty', function (userService) {
|
||||
return {
|
||||
scope: {
|
||||
property: "=",
|
||||
@@ -17,7 +17,7 @@ angular.module("umbraco.directives")
|
||||
templateUrl: 'views/components/property/umb-property.html',
|
||||
link: function (scope) {
|
||||
|
||||
scope.propertyEditorAPI = {};
|
||||
scope.propertyActions = [];
|
||||
|
||||
userService.getCurrentUser().then(function (u) {
|
||||
var isAdmin = u.userGroups.indexOf('admin') !== -1;
|
||||
@@ -25,28 +25,20 @@ angular.module("umbraco.directives")
|
||||
});
|
||||
},
|
||||
//Define a controller for this directive to expose APIs to other directives
|
||||
controller: function ($scope, $timeout) {
|
||||
controller: function ($scope) {
|
||||
|
||||
var self = this;
|
||||
|
||||
|
||||
//set the API properties/methods
|
||||
|
||||
self.property = $scope.property;
|
||||
self.setPropertyError = function (errorMsg) {
|
||||
$scope.property.propertyErrorMessage = errorMsg;
|
||||
};
|
||||
|
||||
var unsubscribe = $scope.$on("ExposePropertyEditorAPI", function(event, api) {
|
||||
|
||||
//avoid eventual parent properties to capture this.
|
||||
event.stopPropagation();
|
||||
|
||||
$scope.propertyEditorAPI = api;
|
||||
});
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
unsubscribe();
|
||||
});
|
||||
self.setPropertyActions = function(actions) {
|
||||
$scope.propertyActions = actions;
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,6 +65,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
|
||||
vm.reloadNode = reloadNode;
|
||||
vm.syncTree = syncTree;
|
||||
vm.loadChildren = loadChildren;
|
||||
vm.hasTree = hasTree;
|
||||
|
||||
//wire up the exposed api object for hosting controllers
|
||||
if ($scope.api) {
|
||||
@@ -72,6 +73,7 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
|
||||
$scope.api.load = vm.load;
|
||||
$scope.api.reloadNode = vm.reloadNode;
|
||||
$scope.api.syncTree = vm.syncTree;
|
||||
$scope.api.hasTree = vm.hasTree;
|
||||
}
|
||||
|
||||
//flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should
|
||||
@@ -203,6 +205,25 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
|
||||
});
|
||||
}
|
||||
|
||||
//given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node
|
||||
function hasTree(treeAlias) {
|
||||
|
||||
if (!$scope.tree) {
|
||||
throw "Err in umbtree.directive.loadActiveTree, $scope.tree is null";
|
||||
}
|
||||
|
||||
if (!treeAlias) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var treeRoots = getTreeRootNodes();
|
||||
var foundTree = _.find(treeRoots, function (node) {
|
||||
return node.metaData.treeAlias.toUpperCase() === treeAlias.toUpperCase();
|
||||
});
|
||||
|
||||
return foundTree !== undefined;
|
||||
}
|
||||
|
||||
//given a tree alias, this will search the current section tree for the specified tree alias and set the current active tree to it's root node
|
||||
function loadActiveTree(treeAlias) {
|
||||
|
||||
|
||||
@@ -549,7 +549,9 @@
|
||||
property.dataTypeIcon = propertyModel.dataTypeIcon;
|
||||
property.dataTypeName = propertyModel.dataTypeName;
|
||||
property.validation.mandatory = propertyModel.validation.mandatory;
|
||||
property.validation.mandatoryMessage = propertyModel.validation.mandatoryMessage;
|
||||
property.validation.pattern = propertyModel.validation.pattern;
|
||||
property.validation.patternMessage = propertyModel.validation.patternMessage;
|
||||
property.showOnMemberProfile = propertyModel.showOnMemberProfile;
|
||||
property.memberCanEdit = propertyModel.memberCanEdit;
|
||||
property.isSensitiveValue = propertyModel.isSensitiveValue;
|
||||
@@ -632,7 +634,9 @@
|
||||
propertyState: "init",
|
||||
validation: {
|
||||
mandatory: false,
|
||||
pattern: null
|
||||
mandatoryMessage: null,
|
||||
pattern: null,
|
||||
patternMessage: null
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -338,6 +338,22 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.navigationService#hasTree
|
||||
* @methodOf umbraco.services.navigationService
|
||||
*
|
||||
* @description
|
||||
* Checks if a tree with the given alias exists.
|
||||
*
|
||||
* @param {String} treeAlias the tree alias to check
|
||||
*/
|
||||
hasTree: function (treeAlias) {
|
||||
return navReadyPromise.promise.then(function () {
|
||||
return mainTreeApi.hasTree(treeAlias);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
|
||||
have to set an active tree and then sync, the new API does this in one method by using syncTree
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
}
|
||||
|
||||
function confirmDelete(overlay) {
|
||||
overlay.confirmType = "delete";
|
||||
confirm(overlay);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function propertyEditorService() {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.propertyEditorService#expose
|
||||
* @methodOf umbraco.services.propertyEditorService
|
||||
* @function
|
||||
*
|
||||
* @param {object} scope An object containing API for the PropertyEditor
|
||||
*/
|
||||
function exposeAPI(scope, api) {
|
||||
if (!scope) {
|
||||
throw "scope cannot be null";
|
||||
}
|
||||
if (!api) {
|
||||
throw "api cannot be null";
|
||||
}
|
||||
scope.$emit("ExposePropertyEditorAPI", api);
|
||||
}
|
||||
|
||||
return {
|
||||
exposeAPI: exposeAPI
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('umbraco.services').factory('propertyEditorService', propertyEditorService);
|
||||
})();
|
||||
@@ -184,7 +184,13 @@
|
||||
});
|
||||
if (cached) return cached[1];
|
||||
|
||||
nodeComputedStyle = nodeComputedStyle || this.doc.defaultView.getComputedStyle(node);
|
||||
if (!nodeComputedStyle) {
|
||||
if (node instanceof DocumentFragment) {
|
||||
return true;// though DocumentFragment doesn't directly have display 'none', we know that it will never be visible, and therefore we return true. (and do not cache this, cause it will change if appended to the DOM)
|
||||
} else {
|
||||
nodeComputedStyle = this.doc.defaultView.getComputedStyle(node);
|
||||
}
|
||||
}
|
||||
|
||||
var result = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function validationMessageService($q, localizationService) {
|
||||
|
||||
// Gets the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type's validation object
|
||||
// or a localised default.
|
||||
function getMandatoryMessage(validation) {
|
||||
if (!validation) {
|
||||
return $q.when("");
|
||||
}
|
||||
|
||||
if (validation.mandatoryMessage) {
|
||||
return $q.when(validation.mandatoryMessage);
|
||||
} else {
|
||||
return localizationService.localize("general_required").then(function (value) {
|
||||
return $q.when(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var service = {
|
||||
getMandatoryMessage: getMandatoryMessage
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.services').factory('validationMessageService', validationMessageService);
|
||||
|
||||
|
||||
})();
|
||||
@@ -493,11 +493,11 @@ input.umb-group-builder__group-sort-value {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: @ui-action-type;
|
||||
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color:@ui-action-type-hover;
|
||||
border-color:@ui-action-border-hover;
|
||||
color: @ui-action-type-hover;
|
||||
border-color: @ui-action-border-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,7 +554,13 @@ input.umb-group-builder__group-sort-value {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.editor-validation-pattern{
|
||||
.editor-validation-message {
|
||||
min-width: 100%;
|
||||
min-height: 25px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.editor-validation-pattern {
|
||||
border: 1px solid @gray-7;
|
||||
margin: 10px 0 0;
|
||||
padding: 6px;
|
||||
|
||||
@@ -14,6 +14,17 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.umb-nested-content--mandatory {
|
||||
/*
|
||||
yeah so this is a pain, but we must be super specific in targeting the mandatory property labels,
|
||||
otherwise all properties within a reqired, nested, nested content property will all appear mandatory
|
||||
*/
|
||||
> ng-form > .control-group > .umb-el-wrap > .control-header label:after {
|
||||
content: '*';
|
||||
color: @red;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-nested-content-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -134,6 +145,8 @@
|
||||
|
||||
|
||||
.umb-nested-content__icon {
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin: 2px;
|
||||
@@ -235,7 +248,6 @@
|
||||
}
|
||||
|
||||
.umb-nested-content__placeholder {
|
||||
height: 22px;
|
||||
padding: 4px 6px;
|
||||
border: 1px dashed #d8d7d9;
|
||||
background: 0 0;
|
||||
@@ -246,33 +258,18 @@
|
||||
text-align: center;
|
||||
|
||||
&--selected {
|
||||
border: 1px solid #d8d7d9;
|
||||
border: none;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-nested-content__placeholder-name{
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.umb-nested-content__placeholder:hover {
|
||||
color: #2152a3;
|
||||
border-color: #2152a3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.umb-nested-content__placeholder-icon-holder {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.umb-nested-content__placeholder-icon {
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
.form-horizontal .umb-nested-content--narrow .controls-row {
|
||||
margin-left: 40% !important;
|
||||
}
|
||||
|
||||
@@ -528,7 +528,8 @@ input[type="checkbox"][readonly] {
|
||||
.help-inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding-left: 5px;
|
||||
padding-top: 4px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
div.help {
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
|
||||
.history-line {
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
margin: 0 0 0 14px;
|
||||
background-color: @gray-8;
|
||||
position: absolute;
|
||||
|
||||
@@ -81,39 +81,55 @@
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group clearfix" ng-if="!model.property.locked">
|
||||
|
||||
|
||||
<h5><localize key="validation_validation"></localize></h5>
|
||||
|
||||
<label>
|
||||
|
||||
<label>
|
||||
<localize key="validation_fieldIsMandatory"></localize>
|
||||
</label>
|
||||
|
||||
<umb-toggle data-element="validation_mandatory"
|
||||
checked="model.property.validation.mandatory"
|
||||
on-click="vm.toggleValidation()"
|
||||
on-click="vm.toggleValidation()">
|
||||
focus-when="{{vm.focusOnMandatoryField}}"
|
||||
>
|
||||
</umb-toggle>
|
||||
|
||||
<label class="mt3">
|
||||
<input type="text"
|
||||
class="editor-validation-message"
|
||||
localize="placeholder"
|
||||
placeholder="@validation_mandatoryMessage"
|
||||
ng-model="model.property.validation.mandatoryMessage"
|
||||
ng-if="model.property.validation.mandatory"
|
||||
ng-keypress="vm.submitOnEnter($event)">
|
||||
</input>
|
||||
|
||||
<label class="mt3">
|
||||
<localize key="validation_customValidation"></localize>
|
||||
</label>
|
||||
|
||||
|
||||
<select class="umb-dropdown" ng-options="validationType.name for validationType in vm.validationTypes" ng-model="vm.selectedValidationType" ng-change="vm.changeValidationType(vm.selectedValidationType)">
|
||||
<option value=""><localize key="validation_noValidation">No validation</localize></option>
|
||||
</select>
|
||||
|
||||
|
||||
<textarea class="editor-validation-pattern"
|
||||
localize="placeholder"
|
||||
placeholder="@validation_validationRegExp"
|
||||
ng-model="model.property.validation.pattern"
|
||||
ng-change="vm.changeValidationPattern()"
|
||||
ng-if="vm.showValidationPattern"
|
||||
umb-auto-resize
|
||||
focus-when="{{vm.focusOnPatternField}}"
|
||||
ng-keypress="vm.submitOnEnter($event)">
|
||||
</textarea>
|
||||
|
||||
localize="placeholder"
|
||||
placeholder="@validation_validationRegExp"
|
||||
ng-model="model.property.validation.pattern"
|
||||
ng-change="vm.changeValidationPattern()"
|
||||
ng-if="vm.showValidationPattern"
|
||||
umb-auto-resize
|
||||
focus-when="{{vm.focusOnPatternField}}"
|
||||
ng-keypress="vm.submitOnEnter($event)">
|
||||
</textarea>
|
||||
|
||||
<input type="text"
|
||||
class="editor-validation-message"
|
||||
localize="placeholder"
|
||||
placeholder="@validation_validationRegExpMessage"
|
||||
ng-model="model.property.validation.patternMessage"
|
||||
ng-if="vm.showValidationPattern"
|
||||
ng-keypress="vm.submitOnEnter($event)" />
|
||||
|
||||
</div>
|
||||
<div class="umb-control-group clearfix" ng-if="model.contentType === 'documentType' && model.contentTypeAllowCultureVariant">
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
</label>
|
||||
|
||||
<umb-property-actions actions="propertyEditorAPI.propertyActions"></umb-property-actions>
|
||||
<umb-property-actions actions="propertyActions"></umb-property-actions>
|
||||
|
||||
<small class="control-description" ng-bind-html="property.description | preserveNewLineInHtml"></small>
|
||||
</div>
|
||||
|
||||
@@ -24,10 +24,6 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
alias: "selectedEditor",
|
||||
description: "Select a property editor",
|
||||
label: "Property editor"
|
||||
},
|
||||
selectedEditorId: {
|
||||
alias: "selectedEditorId",
|
||||
label: "Property editor alias"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -205,7 +201,7 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
|
||||
var labelKeys = [
|
||||
"general_settings",
|
||||
"references_tabName"
|
||||
"general_info"
|
||||
];
|
||||
|
||||
localizationService.localizeMany(labelKeys).then(function (values) {
|
||||
@@ -220,9 +216,9 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
},
|
||||
{
|
||||
"name": values[1],
|
||||
"alias": "references",
|
||||
"icon": "icon-molecular-network",
|
||||
"view": "views/datatypes/views/datatype.references.html"
|
||||
"alias": "info",
|
||||
"icon": "icon-info",
|
||||
"view": "views/datatypes/views/datatype.info.html"
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.Editors.DataType.ReferencesController
|
||||
* @name Umbraco.Editors.DataType.InfoController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The controller for the references view of the datatype editor
|
||||
* The controller for the info view of the datatype editor
|
||||
*/
|
||||
function DataTypeReferencesController($scope, $routeParams, dataTypeResource, eventsService, $timeout) {
|
||||
function DataTypeInfoController($scope, $routeParams, dataTypeResource, eventsService, $timeout) {
|
||||
|
||||
var vm = this;
|
||||
var evts = [];
|
||||
@@ -34,7 +34,7 @@ function DataTypeReferencesController($scope, $routeParams, dataTypeResource, ev
|
||||
// load data type references when the references tab is activated
|
||||
evts.push(eventsService.on("app.tabChange", function (event, args) {
|
||||
$timeout(function () {
|
||||
if (args.alias === "references") {
|
||||
if (args.alias === "info") {
|
||||
loadRelations();
|
||||
}
|
||||
});
|
||||
@@ -52,4 +52,4 @@ function DataTypeReferencesController($scope, $routeParams, dataTypeResource, ev
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DataType.ReferencesController", DataTypeReferencesController);
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DataType.InfoController", DataTypeInfoController);
|
||||
@@ -0,0 +1,140 @@
|
||||
<div ng-controller="Umbraco.Editors.DataType.InfoController as vm">
|
||||
|
||||
<div class="umb-package-details">
|
||||
<div class="umb-package-details__main-content">
|
||||
|
||||
|
||||
<umb-load-indicator ng-if="vm.view.loading === true"></umb-load-indicator>
|
||||
|
||||
|
||||
<umb-box ng-if="vm.view.loading === false && vm.hasReferences === false">
|
||||
<umb-box-header title-key="references_tabName"></umb-box-header>
|
||||
<umb-box-content>
|
||||
<umb-empty-state size="small">
|
||||
<localize key="references_DataTypeNoReferences">This Data Type has no references.</localize>
|
||||
</umb-empty-state>
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
|
||||
|
||||
<div ng-if="vm.view.loading === false && vm.hasReferences === true">
|
||||
|
||||
<!-- Document Types -->
|
||||
|
||||
<div ng-if="vm.references.documentTypes.length > 0" class="mb4">
|
||||
|
||||
<h5 class="mt0" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByDocumentTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.documentTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/documentTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Media Types -->
|
||||
<div ng-if="vm.references.mediaTypes.length > 0" class="mb4">
|
||||
|
||||
<h5 class="mt0" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByMediaTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.mediaTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/mediaTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Member Types -->
|
||||
<div ng-if="vm.references.memberTypes.length > 0" class="mb4">
|
||||
|
||||
<h5 class="mt0" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByMemberTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.memberTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/memberTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</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">
|
||||
|
||||
<umb-control-group label="Id" ng-if="model.showIdentifier">
|
||||
<div>{{model.content.id}}</div>
|
||||
<small>{{model.content.key}}</small>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Property Editor Alias" ng-if="model.content.selectedEditor">
|
||||
<div>{{model.content.selectedEditor}}</div>
|
||||
</umb-control-group>
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
@@ -1,112 +0,0 @@
|
||||
<div ng-controller="Umbraco.Editors.DataType.ReferencesController as vm">
|
||||
|
||||
<umb-load-indicator ng-if="vm.view.loading === true"></umb-load-indicator>
|
||||
|
||||
<umb-box ng-if="vm.view.loading === false && vm.hasReferences === false">
|
||||
<umb-box-header title-key="references_tabName"></umb-box-header>
|
||||
<umb-box-content>
|
||||
<umb-empty-state size="small">
|
||||
<localize key="references_DataTypeNoReferences">This Data Type has no references.</localize>
|
||||
</umb-empty-state>
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
|
||||
|
||||
<div ng-if="vm.view.loading === false && vm.hasReferences === true">
|
||||
|
||||
<!-- Document Types -->
|
||||
|
||||
<div ng-if="vm.references.documentTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByDocumentTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.documentTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/documentTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Media Types -->
|
||||
<div ng-if="vm.references.mediaTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByMediaTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.mediaTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/mediaTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Member Types -->
|
||||
<div ng-if="vm.references.memberTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByMemberTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.memberTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/memberTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
@@ -2,11 +2,6 @@
|
||||
|
||||
<umb-box-content>
|
||||
|
||||
<umb-control-group label="Id" ng-if="model.showIdentifier">
|
||||
<div>{{model.content.id}}</div>
|
||||
<small>{{model.content.key}}</small>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-property property="model.properties.selectedEditor">
|
||||
<div>
|
||||
<select name="selectedEditor"
|
||||
@@ -20,10 +15,6 @@
|
||||
|
||||
</umb-property>
|
||||
|
||||
<umb-property property="model.properties.selectedEditorId">
|
||||
<div>{{model.content.selectedEditor}}</div>
|
||||
</umb-property>
|
||||
|
||||
<umb-property property="preValue"
|
||||
ng-repeat="preValue in model.preValues">
|
||||
<umb-property-editor model="preValue" is-pre-value="true"></umb-property-editor>
|
||||
@@ -31,4 +22,4 @@
|
||||
|
||||
</umb-box-content>
|
||||
|
||||
</umb-box>
|
||||
</umb-box>
|
||||
|
||||
@@ -519,11 +519,16 @@
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("editors.documentType.saved", function(name, args) {
|
||||
if(args.documentType.allowedTemplates.length > 0){
|
||||
navigationService.syncTree({ tree: "templates", path: [], forceReload: true })
|
||||
.then(function (syncArgs) {
|
||||
navigationService.reloadNode(syncArgs.node)
|
||||
});
|
||||
if(args.documentType.allowedTemplates.length > 0) {
|
||||
navigationService.hasTree("templates").then(function (treeExists) {
|
||||
if (treeExists) {
|
||||
navigationService.syncTree({ tree: "templates", path: [], forceReload: true })
|
||||
.then(function (syncArgs) {
|
||||
navigationService.reloadNode(syncArgs.node)
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ function ColorPickerController($scope, $timeout) {
|
||||
);
|
||||
return {
|
||||
isValid: isValid,
|
||||
errorMsg: "Value cannot be empty",
|
||||
errorMsg: $scope.model.validation.mandatoryMessage || "Value cannot be empty",
|
||||
errorKey: "required"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element, dateHelper) {
|
||||
function dateTimePickerController($scope, angularHelper, dateHelper, validationMessageService) {
|
||||
|
||||
let flatPickr = null;
|
||||
|
||||
@@ -62,6 +62,11 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
|
||||
|
||||
setDatePickerVal();
|
||||
|
||||
// Set the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type or a localised default.
|
||||
validationMessageService.getMandatoryMessage($scope.model.validation).then(function (value) {
|
||||
$scope.mandatoryMessage = value;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.clearDate = function() {
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
|
||||
</div>
|
||||
|
||||
<span ng-messages="datePickerForm.datepicker.$error" show-validation-on-submit >
|
||||
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
|
||||
<span class="help-inline" ng-message="valServer">{{datePickerForm.datepicker.errorMsg}}</span>
|
||||
<span class="help-inline" ng-message="pickerError"><localize key="validation_invalidDate">Invalid date</localize></span>
|
||||
</span>
|
||||
<div ng-messages="datePickerForm.datepicker.$error" show-validation-on-submit >
|
||||
<p class="help-inline" ng-message="required">{{mandatoryMessage}}</p>
|
||||
<p class="help-inline" ng-message="valServer">{{datePickerForm.datepicker.errorMsg}}</p>
|
||||
<p class="help-inline" ng-message="pickerError"><localize key="validation_invalidDate">Invalid date</localize></p>
|
||||
</div>
|
||||
|
||||
<p ng-if="model.config.offsetTime === '1' && serverTimeNeedsOffsetting && model.value" class="muted">
|
||||
<small><localize key="content_scheduledPublishServerTime">This translates to the following time on the server:</localize> {{serverTime}}</small><br />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleController",
|
||||
function($scope) {
|
||||
function ($scope, validationMessageService) {
|
||||
|
||||
//setup the default config
|
||||
var config = {
|
||||
@@ -89,4 +89,10 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo
|
||||
$scope.model.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type or a localised default.
|
||||
validationMessageService.getMandatoryMessage($scope.model.validation).then(function (value) {
|
||||
$scope.mandatoryMessage = value;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.DropdownFlexibleController" ng-switch="model.config.multiple">
|
||||
|
||||
<select name="dropDownList"
|
||||
class="umb-property-editor umb-dropdown"
|
||||
ng-switch-default
|
||||
ng-change="updateSingleDropdownValue()"
|
||||
ng-model="model.singleDropdownValue"
|
||||
ng-options="item.value as item.value for item in model.config.items"
|
||||
ng-required="model.validation.mandatory">
|
||||
<option></option>
|
||||
</select>
|
||||
<ng-form name="dropDownListForm">
|
||||
|
||||
<select name="dropDownList"
|
||||
class="umb-property-editor umb-dropdown"
|
||||
ng-switch-default
|
||||
ng-change="updateSingleDropdownValue()"
|
||||
ng-model="model.singleDropdownValue"
|
||||
ng-options="item.value as item.value for item in model.config.items"
|
||||
ng-required="model.validation.mandatory">
|
||||
<option></option>
|
||||
</select>
|
||||
|
||||
<!--NOTE: This ng-switch is required because ng-multiple doesn't actually support dynamic bindings with multi-select lists -->
|
||||
<select name="dropDownList"
|
||||
class="umb-property-editor umb-dropdown"
|
||||
ng-switch-when="true"
|
||||
multiple
|
||||
ng-model="model.value"
|
||||
ng-options="item.value as item.value for item in model.config.items"
|
||||
ng-required="model.validation.mandatory"></select>
|
||||
<!--NOTE: This ng-switch is required because ng-multiple doesn't actually support dynamic bindings with multi-select lists -->
|
||||
<select name="dropDownList"
|
||||
class="umb-property-editor umb-dropdown"
|
||||
ng-switch-when="true"
|
||||
multiple
|
||||
ng-model="model.value"
|
||||
ng-options="item.value as item.value for item in model.config.items"
|
||||
ng-required="model.validation.mandatory"></select>
|
||||
|
||||
<div ng-messages="dropDownListForm.dropDownList.$error" show-validation-on-submit>
|
||||
<p class="help-inline" ng-message="required">{{mandatoryMessage}}</p>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
function emailController($scope, validationMessageService) {
|
||||
|
||||
// Set the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type or a localised default.
|
||||
validationMessageService.getMandatoryMessage($scope.model.validation).then(function(value) {
|
||||
$scope.mandatoryMessage = value;
|
||||
});
|
||||
|
||||
}
|
||||
angular.module('umbraco').controller("Umbraco.PropertyEditors.EmailController", emailController);
|
||||
@@ -1,4 +1,4 @@
|
||||
<div>
|
||||
<div ng-controller="Umbraco.PropertyEditors.EmailController">
|
||||
<ng-form name="emailFieldForm">
|
||||
<input type="email" name="textbox"
|
||||
ng-model="model.value"
|
||||
@@ -8,10 +8,10 @@
|
||||
ng-required="model.config.IsRequired || model.validation.mandatory"
|
||||
val-server="value" />
|
||||
|
||||
<span ng-messages="emailFieldForm.textbox.$error" show-validation-on-submit >
|
||||
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
|
||||
<span class="help-inline" ng-message="valEmail"><localize key="validation_invalidEmail">Invalid email</localize></span>
|
||||
<span class="help-inline" ng-message="valServer">{{emailFieldForm.textbox.errorMsg}}</span>
|
||||
</span>
|
||||
<div ng-messages="emailFieldForm.textbox.$error" show-validation-on-submit >
|
||||
<p class="help-inline" ng-message="required">{{mandatoryMessage}}</p>
|
||||
<p class="help-inline" ng-message="valEmail"><localize key="validation_invalidEmail">Invalid email</localize></p>
|
||||
<p class="help-inline" ng-message="valServer">{{emailFieldForm.textbox.errorMsg}}</p>
|
||||
</div>
|
||||
</ng-form>
|
||||
</div>
|
||||
|
||||
@@ -1,322 +1,195 @@
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.DocTypePickerController", [
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
"$scope",
|
||||
"Umbraco.PropertyEditors.NestedContent.Resources",
|
||||
"overlayService",
|
||||
"localizationService",
|
||||
"iconHelper",
|
||||
|
||||
function ($scope, ncResources, overlayService, localizationService, iconHelper) {
|
||||
var selectElementTypeModalTitle = "";
|
||||
|
||||
$scope.elemTypeTabs = [];
|
||||
|
||||
|
||||
init();
|
||||
|
||||
|
||||
function init() {
|
||||
localizationService.localize("content_nestedContentSelectElementTypeModalTitle").then(function (value) {
|
||||
selectElementTypeModalTitle = value;
|
||||
});
|
||||
|
||||
ncResources.getContentTypes().then(function (elemTypes) {
|
||||
$scope.model.elemTypes = elemTypes;
|
||||
|
||||
// convert legacy icons
|
||||
iconHelper.formatContentTypeIcons($scope.model.elemTypes);
|
||||
|
||||
// Count doctype name occurrences
|
||||
var elTypeNameOccurrences= _.countBy(elemTypes, 'name');
|
||||
|
||||
// Populate document type tab dictionary
|
||||
// And append alias to name if multiple doctypes have the same name
|
||||
elemTypes.forEach(function (value) {
|
||||
$scope.elemTypeTabs[value.alias] = value.tabs;
|
||||
|
||||
if (elTypeNameOccurrences[value.name] > 1) {
|
||||
value.name += " (" + value.alias + ")";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
$scope.add = function () {
|
||||
$scope.model.value.push({
|
||||
// As per PR #4, all stored content type aliases must be prefixed "nc" for easier recognition.
|
||||
// For good measure we'll also prefix the tab alias "nc"
|
||||
ncAlias: "",
|
||||
ncTabAlias: "",
|
||||
nameTemplate: ""
|
||||
});
|
||||
}
|
||||
|
||||
$scope.canAdd = function () {
|
||||
return !$scope.model.docTypes || !$scope.model.value || $scope.model.value.length < $scope.model.docTypes.length;
|
||||
}
|
||||
|
||||
$scope.remove = function (index) {
|
||||
$scope.model.value.splice(index, 1);
|
||||
}
|
||||
|
||||
$scope.sortableOptions = {
|
||||
axis: "y",
|
||||
cursor: "move",
|
||||
handle: ".handle",
|
||||
placeholder: 'sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
helper: function (e, ui) {
|
||||
// When sorting table rows, the cells collapse. This helper fixes that: https://www.foliotek.com/devblog/make-table-rows-sortable-using-jquery-ui-sortable/
|
||||
ui.children().each(function () {
|
||||
$(this).width($(this).width());
|
||||
});
|
||||
return ui;
|
||||
},
|
||||
start: function (e, ui) {
|
||||
|
||||
var cellHeight = ui.item.height();
|
||||
|
||||
// Build a placeholder cell that spans all the cells in the row: https://stackoverflow.com/questions/25845310/jquery-ui-sortable-and-table-cell-size
|
||||
var cellCount = 0;
|
||||
$('td, th', ui.helper).each(function () {
|
||||
// For each td or th try and get it's colspan attribute, and add that or 1 to the total
|
||||
var colspan = 1;
|
||||
var colspanAttr = $(this).attr('colspan');
|
||||
if (colspanAttr > 1) {
|
||||
colspan = colspanAttr;
|
||||
}
|
||||
cellCount += colspan;
|
||||
});
|
||||
|
||||
// Add the placeholder UI - note that this is the item's content, so td rather than tr - and set height of tr
|
||||
ui.placeholder.html('<td colspan="' + cellCount + '"></td>').height(cellHeight);
|
||||
angular
|
||||
.module('umbraco')
|
||||
.component('nestedContentPropertyEditor', {
|
||||
templateUrl: 'views/propertyeditors/nestedcontent/nestedcontent.propertyeditor.html',
|
||||
controller: NestedContentController,
|
||||
controllerAs: 'vm',
|
||||
require: {
|
||||
umbProperty: '?^umbProperty',
|
||||
umbVariantContent: '?^^umbVariantContent'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
$scope.placeholder = function (config) {
|
||||
return _.find($scope.model.elemTypes, function (elType) {
|
||||
return elType.alias === config.ncAlias;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.selectableElemTypesFor = function (config) {
|
||||
// return all elemTypes that are:
|
||||
// 1. either already selected for this config, or
|
||||
// 2. not selected in any other config
|
||||
return _.filter($scope.model.elemTypes, function (elType) {
|
||||
return elType.alias === config.ncAlias || !_.find($scope.model.value, function (c) {
|
||||
return elType.alias === c.ncAlias;
|
||||
});
|
||||
});
|
||||
}
|
||||
$scope.canAdd = function () {
|
||||
return !$scope.model.value || _.some($scope.model.elemTypes, function (elType) {
|
||||
return !_.find($scope.model.value, function (c) {
|
||||
return elType.alias === c.ncAlias;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.openElemTypeModal = function ($event, config) {
|
||||
|
||||
//we have to add the alias to the objects (they are stored as ncAlias)
|
||||
var selectedItems = _.each($scope.model.value, function (obj) {
|
||||
obj.alias = obj.ncAlias;
|
||||
return obj;
|
||||
})
|
||||
|
||||
var elemTypeSelectorOverlay = {
|
||||
view: "itempicker",
|
||||
title: selectElementTypeModalTitle,
|
||||
availableItems: $scope.selectableElemTypesFor(config),
|
||||
selectedItems: selectedItems,
|
||||
position: "target",
|
||||
event: $event,
|
||||
submit: function (model) {
|
||||
config.ncAlias = model.selectedItem.alias;
|
||||
overlayService.close();
|
||||
},
|
||||
close: function () {
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
overlayService.open(elemTypeSelectorOverlay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!$scope.model.value) {
|
||||
$scope.model.value = [];
|
||||
$scope.add();
|
||||
}
|
||||
|
||||
}
|
||||
]);
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.PropertyEditorController", [
|
||||
|
||||
"$scope",
|
||||
"$interpolate",
|
||||
"$filter",
|
||||
"$timeout",
|
||||
"contentResource",
|
||||
"localizationService",
|
||||
"iconHelper",
|
||||
"clipboardService",
|
||||
"eventsService",
|
||||
"overlayService",
|
||||
"$routeParams",
|
||||
"editorState",
|
||||
"propertyEditorService",
|
||||
|
||||
function ($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, $routeParams, editorState, propertyEditorService) {
|
||||
function NestedContentController($scope, $interpolate, $filter, $timeout, contentResource, localizationService, iconHelper, clipboardService, eventsService, overlayService, $routeParams, editorState) {
|
||||
|
||||
var vm = this;
|
||||
var model = $scope.$parent.$parent.model;
|
||||
|
||||
var contentTypeAliases = [];
|
||||
_.each($scope.model.config.contentTypes, function (contentType) {
|
||||
_.each(model.config.contentTypes, function (contentType) {
|
||||
contentTypeAliases.push(contentType.ncAlias);
|
||||
});
|
||||
|
||||
_.each($scope.model.config.contentTypes, function (contentType) {
|
||||
_.each(model.config.contentTypes, function (contentType) {
|
||||
contentType.nameExp = !!contentType.nameTemplate
|
||||
? $interpolate(contentType.nameTemplate)
|
||||
: undefined;
|
||||
});
|
||||
|
||||
$scope.nodes = [];
|
||||
$scope.currentNode = undefined;
|
||||
$scope.realCurrentNode = undefined;
|
||||
$scope.scaffolds = undefined;
|
||||
$scope.sorting = false;
|
||||
$scope.inited = false;
|
||||
vm.nodes = [];
|
||||
vm.currentNode = null;
|
||||
vm.scaffolds = null;
|
||||
vm.sorting = false;
|
||||
vm.inited = false;
|
||||
|
||||
$scope.minItems = $scope.model.config.minItems || 0;
|
||||
$scope.maxItems = $scope.model.config.maxItems || 0;
|
||||
vm.minItems = model.config.minItems || 0;
|
||||
vm.maxItems = model.config.maxItems || 0;
|
||||
|
||||
if ($scope.maxItems === 0)
|
||||
$scope.maxItems = 1000;
|
||||
if (vm.maxItems === 0)
|
||||
vm.maxItems = 1000;
|
||||
|
||||
$scope.singleMode = $scope.minItems === 1 && $scope.maxItems === 1;
|
||||
$scope.showIcons = Object.toBoolean($scope.model.config.showIcons);
|
||||
$scope.wideMode = Object.toBoolean($scope.model.config.hideLabel);
|
||||
$scope.hasContentTypes = $scope.model.config.contentTypes.length > 0;
|
||||
vm.singleMode = vm.minItems === 1 && vm.maxItems === 1;
|
||||
vm.showIcons = Object.toBoolean(model.config.showIcons);
|
||||
vm.wideMode = Object.toBoolean(model.config.hideLabel);
|
||||
vm.hasContentTypes = model.config.contentTypes.length > 0;
|
||||
|
||||
$scope.labels = {};
|
||||
localizationService.localizeMany(["grid_addElement", "content_createEmpty"]).then(function (data) {
|
||||
$scope.labels.grid_addElement = data[0];
|
||||
$scope.labels.content_createEmpty = data[1];
|
||||
var labels = {};
|
||||
vm.labels = labels;
|
||||
localizationService.localizeMany(["grid_addElement", "content_createEmpty", "actions_copy"]).then(function (data) {
|
||||
labels.grid_addElement = data[0];
|
||||
labels.content_createEmpty = data[1];
|
||||
labels.copy_icon_title = data[2]
|
||||
});
|
||||
|
||||
|
||||
function setCurrentNode(node) {
|
||||
vm.currentNode = node;
|
||||
updateModel();
|
||||
}
|
||||
|
||||
var copyAllEntries = function() {
|
||||
|
||||
syncCurrentNode();
|
||||
|
||||
// list aliases
|
||||
var aliases = $scope.nodes.map((node) => node.contentTypeAlias);
|
||||
var aliases = vm.nodes.map((node) => node.contentTypeAlias);
|
||||
|
||||
// remove dublicates
|
||||
aliases = aliases.filter((item, index) => aliases.indexOf(item) === index);
|
||||
|
||||
var nodeName = "";
|
||||
|
||||
// Retrive variant name
|
||||
var culture = $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture;
|
||||
var activeVariant = _.find(editorState.current.variants, function (v) {
|
||||
return !v.language || v.language.culture === culture;
|
||||
});
|
||||
if(vm.umbVariantContent) {
|
||||
nodeName = vm.umbVariantContent.editor.content.name;
|
||||
}
|
||||
|
||||
localizationService.localize("clipboard_labelForArrayOfItemsFrom", [$scope.model.label, activeVariant.name]).then(function(data) {
|
||||
clipboardService.copyArray("elementTypeArray", aliases, $scope.nodes, data, "icon-thumbnail-list", $scope.model.id);
|
||||
localizationService.localize("clipboard_labelForArrayOfItemsFrom", [model.label, nodeName]).then(function(data) {
|
||||
clipboardService.copyArray("elementTypeArray", aliases, vm.nodes, data, "icon-thumbnail-list", model.id);
|
||||
});
|
||||
}
|
||||
|
||||
var copyAllEntriesAction = {
|
||||
labelKey: 'clipboard_labelForCopyAllEntries',
|
||||
labelTokens: [$scope.model.label],
|
||||
labelTokens: [model.label],
|
||||
icon: 'documents',
|
||||
method: copyAllEntries,
|
||||
isDisabled: true
|
||||
}
|
||||
|
||||
|
||||
var removeAllEntries = function () {
|
||||
localizationService.localizeMany(["content_nestedContentDeleteAllItems", "general_delete"]).then(function (data) {
|
||||
overlayService.confirmDelete({
|
||||
title: data[1],
|
||||
content: data[0],
|
||||
close: function () {
|
||||
overlayService.close();
|
||||
},
|
||||
submit: function () {
|
||||
vm.nodes = [];
|
||||
setDirty();
|
||||
updateModel();
|
||||
overlayService.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var removeAllEntriesAction = {
|
||||
labelKey: 'clipboard_labelForRemoveAllEntries',
|
||||
labelTokens: [],
|
||||
icon: 'trash',
|
||||
method: removeAllEntries,
|
||||
isDisabled: true
|
||||
}
|
||||
|
||||
|
||||
|
||||
// helper to force the current form into the dirty state
|
||||
$scope.setDirty = function () {
|
||||
if ($scope.propertyForm) {
|
||||
$scope.propertyForm.$setDirty();
|
||||
function setDirty() {
|
||||
if ($scope.$parent.$parent.propertyForm) {
|
||||
$scope.$parent.$parent.propertyForm.$setDirty();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.addNode = function (alias) {
|
||||
var scaffold = $scope.getScaffold(alias);
|
||||
function addNode(alias) {
|
||||
var scaffold = getScaffold(alias);
|
||||
|
||||
var newNode = createNode(scaffold, null);
|
||||
|
||||
$scope.currentNode = newNode;
|
||||
$scope.setDirty();
|
||||
setCurrentNode(newNode);
|
||||
setDirty();
|
||||
};
|
||||
|
||||
$scope.openNodeTypePicker = function ($event) {
|
||||
if ($scope.nodes.length >= $scope.maxItems) {
|
||||
vm.openNodeTypePicker = function ($event) {
|
||||
if (vm.nodes.length >= vm.maxItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.overlayMenu = {
|
||||
vm.overlayMenu = {
|
||||
show: false,
|
||||
style: {},
|
||||
filter: $scope.scaffolds.length > 12 ? true : false,
|
||||
filter: vm.scaffolds.length > 12 ? true : false,
|
||||
orderBy: "$index",
|
||||
view: "itempicker",
|
||||
event: $event,
|
||||
clickPasteItem: function(item) {
|
||||
if (item.type === "elementTypeArray") {
|
||||
_.each(item.data, function (entry) {
|
||||
$scope.pasteFromClipboard(entry);
|
||||
pasteFromClipboard(entry);
|
||||
});
|
||||
} else {
|
||||
$scope.pasteFromClipboard(item.data);
|
||||
pasteFromClipboard(item.data);
|
||||
}
|
||||
$scope.overlayMenu.show = false;
|
||||
$scope.overlayMenu = null;
|
||||
vm.overlayMenu.show = false;
|
||||
vm.overlayMenu = null;
|
||||
},
|
||||
submit: function (model) {
|
||||
if (model && model.selectedItem) {
|
||||
$scope.addNode(model.selectedItem.alias);
|
||||
addNode(model.selectedItem.alias);
|
||||
}
|
||||
$scope.overlayMenu.show = false;
|
||||
$scope.overlayMenu = null;
|
||||
vm.overlayMenu.show = false;
|
||||
vm.overlayMenu = null;
|
||||
},
|
||||
close: function () {
|
||||
$scope.overlayMenu.show = false;
|
||||
$scope.overlayMenu = null;
|
||||
vm.overlayMenu.show = false;
|
||||
vm.overlayMenu = null;
|
||||
}
|
||||
};
|
||||
|
||||
// this could be used for future limiting on node types
|
||||
$scope.overlayMenu.availableItems = [];
|
||||
_.each($scope.scaffolds, function (scaffold) {
|
||||
$scope.overlayMenu.availableItems.push({
|
||||
vm.overlayMenu.availableItems = [];
|
||||
_.each(vm.scaffolds, function (scaffold) {
|
||||
vm.overlayMenu.availableItems.push({
|
||||
alias: scaffold.contentTypeAlias,
|
||||
name: scaffold.contentTypeName,
|
||||
icon: iconHelper.convertFromLegacyIcon(scaffold.icon)
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.overlayMenu.availableItems.length === 0) {
|
||||
if (vm.overlayMenu.availableItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.overlayMenu.size = $scope.overlayMenu.availableItems.length > 6 ? "medium" : "small";
|
||||
vm.overlayMenu.size = vm.overlayMenu.availableItems.length > 6 ? "medium" : "small";
|
||||
|
||||
$scope.overlayMenu.pasteItems = [];
|
||||
vm.overlayMenu.pasteItems = [];
|
||||
|
||||
var singleEntriesForPaste = clipboardService.retriveEntriesOfType("elementType", contentTypeAliases);
|
||||
_.each(singleEntriesForPaste, function (entry) {
|
||||
$scope.overlayMenu.pasteItems.push({
|
||||
vm.overlayMenu.pasteItems.push({
|
||||
type: "elementType",
|
||||
name: entry.label,
|
||||
data: entry.data,
|
||||
@@ -326,7 +199,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
|
||||
var arrayEntriesForPaste = clipboardService.retriveEntriesOfType("elementTypeArray", contentTypeAliases);
|
||||
_.each(arrayEntriesForPaste, function (entry) {
|
||||
$scope.overlayMenu.pasteItems.push({
|
||||
vm.overlayMenu.pasteItems.push({
|
||||
type: "elementTypeArray",
|
||||
name: entry.label,
|
||||
data: entry.data,
|
||||
@@ -334,40 +207,40 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
});
|
||||
});
|
||||
|
||||
$scope.overlayMenu.title = $scope.overlayMenu.pasteItems.length > 0 ? $scope.labels.grid_addElement : $scope.labels.content_createEmpty;
|
||||
vm.overlayMenu.title = vm.overlayMenu.pasteItems.length > 0 ? labels.grid_addElement : labels.content_createEmpty;
|
||||
|
||||
$scope.overlayMenu.clickClearPaste = function ($event) {
|
||||
vm.overlayMenu.clickClearPaste = function ($event) {
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
clipboardService.clearEntriesOfType("elementType", contentTypeAliases);
|
||||
clipboardService.clearEntriesOfType("elementTypeArray", contentTypeAliases);
|
||||
$scope.overlayMenu.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually.
|
||||
vm.overlayMenu.pasteItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually.
|
||||
};
|
||||
|
||||
if ($scope.overlayMenu.availableItems.length === 1 && $scope.overlayMenu.pasteItems.length === 0) {
|
||||
if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) {
|
||||
// only one scaffold type - no need to display the picker
|
||||
$scope.addNode($scope.scaffolds[0].contentTypeAlias);
|
||||
addNode(vm.scaffolds[0].contentTypeAlias);
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.overlayMenu.show = true;
|
||||
vm.overlayMenu.show = true;
|
||||
};
|
||||
|
||||
$scope.editNode = function (idx) {
|
||||
if ($scope.currentNode && $scope.currentNode.key === $scope.nodes[idx].key) {
|
||||
$scope.currentNode = undefined;
|
||||
vm.editNode = function (idx) {
|
||||
if (vm.currentNode && vm.currentNode.key === vm.nodes[idx].key) {
|
||||
setCurrentNode(null);
|
||||
} else {
|
||||
$scope.currentNode = $scope.nodes[idx];
|
||||
setCurrentNode(vm.nodes[idx]);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteNode = function (idx) {
|
||||
$scope.nodes.splice(idx, 1);
|
||||
$scope.setDirty();
|
||||
function deleteNode(idx) {
|
||||
vm.nodes.splice(idx, 1);
|
||||
setDirty();
|
||||
updateModel();
|
||||
};
|
||||
$scope.requestDeleteNode = function (idx) {
|
||||
if ($scope.model.config.confirmDeletes === true) {
|
||||
vm.requestDeleteNode = function (idx) {
|
||||
if (model.config.confirmDeletes === true) {
|
||||
localizationService.localizeMany(["content_nestedContentDeleteItem", "general_delete", "general_cancel", "contentTypeEditor_yesDelete"]).then(function (data) {
|
||||
const overlay = {
|
||||
title: data[1],
|
||||
@@ -379,7 +252,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
overlayService.close();
|
||||
},
|
||||
submit: function () {
|
||||
$scope.deleteNode(idx);
|
||||
deleteNode(idx);
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
@@ -387,23 +260,23 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
overlayService.open(overlay);
|
||||
});
|
||||
} else {
|
||||
$scope.deleteNode(idx);
|
||||
deleteNode(idx);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getName = function (idx) {
|
||||
vm.getName = function (idx) {
|
||||
|
||||
var name = "";
|
||||
|
||||
if ($scope.model.value[idx]) {
|
||||
if (model.value[idx]) {
|
||||
|
||||
var contentType = $scope.getContentTypeConfig($scope.model.value[idx].ncContentTypeAlias);
|
||||
var contentType = getContentTypeConfig(model.value[idx].ncContentTypeAlias);
|
||||
|
||||
if (contentType != null) {
|
||||
// first try getting a name using the configured label template
|
||||
if (contentType.nameExp) {
|
||||
// Run the expression against the stored dictionary value, NOT the node object
|
||||
var item = $scope.model.value[idx];
|
||||
var item = model.value[idx];
|
||||
|
||||
// Add a temporary index property
|
||||
item["$index"] = (idx + 1);
|
||||
@@ -418,8 +291,8 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
}
|
||||
|
||||
// if we still do not have a name and we have multiple content types to choose from, use the content type name (same as is shown in the content type picker)
|
||||
if (!name && $scope.scaffolds.length > 1) {
|
||||
var scaffold = $scope.getScaffold(contentType.ncAlias);
|
||||
if (!name && vm.scaffolds.length > 1) {
|
||||
var scaffold = getScaffold(contentType.ncAlias);
|
||||
if (scaffold) {
|
||||
name = scaffold.contentTypeName;
|
||||
}
|
||||
@@ -433,18 +306,19 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
}
|
||||
|
||||
// Update the nodes actual name value
|
||||
if ($scope.nodes[idx].name !== name) {
|
||||
$scope.nodes[idx].name = name;
|
||||
if (vm.nodes[idx].name !== name) {
|
||||
vm.nodes[idx].name = name;
|
||||
}
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
$scope.getIcon = function (idx) {
|
||||
var scaffold = $scope.getScaffold($scope.model.value[idx].ncContentTypeAlias);
|
||||
vm.getIcon = function (idx) {
|
||||
var scaffold = getScaffold(model.value[idx].ncContentTypeAlias);
|
||||
return scaffold && scaffold.icon ? iconHelper.convertFromLegacyIcon(scaffold.icon) : "icon-folder";
|
||||
}
|
||||
$scope.sortableOptions = {
|
||||
|
||||
vm.sortableOptions = {
|
||||
axis: "y",
|
||||
cursor: "move",
|
||||
handle: '.umb-nested-content__header-bar',
|
||||
@@ -455,46 +329,45 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
start: function (ev, ui) {
|
||||
updateModel();
|
||||
// Yea, yea, we shouldn't modify the dom, sue me
|
||||
$("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () {
|
||||
$("#umb-nested-content--" + model.id + " .umb-rte textarea").each(function () {
|
||||
tinymce.execCommand("mceRemoveEditor", false, $(this).attr("id"));
|
||||
$(this).css("visibility", "hidden");
|
||||
});
|
||||
$scope.$apply(function () {
|
||||
$scope.sorting = true;
|
||||
vm.sorting = true;
|
||||
});
|
||||
},
|
||||
update: function (ev, ui) {
|
||||
$scope.setDirty();
|
||||
setDirty();
|
||||
},
|
||||
stop: function (ev, ui) {
|
||||
$("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () {
|
||||
$("#umb-nested-content--" + model.id + " .umb-rte textarea").each(function () {
|
||||
tinymce.execCommand("mceAddEditor", true, $(this).attr("id"));
|
||||
$(this).css("visibility", "visible");
|
||||
});
|
||||
$scope.$apply(function () {
|
||||
$scope.sorting = false;
|
||||
vm.sorting = false;
|
||||
updateModel();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getScaffold = function (alias) {
|
||||
return _.find($scope.scaffolds, function (scaffold) {
|
||||
function getScaffold(alias) {
|
||||
return _.find(vm.scaffolds, function (scaffold) {
|
||||
return scaffold.contentTypeAlias === alias;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.getContentTypeConfig = function (alias) {
|
||||
return _.find($scope.model.config.contentTypes, function (contentType) {
|
||||
function getContentTypeConfig(alias) {
|
||||
return _.find(model.config.contentTypes, function (contentType) {
|
||||
return contentType.ncAlias === alias;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.showCopy = clipboardService.isSupported();
|
||||
vm.showCopy = clipboardService.isSupported();
|
||||
vm.showPaste = false;
|
||||
|
||||
$scope.showPaste = false;
|
||||
|
||||
$scope.clickCopy = function ($event, node) {
|
||||
vm.clickCopy = function ($event, node) {
|
||||
|
||||
syncCurrentNode();
|
||||
|
||||
@@ -503,7 +376,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
}
|
||||
|
||||
|
||||
$scope.pasteFromClipboard = function(newNode) {
|
||||
function pasteFromClipboard(newNode) {
|
||||
|
||||
if (newNode === undefined) {
|
||||
return;
|
||||
@@ -512,15 +385,15 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
// generate a new key.
|
||||
newNode.key = String.CreateGuid();
|
||||
|
||||
$scope.nodes.push(newNode);
|
||||
$scope.setDirty();
|
||||
vm.nodes.push(newNode);
|
||||
setDirty();
|
||||
//updateModel();// done by setting current node...
|
||||
|
||||
$scope.currentNode = newNode;
|
||||
setCurrentNode(newNode);
|
||||
}
|
||||
|
||||
function checkAbilityToPasteContent() {
|
||||
$scope.showPaste = clipboardService.hasEntriesOfType("elementType", contentTypeAliases) || clipboardService.hasEntriesOfType("elementTypeArray", contentTypeAliases);
|
||||
vm.showPaste = clipboardService.hasEntriesOfType("elementType", contentTypeAliases) || clipboardService.hasEntriesOfType("elementTypeArray", contentTypeAliases);
|
||||
}
|
||||
|
||||
eventsService.on("clipboardService.storageUpdate", checkAbilityToPasteContent);
|
||||
@@ -533,8 +406,8 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
|
||||
// Initialize
|
||||
var scaffoldsLoaded = 0;
|
||||
$scope.scaffolds = [];
|
||||
_.each($scope.model.config.contentTypes, function (contentType) {
|
||||
vm.scaffolds = [];
|
||||
_.each(model.config.contentTypes, function (contentType) {
|
||||
contentResource.getScaffold(-20, contentType.ncAlias).then(function (scaffold) {
|
||||
// make sure it's an element type before allowing the user to create new ones
|
||||
if (scaffold.isElement) {
|
||||
@@ -558,7 +431,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
}
|
||||
|
||||
// Store the scaffold object
|
||||
$scope.scaffolds.push(scaffold);
|
||||
vm.scaffolds.push(scaffold);
|
||||
}
|
||||
|
||||
scaffoldsLoaded++;
|
||||
@@ -571,22 +444,22 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
|
||||
var initIfAllScaffoldsHaveLoaded = function () {
|
||||
// Initialize when all scaffolds have loaded
|
||||
if ($scope.model.config.contentTypes.length === scaffoldsLoaded) {
|
||||
if (model.config.contentTypes.length === scaffoldsLoaded) {
|
||||
// Because we're loading the scaffolds async one at a time, we need to
|
||||
// sort them explicitly according to the sort order defined by the data type.
|
||||
contentTypeAliases = [];
|
||||
_.each($scope.model.config.contentTypes, function (contentType) {
|
||||
_.each(model.config.contentTypes, function (contentType) {
|
||||
contentTypeAliases.push(contentType.ncAlias);
|
||||
});
|
||||
$scope.scaffolds = $filter("orderBy")($scope.scaffolds, function (s) {
|
||||
vm.scaffolds = $filter("orderBy")(vm.scaffolds, function (s) {
|
||||
return contentTypeAliases.indexOf(s.contentTypeAlias);
|
||||
});
|
||||
|
||||
// Convert stored nodes
|
||||
if ($scope.model.value) {
|
||||
for (var i = 0; i < $scope.model.value.length; i++) {
|
||||
var item = $scope.model.value[i];
|
||||
var scaffold = $scope.getScaffold(item.ncContentTypeAlias);
|
||||
if (model.value) {
|
||||
for (var i = 0; i < model.value.length; i++) {
|
||||
var item = model.value[i];
|
||||
var scaffold = getScaffold(item.ncContentTypeAlias);
|
||||
if (scaffold == null) {
|
||||
// No such scaffold - the content type might have been deleted. We need to skip it.
|
||||
continue;
|
||||
@@ -596,18 +469,18 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
}
|
||||
|
||||
// Auto-fill with elementTypes, but only if we have one type to choose from, and if this property is empty.
|
||||
if ($scope.singleMode === true && $scope.nodes.length === 0 && $scope.model.config.minItems > 0) {
|
||||
for (var i = $scope.nodes.length; i < $scope.model.config.minItems; i++) {
|
||||
$scope.addNode($scope.scaffolds[0].contentTypeAlias);
|
||||
if (vm.singleMode === true && vm.nodes.length === 0 && model.config.minItems > 0) {
|
||||
for (var i = vm.nodes.length; i < model.config.minItems; i++) {
|
||||
addNode(vm.scaffolds[0].contentTypeAlias);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is only one item, set it as current node
|
||||
if ($scope.singleMode || ($scope.nodes.length === 1 && $scope.maxItems === 1)) {
|
||||
$scope.currentNode = $scope.nodes[0];
|
||||
if (vm.singleMode || (vm.nodes.length === 1 && vm.maxItems === 1)) {
|
||||
setCurrentNode(vm.nodes[0]);
|
||||
}
|
||||
|
||||
$scope.inited = true;
|
||||
vm.inited = true;
|
||||
|
||||
updatePropertyActionStates();
|
||||
checkAbilityToPasteContent();
|
||||
@@ -628,10 +501,11 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
var prop = tab.properties[p];
|
||||
|
||||
prop.propertyAlias = prop.alias;
|
||||
prop.alias = $scope.model.alias + "___" + prop.alias;
|
||||
prop.alias = model.alias + "___" + prop.alias;
|
||||
// Force validation to occur server side as this is the
|
||||
// only way we can have consistency between mandatory and
|
||||
// regex validation messages. Not ideal, but it works.
|
||||
prop.ncMandatory = prop.validation.mandatory;
|
||||
prop.validation = {
|
||||
mandatory: false,
|
||||
pattern: ""
|
||||
@@ -643,7 +517,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
}
|
||||
}
|
||||
|
||||
$scope.nodes.push(node);
|
||||
vm.nodes.push(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -670,60 +544,61 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
|
||||
|
||||
function syncCurrentNode() {
|
||||
if ($scope.realCurrentNode) {
|
||||
$scope.$broadcast("ncSyncVal", { key: $scope.realCurrentNode.key });
|
||||
if (vm.currentNode) {
|
||||
$scope.$broadcast("ncSyncVal", { key: vm.currentNode.key });
|
||||
}
|
||||
}
|
||||
|
||||
function updateModel() {
|
||||
syncCurrentNode();
|
||||
|
||||
if ($scope.inited) {
|
||||
if (vm.inited) {
|
||||
var newValues = [];
|
||||
for (var i = 0; i < $scope.nodes.length; i++) {
|
||||
newValues.push(convertNodeIntoNCEntry($scope.nodes[i]));
|
||||
for (var i = 0; i < vm.nodes.length; i++) {
|
||||
newValues.push(convertNodeIntoNCEntry(vm.nodes[i]));
|
||||
}
|
||||
$scope.model.value = newValues;
|
||||
model.value = newValues;
|
||||
}
|
||||
|
||||
updatePropertyActionStates();
|
||||
}
|
||||
|
||||
function updatePropertyActionStates() {
|
||||
copyAllEntriesAction.isDisabled = $scope.model.value.length === 0;
|
||||
copyAllEntriesAction.isDisabled = !model.value || model.value.length === 0;
|
||||
removeAllEntriesAction.isDisabled = !model.value || model.value.length === 0;
|
||||
}
|
||||
|
||||
$scope.$watch("currentNode", function (newVal) {
|
||||
updateModel();
|
||||
$scope.realCurrentNode = newVal;
|
||||
});
|
||||
|
||||
|
||||
var api = {};
|
||||
api.propertyActions = [
|
||||
copyAllEntriesAction
|
||||
var propertyActions = [
|
||||
copyAllEntriesAction,
|
||||
removeAllEntriesAction
|
||||
];
|
||||
|
||||
propertyEditorService.exposeAPI($scope, api);// must be executed at a state where the API is set.
|
||||
|
||||
this.$onInit = function () {
|
||||
if (this.umbProperty) {
|
||||
this.umbProperty.setPropertyActions(propertyActions);
|
||||
}
|
||||
};
|
||||
|
||||
var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
|
||||
updateModel();
|
||||
});
|
||||
|
||||
var watcher = $scope.$watch(
|
||||
function () {
|
||||
return $scope.nodes.length;
|
||||
return vm.nodes.length;
|
||||
},
|
||||
function () {
|
||||
//Validate!
|
||||
if ($scope.nodes.length < $scope.minItems) {
|
||||
if (vm.nodes.length < vm.minItems) {
|
||||
$scope.nestedContentForm.minCount.$setValidity("minCount", false);
|
||||
}
|
||||
else {
|
||||
$scope.nestedContentForm.minCount.$setValidity("minCount", true);
|
||||
}
|
||||
|
||||
if ($scope.nodes.length > $scope.maxItems) {
|
||||
if (vm.nodes.length > vm.maxItems) {
|
||||
$scope.nestedContentForm.maxCount.$setValidity("maxCount", false);
|
||||
}
|
||||
else {
|
||||
@@ -739,4 +614,4 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
|
||||
}
|
||||
|
||||
]);
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.DocTypePickerController", [
|
||||
|
||||
"$scope",
|
||||
"Umbraco.PropertyEditors.NestedContent.Resources",
|
||||
"overlayService",
|
||||
"localizationService",
|
||||
"iconHelper",
|
||||
|
||||
function ($scope, ncResources, overlayService, localizationService, iconHelper) {
|
||||
var selectElementTypeModalTitle = "";
|
||||
|
||||
$scope.elemTypeTabs = [];
|
||||
|
||||
|
||||
init();
|
||||
|
||||
|
||||
function init() {
|
||||
localizationService.localize("content_nestedContentSelectElementTypeModalTitle").then(function (value) {
|
||||
selectElementTypeModalTitle = value;
|
||||
});
|
||||
|
||||
ncResources.getContentTypes().then(function (elemTypes) {
|
||||
$scope.model.elemTypes = elemTypes;
|
||||
|
||||
// convert legacy icons
|
||||
iconHelper.formatContentTypeIcons($scope.model.elemTypes);
|
||||
|
||||
// Count doctype name occurrences
|
||||
var elTypeNameOccurrences= _.countBy(elemTypes, 'name');
|
||||
|
||||
// Populate document type tab dictionary
|
||||
// And append alias to name if multiple doctypes have the same name
|
||||
elemTypes.forEach(function (value) {
|
||||
$scope.elemTypeTabs[value.alias] = value.tabs;
|
||||
|
||||
if (elTypeNameOccurrences[value.name] > 1) {
|
||||
value.name += " (" + value.alias + ")";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
$scope.add = function () {
|
||||
$scope.model.value.push({
|
||||
// As per PR #4, all stored content type aliases must be prefixed "nc" for easier recognition.
|
||||
// For good measure we'll also prefix the tab alias "nc"
|
||||
ncAlias: "",
|
||||
ncTabAlias: "",
|
||||
nameTemplate: ""
|
||||
});
|
||||
}
|
||||
|
||||
$scope.canAdd = function () {
|
||||
return !$scope.model.docTypes || !$scope.model.value || $scope.model.value.length < $scope.model.docTypes.length;
|
||||
}
|
||||
|
||||
$scope.remove = function (index) {
|
||||
$scope.model.value.splice(index, 1);
|
||||
}
|
||||
|
||||
$scope.sortableOptions = {
|
||||
axis: "y",
|
||||
cursor: "move",
|
||||
handle: ".handle",
|
||||
placeholder: 'sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
helper: function (e, ui) {
|
||||
// When sorting table rows, the cells collapse. This helper fixes that: https://www.foliotek.com/devblog/make-table-rows-sortable-using-jquery-ui-sortable/
|
||||
ui.children().each(function () {
|
||||
$(this).width($(this).width());
|
||||
});
|
||||
return ui;
|
||||
},
|
||||
start: function (e, ui) {
|
||||
|
||||
var cellHeight = ui.item.height();
|
||||
|
||||
// Build a placeholder cell that spans all the cells in the row: https://stackoverflow.com/questions/25845310/jquery-ui-sortable-and-table-cell-size
|
||||
var cellCount = 0;
|
||||
$('td, th', ui.helper).each(function () {
|
||||
// For each td or th try and get it's colspan attribute, and add that or 1 to the total
|
||||
var colspan = 1;
|
||||
var colspanAttr = $(this).attr('colspan');
|
||||
if (colspanAttr > 1) {
|
||||
colspan = colspanAttr;
|
||||
}
|
||||
cellCount += colspan;
|
||||
});
|
||||
|
||||
// Add the placeholder UI - note that this is the item's content, so td rather than tr - and set height of tr
|
||||
ui.placeholder.html('<td colspan="' + cellCount + '"></td>').height(cellHeight);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.placeholder = function (config) {
|
||||
return _.find($scope.model.elemTypes, function (elType) {
|
||||
return elType.alias === config.ncAlias;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.selectableElemTypesFor = function (config) {
|
||||
// return all elemTypes that are:
|
||||
// 1. either already selected for this config, or
|
||||
// 2. not selected in any other config
|
||||
return _.filter($scope.model.elemTypes, function (elType) {
|
||||
return elType.alias === config.ncAlias || !_.find($scope.model.value, function (c) {
|
||||
return elType.alias === c.ncAlias;
|
||||
});
|
||||
});
|
||||
}
|
||||
$scope.canAdd = function () {
|
||||
return !$scope.model.value || _.some($scope.model.elemTypes, function (elType) {
|
||||
return !_.find($scope.model.value, function (c) {
|
||||
return elType.alias === c.ncAlias;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.openElemTypeModal = function ($event, config) {
|
||||
|
||||
//we have to add the alias to the objects (they are stored as ncAlias)
|
||||
var selectedItems = _.each($scope.model.value, function (obj) {
|
||||
obj.alias = obj.ncAlias;
|
||||
return obj;
|
||||
})
|
||||
|
||||
var elemTypeSelectorOverlay = {
|
||||
view: "itempicker",
|
||||
title: selectElementTypeModalTitle,
|
||||
availableItems: $scope.selectableElemTypesFor(config),
|
||||
selectedItems: selectedItems,
|
||||
position: "target",
|
||||
event: $event,
|
||||
submit: function (model) {
|
||||
config.ncAlias = model.selectedItem.alias;
|
||||
overlayService.close();
|
||||
},
|
||||
close: function () {
|
||||
overlayService.close();
|
||||
}
|
||||
};
|
||||
|
||||
overlayService.open(elemTypeSelectorOverlay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!$scope.model.value) {
|
||||
$scope.model.value = [];
|
||||
$scope.add();
|
||||
}
|
||||
|
||||
}
|
||||
]);
|
||||
@@ -24,12 +24,7 @@
|
||||
<td>
|
||||
{{ph = placeholder(config);""}}
|
||||
<div class="umb-nested-content__placeholder" ng-class="{'umb-nested-content__placeholder--selected':ph}" ng-click="openElemTypeModal($event, config)">
|
||||
<span class="umb-nested-content__placeholder-icon-holder" ng-if="ph">
|
||||
<i class="umb-nested-content__placeholder-icon {{ ph.icon }}"></i>
|
||||
</span>
|
||||
<span class="umb-nested-content__placeholder-name" ng-if="ph">
|
||||
{{ ph.name }}
|
||||
</span>
|
||||
<umb-node-preview ng-if="ph" icon="ph.icon" name="ph.name"></umb-node-preview>
|
||||
<localize key="content_nestedContentAddElementType" ng-if="!ph">Add element type</localize>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="umb-pane">
|
||||
<div ng-repeat="property in tab.properties" class="umb-nested-content-property-container">
|
||||
|
||||
<umb-property property="property" ng-class="{'umb-nested-content--not-supported': property.notSupported}" data-element="property-{{property.alias}}">
|
||||
<umb-property property="property" ng-class="{'umb-nested-content--not-supported': property.notSupported, 'umb-nested-content--mandatory': property.ncMandatory}" data-element="property-{{property.alias}}">
|
||||
<umb-property-editor model="property"></umb-property-editor>
|
||||
</umb-property>
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
<p ng-if="property.notSupported">{{property.notSupportedMessage}}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,73 +1,5 @@
|
||||
<div id="umb-nested-content--{{model.id}}" class="umb-nested-content"
|
||||
ng-controller="Umbraco.PropertyEditors.NestedContent.PropertyEditorController"
|
||||
ng-class="{'umb-nested-content--narrow':!wideMode, 'umb-nested-content--wide':wideMode}">
|
||||
<div>
|
||||
|
||||
<umb-load-indicator ng-if="!inited"></umb-load-indicator>
|
||||
|
||||
<ng-form name="nestedContentForm">
|
||||
|
||||
<div class="umb-nested-content__items" ng-hide="nodes.length === 0" ui-sortable="sortableOptions" ng-model="nodes">
|
||||
|
||||
<div class="umb-nested-content__item" ng-repeat="node in nodes" ng-class="{ 'umb-nested-content__item--active' : $parent.realCurrentNode.key === node.key, 'umb-nested-content__item--single' : $parent.singleMode }">
|
||||
|
||||
<div class="umb-nested-content__header-bar" ng-click="$parent.editNode($index)" ng-hide="$parent.singleMode">
|
||||
|
||||
<div class="umb-nested-content__heading"><i ng-if="showIcons" class="icon" ng-class="$parent.getIcon($index)"></i><span class="umb-nested-content__item-name" ng-class="{'--has-icon': showIcons}" ng-bind="$parent.getName($index)"></span></div>
|
||||
|
||||
<div class="umb-nested-content__icons">
|
||||
<a class="umb-nested-content__icon umb-nested-content__icon--copy" title="{{copyIconTitle}}" ng-click="clickCopy($event, node);" ng-if="showCopy" prevent-default>
|
||||
<i class="icon icon-documents"></i>
|
||||
</a>
|
||||
<a class="umb-nested-content__icon umb-nested-content__icon--delete" localize="title" title="general_delete" ng-click="$parent.requestDeleteNode($index); $event.stopPropagation();" prevent-default>
|
||||
<i class="icon icon-trash"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-nested-content__content" ng-if="$parent.realCurrentNode.key === node.key && !$parent.sorting">
|
||||
<umb-nested-content-editor ng-model="node" tab-alias="ncTabAlias" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-hide="hasContentTypes">
|
||||
<div class="umb-nested-content__help-text">
|
||||
<localize key="content_nestedContentNoContentTypes"></localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-nested-content__footer-bar" ng-hide="hasContentTypes === false">
|
||||
<button class="btn-reset umb-nested-content__add-content umb-focus" ng-class="{ '--disabled': (!scaffolds.length || nodes.length >= maxItems) }" ng-click="openNodeTypePicker($event)" prevent-default>
|
||||
<localize key="grid_addElement"></localize>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!--These are here because we need ng-form fields to validate against-->
|
||||
<input type="hidden" name="minCount" ng-model="nodes" />
|
||||
<input type="hidden" name="maxCount" ng-model="nodes" />
|
||||
|
||||
<div ng-messages="nestedContentForm.minCount.$error" show-validation-on-submit>
|
||||
<div class="help text-error" ng-message="minCount">
|
||||
<localize key="validation_entriesShort" tokens="[minItems, minItems - nodes.length]" watch-tokens="true">Minimum %0% entries, needs <strong>%1%</strong> more.</localize>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="nestedContentForm.minCount.$error === true || nodes.length > maxItems">
|
||||
<div class="help text-error">
|
||||
<localize key="validation_entriesExceed" tokens="[maxItems, nodes.length - maxItems]" watch-tokens="true">Maximum %0% entries, <strong>%1%</strong> too many.</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
|
||||
<umb-overlay
|
||||
ng-if="overlayMenu.show"
|
||||
position="target"
|
||||
size="overlayMenu.size"
|
||||
view="overlayMenu.view"
|
||||
model="overlayMenu">
|
||||
</umb-overlay>
|
||||
<nested-content-property-editor></nested-content-property-editor>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<div id="umb-nested-content--{{model.id}}" class="umb-nested-content" ng-class="{'umb-nested-content--narrow':!vm.wideMode, 'umb-nested-content--wide':vm.wideMode}">
|
||||
|
||||
<umb-load-indicator ng-if="!vm.inited"></umb-load-indicator>
|
||||
|
||||
<ng-form name="nestedContentForm">
|
||||
|
||||
<div class="umb-nested-content__items" ng-hide="vm.nodes.length === 0" ui-sortable="vm.sortableOptions" ng-model="vm.nodes">
|
||||
|
||||
<div class="umb-nested-content__item" ng-repeat="node in vm.nodes" ng-class="{ 'umb-nested-content__item--active' : vm.currentNode.key === node.key, 'umb-nested-content__item--single' : vm.singleMode }">
|
||||
|
||||
<div class="umb-nested-content__header-bar" ng-click="vm.editNode($index)" ng-hide="vm.singleMode">
|
||||
|
||||
<div class="umb-nested-content__heading"><i ng-if="vm.showIcons" class="icon" ng-class="vm.getIcon($index)"></i><span class="umb-nested-content__item-name" ng-class="{'--has-icon': vm.showIcons}" ng-bind="vm.getName($index)"></span></div>
|
||||
|
||||
<div class="umb-nested-content__icons">
|
||||
<button type="button" class="umb-nested-content__icon umb-nested-content__icon--copy" title="{{vm.labels.copy_icon_title}}" ng-click="vm.clickCopy($event, node);" ng-if="vm.showCopy">
|
||||
<i class="icon icon-documents" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{vm.labels.copy_icon_title}}</span>
|
||||
</button>
|
||||
<button type="button" class="umb-nested-content__icon umb-nested-content__icon--delete" localize="title" title="general_delete" ng-click="vm.requestDeleteNode($index); $event.stopPropagation();">
|
||||
<i class="icon icon-trash" aria-hidden="true"></i>
|
||||
<span class="sr-only">
|
||||
<localize key="general_delete">Delete</localize>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-nested-content__content" ng-if="vm.currentNode.key === node.key && !vm.sorting">
|
||||
<umb-nested-content-editor ng-model="node" tab-alias="ncTabAlias" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-hide="vm.hasContentTypes">
|
||||
<div class="umb-nested-content__help-text">
|
||||
<localize key="content_nestedContentNoContentTypes"></localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-nested-content__footer-bar" ng-hide="vm.hasContentTypes === false">
|
||||
<button class="btn-reset umb-nested-content__add-content umb-focus" ng-class="{ '--disabled': (!vm.scaffolds.length || vm.nodes.length >= maxItems) }" ng-click="vm.openNodeTypePicker($event)" prevent-default>
|
||||
<localize key="grid_addElement"></localize>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!--These are here because we need ng-form fields to validate against-->
|
||||
<input type="hidden" name="minCount" ng-model="vm.nodes" />
|
||||
<input type="hidden" name="maxCount" ng-model="vm.nodes" />
|
||||
|
||||
<div ng-messages="nestedContentForm.minCount.$error" show-validation-on-submit>
|
||||
<div class="help text-error" ng-message="minCount">
|
||||
<localize key="validation_entriesShort" tokens="[vm.minItems, vm.minItems - vm.nodes.length]" watch-tokens="true">Minimum %0% entries, needs <strong>%1%</strong> more.</localize>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="nestedContentForm.minCount.$error === true || vm.nodes.length > vm.maxItems">
|
||||
<div class="help text-error">
|
||||
<localize key="validation_entriesExceed" tokens="[vm.maxItems, vm.nodes.length - vm.maxItems]" watch-tokens="true">Maximum %0% entries, <strong>%1%</strong> too many.</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
|
||||
<umb-overlay
|
||||
ng-if="vm.overlayMenu.show"
|
||||
position="target"
|
||||
size="vm.overlayMenu.size"
|
||||
view="vm.overlayMenu.view"
|
||||
model="vm.overlayMenu">
|
||||
</umb-overlay>
|
||||
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController",
|
||||
function ($scope) {
|
||||
function ($scope, validationMessageService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
@@ -23,6 +23,12 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsContro
|
||||
|
||||
vm.viewItems = sortedItems;
|
||||
}
|
||||
|
||||
// Set the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type or a localised default.
|
||||
validationMessageService.getMandatoryMessage($scope.model.validation).then(function (value) {
|
||||
$scope.mandatoryMessage = value;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<div class="umb-property-editor umb-radiobuttons" ng-controller="Umbraco.PropertyEditors.RadioButtonsController as vm">
|
||||
<ul class="unstyled">
|
||||
<li ng-repeat="item in vm.viewItems track by item.key">
|
||||
<umb-radiobutton name="{{model.alias}}" value="{{item.value}}" model="model.value" text="{{item.value}}" required="model.validation.mandatory && model.value == ''"></umb-radiobutton>
|
||||
</li>
|
||||
</ul>
|
||||
<ng-form name="radioButtonsFieldForm">
|
||||
|
||||
<ul class="unstyled">
|
||||
<li ng-repeat="item in vm.viewItems track by item.key">
|
||||
<umb-radiobutton name="{{model.alias}}" value="{{item.value}}" model="model.value" text="{{item.value}}" required="model.validation.mandatory && model.value == ''"></umb-radiobutton>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-messages="radioButtonsFieldForm[model.alias].$error" show-validation-on-submit>
|
||||
<p class="help-inline" ng-message="required">{{mandatoryMessage}}</p>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function textAreaController($scope) {
|
||||
function textAreaController($scope, validationMessageService) {
|
||||
|
||||
// macro parameter editor doesn't contains a config object,
|
||||
// so we create a new one to hold any properties
|
||||
@@ -22,5 +22,11 @@ function textAreaController($scope) {
|
||||
}
|
||||
}
|
||||
$scope.model.change();
|
||||
|
||||
// Set the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type or a localised default.
|
||||
validationMessageService.getMandatoryMessage($scope.model.validation).then(function (value) {
|
||||
$scope.mandatoryMessage = value;
|
||||
});
|
||||
}
|
||||
angular.module('umbraco').controller("Umbraco.PropertyEditors.textAreaController", textAreaController);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<textarea ng-model="model.value" id="{{model.alias}}" name="textarea" rows="{{model.config.rows || 10}}" class="umb-property-editor umb-textarea textstring" val-server="value" ng-keyup="model.change()" ng-required="model.validation.mandatory" aria-required="{{model.validation.mandatory}}"></textarea>
|
||||
|
||||
<span ng-messages="textareaFieldForm.textarea.$error" show-validation-on-submit >
|
||||
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
|
||||
<span class="help-inline" ng-message="required">{{mandatoryMessage}}</span>
|
||||
<span class="help-inline" ng-message="valServer">{{textareaFieldForm.textarea.errorMsg}}</span>
|
||||
</span>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function textboxController($scope) {
|
||||
function textboxController($scope, validationMessageService) {
|
||||
// macro parameter editor doesn't contains a config object,
|
||||
// so we create a new one to hold any properties
|
||||
if (!$scope.model.config) {
|
||||
@@ -18,6 +18,11 @@ function textboxController($scope) {
|
||||
}
|
||||
}
|
||||
$scope.model.change();
|
||||
|
||||
|
||||
// Set the message to use for when a mandatory field isn't completed.
|
||||
// Will either use the one provided on the property type or a localised default.
|
||||
validationMessageService.getMandatoryMessage($scope.model.validation).then(function(value) {
|
||||
$scope.mandatoryMessage = value;
|
||||
});
|
||||
}
|
||||
angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div ng-messages="textboxFieldForm.textbox.$error" show-validation-on-submit>
|
||||
<p class="sr-only" ng-message="valServer" tabindex="0">{{model.label}} {{textboxFieldForm.textbox.errorMsg}}</p>
|
||||
<p class="help-inline" ng-message="valServer" tabindex="0" aria-hidden="true">{{textboxFieldForm.textbox.errorMsg}}</p>
|
||||
<p class="help-inline" ng-message="required"><localize key="general_required">Required</localize></p>
|
||||
<p class="help-inline" ng-message="required">{{mandatoryMessage}}</p>
|
||||
</div>
|
||||
|
||||
<div class="help" ng-if="model.count >= (model.config.maxChars*.8) && model.count <= model.config.maxChars">
|
||||
|
||||
Reference in New Issue
Block a user