Merge branch temp8 into temp8-11502

This commit is contained in:
Stephan
2018-10-03 15:08:12 +02:00
155 changed files with 7853 additions and 3879 deletions

View File

@@ -157,10 +157,19 @@
if(app && app.alias !== "umbContent" && app.alias !== "umbInfo") {
$scope.defaultButton = null;
$scope.subButtons = null;
$scope.page.showSaveButton = false;
$scope.page.showPreviewButton = false;
return;
}
// create the save button
if(_.contains($scope.content.allowedActions, "A")) {
$scope.page.showSaveButton = true;
// add ellipsis to the save button if it opens the variant overlay
$scope.page.saveButtonEllipsis = content.variants && content.variants.length > 1 ? "true" : "false";
}
// create the pubish combo button
$scope.page.buttonGroupState = "init";
var buttons = contentEditingHelper.configureContentEditorButtons({
create: $scope.page.isNew,
@@ -168,8 +177,7 @@
methods: {
saveAndPublish: $scope.saveAndPublish,
sendToPublish: $scope.sendToPublish,
save: $scope.save,
unPublish: $scope.unPublish
unpublish: $scope.unpublish
}
});
@@ -225,9 +233,7 @@
// This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
function performSave(args) {
$scope.page.buttonGroupState = "busy";
eventsService.emit("content.saving", { content: $scope.content, action: args.action });
return contentEditingHelper.contentEditorPerformSave({
@@ -242,8 +248,6 @@
syncTreeNode($scope.content, data.path);
$scope.page.buttonGroupState = "success";
eventsService.emit("content.saved", { content: $scope.content, action: args.action });
return $q.when(data);
@@ -257,8 +261,6 @@
editorState.set($scope.content);
}
$scope.page.buttonGroupState = "error";
return $q.reject(err);
});
}
@@ -332,52 +334,45 @@
});
}
$scope.unPublish = function () {
$scope.unpublish = function() {
clearNotifications($scope.content);
if (formHelper.submitForm({ scope: $scope, action: "unpublish", skipValidation: true })) {
var dialog = {
parentScope: $scope,
view: "views/content/overlays/unpublish.html",
variants: $scope.content.variants, //set a model property for the dialog
skipFormValidation: true, //when submitting the overlay form, skip any client side validation
submitButtonLabelKey: "content_unpublish",
submit: function (model) {
//if there's any variants than we need to set the language and include the variants to publish
var culture = null;
if ($scope.content.variants.length > 0) {
_.each($scope.content.variants,
function (d) {
//set the culture if this is active
if (d.active === true) {
culture = d.language.culture;
}
});
model.submitButtonState = "busy";
var selectedVariants = _.filter(model.variants, function(variant) { return variant.save; });
var culturesForUnpublishing = _.map(selectedVariants, function(variant) { return variant.language.culture; });
contentResource.unpublish($scope.content.id, culturesForUnpublishing)
.then(function (data) {
formHelper.resetForm({ scope: $scope });
contentEditingHelper.reBindChangedProperties($scope.content, data);
init($scope.content);
syncTreeNode($scope.content, data.path);
$scope.page.buttonGroupState = "success";
eventsService.emit("content.unpublished", { content: $scope.content });
overlayService.close();
}, function (err) {
$scope.page.buttonGroupState = 'error';
});
},
close: function () {
overlayService.close();
}
};
overlayService.open(dialog);
}
if (formHelper.submitForm({ scope: $scope, skipValidation: true })) {
$scope.page.buttonGroupState = "busy";
eventsService.emit("content.unpublishing", { content: $scope.content });
contentResource.unPublish($scope.content.id, culture)
.then(function (data) {
formHelper.resetForm({ scope: $scope });
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
savedContent: data,
rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
});
init($scope.content);
syncTreeNode($scope.content, data.path);
$scope.page.buttonGroupState = "success";
eventsService.emit("content.unpublished", { content: $scope.content });
}, function (err) {
$scope.page.buttonGroupState = 'error';
});
}
};
$scope.sendToPublish = function () {
clearNotifications($scope.content);
if (showSaveOrPublishDialog()) {
@@ -405,13 +400,20 @@
}
}
else {
return performSave({ saveMethod: contentResource.sendToPublish, action: "sendToPublish" });
$scope.page.buttonGroupState = "busy";
return performSave({
saveMethod: contentResource.sendToPublish,
action: "sendToPublish"
}).then(function(){
$scope.page.buttonGroupState = "success";
}, function () {
$scope.page.buttonGroupState = "error";
});;
}
};
$scope.saveAndPublish = function () {
clearNotifications($scope.content);
// TODO: Add "..." to publish 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 (showSaveOrPublishDialog()) {
//before we launch the dialog we want to execute all client side validations first
if (formHelper.submitForm({ scope: $scope, action: "publish" })) {
@@ -457,7 +459,15 @@
else {
//ensure the publish flag is set
$scope.content.variants[0].publish = true;
return performSave({ saveMethod: contentResource.publish, action: "publish" });
$scope.page.buttonGroupState = "busy";
return performSave({
saveMethod: contentResource.publish,
action: "publish"
}).then(function(){
$scope.page.buttonGroupState = "success";
}, function () {
$scope.page.buttonGroupState = "error";
});;
}
};
@@ -488,15 +498,14 @@
clearNotifications($scope.content);
overlayService.close();
return $q.when(data);
},
function (err) {
clearDirtyState($scope.content.variants);
model.submitButtonState = "error";
//re-map the dialog model since we've re-bound the properties
dialog.variants = $scope.content.variants;
//don't reject, we've handled the error
return $q.when(err);
});
}, function (err) {
clearDirtyState($scope.content.variants);
model.submitButtonState = "error";
//re-map the dialog model since we've re-bound the properties
dialog.variants = $scope.content.variants;
//don't reject, we've handled the error
return $q.when(err);
});
},
close: function (oldModel) {
overlayService.close();
@@ -507,7 +516,15 @@
}
}
else {
return performSave({ saveMethod: $scope.saveMethod(), action: "save" });
$scope.page.saveButtonState = "busy";
return performSave({
saveMethod: $scope.saveMethod(),
action: "save"
}).then(function(){
$scope.page.saveButtonState = "success";
}, function () {
$scope.page.saveButtonState = "error";
});
}
};

View File

@@ -1,7 +1,7 @@
(function () {
'use strict';
function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper, editorService) {
function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource) {
function link(scope, element, attrs, ctrl) {
@@ -72,6 +72,9 @@
// make sure dates are formatted to the user's locale
formatDatesToLocal();
//default setting for redirect url management
scope.urlTrackerDisabled = false;
// Declare a fallback URL for the <umb-node-preview/> directive
if (scope.documentType !== null) {
scope.previewOpenUrl = '#/settings/documenttypes/edit/' + scope.documentType.id;
@@ -139,7 +142,7 @@
// get current backoffice user and format dates
userService.getCurrentUser().then(function (currentUser) {
angular.forEach(data.items, function(item) {
angular.forEach(data.items, function (item) {
item.timestampFormatted = dateHelper.getLocalDate(item.timestamp, currentUser.locale, 'LLL');
});
});
@@ -156,6 +159,25 @@
});
}
function loadRedirectUrls() {
scope.loadingRedirectUrls = true;
//check if Redirect Url Management is enabled
redirectUrlsResource.getEnableState().then(function (response) {
scope.urlTrackerDisabled = response.enabled !== true;
if (scope.urlTrackerDisabled === false) {
redirectUrlsResource.getRedirectsForContentItem(scope.node.udi)
.then(function (data) {
scope.redirectUrls = data.searchResults;
scope.hasRedirects = (typeof data.searchResults !== 'undefined' && data.searchResults.length > 0);
scope.loadingRedirectUrls = false;
});
}
else {
scope.loadingRedirectUrls = false;
}
});
}
function setAuditTrailLogTypeColor(auditTrail) {
angular.forEach(auditTrail, function (item) {
@@ -164,7 +186,7 @@
case "Publish":
item.logTypeColor = "success";
break;
case "UnPublish":
case "Unpublish":
case "Delete":
item.logTypeColor = "danger";
break;
@@ -312,12 +334,13 @@
});
}
// load audit trail when on the info tab
// load audit trail and redirects when on the info tab
evts.push(eventsService.on("app.tabChange", function (event, args) {
$timeout(function(){
$timeout(function () {
if (args.alias === "umbInfo") {
isInfoTab = true;
loadAuditTrail();
loadRedirectUrls();
} else {
isInfoTab = false;
}
@@ -325,13 +348,14 @@
}));
// watch for content state updates
scope.$watch('node.updateDate', function(newValue, oldValue){
scope.$watch('node.updateDate', function (newValue, oldValue) {
if(!newValue) { return; }
if(newValue === oldValue) { return; }
if (!newValue) { return; }
if (newValue === oldValue) { return; }
if(isInfoTab) {
loadAuditTrail();
loadRedirectUrls();
formatDatesToLocal();
setNodePublishStatus(scope.node);
}

View File

@@ -11,32 +11,36 @@ function hexBgColor() {
restrict: "A",
link: function (scope, element, attr, formCtrl) {
var origColor = null;
if (attr.hexBgOrig) {
//set the orig based on the attribute if there is one
origColor = attr.hexBgOrig;
}
attr.$observe("hexBgColor", function (newVal) {
if (newVal) {
if (!origColor) {
//get the orig color before changing it
origColor = element.css("border-color");
}
//validate it - test with and without the leading hash.
if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
element.css("background-color", "#" + newVal);
return;
}
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
element.css("background-color", newVal);
return;
}
}
element.css("background-color", origColor);
});
// Only add inline hex background color if defined and not "true".
if (attr.hexBgInline === undefined || (attr.hexBgInline !== undefined && attr.hexBgInline === "true")) {
var origColor = null;
if (attr.hexBgOrig) {
// Set the orig based on the attribute if there is one.
origColor = attr.hexBgOrig;
}
attr.$observe("hexBgColor", function (newVal) {
if (newVal) {
if (!origColor) {
// Get the orig color before changing it.
origColor = element.css("border-color");
}
// Validate it - test with and without the leading hash.
if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
element.css("background-color", "#" + newVal);
return;
}
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
element.css("background-color", newVal);
return;
}
}
element.css("background-color", origColor);
});
}
}
};
}
angular.module('umbraco.directives').directive("hexBgColor", hexBgColor);
angular.module('umbraco.directives').directive("hexBgColor", hexBgColor);

View File

@@ -41,7 +41,6 @@
};
function setFocalPoint (event) {
$scope.$emit("imageFocalPointStart");
var offsetX = event.offsetX - 10;
@@ -50,7 +49,6 @@
calculateGravity(offsetX, offsetY);
lazyEndEvent();
};
/** Initializes the component */
@@ -148,12 +146,26 @@
/** Sets the width/height/left/top dimentions based on the image size and the "center" value */
function setDimensions() {
if (htmlImage && vm.center) {
if (vm.src.endsWith(".svg")) {
// svg files don't automatically get a size by
// loading them set a default size for now
vm.dimensions.width = 200;
vm.dimensions.height = 200;
vm.dimensions.left = vm.center.left * vm.dimensions.width - 10;
vm.dimensions.top = vm.center.top * vm.dimensions.height - 10;
// can't crop an svg file, don't show the focal point
if (htmlOverlay) {
htmlOverlay.remove();
}
}
else if (htmlImage && vm.center) {
vm.dimensions.width = htmlImage.width();
vm.dimensions.height = htmlImage.height();
vm.dimensions.left = vm.center.left * vm.dimensions.width - 10;
vm.dimensions.top = vm.center.top * vm.dimensions.height - 10;
}
return vm.dimensions.width;
};

View File

@@ -73,7 +73,15 @@ angular.module("umbraco.directives")
if (node.selected) {
css.push("umb-tree-node-checked");
}
//is this the current action node (this is not the same as the current selected node!)
var actionNode = appState.getMenuState("currentNode");
if (actionNode) {
if (actionNode.id === node.id) {
css.push("active");
}
}
return css.join(" ");
};

View File

@@ -1,5 +1,4 @@
/**
/**
@ngdoc directive
@name umbraco.directives.directive:umbColorSwatches
@restrict E
@@ -15,9 +14,10 @@ Use this directive to generate color swatches to pick from.
</umb-color-swatches>
</pre>
@param {array} colors (<code>attribute</code>): The array of colors.
@param {string} colors (<code>attribute</code>): The array of colors.
@param {string} selectedColor (<code>attribute</code>): The selected color.
@param {string} size (<code>attribute</code>): The size (s, m).
@param {string} useLabel (<code>attribute</code>): Specify if labels should be used.
@param {string} useColorClass (<code>attribute</code>): Specify if color values are css classes.
@param {function} onSelect (<code>expression</code>): Callback function when the item is selected.
**/
@@ -28,6 +28,11 @@ Use this directive to generate color swatches to pick from.
function link(scope, el, attr, ctrl) {
// Set default to true if not defined
if (angular.isUndefined(scope.useColorClass)) {
scope.useColorClass = false;
}
scope.setColor = function (color) {
scope.selectedColor = color;
if (scope.onSelect) {
@@ -45,7 +50,9 @@ Use this directive to generate color swatches to pick from.
colors: '=?',
size: '@',
selectedColor: '=',
onSelect: '&'
onSelect: '&',
useLabel: '=',
useColorClass: '=?'
},
link: link
};

View File

@@ -125,6 +125,14 @@ Use this directive to generate a thumbnail grid of media items.
i--;
}
if (scope.includeSubFolders !== 'true') {
if (item.parentId !== parseInt(scope.currentFolderId)) {
scope.items.splice(i, 1);
i--;
}
}
}
if (scope.items.length > 0) {
@@ -316,7 +324,9 @@ Use this directive to generate a thumbnail grid of media items.
itemMaxHeight: "@",
itemMinWidth: "@",
itemMinHeight: "@",
onlyImages: "@"
onlyImages: "@",
includeSubFolders: "@",
currentFolderId: "@"
},
link: link
};

View File

@@ -221,7 +221,8 @@
isClientSide: true
});
newVal += files[i].name + ",";
//special check for a comma in the name
newVal += files[i].name.replace(',', '-') + ",";
if (isImage) {

View File

@@ -145,7 +145,7 @@ angular.module('umbraco.mocks').
"content_statistics": "Statistics",
"content_titleOptional": "Title (optional)",
"content_type": "Type",
"content_unPublish": "Unpublish",
"content_unpublish": "Unpublish",
"content_updateDate": "Last edited",
"content_updateDateDesc": "Date/time this document was created",
"content_uploadClear": "Remove file",

View File

@@ -206,7 +206,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
/**
* @ngdoc method
* @name umbraco.resources.contentResource#unPublish
* @name umbraco.resources.contentResource#unpublish
* @methodOf umbraco.resources.contentResource
*
* @description
@@ -214,7 +214,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
*
* ##usage
* <pre>
* contentResource.unPublish(1234)
* contentResource.unpublish(1234)
* .then(function() {
* alert("node was unpulished");
* }, function(err){
@@ -225,21 +225,20 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* @returns {Promise} resourcePromise object.
*
*/
unPublish: function (id, culture) {
unpublish: function (id, cultures) {
if (!id) {
throw "id cannot be null";
}
if (!culture) {
culture = null;
if (!cultures) {
cultures = [];
}
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"PostUnPublish",
{ id: id, culture: culture })),
"PostUnpublish"), { id: id, cultures: cultures }),
'Failed to publish content with id ' + id);
},
/**

View File

@@ -7,10 +7,60 @@
**/
function logResource($q, $http, umbRequestHelper) {
function isValidDate(input) {
if (input) {
if (Object.prototype.toString.call(input) === "[object Date]" && !isNaN(input.getTime())) {
return true;
}
}
return false;
};
function dateToValidIsoString(input) {
if (isValidDate(input)) {
return input.toISOString();
}
return '';
};
//the factory object returned
return {
getPagedEntityLog: function (options) {
/**
* @ngdoc method
* @name umbraco.resources.logResource#getPagedEntityLog
* @methodOf umbraco.resources.logResource
*
* @description
* Gets a paginated log history for a entity
*
* ##usage
* <pre>
* var options = {
* id : 1234
* pageSize : 10,
* pageNumber : 1,
* orderDirection : "Descending",
* sinceDate : new Date(2018,0,1)
* };
* logResource.getPagedEntityLog(options)
* .then(function(log) {
* alert('its here!');
* });
* </pre>
*
* @param {Object} options options object
* @param {Int} options.id the id of the entity
* @param {Int} options.pageSize if paging data, number of nodes per page, default = 10, set to 0 to disable paging
* @param {Int} options.pageNumber if paging data, current page index, default = 1
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Descending`
* @param {Date} options.sinceDate if provided this will only get log entries going back to this date
* @returns {Promise} resourcePromise object containing the log.
*
*/
getPagedEntityLog: function(options) {
var defaults = {
pageSize: 10,
@@ -24,11 +74,15 @@ function logResource($q, $http, umbRequestHelper) {
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
if (options.hasOwnProperty('sinceDate')) {
options.sinceDate = dateToValidIsoString(options.sinceDate);
}
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
} else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
@@ -45,7 +99,37 @@ function logResource($q, $http, umbRequestHelper) {
'Failed to retrieve log data for id');
},
getPagedUserLog: function (options) {
/**
* @ngdoc method
* @name umbraco.resources.logResource#getPagedUserLog
* @methodOf umbraco.resources.logResource
*
* @description
* Gets a paginated log history for the current user
*
* ##usage
* <pre>
* var options = {
* pageSize : 10,
* pageNumber : 1,
* orderDirection : "Descending",
* sinceDate : new Date(2018,0,1)
* };
* logResource.getPagedUserLog(options)
* .then(function(log) {
* alert('its here!');
* });
* </pre>
*
* @param {Object} options options object
* @param {Int} options.pageSize if paging data, number of nodes per page, default = 10, set to 0 to disable paging
* @param {Int} options.pageNumber if paging data, current page index, default = 1
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Descending`
* @param {Date} options.sinceDate if provided this will only get log entries going back to this date
* @returns {Promise} resourcePromise object containing the log.
*
*/
getPagedUserLog: function(options) {
var defaults = {
pageSize: 10,
@@ -59,11 +143,15 @@ function logResource($q, $http, umbRequestHelper) {
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
if (options.hasOwnProperty('sinceDate')) {
options.sinceDate = dateToValidIsoString(options.sinceDate);
}
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
} else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
@@ -71,7 +159,7 @@ function logResource($q, $http, umbRequestHelper) {
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetPagedEntityLog",
"GetPagedCurrentUserLog",
options)),
'Failed to retrieve log data for id');
},
@@ -82,6 +170,7 @@ function logResource($q, $http, umbRequestHelper) {
* @methodOf umbraco.resources.logResource
*
* @description
* <strong>[OBSOLETE] use getPagedEntityLog instead</strong><br />
* Gets the log history for a give entity id
*
* ##usage
@@ -96,23 +185,24 @@ function logResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the log.
*
*/
getEntityLog: function (id) {
getEntityLog: function(id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetEntityLog",
[{ id: id }])),
'Failed to retrieve user data for id ' + id);
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetEntityLog",
[{ id: id }])),
'Failed to retrieve user data for id ' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.logResource#getUserLog
* @methodOf umbraco.resources.logResource
*
* @description
* Gets the current users' log history for a given type of log entry
* <strong>[OBSOLETE] use getPagedUserLog instead</strong><br />
* Gets the current user's log history for a given type of log entry
*
* ##usage
* <pre>
@@ -127,14 +217,14 @@ function logResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the log.
*
*/
getUserLog: function (type, since) {
getUserLog: function(type, since) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetCurrentUserLog",
[{ logtype: type}, {sinceDate: since }])),
'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetCurrentUserLog",
[{ logtype: type }, { sinceDate: dateToValidIsoString(since) }])),
'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
},
/**
@@ -158,16 +248,16 @@ function logResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the log.
*
*/
getLog: function (type, since) {
getLog: function(type, since) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetLog",
[{ logtype: type}, {sinceDate: since }])),
'Failed to retrieve log data of type ' + type + ' since ' + since);
}
};
$http.get(
umbRequestHelper.getApiUrl(
"logApiBaseUrl",
"GetLog",
[{ logtype: type }, { sinceDate: dateToValidIsoString(since) }])),
'Failed to retrieve log data of type ' + type + ' since ' + since);
}
};
}
angular.module('umbraco.resources').factory('logResource', logResource);

View File

@@ -40,6 +40,32 @@
{ searchTerm: searchTerm, page: pageIndex, pageSize: pageSize })),
'Failed to retrieve data for searching redirect urls');
}
/**
* @ngdoc function
* @name umbraco.resources.redirectUrlResource#getRedirectsForContentItem
* @methodOf umbraco.resources.redirectUrlResource
* @function
*
* @description
* Used to retrieve RedirectUrls for a specific item of content for Information tab
* ##usage
* <pre>
* redirectUrlsResource.getRedirectsForContentItem("udi:123456")
* .then(function(response) {
*
* });
* </pre>
* @param {String} contentUdi identifier for the content item to retrieve redirects for
*/
function getRedirectsForContentItem(contentUdi) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"redirectUrlManagementApiBaseUrl",
"RedirectUrlsForContentItem",
{ contentUdi: contentUdi })),
'Failed to retrieve redirects for content: ' + contentUdi);
}
function getEnableState() {
@@ -50,7 +76,7 @@
"GetEnableState")),
'Failed to retrieve data to check if the 301 redirect is enabled');
}
/**
* @ngdoc function
* @name umbraco.resources.redirectUrlResource#deleteRedirectUrl
@@ -107,7 +133,8 @@
searchRedirectUrls: searchRedirectUrls,
deleteRedirectUrl: deleteRedirectUrl,
toggleUrlTracker: toggleUrlTracker,
getEnableState: getEnableState
getEnableState: getEnableState,
getRedirectsForContentItem: getRedirectsForContentItem
};
return resource;

View File

@@ -65,17 +65,14 @@ angular.module('umbraco.services')
return path;
}
/**
* Loads in moment.js requirements during the _loadInitAssets call
*/
function loadMomentLocaleForCurrentUser() {
function getMomentLocales(locales, supportedLocales) {
var self = this;
function loadLocales(currentUser, supportedLocales) {
var locale = currentUser.locale.toLowerCase();
var localeUrls = [];
var locales = locales.split(',');
for (var i = 0; i < locales.length; i++) {
var locale = locales[i].toString().toLowerCase();
if (locale !== 'en-us') {
var localeUrls = [];
if (supportedLocales.indexOf(locale + '.js') > -1) {
localeUrls.push('lib/moment/' + locale + '.js');
}
@@ -85,16 +82,35 @@ angular.module('umbraco.services')
localeUrls.push('lib/moment/' + majorLocale);
}
}
return self.load(localeUrls, $rootScope);
}
else {
$q.when(true);
}
}
return localeUrls;
}
/**
* Loads specific Moment.js Locales.
* @param {any} locales
* @param {any} supportedLocales
*/
function loadLocales(locales, supportedLocales) {
var localeUrls = getMomentLocales(locales, supportedLocales);
if (localeUrls.length >= 1) {
return assetsService.load(localeUrls, $rootScope);
}
else {
$q.when(true);
}
}
/**
* Loads in moment.js requirements during the _loadInitAssets call
*/
function loadMomentLocaleForCurrentUser() {
userService.getCurrentUser().then(function (currentUser) {
return javascriptLibraryResource.getSupportedLocalesForMoment().then(function (supportedLocales) {
return loadLocales(currentUser, supportedLocales);
return loadLocales(currentUser.locale, supportedLocales);
});
});
@@ -136,7 +152,9 @@ angular.module('umbraco.services')
return $q.when(true);
}
},
loadLocales: loadLocales,
/**
* @ngdoc method
* @name umbraco.services.assetsService#loadCss

View File

@@ -146,7 +146,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
if (!args.methods) {
throw "args.methods is not defined";
}
if (!args.methods.saveAndPublish || !args.methods.sendToPublish || !args.methods.save || !args.methods.unPublish) {
if (!args.methods.saveAndPublish || !args.methods.sendToPublish || !args.methods.unpublish) {
throw "args.methods does not contain all required defined methods";
}
@@ -179,26 +179,16 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
alias: "sendToPublish",
addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false"
};
case "A":
//save
return {
letter: ch,
labelKey: "buttons_save",
handler: args.methods.save,
hotKey: "ctrl+s",
hotKeyWhenHidden: true,
alias: "save",
addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false"
};
case "Z":
//unpublish
return {
letter: ch,
labelKey: "content_unPublish",
handler: args.methods.unPublish,
labelKey: "content_unpublish",
handler: args.methods.unpublish,
hotKey: "ctrl+u",
hotKeyWhenHidden: true,
alias: "unpublish"
alias: "unpublish",
addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false"
};
default:
return null;
@@ -209,8 +199,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
buttons.subButtons = [];
//This is the ideal button order but depends on circumstance, we'll use this array to create the button list
// Publish, SendToPublish, Save
var buttonOrder = ["U", "H", "A"];
// Publish, SendToPublish
var buttonOrder = ["U", "H"];
//Create the first button (primary button)
//We cannot have the Save or SaveAndPublish buttons if they don't have create permissions when we are creating a new item.
@@ -252,7 +242,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
// so long as it's already published and if the user has access to publish
// and the user has access to unpublish (may have been removed via Event)
if (!args.create) {
if (args.content.publishDate && _.contains(args.content.allowedActions, "U") && _.contains(args.content.allowedActions, "Z")) {
var hasPublishedVariant = args.content.variants.filter(function(variant) { return (variant.state === "Published" || variant.state === "PublishedPendingChanges"); }).length > 0;
if (hasPublishedVariant && _.contains(args.content.allowedActions, "U") && _.contains(args.content.allowedActions, "Z")) {
buttons.subButtons.push(createButtonDefinition("Z"));
}
}
@@ -412,8 +403,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
case "Z":
return {
letter: ch,
labelKey: "content_unPublish",
handler: "unPublish"
labelKey: "content_unpublish",
handler: "unpublish"
};
default:

View File

@@ -85,6 +85,7 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
appState.setSectionState("showSearchResults", false);
appState.setGlobalState("stickyNavigation", false);
appState.setGlobalState("showTray", false);
appState.setMenuState("currentNode", null);
if (appState.getGlobalState("isTablet") === true) {
appState.setGlobalState("showNavigation", false);
@@ -365,7 +366,8 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) {
//reset it to whatever is in the url
appState.setSectionState("currentSection", $routeParams.section);
appState.setSectionState("currentSection", $routeParams.section);
setMode("default-hidesectiontree");
}

View File

@@ -143,7 +143,6 @@ angular.module('umbraco.services')
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;

View File

@@ -138,6 +138,7 @@
@import "components/umb-querybuilder.less";
@import "components/umb-pagination.less";
@import "components/umb-mini-list-view.less";
@import "components/umb-multiple-textbox.less";
@import "components/umb-badge.less";
@import "components/umb-nested-content.less";
@import "components/umb-checkmark.less";

View File

@@ -227,13 +227,29 @@ input[type="button"] {
font-size: 16px;
border: none;
background: @green;
color: white;
color: @white;
font-weight: bold;
&:hover {
background: @green-d1;
}
}
// outlined
.btn-outline {
border: 1px solid @gray-8;
background: @white;
color: @black;
padding: 5px 13px;
}
.btn-outline:hover,
.btn-outline:focus,
.btn-outline:active {
border-color: @gray-7;
background: transparent;
color: @black;
}
// Cross-browser Jank
// --------------------------------------------------

View File

@@ -790,13 +790,13 @@ h4.panel-title {
}
.smartphone-portrait {
width: 320px;
height: 504px;
width: 360px;
height: 640px;
}
.smartphone-landscape {
width: 480px;
height: 256px;
width: 640px;
height: 360px;
}
.border {

View File

@@ -20,7 +20,7 @@
}
.umb-dashboard__content {
padding: 30px 20px;
padding: 20px;
overflow: auto;
}

View File

@@ -16,12 +16,15 @@
.umb-button-group {
.umb-button__button {
border-radius: 3px 0px 0px 3px;
border-radius: @baseBorderRadius;
}
.umb-button-group__toggle {
border-radius: 0px 3px 3px 0;
border-radius: 0px @baseBorderRadius @baseBorderRadius 0;
border-left: 1px solid rgba(0,0,0,0.09);
margin-left: -2px;
padding-left: 10px;
padding-right: 10px;
}
}

View File

@@ -96,7 +96,6 @@
font-size: 12px;
text-align: center;
width: 100px;
height: 105px;
box-sizing: border-box;
position: relative;
}
@@ -114,6 +113,7 @@
width: 100%;
height: 100%;
border-radius: 3px;
padding-bottom: 5px;
}

View File

@@ -4,7 +4,7 @@
background: @gray-10;
display: flex;
justify-content: space-between;
margin-top: -20px;
margin-top: -10px;
position: relative;
top: 0;

View File

@@ -10,6 +10,7 @@
.umb-prevalues-multivalues__left {
display: flex;
flex: 1 1 auto;
overflow: hidden;
}
.umb-prevalues-multivalues__right {

View File

@@ -94,6 +94,10 @@ body.touch .umb-tree {
flex-wrap: nowrap;
align-items: center;
&.active {
background: @gray-10;
}
&:hover {
background: @gray-10;

View File

@@ -1,18 +1,22 @@
.umb-color-swatches {
display: flex;
flex-flow: row wrap;
.umb-color-box {
border: none;
color: white;
cursor: pointer;
padding: 5px;
padding: 1px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 5px;
border-radius: 3px;
width: 30px;
height: 30px;
transition: box-shadow .3s;
display: flex;
align-items: center;
justify-content: center;
&:hover, &:focus {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
@@ -28,4 +32,55 @@
}
}
}
&.with-labels {
.umb-color-box {
width: 120px;
height: 100%;
display: flex;
flex-flow: row wrap;
.umb-color-box-inner {
display: flex;
flex-flow: column wrap;
flex: 0 0 100%;
max-width: 100%;
min-height: 80px;
padding-top: 10px;
.umb-color-box__label {
background: #fff;
font-size: 14px;
display: flex;
flex-flow: column wrap;
flex: 0 0 100%;
padding: 1px 5px;
max-width: 100%;
margin-top: auto;
margin-bottom: -3px;
margin-left: -1px;
margin-right: -1px;
text-indent: 0;
text-align: left;
border: 1px solid @gray-8;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
overflow: hidden;
.umb-color-box__name {
color: @black;
font-weight: bold;
margin-top: 3px;
}
.umb-color-box__description {
font-size: 12px;
line-height: 1.5em;
color: @gray-3;
}
}
}
}
}
}

View File

@@ -33,6 +33,18 @@
background-color: @white;
}
.umb-media-grid__item-file-icon > span {
color: @white;
background: @gray-4;
padding: 1px 3px;
font-size: 10px;
line-height: 130%;
display: block;
margin-top: -30px;
margin-left: -10px;
position: relative;
}
.umb-media-grid__item.-selected {
box-shadow: 0 2px 8px 0 rgba(0,0,0,.35);
}

View File

@@ -0,0 +1,34 @@
.umb-multiple-textbox .textbox-wrapper {
align-items: center;
margin-bottom: 15px;
}
.umb-multiple-textbox .textbox-wrapper .umb-editor {
margin-bottom: 0;
}
.umb-multiple-textbox .textbox-wrapper i {
margin-right: 5px;
}
.umb-multiple-textbox .textbox-wrapper i.handle {
margin-left: 5px;
}
.umb-multiple-textbox .textbox-wrapper a.remove {
margin-left: 5px;
text-decoration: none;
}
.umb-multiple-textbox .add-link {
&:extend(.umb-node-preview-add);
}
.umb-editor-wrapper .umb-multiple-textbox .add-link {
&:extend(.umb-editor-wrapper .umb-node-preview);
}
.umb-modal .umb-multiple-textbox .textbox-wrapper .umb-editor {
flex: 1 1 auto;
width: auto;
}

View File

@@ -166,7 +166,7 @@ input.umb-table__input {
}
.-content .-unpublished {
.-content :not(.with-unpublished-version).-unpublished {
.umb-table__name > * {
opacity: .4;
}

View File

@@ -27,5 +27,5 @@
.umb-permission__description {
font-size: 13px;
color: @gray-5;
color: @gray-4;
}

View File

@@ -133,27 +133,6 @@ div.umb-codeeditor .umb-btn-toolbar {
//
// Color picker
// --------------------------------------------------
ul.color-picker li {
padding: 2px;
margin: 3px;
border: 2px solid transparent;
width: 60px;
.thumbnail{
min-width: auto;
width: inherit;
padding: 0;
}
a {
height: 50px;
display:flex;
align-items: center;
justify-content: center;
cursor:pointer;
margin: 0 0 5px;
}
}
/* pre-value editor */
.control-group.color-picker-preval {
@@ -174,7 +153,7 @@ ul.color-picker li {
div.color-picker-prediv {
display: inline-flex;
align-items: center;
max-width: 80%;
max-width: 85%;
pre {
display: inline-flex;
@@ -357,13 +336,30 @@ ul.color-picker li {
text-align: center;
}
.umb-sortable-thumbnails .umb-icon-holder .icon{
.umb-sortable-thumbnails .umb-icon-holder .icon {
font-size: 40px;
line-height: 50px;
color: @gray-3;
display: block;
}
.umb-sortable-thumbnails .umb-icon-holder .file-icon > span {
color: @white;
background: @gray-4;
padding: 1px 3px;
font-size: 10px;
line-height: 130%;
display: block;
margin-top: -30px;
width: 2em;
}
.umb-sortable-thumbnails .umb-icon-holder .file-icon + small {
display: block;
margin-top: 1em;
}
.umb-sortable-thumbnails .umb-sortable-thumbnails__wrapper {
width: 124px;
height: 124px;

View File

@@ -29,6 +29,7 @@
function init() {
// Check if it is a new user
var inviteVal = $location.search().invite;
//1 = enter password, 2 = password set, 3 = invalid token
if (inviteVal && (inviteVal === "1" || inviteVal === "2")) {
$q.all([
@@ -58,6 +59,8 @@
$scope.inviteStep = Number(inviteVal);
});
} else if (inviteVal && inviteVal === "3") {
$scope.inviteStep = Number(inviteVal);
}
}

View File

@@ -104,10 +104,18 @@
</umb-button>
</div>
</div>
</div>
<div ng-show="invitedUser == null && inviteStep === 3" ng-if="inviteStep === 3" class="umb-login-container">
<div class="form">
<h1 style="margin-bottom: 10px; text-align: left;">Hi there</h1>
<p style="line-height: 1.6; margin-bottom: 25px;">
<localize key="user_userinviteExpiredMessage">Welcome to Umbraco! Unfortunately your invite has expired. Please contact your administrator and ask them to resend it.</localize>
</p>
<div ng-show="invitedUser == null" class="umb-login-container">
</div>
</div>
<div ng-show="invitedUser == null && !inviteStep" class="umb-login-container">
<div class="form">
<h1>{{greeting}}</h1>

View File

@@ -313,7 +313,7 @@ angular.module("umbraco")
function searchMedia() {
$scope.loading = true;
entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions)
entityResource.getPagedDescendants($scope.currentFolder.id, "Media", $scope.searchOptions)
.then(function(data) {
// update image data to work with image grid
angular.forEach(data.items, function(mediaItem) {

View File

@@ -23,13 +23,19 @@
<div class="form-search">
<i class="icon-search"></i>
<input class="umb-search-field search-query -full-width-input"
ng-model="searchOptions.filter"
localize="placeholder"
placeholder="@placeholders_search"
ng-change="changeSearch()"
type="text"
no-dirty-check />
<input class="umb-search-field search-query -full-width-input"
ng-model="searchOptions.filter"
localize="placeholder"
placeholder="@placeholders_search"
ng-change="changeSearch()"
type="text"
no-dirty-check />
<br />
<label>
<localize key="general_includeFromsubFolders">Include subfolders in search</localize>
<input type="checkbox" ng-model="showChilds"
ng-change="changeSearch()">
</label>
</div>
<div class="upload-button">
@@ -81,17 +87,19 @@
</umb-file-dropzone>
<umb-media-grid
ng-if="!loading"
items="images"
on-click="clickHandler"
on-click-name="clickItemName"
on-click-edit="editMediaItem(item)"
allow-on-click-edit="{{allowMediaEdit}}"
item-max-width="150"
item-max-height="150"
item-min-width="100"
item-min-height="100"
only-images={{onlyImages}}>
ng-if="!loading"
items="images"
on-click="clickHandler"
on-click-name="clickItemName"
on-click-edit="editMediaItem(item)"
allow-on-click-edit="{{allowMediaEdit}}"
item-max-width="150"
item-max-height="150"
item-min-width="100"
item-min-height="100"
only-images={{onlyImages}}
include-sub-folders={{showChilds}}
current-Folder-id="{{currentFolder.id}}">
</umb-media-grid>
<div class="flex justify-center">

View File

@@ -39,12 +39,23 @@
<umb-button
alias="preview"
ng-if="!page.isNew && content.allowPreview && page.showPreviewButton"
type="button"
button-style="info"
type="button"
button-style="outline"
action="preview(content)"
label="Preview page"
label-key="buttons_showPage">
</umb-button>
<umb-button
ng-if="page.showSaveButton"
alias="save"
type="button"
button-style="info"
state="page.saveButtonState"
action="save(content)"
label-key="buttons_save"
shortcut="ctrl+s"
add-ellipsis="{{page.saveButtonEllipsis}}">
</umb-button>
<umb-button-group
ng-if="defaultButton && !content.trashed && !infiniteModel.infiniteMode"

View File

@@ -22,6 +22,23 @@
</ul>
</umb-box-content>
</umb-box>
<umb-box data-element="node-info-redirects" style="display:none;" ng-cloak ng-show="!urlTrackerDisabled && hasRedirects">
<umb-box-header title-key="redirectUrls_redirectUrlManagement"></umb-box-header>
<umb-box-content class="block-form">
<div style="position: relative;">
<div ng-if="loadingRedirectUrls" style="background: rgba(255, 255, 255, 0.8); position: absolute; top: 0; left: 0; right: 0; bottom: 0;"></div>
<umb-load-indicator ng-if="loadingRedirectUrls"></umb-load-indicator>
<div ng-show="hasRedirects">
<p><localize key="redirectUrls_panelInformation" class="ng-isolate-scope ng-scope">The following URLs redirect to this content item:</localize></p>
<ul class="nav nav-stacked" style="margin-bottom: 0;">
<li ng-repeat="redirectUrl in redirectUrls">
<a href="{{redirectUrl.originalUrl}}" target="_blank"><i ng-class="value.icon" class="icon-out"></i> {{redirectUrl.originalUrl}}</a>
</li>
</ul>
</div>
</div>
</umb-box-content>
</umb-box>
<umb-box data-element="node-info-history">
<umb-box-header title-key="general_history"></umb-box-header>
@@ -67,6 +84,7 @@
class="history-item__badge"
size="xs"
color="{{item.logTypeColor}}">
<localize key="auditTrails_small{{ item.logType }}">{{ item.logType }}</localize>
</umb-badge>
<span>
@@ -179,7 +197,7 @@
</div>
</umb-control-group>
<umb-control-group data-element="node-info-create-date" label="@template_createdDate">
<umb-control-group ng-if="node.id !== 0" data-element="node-info-create-date" label="@template_createdDate">
{{node.createDateFormatted}} <localize key="general_by">by</localize> {{ node.owner.name }}
</umb-control-group>
@@ -209,7 +227,7 @@
</div>
</umb-control-group>
<umb-control-group data-element="node-info-id" label="Id">
<umb-control-group ng-if="node.id !== 0" data-element="node-info-id" label="Id">
<div>{{ node.id }}</div>
<small>{{ node.key }}</small>
</umb-control-group>

View File

@@ -9,12 +9,15 @@
<div ng-if="notification.view">
<div ng-include="notification.view"></div>
</div>
<div ng-if="notification.headline">
<a ng-href="{{notification.url}}" target="_blank">
<div ng-if="notification.headline" ng-switch on="{{notification}}">
<a ng-href="{{notification.url}}" ng-switch-when="{{notification.url && notification.url.trim() != ''}}" target="_blank">
<strong>{{notification.headline}}</strong>
<span ng-bind-html="notification.message"></span>
</a>
<div ng-switch-default>
<strong>{{notification.headline}}</strong>
<span ng-bind-html="notification.message"></span>
</div>
</div>
</li>
</ul>

View File

@@ -1,9 +1,16 @@
<div class="umb-color-swatches">
<div class="umb-color-swatches" ng-class="{ 'with-labels': useLabel }">
<button class="umb-color-box umb-color-box--{{size}} btn-{{color.value}}" ng-repeat="color in colors" title="{{color.name}}" ng-click="setColor(color.value)">
<div class="check_circle" ng-if="color.value === selectedColor">
<i class="icon icon-check small"></i>
<button type="button" class="umb-color-box umb-color-box--{{size}} btn-{{color.value}}" ng-repeat="color in colors" title="{{color.label}}" hex-bg-inline="{{useColorClass === false}}" hex-bg-color="{{color.value}}" ng-class="{ 'active': color.value === selectedColor }" ng-click="setColor(color.value)">
<div class="umb-color-box-inner">
<div class="check_circle" ng-if="color.value === selectedColor">
<i class="icon icon-check small"></i>
</div>
<div class="umb-color-box__label" ng-if="useLabel">
<div class="umb-color-box__name truncate">{{ color.label }}</div>
<div class="umb-color-box__description">#{{ color.value }}</div>
</div>
</div>
</button>
</div>

View File

@@ -5,23 +5,26 @@
<umb-editor-sub-header-content-right>
<umb-button
style="margin-right: 5px;"
alias="compositions"
ng-if="compositions !== false"
type="button"
button-style="link"
button-style="outline"
label-key="contentTypeEditor_compositions"
icon="icon-merge"
action="openCompositionsDialog()">
action="openCompositionsDialog()"
size="xs">
</umb-button>
<umb-button
alias="reorder"
ng-if="sorting !== false"
type="button"
button-style="link"
button-style="outline"
label-key="{{sortingButtonKey}}"
icon="icon-navigation"
action="toggleSortingMode();">
action="toggleSortingMode();"
size="xs">
</umb-button>
</umb-editor-sub-header-content-right>

View File

@@ -22,8 +22,10 @@
<img class="umb-media-grid__item-image-placeholder" ng-if="!item.thumbnail && item.extension != 'svg'" src="assets/img/transparent.png" alt="{{item.name}}" draggable="false" />
<!-- Icon for files -->
<i class="umb-media-grid__item-icon {{item.icon}}" ng-if="!item.thumbnail && item.extension != 'svg'"></i>
<span class="umb-media-grid__item-file-icon" ng-if="!item.thumbnail && item.extension != 'svg'">
<i class="umb-media-grid__item-icon {{item.icon}}"></i>
<span>.{{item.extension}}</span>
</span>
</div>
</div>
</div>

View File

@@ -35,7 +35,8 @@
ng-class="{
'-selected':item.selected,
'-published':item.published,
'-unpublished':!item.published
'-unpublished':!item.published,
'with-unpublished-version':!item.published && item.hasPublishedVersion
}"
ng-click="vm.selectItem(item, $index, $event)">

View File

@@ -21,13 +21,13 @@
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}">
<span style="margin-bottom: 2px;">{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
</label>
<div ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state class="umb-permission__description" variant="variant"></umb-variant-state>
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<div ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>

View File

@@ -21,8 +21,8 @@
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}">
<span style="margin-bottom: 2px;">{{ variant.language.name }}</span>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
</label>

View File

@@ -21,8 +21,8 @@
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}">
<span style="margin-bottom: 2px;">{{ variant.language.name }}</span>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
</label>

View File

@@ -0,0 +1,120 @@
(function () {
"use strict";
function UnpublishController($scope, localizationService) {
var vm = this;
var autoSelectedVariants = [];
vm.changeSelection = changeSelection;
vm.publishedVariantFilter = publishedVariantFilter;
vm.unpublishedVariantFilter = unpublishedVariantFilter;
function onInit() {
vm.variants = $scope.model.variants;
// set dialog title
if (!$scope.model.title) {
localizationService.localize("content_unpublish").then(function (value) {
$scope.model.title = value;
});
}
// 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;
});
var active = _.find(vm.variants, function (v) {
return v.active;
});
if (active) {
//ensure that the current one is selected
active.save = true;
}
// autoselect other variants if needed
changeSelection(active);
}
}
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 selet all other variants
// and disable selection for the others
if(selectedVariant.save && selectedVariant.language.isMandatory) {
angular.forEach(vm.variants, function(variant){
if(!variant.save && publishedVariantFilter(variant)) {
// keep track of the variants we automaically select
// so we can remove the selection again
autoSelectedVariants.push(variant.language.culture);
variant.save = true;
}
variant.disabled = true;
});
// make sure the mandatory isn't disabled so we can deselect again
selectedVariant.disabled = false;
}
// 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) {
angular.forEach(vm.variants, function(variant){
// check if variant was auto selected, then deselect
if(_.contains(autoSelectedVariants, variant.language.culture)) {
variant.save = false;
};
variant.disabled = false;
});
autoSelectedVariants = [];
}
}
function publishedVariantFilter(variant) {
//determine a variant is 'published' (meaning it will show up as able unpublish)
// * it has been published
// * it has been published with pending changes
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++) {
vm.variants[i].save = false;
vm.variants[i].disabled = false;
}
});
onInit();
}
angular.module("umbraco").controller("Umbraco.Overlays.UnpublishController", UnpublishController);
})();

View File

@@ -0,0 +1,66 @@
<div ng-controller="Umbraco.Overlays.UnpublishController as vm">
<!-- 1 language -->
<div ng-if="vm.variants.length === 1">
<p><localize key="prompt_confirmUnpublish"></localize></p>
</div>
<!-- Multiple languages -->
<div ng-if="vm.variants.length > 1">
<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">
<ng-form name="unpublishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="unpublishVariantSelector"
type="checkbox"
ng-model="variant.save"
ng-change="vm.changeSelection(variant)"
ng-disabled="variant.disabled"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
</label>
<div class="umb-permission__description">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
</div>
</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 style="margin-bottom: 15px; font-weight: bold;">
<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-permission__description" variant="variant"></umb-variant-state>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,20 +1,15 @@
<div ng-controller="Umbraco.PropertyEditors.ColorPickerController">
<div ng-if="!isConfigured" >
<localize key="colorpicker_noColors">You haven't defined any colors</localize>
</div>
<div ng-if="!isConfigured">
<localize key="colorpicker_noColors">You haven't defined any colors</localize>
</div>
<ul class="thumbnails color-picker">
<li ng-repeat="(key, val) in model.config.items">
<a ng-click="toggleItem(val)" class="thumbnail" hex-bg-color="{{val.value}}">
<div class="check_circle" ng-if="isActiveColor(val)">
<i class="icon icon-check small"></i>
</div>
</a>
<span class="color-label" ng-if="model.useLabel" ng-bind="val.label"></span>
</li>
</ul>
<umb-color-swatches colors="model.config.items"
selected-color="model.value.value"
size="m"
use-label="model.useLabel">
</umb-color-swatches>
<input type="hidden" name="modelValue" ng-model="model.value" val-property-validator="validateMandatory"/>
<input type="hidden" name="modelValue" ng-model="model.value" val-property-validator="validateMandatory" />
</div>

View File

@@ -33,6 +33,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
/** Performs validation based on the renderModel data */
function validate() {
if ($scope.contentPickerForm) {
angularHelper.getCurrentForm($scope).$setDirty();
//Validate!
if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) {
$scope.contentPickerForm.minCount.$setValidity("minCount", false);
@@ -199,6 +200,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
_.each(model.selection, function (item, i) {
$scope.add(item);
});
angularHelper.getCurrentForm($scope).$setDirty();
}
angularHelper.getCurrentForm($scope).$setDirty();
editorService.close();

View File

@@ -42,8 +42,9 @@
</umb-image-gravity>
<a href class="btn btn-link btn-crop-delete" ng-click="clear()"><i class="icon-delete red"></i> <localize key="content_uploadClear">Remove file</localize></a>
</div>
<ul class="umb-sortable-thumbnails cropList clearfix">
<ul ng-if="!imageSrc.endsWith('.svg')" class="umb-sortable-thumbnails cropList clearfix">
<li ng-repeat=" (key,value) in model.value.crops" ng-class="{'current':currentCrop.alias === value.alias}" ng-click="crop(value)">
<umb-image-thumbnail center="model.value.focalPoint"

View File

@@ -82,7 +82,7 @@
</umb-empty-state>
<umb-empty-state
ng-if="vm.itemsWithoutFolders.length === 0 && options.filter.length > 0"
ng-if="vm.itemsWithoutFolders.length === 0 && folders.length === 0 && options.filter.length > 0"
position="center">
<localize key="general_searchNoResult"></localize>
</umb-empty-state>

View File

@@ -227,10 +227,6 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
},
500);
if (reload === true) {
$scope.reloadView($scope.contentId, true);
}
if (successMsg) {
localizationService.localize("bulk_done")
.then(function (v) {
@@ -260,12 +256,13 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
with simple values */
$scope.getContent = function (contentId) {
$scope.reloadView($scope.contentId, true);
$scope.reloadView($scope.contentId);
}
$scope.reloadView = function (id, reloadFolders) {
$scope.reloadView = function (id) {
$scope.viewLoaded = false;
$scope.folders = [];
listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection);
@@ -278,20 +275,13 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
if ($scope.listViewResultSet.items) {
_.each($scope.listViewResultSet.items, function (e, index) {
setPropertyValues(e);
});
}
if (e.contentTypeAlias === 'Folder') {
$scope.folders.push(e);
}
});
}
if (reloadFolders && $scope.entityType === 'media') {
//The folders aren't loaded - we only need to do this once since we're never changing node ids
mediaResource.getChildFolders($scope.contentId)
.then(function (page) {
$scope.folders = page.items;
$scope.viewLoaded = true;
});
} else {
$scope.viewLoaded = true;
}
$scope.viewLoaded = true;
//NOTE: This might occur if we are requesting a higher page number than what is actually available, for example
// if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last
@@ -421,7 +411,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
$scope.unpublish = function () {
applySelected(
function (selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); },
function (selected, index) { return contentResource.unpublish(getIdCallback(selected[index])); },
function (count, total) {
var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems");
return localizationService.localize(key, [count, total]);
@@ -609,7 +599,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
$scope.options.allowBulkMove ||
$scope.options.allowBulkDelete;
$scope.reloadView($scope.contentId, true);
$scope.reloadView($scope.contentId);
}
function getLocalizedKey(alias) {

View File

@@ -21,7 +21,10 @@
<!-- FILE -->
<span class="umb-icon-holder" ng-hide="image.thumbnail || image.metaData.umbracoExtension.Value === 'svg' || image.extension === 'svg'">
<i class="icon {{image.icon}} large"></i>
<span class="file-icon">
<i class="icon {{image.icon}} large"></i>
<span>.{{image.extension}}</span>
</span>
<small>{{image.name}}</small>
</span>

View File

@@ -395,7 +395,9 @@ angular.module("umbraco")
var unsubscribe = $scope.$on("formSubmitting", function () {
//TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer
// we do parse it out on the server side but would be nice to do that on the client side before as well.
$scope.model.value = tinyMceEditor ? tinyMceEditor.getContent() : null;
if (tinyMceEditor !== undefined && tinyMceEditor != null && !$scope.isLoading) {
$scope.model.value = tinyMceEditor.getContent();
}
});
//when the element is disposed we need to unsubscribe!

View File

@@ -1,6 +1,6 @@
<div ng-controller="Umbraco.PropertyEditors.textAreaController">
<ng-form name="textareaFieldForm">
<textarea ng-model="model.value" id="{{model.alias}}" name="textarea" rows="10" class="umb-property-editor umb-textarea textstring" val-server="value" ng-keyup="model.change()"></textarea>
<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()"></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>

View File

@@ -48,8 +48,8 @@
<umb-editor-sub-header-section>
<umb-button
type="button"
label="Clear selection"
size="xs"
button-style="outline"
label-key="buttons_clearSelection"
action="vm.clearSelection()"
disabled="actionInProgress">
@@ -62,48 +62,52 @@
</umb-editor-sub-header-content-left>
<umb-editor-sub-header-content-right ng-if="vm.selection.length > 0">
<div style="margin-right: 5px;">
<umb-button
ng-if="vm.allowSetUserGroup"
type="button" size="xs"
label-key="actions_setGroup"
icon="icon-users"
action="vm.openBulkUserGroupPicker()">
</umb-button>
</div>
<div style="margin-right: 5px;">
<umb-button
ng-if="vm.allowEnableUser"
type="button"
size="xs"
state="vm.enableUserButtonState"
label-key="actions_enable"
icon="icon-check"
action="vm.enableUsers()">
</umb-button>
</div>
<div style="margin-right: 5px;">
<umb-button
ng-if="vm.allowUnlockUser"
type="button"
size="xs"
state="vm.unlockUserButtonState"
label-key="actions_unlock"
icon="icon-unlocked"
action="vm.unlockUsers()">
</umb-button>
</div>
<div>
<umb-button
ng-if="vm.allowDisableUser"
type="button"
size="xs"
state="vm.disableUserButtonState"
label-key="actions_disable"
icon="icon-block"
action="vm.disableUsers()">
</umb-button>
</div>
<umb-button
style="margin-right: 5px;"
ng-if="vm.allowSetUserGroup"
type="button"
size="xs"
button-style="outline"
label-key="actions_setGroup"
icon="icon-users"
action="vm.openBulkUserGroupPicker()">
</umb-button>
<umb-button
style="margin-right: 5px;"
ng-if="vm.allowEnableUser"
type="button"
size="xs"
button-style="outline"
state="vm.enableUserButtonState"
label-key="actions_enable"
icon="icon-check"
action="vm.enableUsers()">
</umb-button>
<umb-button
style="margin-right: 5px;"
ng-if="vm.allowUnlockUser"
type="button"
size="xs"
button-style="outline"
state="vm.unlockUserButtonState"
label-key="actions_unlock"
icon="icon-unlocked"
action="vm.unlockUsers()">
</umb-button>
<umb-button
ng-if="vm.allowDisableUser"
type="button"
size="xs"
button-style="outline"
state="vm.disableUserButtonState"
label-key="actions_disable"
icon="icon-block"
action="vm.disableUsers()">
</umb-button>
</umb-editor-sub-header-content-right>
</umb-editor-sub-header>