{{content}}
+diff --git a/.github/CONTRIBUTION_GUIDELINES.md b/.github/CONTRIBUTION_GUIDELINES.md
new file mode 100644
index 0000000000..7d2afb46bf
--- /dev/null
+++ b/.github/CONTRIBUTION_GUIDELINES.md
@@ -0,0 +1,35 @@
+# Contributing to Umbraco CMS
+
+When you’re considering creating a pull request for Umbraco CMS, we will categorize them in two different sizes, small and large.
+
+The process for both sizes is very similar, as [explained in the contribution document](CONTRIBUTING.md#how-do-i-begin).
+
+## Small PRs
+Bug fixes and small improvements - can be recognized by seeing a small number of changes and possibly a small number of new files.
+
+We’re usually able to handle small PRs pretty quickly. A community volunteer will do the initial review and flag it for Umbraco HQ as “community tested”. If everything looks good, it will be merged pretty quickly [as per the described process](REVIEW_PROCESS.md).
+
+### Up for grabs
+
+Umbraco HQ will regularly mark newly created issues on the issue tracker with the `Up for grabs` tag. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. We encourage anyone to pick them up and help out.
+
+If you do start working on something, make sure leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
+
+## Large PRs
+New features and large refactorings - can be recognized by seeing a large number of changes, plenty of new files, updates to package manager files (NuGet’s packages.config, NPM’s packages.json, etc.).
+
+We would love to follow the same process for larger PRs but this is not always possible due to time limitations and priorities that need to be aligned. We don’t want to put up any barriers, but this document should set the correct expectations.
+
+Please make sure to describe your idea in an issue, it helps to put in mockup screenshots or videos.
+
+If the change makes sense for HQ to include in Umbraco CMS we will leave you some feedback on how we’d like to see it being implemented.
+
+If a larger pull request is encouraged by Umbraco HQ, the process will be similar to what is described in the [small PRs process](#small-prs) above, we strive to feedback within 14 days. Finalizing and merging the PR might take longer though as it will likely need to be picked up by the development team to make sure everything is in order. We’ll keep you posted on the progress.
+
+It is highly recommended that you speak to the HQ before making large, complex changes.
+
+### Pull request or package?
+
+If it doesn’t fit in CMS right now, we will likely encourage you to make it into a package instead. A package is a great way to check out popularity of a feature, learn how people use it, validate good usability and to fix bugs.
+
+Eventually, a package could "graduate" to be included in the CMS.
diff --git a/.github/REVIEW_PROCESS.md b/.github/REVIEW_PROCESS.md
new file mode 100644
index 0000000000..917d25b090
--- /dev/null
+++ b/.github/REVIEW_PROCESS.md
@@ -0,0 +1,25 @@
+# Review process
+
+You're an awesome person and have sent us your contribution in the form of a pull request! It's now time to relax for a bit and wait for our response.
+
+In order to set some expectations, here's what happens next.
+
+## Review process
+
+You will get an initial reply within 48 hours (workdays) to acknowledge that we’ve seen your PR and we’ll pick it up as soon as we can.
+
+You will get feedback within at most 14 days after opening the PR. You’ll most likely get feedback sooner though. Then there are a few possible outcomes:
+
+- Your proposed change is awesome! We merge it in and it will be included in the next minor release of Umbraco
+- If the change is a high priority bug fix, we will cherry-pick it into the next patch release as well so that we can release it as soon as possible
+- Your proposed change is awesome but needs a bit more work, we’ll give you feedback on the changes we’d like to see
+- Your proposed change is awesome but.. not something we’re looking to include at this point. We’ll close your PR and the related issue (we’ll be nice about it!)
+
+## Are you still available?
+
+We understand you have other things to do and can't just drop everything to help us out.
+So if we’re asking for your help to improve the PR we’ll wait for two weeks to give you a fair chance to make changes. We’ll ask for an update if we don’t hear back from you after that time.
+
+If we don’t hear back from you for 4 weeks, we’ll close the PR so that it doesn’t just hang around forever. You’re very welcome to re-open it once you have some more time to spend on it.
+
+There will be times that we really like your proposed changes and we’ll finish the final improvements we’d like to see ourselves. You still get the credits and your commits will live on in the git repository.
\ No newline at end of file
diff --git a/build/NuSpecs/tools/Readme.txt b/build/NuSpecs/tools/Readme.txt
index e40b0dbc7e..b0583a2b4d 100644
--- a/build/NuSpecs/tools/Readme.txt
+++ b/build/NuSpecs/tools/Readme.txt
@@ -1,11 +1,11 @@
- 888
- 888
-888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
-888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
-888 888 888 888 888 888 888 888 .d888888 888 888 888
-Y88b 888 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
- "Y88888 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
+ 888
+ 888
+888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
+888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
+888 888 888 888 888 888 888 888 .d888888 888 888 888
+Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
+ "Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
------------------------------------------------------------------
diff --git a/build/NuSpecs/tools/ReadmeUpgrade.txt b/build/NuSpecs/tools/ReadmeUpgrade.txt
index df364c64ed..2f52d03776 100644
--- a/build/NuSpecs/tools/ReadmeUpgrade.txt
+++ b/build/NuSpecs/tools/ReadmeUpgrade.txt
@@ -1,12 +1,13 @@
- _ _ __ __ ____ _____ _____ ____
- | | | | \/ | _ \| __ \ /\ / ____/ __ \
- | | | | \ / | |_) | |__) | / \ | | | | | |
- | | | | |\/| | _ <| _ / / /\ \| | | | | |
- | |__| | | | | |_) | | \ \ / ____ | |___| |__| |
- \____/|_| |_|____/|_| \_/_/ \_\_____\____/
+ 888
+ 888
+888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
+888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
+888 888 888 888 888 888 888 888 .d888888 888 888 888
+Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
+ "Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
-----------------------------------------------------
+------------------------------------------------------------------
Don't forget to build!
diff --git a/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs b/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs
index 0baefe104b..d252c58730 100644
--- a/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs
+++ b/src/Umbraco.Core/Composing/CompositionExtensions/Services.cs
@@ -96,7 +96,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
var pluginLangFolders = appPlugins.Exists == false
? Enumerable.Empty {{content}} Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure? Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?' +
+ '';
+
+ popup.document.open();
+ popup.document.write(html);
+ popup.document.close();
+ }
+
// watch for content updates - reload content when node is saved, published etc.
scope.$watch('node.updateDate', function(newValue, oldValue){
if(!newValue) { return; }
@@ -72,6 +89,9 @@
// Update the create and update dates
formatDatesToLocal();
+
+ //Update the media file format
+ setMediaExtension();
});
//ensure to unregister from all events!
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbflatpickr.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js
similarity index 93%
rename from src/Umbraco.Web.UI.Client/src/common/directives/components/umbflatpickr.directive.js
rename to src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js
index 79b29b407b..899b8f3c23 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbflatpickr.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js
@@ -1,13 +1,13 @@
/**
@ngdoc directive
-@name umbraco.directives.directive:umbFlatpickr
+@name umbraco.directives.directive:umbDateTimePicker
@restrict E
@scope
@description
Added in Umbraco version 8.0
This directive is a wrapper of the flatpickr library. Use it to render a date time picker.
-For extra details about options and events take a look here: https://flatpickr.js.org/
+For extra details about options and events take a look here: https://flatpickr.js.org
Use this directive to render a date time picker
@@ -15,11 +15,11 @@ Use this directive to render a date time picker
@@ -70,12 +70,12 @@ Use this directive to render a date time picker
(function() {
'use strict';
- var umbFlatpickr = {
+ var umbDateTimePicker = {
template: 'Markup example
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller(myService) {
+
+ var vm = this;
+
+ vm.content = "";
+ vm.loading = true;
+
+ myService.getContent().then(function(content){
+ vm.content = content;
+ vm.loading = false;
+ });
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {string=} position The loader position ("top", "bottom").
+
+**/
+
+(function() {
+ 'use strict';
+
+ function UmbLoaderDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-loader.html',
+ scope: {
+ position: "@?"
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbLoader', UmbLoaderDirective);
+
+})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbloadindicator.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbloadindicator.directive.js
index 0671770796..c45b8f3f47 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbloadindicator.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbloadindicator.directive.js
@@ -39,7 +39,7 @@ Use this directive to generate a loading indicator.
});
}
-½
+
angular.module("umbraco").controller("My.Controller", Controller);
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js
index f8dbefa5d7..97eb2bf708 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js
@@ -1,167 +1,172 @@
(function () {
- 'use strict';
+ 'use strict';
- function ChangePasswordController($scope) {
+ function ChangePasswordController($scope) {
- function resetModel(isNew) {
- //the model config will contain an object, if it does not we'll create defaults
- //NOTE: We will not support doing the password regex on the client side because the regex on the server side
- //based on the membership provider cannot always be ported to js from .net directly.
- /*
- {
- hasPassword: true/false,
- requiresQuestionAnswer: true/false,
- enableReset: true/false,
- enablePasswordRetrieval: true/false,
- minPasswordLength: 10
- }
- */
+ var vm = this;
- $scope.showReset = false;
+ vm.$onInit = onInit;
+ vm.$onDestroy = onDestroy;
+ vm.doChange = doChange;
+ vm.cancelChange = cancelChange;
+ vm.showOldPass = showOldPass;
+ vm.showCancelBtn = showCancelBtn;
- //set defaults if they are not available
- if ($scope.config.disableToggle === undefined) {
- $scope.config.disableToggle = false;
- }
- if ($scope.config.hasPassword === undefined) {
- $scope.config.hasPassword = false;
- }
- if ($scope.config.enablePasswordRetrieval === undefined) {
- $scope.config.enablePasswordRetrieval = true;
- }
- if ($scope.config.requiresQuestionAnswer === undefined) {
- $scope.config.requiresQuestionAnswer = false;
- }
- //don't enable reset if it is new - that doesn't make sense
- if (isNew === "true") {
- $scope.config.enableReset = false;
- }
- else if ($scope.config.enableReset === undefined) {
- $scope.config.enableReset = true;
- }
-
- if ($scope.config.minPasswordLength === undefined) {
- $scope.config.minPasswordLength = 0;
- }
-
- //set the model defaults
- if (!angular.isObject($scope.passwordValues)) {
- //if it's not an object then just create a new one
- $scope.passwordValues = {
- newPassword: null,
- oldPassword: null,
- reset: null,
- answer: null
- };
- }
- else {
- //just reset the values
+ var unsubscribe = [];
+
+ function resetModel(isNew) {
+ //the model config will contain an object, if it does not we'll create defaults
+ //NOTE: We will not support doing the password regex on the client side because the regex on the server side
+ //based on the membership provider cannot always be ported to js from .net directly.
+ /*
+ {
+ hasPassword: true/false,
+ requiresQuestionAnswer: true/false,
+ enableReset: true/false,
+ enablePasswordRetrieval: true/false,
+ minPasswordLength: 10
+ }
+ */
+
+ vm.showReset = false;
+
+ //set defaults if they are not available
+ if (vm.config.disableToggle === undefined) {
+ vm.config.disableToggle = false;
+ }
+ if (vm.config.hasPassword === undefined) {
+ vm.config.hasPassword = false;
+ }
+ if (vm.config.enablePasswordRetrieval === undefined) {
+ vm.config.enablePasswordRetrieval = true;
+ }
+ if (vm.config.requiresQuestionAnswer === undefined) {
+ vm.config.requiresQuestionAnswer = false;
+ }
+ //don't enable reset if it is new - that doesn't make sense
+ if (isNew === "true") {
+ vm.config.enableReset = false;
+ }
+ else if (vm.config.enableReset === undefined) {
+ vm.config.enableReset = true;
+ }
+
+ if (vm.config.minPasswordLength === undefined) {
+ vm.config.minPasswordLength = 0;
+ }
+
+ //set the model defaults
+ if (!angular.isObject(vm.passwordValues)) {
+ //if it's not an object then just create a new one
+ vm.passwordValues = {
+ newPassword: null,
+ oldPassword: null,
+ reset: null,
+ answer: null
+ };
+ }
+ else {
+ //just reset the values
+
+ if (!isNew) {
+ //if it is new, then leave the generated pass displayed
+ vm.passwordValues.newPassword = null;
+ vm.passwordValues.oldPassword = null;
+ }
+ vm.passwordValues.reset = null;
+ vm.passwordValues.answer = null;
+ }
+
+ //the value to compare to match passwords
+ if (!isNew) {
+ vm.passwordValues.confirm = "";
+ }
+ else if (vm.passwordValues.newPassword && vm.passwordValues.newPassword.length > 0) {
+ //if it is new and a new password has been set, then set the confirm password too
+ vm.passwordValues.confirm = vm.passwordValues.newPassword;
+ }
- if (!isNew) {
- //if it is new, then leave the generated pass displayed
- $scope.passwordValues.newPassword = null;
- $scope.passwordValues.oldPassword = null;
}
- $scope.passwordValues.reset = null;
- $scope.passwordValues.answer = null;
- }
- //the value to compare to match passwords
- if (!isNew) {
- $scope.passwordValues.confirm = "";
- }
- else if ($scope.passwordValues.newPassword && $scope.passwordValues.newPassword.length > 0) {
- //if it is new and a new password has been set, then set the confirm password too
- $scope.passwordValues.confirm = $scope.passwordValues.newPassword;
- }
+ //when the scope is destroyed we need to unsubscribe
+ function onDestroy() {
+ for (var u in unsubscribe) {
+ unsubscribe[u]();
+ }
+ }
+
+ function onInit() {
+ //listen for the saved event, when that occurs we'll
+ //change to changing = false;
+ unsubscribe.push($scope.$on("formSubmitted", function () {
+ if (vm.config.disableToggle === false) {
+ vm.changing = false;
+ }
+ }));
+
+ unsubscribe.push($scope.$on("formSubmitting", function () {
+ //if there was a previously generated password displaying, clear it
+ if (vm.changing && vm.passwordValues) {
+ vm.passwordValues.generatedPassword = null;
+ }
+ else if (!vm.changing) {
+ //we are not changing, so the model needs to be null
+ vm.passwordValues = null;
+ }
+ }));
+
+ resetModel(vm.isNew);
+
+ //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there
+ //with validators turned on.
+ vm.changing = vm.config.disableToggle === true || !vm.config.hasPassword;
+
+ //we're not currently changing so set the model to null
+ if (!vm.changing) {
+ vm.passwordValues = null;
+ }
+ }
+
+ function doChange() {
+ resetModel();
+ vm.changing = true;
+ //if there was a previously generated password displaying, clear it
+ vm.passwordValues.generatedPassword = null;
+ vm.passwordValues.confirm = null;
+ };
+
+ function cancelChange() {
+ vm.changing = false;
+ //set model to null
+ vm.passwordValues = null;
+ };
+
+ function showOldPass() {
+ return vm.config.hasPassword &&
+ !vm.config.allowManuallyChangingPassword &&
+ !vm.config.enablePasswordRetrieval && !vm.showReset;
+ };
+
+ // TODO: I don't think we need this or the cancel button, this can be up to the editor rendering this component
+ function showCancelBtn() {
+ return vm.config.disableToggle !== true && vm.config.hasPassword;
+ };
}
- resetModel($scope.isNew);
-
- //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there
- //with validators turned on.
- $scope.changing = $scope.config.disableToggle === true || !$scope.config.hasPassword;
-
- //we're not currently changing so set the model to null
- if (!$scope.changing) {
- $scope.passwordValues = null;
- }
-
- $scope.doChange = function () {
- resetModel();
- $scope.changing = true;
- //if there was a previously generated password displaying, clear it
- $scope.passwordValues.generatedPassword = null;
- $scope.passwordValues.confirm = null;
+ var component = {
+ templateUrl: 'views/components/users/change-password.html',
+ controller: ChangePasswordController,
+ controllerAs: 'vm',
+ bindings: {
+ isNew: "<",
+ passwordValues: "=", //TODO: Do we need bi-directional vals?
+ config: "=" //TODO: Do we need bi-directional vals?
+ //TODO: Do we need callbacks?
+ }
};
- $scope.cancelChange = function () {
- $scope.changing = false;
- //set model to null
- $scope.passwordValues = null;
- };
-
- var unsubscribe = [];
-
- //listen for the saved event, when that occurs we'll
- //change to changing = false;
- unsubscribe.push($scope.$on("formSubmitted", function () {
- if ($scope.config.disableToggle === false) {
- $scope.changing = false;
- }
- }));
- unsubscribe.push($scope.$on("formSubmitting", function () {
- //if there was a previously generated password displaying, clear it
- if ($scope.changing && $scope.passwordValues) {
- $scope.passwordValues.generatedPassword = null;
- }
- else if (!$scope.changing) {
- //we are not changing, so the model needs to be null
- $scope.passwordValues = null;
- }
- }));
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on('$destroy', function () {
- for (var u in unsubscribe) {
- unsubscribe[u]();
- }
- });
-
- $scope.showOldPass = function () {
- return $scope.config.hasPassword &&
- !$scope.config.allowManuallyChangingPassword &&
- !$scope.config.enablePasswordRetrieval && !$scope.showReset;
- };
-
- // TODO: I don't think we need this or the cancel button, this can be up to the editor rendering this directive
- $scope.showCancelBtn = function () {
- return $scope.config.disableToggle !== true && $scope.config.hasPassword;
- };
-
- }
-
- function ChangePasswordDirective() {
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/users/change-password.html',
- controller: 'Umbraco.Editors.Users.ChangePasswordDirectiveController',
- scope: {
- isNew: "=?",
- passwordValues: "=",
- config: "="
- }
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').controller('Umbraco.Editors.Users.ChangePasswordDirectiveController', ChangePasswordController);
- angular.module('umbraco.directives').directive('changePassword', ChangePasswordDirective);
+ angular.module('umbraco.directives').component('changePassword', component);
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
index 7852180dfa..9cf1181cfa 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
@@ -104,21 +104,26 @@ function entityResource($q, $http, umbRequestHelper) {
*
* @param {Int} id Id of node to return the public url to
* @param {string} type Object type name
+ * @param {string} culture Culture
* @returns {Promise} resourcePromise object containing the url.
*
*/
- getUrl: function (id, type) {
+ getUrl: function (id, type, culture) {
if (id === -1 || id === "-1") {
return "";
}
+ if (!culture) {
+ culture = "";
+ }
+
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetUrl",
- [{ id: id }, {type: type }])),
+ [{ id: id }, {type: type }, {culture: culture }])),
'Failed to retrieve url for id:' + id);
},
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
index 3525310e48..200e61ee96 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js
@@ -491,7 +491,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
var savedVariants = [];
if (origContent.variants) {
isContent = true;
- //it's contnet so assign the variants as they exist
+ //it's content so assign the variants as they exist
origVariants = origContent.variants;
savedVariants = savedContent.variants;
}
@@ -517,7 +517,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
//special case for content, don't sync this variant if it wasn't tagged
//for saving in the first place
- if (!origVariant.save) {
+ if (isContent && !origVariant.save) {
continue;
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less
index bd1cdd5b4f..b0b7e80762 100644
--- a/src/Umbraco.Web.UI.Client/src/less/belle.less
+++ b/src/Umbraco.Web.UI.Client/src/less/belle.less
@@ -112,7 +112,7 @@
@import "components/umb-editor-navigation-item.less";
@import "components/umb-editor-sub-views.less";
@import "components/editor/subheader/umb-editor-sub-header.less";
-@import "components/umb-flatpickr.less";
+@import "components/umb-date-time-picker.less";
@import "components/umb-grid-selector.less";
@import "components/umb-child-selector.less";
@import "components/umb-group-builder.less";
@@ -124,6 +124,7 @@
@import "components/umb-form-check.less";
@import "components/umb-locked-field.less";
@import "components/umb-tabs.less";
+@import "components/umb-loader.less";
@import "components/umb-load-indicator.less";
@import "components/umb-breadcrumbs.less";
@import "components/umb-media-grid.less";
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less
index 315cd91dbd..33a723a3f7 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less
@@ -1,8 +1,12 @@
-.umb-tour__loader {
- background: @white;
- z-index: @zindexTourModal;
+.umb-loader-wrapper.umb-tour__loader {
+ margin: 0;
position: fixed;
- height: 5px;
+ z-index: @zindexTourModal;
+
+ .umb-loader {
+ background-color: @white;
+ height: 5px;
+ }
}
.umb-tour__pulse {
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less
index 2a1d047ca1..85fcc249f9 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less
@@ -376,14 +376,3 @@ a.umb-variant-switcher__toggle {
margin-right: auto;
padding-right: 10px;
}
-
-/* Confirm */
-.umb-editor-confirm {
- background-color: @white;
- padding: 20px;
- position: absolute;
- left: 0;
- bottom: 0;
- z-index: 10;
- box-shadow: 0 -3px 12px 0px rgba(0,0,0,0.16);
-}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less
index a969f876a4..8945d15ec6 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree-item.less
@@ -39,17 +39,10 @@
// Loading Animation
// ------------------------
- .l {
+ .umb-tree-item__loader {
width: 100%;
- height: 2px;
- overflow: hidden;
position: absolute;
- left: 0;
- bottom: -1px;
-
- div {
- .umb-loader;
- }
+ margin: 0;
}
.umb-tree-item__label {
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less
index 7a70046de2..93850656f0 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/tree/umb-tree.less
@@ -33,12 +33,6 @@
display: none;
}
}
-
- //loader defaults
- .umb-loader {
- height: 10px;
- margin: 10px 10px 10px 10px;
- }
.search-subtitle {
color: @gray-7;
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-flatpickr.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less
similarity index 90%
rename from src/Umbraco.Web.UI.Client/src/less/components/umb-flatpickr.less
rename to src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less
index 8cdcc8b877..2df0cc5fd8 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-flatpickr.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-date-time-picker.less
@@ -1,4 +1,4 @@
-.flatpickr-calendar.flatpickr-calendar {
+.flatpickr-calendar {
border-radius: @baseBorderRadius;
box-shadow: 0 5px 10px 0 rgba(0,0,0,0.16);
}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-loader.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-loader.less
new file mode 100644
index 0000000000..260710ce72
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-loader.less
@@ -0,0 +1,42 @@
+// Loading Animation
+// ------------------------
+
+.umb-loader {
+ background-color: @blue;
+ margin-top: 0;
+ margin-left: -100%;
+ animation-name: bounce_loadingProgressG;
+ animation-duration: 1s;
+ animation-iteration-count: infinite;
+ animation-timing-function: linear;
+ width: 100%;
+ height: 2px;
+}
+
+@keyframes bounce_loadingProgressG {
+ 0% {
+ margin-left: -100%;
+ }
+
+ 100% {
+ margin-left: 100%;
+ }
+}
+
+.umb-loader-wrapper {
+ position: absolute;
+ right: 0;
+ left: 0;
+ margin: 10px 0;
+ overflow: hidden;
+}
+
+.umb-loader-wrapper.-top {
+ top: 0;
+ bottom: auto;
+}
+
+.umb-loader-wrapper.-bottom {
+ top: auto;
+ bottom: 0;
+}
diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
index f387b6540b..27b64f85fb 100644
--- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
+++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less
@@ -295,7 +295,11 @@ input.umb-table__input {
.umb-table__row-expand {
font-size: 12px;
text-decoration: none;
- color: @black;
+ color: @gray-4;
+
+ &:hover {
+ color: @black;
+ }
}
.umb-table__row-expand--hidden {
diff --git a/src/Umbraco.Web.UI.Client/src/less/installer.less b/src/Umbraco.Web.UI.Client/src/less/installer.less
index 798252c394..8090b971fc 100644
--- a/src/Umbraco.Web.UI.Client/src/less/installer.less
+++ b/src/Umbraco.Web.UI.Client/src/less/installer.less
@@ -13,6 +13,9 @@
@import "../../lib/bootstrap/less/thumbnails.less";
@import "../../lib/bootstrap/less/media.less";
+// Umbraco Components
+@import "components/umb-loader.less";
+
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
@@ -165,45 +168,17 @@ input.ng-dirty.ng-invalid{border-color: #b94a48; color: #b94a48;}
opacity:1;
}
+.umb-installer-loader {
+ margin: 0;
+ width: 0;
+ z-index: 777;
-.umb-loader{
- background-color: @white;
- margin-top:0;
- margin-left:-100%;
- animation-name:bounce_loadingProgressG;
- animation-duration:1s;
- animation-iteration-count:infinite;
- animation-timing-function:linear;
- width:100%;
- height: 5px;
-}
-
-@keyframes bounce_loadingProgressG{
- 0%{
- margin-left:-100%;
- }
- 100%{
- margin-left:100%;
+ .umb-loader {
+ background-color: @white;
+ height: 5px;
}
}
-//loader defaults
-.umb-loader-container, .umb-loader-done{
- height: 3px;
- position: absolute;
- bottom: 0;
- left: 0;
- overflow: hidden;
- width: 0%;
- z-index: 777;
-}
-
-.umb-loader-done{
- right: 0%;
- background: @white;
-}
-
-
.permissions-report {
overflow:auto;
height:320px;
diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less
index c10f846ca5..920fcdb1eb 100644
--- a/src/Umbraco.Web.UI.Client/src/less/main.less
+++ b/src/Umbraco.Web.UI.Client/src/less/main.less
@@ -486,42 +486,6 @@ table thead a {
color:@green;
}
-// Loading Animation
-// ------------------------
-
-.umb-loader{
- background-color: @blue;
- margin-top:0;
- margin-left:-100%;
- animation-name:bounce_loadingProgressG;
- animation-duration:1s;
- animation-iteration-count:infinite;
- animation-timing-function:linear;
- width:100%;
- height:2px;
-}
-
-@keyframes bounce_loadingProgressG{
- 0%{
- margin-left:-100%;
- }
- 100%{
- margin-left:100%;
- }
-}
-
-.umb-loader-wrapper {
- position: absolute;
- right: 0;
- left: 0;
- margin: 10px 0;
- overflow: hidden;
-}
-
-.umb-loader-wrapper.-bottom {
- bottom: 0;
-}
-
// Helpers
.strong {
diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
index 92351d09ca..435b64ad34 100644
--- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less
+++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less
@@ -5,7 +5,6 @@
// --------------------------------------------------
.umb-property-editor {
width: 100%;
- position:relative;
}
.umb-property-editor-tiny {
diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js
index 654bbb1d03..93870f8a56 100644
--- a/src/Umbraco.Web.UI.Client/src/main.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/main.controller.js
@@ -10,7 +10,7 @@
*/
function MainController($scope, $location, appState, treeService, notificationsService,
userService, historyService, updateChecker, navigationService, eventsService,
- tmhDynamicLocale, localStorageService, editorService, overlayService) {
+ tmhDynamicLocale, localStorageService, editorService, overlayService, assetsService, tinyMceAssets) {
//the null is important because we do an explicit bool check on this in the view
$scope.authenticated = null;
@@ -21,7 +21,13 @@ function MainController($scope, $location, appState, treeService, notificationsS
$scope.search = {};
$scope.login = {};
$scope.tabbingActive = false;
-
+
+ // Load TinyMCE assets ahead of time in the background for the user
+ // To help with first load of the RTE
+ tinyMceAssets.forEach(function (tinyJsAsset) {
+ assetsService.loadJs(tinyJsAsset, $scope);
+ });
+
// There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution.
// For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2
function handleFirstTab(evt) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js
index 75bf414099..c9d2e43a98 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js
@@ -1,7 +1,7 @@
(function () {
"use strict";
- function CompositionsController($scope, $location, $filter) {
+ function CompositionsController($scope, $location, $filter, overlayService) {
var vm = this;
var oldModel = null;
@@ -56,19 +56,35 @@
if ($scope.model && $scope.model.submit) {
// check if any compositions has been removed
- vm.compositionRemoved = false;
+ var compositionRemoved = false;
for (var i = 0; oldModel.compositeContentTypes.length > i; i++) {
var oldComposition = oldModel.compositeContentTypes[i];
if (_.contains($scope.model.compositeContentTypes, oldComposition) === false) {
- vm.compositionRemoved = true;
+ compositionRemoved = true;
}
}
/* submit the form if there havne't been removed any composition
or the confirm checkbox has been checked */
- if (!vm.compositionRemoved || vm.allowSubmit) {
- $scope.model.submit($scope.model);
+ if (compositionRemoved) {
+ vm.allowSubmit = false;
+ const dialog = {
+ view: "views/common/infiniteeditors/compositions/overlays/confirmremove.html",
+ submitButtonLabelKey: "general_ok",
+ closeButtonLabelKey: "general_cancel",
+ submit: function (model) {
+ $scope.model.submit($scope.model);
+ overlayService.close();
+ },
+ close: function () {
+ overlayService.close();
+ }
+ };
+ overlayService.open(dialog);
+ return;
}
+
+ $scope.model.submit($scope.model);
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html
index bf74431d96..bb0804e2bf 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html
@@ -92,16 +92,6 @@
-
- Warning
- Warning
+
{{vm.errorMsg}}