diff --git a/src/Umbraco.Core/Composing/Composition.cs b/src/Umbraco.Core/Composing/Composition.cs index d7686ccd07..34c5296dce 100644 --- a/src/Umbraco.Core/Composing/Composition.cs +++ b/src/Umbraco.Core/Composing/Composition.cs @@ -133,7 +133,11 @@ namespace Umbraco.Core.Composing Configs.RegisterWith(_register); - return _register.CreateFactory(); + IFactory factory = null; + // ReSharper disable once AccessToModifiedClosure -- on purpose + _register.Register(_ => factory, Lifetime.Singleton); + factory = _register.CreateFactory(); + return factory; } /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index cc962fbe22..f2bef52922 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -2875,7 +2875,14 @@ namespace Umbraco.Core.Services.Implement { foreach (var property in blueprint.Properties) { - content.SetValue(property.Alias, property.GetValue(culture), culture); + if (property.PropertyType.VariesByCulture()) + { + content.SetValue(property.Alias, property.GetValue(culture), culture); + } + else + { + content.SetValue(property.Alias, property.GetValue()); + } } content.Name = blueprint.Name; diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs new file mode 100644 index 0000000000..f4478e2add --- /dev/null +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -0,0 +1,53 @@ +using System; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; + +namespace Umbraco.Tests.Composing +{ + [TestFixture] + public class CompositionTests + { + [Test] + public void FactoryIsResolvable() + { + Func factoryFactory = null; + + var mockedRegister = Mock.Of(); + var mockedFactory = Mock.Of(); + + // the mocked register creates the mocked factory + Mock.Get(mockedRegister) + .Setup(x => x.CreateFactory()) + .Returns(mockedFactory); + + // the mocked register can register a factory factory + Mock.Get(mockedRegister) + .Setup(x => x.Register(It.IsAny>(), Lifetime.Singleton)) + .Callback, Lifetime>((ff, lt) => factoryFactory = ff); + + // the mocked factory can invoke the factory factory + Mock.Get(mockedFactory) + .Setup(x => x.GetInstance(typeof(IFactory))) + .Returns(() => factoryFactory?.Invoke(mockedFactory)); + + var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); + var typeLoader = new TypeLoader(Mock.Of(), "", logger); + var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of()); + + // create the factory, ensure it is the mocked factory + var factory = composition.CreateFactory(); + Assert.AreSame(mockedFactory, factory); + + // ensure we can get an IFactory instance, + // meaning that it has been properly registered + + var resolved = factory.GetInstance(); + Assert.IsNotNull(resolved); + Assert.AreSame(factory, resolved); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 20cea53b09..e0622f0c27 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -118,6 +118,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index e5b0dbf201..80b5f4ed39 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -331,8 +331,35 @@ // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish function performSave(args) { - - + + // Check that all variants for publishing have a name. + if (args.action === "publish" || args.action === "sendToPublish") { + + if ($scope.content.variants) { + var iVariant; + for (var i = 0; i < $scope.content.variants.length; i++) { + iVariant = $scope.content.variants[i]; + + iVariant.notifications = [];// maybe not needed, need to investigate. + + if(iVariant.publish === true) { + if (iVariant.name == null) { + + var tokens = [iVariant.language.name]; + + localizationService.localize("publish_contentPublishedFailedByMissingName", tokens).then(function (value) { + iVariant.notifications.push({"message": value, "type": 1}); + }); + + return $q.reject(); + } + } + } + } + + } + + //Used to check validility of nested form - coming from Content Apps mostly //Set them all to be invalid var fieldsToRollback = checkValidility(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js index d44ca36a5a..47381a15c0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js @@ -22,38 +22,51 @@ @param {boolean} model Set to true or false to set the checkbox to checked or unchecked. +@param {string} input-id Set the id of the checkbox. @param {string} value Set the value of the checkbox. @param {string} name Set the name of the checkbox. @param {string} text Set the text for the checkbox label. +@param {string} server-validation-field Set the val-server-field of the checkbox. @param {boolean} disabled Set the checkbox to be disabled. @param {boolean} required Set the checkbox to be required. -@param {string} onChange Callback when the value of the input element changes. +@param {string} on-change Callback when the value of the checkbox changed by interaction. **/ (function () { 'use strict'; - - function CheckboxDirective() { - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/forms/umb-checkbox.html', - scope: { - model: "=", - value: "@", - name: "@", - text: "@", - disabled: "=", - required: "=", - onChange: "&" - } - }; - - return directive; - + + + function UmbCheckboxController($timeout) { + + var vm = this; + + vm.callOnChange = function() { + $timeout(function() { + vm.onChange({model:vm.model, value:vm.value}); + }, 0); + } + } + + + var component = { + templateUrl: 'views/components/forms/umb-checkbox.html', + controller: UmbCheckboxController, + controllerAs: 'vm', + bindings: { + model: "=", + inputId: "@", + value: "@", + name: "@", + text: "@", + serverValidationField: "@", + disabled: "<", + required: "<", + onChange: "&" + } + }; - angular.module('umbraco.directives').directive('umbCheckbox', CheckboxDirective); + angular.module('umbraco.directives').component('umbCheckbox', component); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js index 6472dd3d38..e95a5992d1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js @@ -19,9 +19,15 @@ angular.module("umbraco.directives") promises.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope)); } - var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"]; - if (scope.configuration && scope.configuration.toolbar) { - toolbar = scope.configuration.toolbar; + var editorConfig = scope.configuration ? scope.configuration : null; + if (!editorConfig || angular.isString(editorConfig)) { + editorConfig = tinyMceService.defaultPrevalues(); + //for the grid by default, we don't want to include the macro toolbar + editorConfig.toolbar = _.without(editorConfig, "umbmacro"); + } + //make sure there's a max image size + if (!scope.configuration.maxImageSize && scope.configuration.maxImageSize !== 0) { + editorConfig.maxImageSize = tinyMceService.defaultPrevalues().maxImageSize; } //stores a reference to the editor @@ -29,9 +35,9 @@ angular.module("umbraco.directives") promises.push(tinyMceService.getTinyMceEditorConfig({ htmlId: scope.uniqueId, - stylesheets: scope.configuration ? scope.configuration.stylesheets : null, - toolbar: toolbar, - mode: scope.configuration.mode + stylesheets: editorConfig.stylesheets, + toolbar: editorConfig.toolbar, + mode: editorConfig.mode })); // pin toolbar to top of screen if we have focus and it scrolls off the screen @@ -46,9 +52,16 @@ angular.module("umbraco.directives") $q.all(promises).then(function (result) { - var tinyMceEditorConfig = result[promises.length - 1]; + var standardConfig = result[promises.length - 1]; - tinyMceEditorConfig.setup = function (editor) { + //create a baseline Config to extend upon + var baseLineConfigObj = { + maxImageSize: editorConfig.maxImageSize + }; + + angular.extend(baseLineConfigObj, standardConfig); + + baseLineConfigObj.setup = function (editor) { //set the reference tinyMceEditor = editor; @@ -111,7 +124,7 @@ angular.module("umbraco.directives") //the elements needed $timeout(function () { tinymce.DOM.events.domLoaded = true; - tinymce.init(tinyMceEditorConfig); + tinymce.init(baseLineConfigObj); }, 150, false); } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less index 846b7d7aee..bfe41ccaac 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less @@ -22,6 +22,10 @@ a.umb-list-item:focus { opacity: 0.6; } +.umb-list-item--error { + color: @red; +} + .umb-list-item:hover .umb-list-checkbox, .umb-list-item--selected .umb-list-checkbox { opacity: 1; @@ -34,4 +38,4 @@ a.umb-list-item:focus { .umb-list-checkbox--visible { opacity: 1; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 7d6584d2f1..4733c58556 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -113,7 +113,12 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.exitPreview = function () { var culture = $location.search().culture || getParameterByName("culture"); - var relativeUrl = "/" + $scope.pageId +'?culture='+ culture; + var relativeUrl = "/" + $scope.pageId; + + if(culture){ + relativeUrl +='?culture='+ culture; + } + window.top.location.href = "../preview/end?redir=" + encodeURIComponent(relativeUrl); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html index b11e682da8..d40263c6b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html @@ -1,16 +1,19 @@ diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js index a9070f40c9..961adc6f97 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js @@ -14,34 +14,51 @@ /** Returns true if publishing is possible based on if there are un-published mandatory languages */ function canPublish() { - var selected = []; + + var possible = false; for (var i = 0; i < vm.variants.length; i++) { var variant = vm.variants[i]; - - //if this variant will show up in the publish-able list - var publishable = dirtyVariantFilter(variant); - var published = !(variant.state === "NotCreated" || variant.state === "Draft"); - - if ((variant.language.isMandatory && !published) && (!publishable || !variant.publish)) { - //if a mandatory variant isn't published - //and it's not publishable or not selected to be published - //then we cannot continue - - // TODO: Show a message when this occurs + var state = canVariantPublish(variant); + if (state === true) { + possible = true; + } + if (state === false) { return false; } - - if (variant.publish) { - selected.push(variant.publish); - } } - return selected.length > 0; + return possible; + } + + /** Returns true if publishing is possible based on if the variant is a un-published mandatory language */ + function canVariantPublish(variant) { + + //if this variant will show up in the publish-able list + var publishable = dirtyVariantFilter(variant); + var published = !(variant.state === "NotCreated" || variant.state === "Draft"); + + // is this variant mandatory: + if (variant.language.isMandatory && !published && !variant.publish) { + //if a mandatory variant isn't published or set to be published + //then we cannot continue + + return false; + } + + // is this variant selected for publish: + if (variant.publish === true) { + return publishable; + } + + return null; } function changeSelection(variant) { + $scope.model.disableSubmitButton = !canPublish(); //need to set the Save state to true if publish is true variant.save = variant.publish; + + variant.willPublish = canVariantPublish(variant); } function dirtyVariantFilter(variant) { @@ -102,32 +119,49 @@ _.each(vm.variants, function (variant) { - if(variant.state !== "NotCreated"){ - vm.isNew = false; + if(variant.state === "NotCreated") { + vm.isNew = true; } - }); + } + ); _.each(vm.variants, function (variant) { variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant); variant.htmlId = "_content_variant_" + variant.compositeId; + + // reset to not be published + variant.publish = false; + variant.save = false; //check for pristine variants if (!vm.hasPristineVariants) { vm.hasPristineVariants = pristineVariantFilter(variant); } - - if(hasAnyData(variant)){ + + // If the variant havent been created jet. + if(variant.state === "NotCreated") { + // If the variant is mandatory, then set the variant to be published. + if (variant.language.isMandatory === true) { + variant.publish = true; + variant.save = true; + } + } + + variant.canPublish = dirtyVariantFilter(variant); + + // if we have data on this variant. + if(variant.canPublish && hasAnyData(variant)) { + // and if some varaints havent been saved before, or they dont have a publishing date set, then we set it for publishing. if(vm.isNew || variant.publishDate == null){ variant.publish = true; variant.save = true; } - }else{ - variant.publish = false; - variant.save = false; - variant.canSave = false; } - }); + + variant.willPublish = canVariantPublish(variant); + } + ); if (vm.variants.length !== 0) { //now sort it so that the current one is at the top @@ -156,7 +190,6 @@ localizationService.localize(labelKey).then(function (value) { vm.headline = value; - vm.loading = false; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html index 732a1b5d7f..e92c16f0ef 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html @@ -10,7 +10,7 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html index db38ad2635..1c7f212225 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publishdescendants.html @@ -22,11 +22,13 @@

-
-