From f663a133915502e461e2e3baae1589b8f10061ab Mon Sep 17 00:00:00 2001 From: hartvig Date: Mon, 9 Mar 2015 08:52:21 +0100 Subject: [PATCH 0001/1710] Updates installer with a new number of tattoos Kudos to Mike Perry for being #3: https://twitter.com/nvmy13t/status/574733012904099840 --- src/Umbraco.Web.UI.Client/src/installer/installer.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js index 1ab84900f4..f2df5dea2b 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -28,7 +28,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop 'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company', "There's a pretty big chance, you've visited a website powered by Umbraco today", "'Umbraco-spotting' is the game of spotting big brands running Umbraco", - "At least 2 people have the Umbraco logo tattooed on them", + "At least 3 people have the Umbraco logo tattooed on them", "'Umbraco' is the danish name for an allen key", "Umbraco has been around since 2005, that's a looong time in IT", "More than 400 people from all over the world meet each year in Denmark in June for our annual conference CodeGarden", @@ -335,4 +335,4 @@ angular.module("umbraco.install").factory('installerService', function($rootScop }; return service; -}); \ No newline at end of file +}); From 3094dea5d4b28cb2f8a87639acb4d647e67c5be1 Mon Sep 17 00:00:00 2001 From: Gavin Faux Date: Wed, 13 May 2015 00:59:03 +0100 Subject: [PATCH 0002/1710] Back office insecure content warning loading Google Web Fonts when running over HTTPS Amend to use https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js --- src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js | 2 +- .../src/canvasdesigner/canvasdesigner.controller.js | 4 ++-- .../src/canvasdesigner/canvasdesigner.front.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js b/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js index 3e4dfe7f54..db9a5fb23c 100644 --- a/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js +++ b/src/Umbraco.Web.UI.Client/src/canvasdesigner.loader.js @@ -9,7 +9,7 @@ LazyLoad.js([ '/Umbraco/js/umbraco.security.js', '/Umbraco/ServerVariables', '/Umbraco/lib/spectrum/spectrum.js', - 'http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js', + 'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js', '/umbraco/js/canvasdesigner.panel.js', ], function () { jQuery(document).ready(function () { diff --git a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js index 127d5882dc..477d1e5f5b 100644 --- a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js +++ b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.controller.js @@ -180,7 +180,7 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider', // TODO: special init for font family picker if (item.type == "googlefontpicker" && item.values.fontFamily) { var variant = item.values.fontWeight != "" || item.values.fontStyle != "" ? ":" + item.values.fontWeight + item.values.fontStyle : ""; - var gimport = "@import url('http://fonts.googleapis.com/css?family=" + item.values.fontFamily + variant + "');"; + var gimport = "@import url('https://fonts.googleapis.com/css?family=" + item.values.fontFamily + variant + "');"; if ($.inArray(gimport, parameters) < 0) { parameters.splice(0, 0, gimport); } @@ -412,7 +412,7 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider', var webFontScriptLoaded = false; var loadGoogleFont = function (font) { if (!webFontScriptLoaded) { - $.getScript('http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js') + $.getScript('https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js') .done(function () { webFontScriptLoaded = true; // Recursively call once webfont script is available. diff --git a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.front.js b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.front.js index 38a0878ca8..042d3ed89a 100644 --- a/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.front.js +++ b/src/Umbraco.Web.UI.Client/src/canvasdesigner/canvasdesigner.front.js @@ -19,7 +19,7 @@ var refreshLayout = function (parameters) { var webFontScriptLoaded = false; var getFont = function (font) { if (!webFontScriptLoaded) { - $.getScript('http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js') + $.getScript('https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js') .done(function () { webFontScriptLoaded = true; // Recursively call once webfont script is available. From ded1ac41e39d83a8680b3c0aa135b6d4851fdc5c Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Wed, 13 May 2015 16:51:00 +0200 Subject: [PATCH 0003/1710] Change of web.mvc version hintpath --- src/Umbraco.Core/Umbraco.Core.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b33ac66c3e..6b6defeea6 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -132,9 +132,9 @@ True ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll - - True - ..\packages\Microsoft.AspNet.Mvc.4.0.30506.0\lib\net40\System.Web.Mvc.dll + + False + ..\packages\Microsoft.AspNet.Mvc.4.0.40804.0\lib\net40\System.Web.Mvc.dll True From ef069e0aa1c2b8c4303c5aa885da5ca551b08ecc Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Wed, 13 May 2015 17:12:28 +0200 Subject: [PATCH 0004/1710] Content Type Editor work merged in --- src/Umbraco.Core/Constants-Applications.cs | 10 + .../editor/umbeditorcontainer.directive.js | 9 + .../editor/umbeditorections.directive.js | 14 + .../editor/umbeditorfooter.directive.js | 9 + .../editor/umbeditorheader.directive.js | 15 + .../editor/umbeditormenu.directive.js | 13 + .../editor/umbeditorview.directive.js | 9 + .../components/field/umbfield.directive.js | 53 +++ .../overlays/umboverlay.directive.js | 54 +++ .../property}/umbproperty.directive.js | 58 +-- .../property/umbpropertyeditor.directive.js | 36 ++ .../property/umbpropertygroup.directive.js | 9 + .../tabs/umbtabcontainer.directive.js | 9 + .../components/tabs/umbtablist.directive.js | 9 + .../editors/contenteditable.directive.js | 35 ++ .../editors/umbAutoResize.directive.js | 6 + .../directives/events/events.directive.js | 90 ++++ .../common/resources/contenttype.resource.js | 21 + .../src/less/animations.less | 191 -------- .../src/less/application/animations.less | 177 ++++++++ .../src/less/{ => application}/grid.less | 372 ++++++++-------- .../src/less/application/shadows.less | 15 + src/Umbraco.Web.UI.Client/src/less/belle.less | 25 +- .../src/less/components/card.less | 160 +++++++ .../src/less/components/editor.less | 53 +++ .../src/less/components/overlays.less | 65 +++ .../src/less/pages/documentTypeEditor.less | 387 +++++++++++++++++ .../editor/umb-editor-container.html | 5 + .../components/editor/umb-editor-footer.html | 7 + .../components/editor/umb-editor-header.html | 19 + .../components/editor/umb-editor-view.html | 3 + .../src/views/components/field/umb-field.html | 13 + .../navigation/umb-editor-actions.html | 5 + .../navigation/umb-editor-menu.html | 22 + .../components/overlays/umb-overlay.html | 16 + .../property/umb-property-editor.html | 1 + .../property/umb-property-group.html | 0 .../components/property/umb-property.html | 19 + .../contenttype.edit.controller.js | 408 +++++++++++++++--- .../src/views/contenttype/edit.html | 364 +++++++++++++--- .../editDataType/editDataType.controller.js | 64 +++ .../dialogs/editDataType/editDataType.html | 16 + .../editPropertySettings.controller.js | 68 +++ .../editPropertySettings.html | 83 ++++ .../dialogs/property.controller.js | 24 ++ .../views/documenttype/dialogs/property.html | 34 ++ .../src/views/documenttype/edit.controller.js | 315 ++++++++++++++ .../src/views/documenttype/edit.html | 271 ++++++++++++ .../src/views/documenttype/property.html | 4 + .../src/views/documenttype/propertygroup.html | 0 .../Editors/ContentTypeController.cs | 36 ++ src/Umbraco.Web/Editors/DataTypeController.cs | 16 + .../ContentEditing/ContentTypeDisplay.cs | 16 + .../ContentEditing/PropertyTypeDisplay.cs | 37 ++ .../PropertyTypeGroupDisplay.cs | 31 ++ .../Models/Mapping/ContentTypeModelMapper.cs | 7 + .../Mapping/PropertyTypeGroupResolver.cs | 77 ++++ .../Trees/DocumentTypeTreeController.cs | 35 ++ src/Umbraco.Web/Umbraco.Web.csproj | 5 + 59 files changed, 3404 insertions(+), 521 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorections.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorfooter.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorview.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/field/umbfield.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js rename src/Umbraco.Web.UI.Client/src/common/directives/{ => components/property}/umbproperty.directive.js (90%) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertygroup.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabcontainer.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtablist.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/editors/contenteditable.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/events/events.directive.js delete mode 100644 src/Umbraco.Web.UI.Client/src/less/animations.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/application/animations.less rename src/Umbraco.Web.UI.Client/src/less/{ => application}/grid.less (93%) create mode 100644 src/Umbraco.Web.UI.Client/src/less/application/shadows.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/card.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/overlays.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-footer.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-view.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/field/umb-field.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-actions.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-menu.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-group.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/property.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttype/propertygroup.html create mode 100644 src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs create mode 100644 src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs create mode 100644 src/Umbraco.Web/Trees/DocumentTypeTreeController.cs diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index f03108f4ac..4c2eaa5fc2 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -80,6 +80,16 @@ public const string Stylesheets = "stylesheets"; + /// + /// alias for the document type tree. + /// + public const string DocumentTypes = "documenttype"; + + /// + /// alias for the media type tree. + /// + public const string MediaTypes = "mediatype"; + /// /// alias for the template tree. /// diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js new file mode 100644 index 0000000000..891bf21cb6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorcontainer.directive.js @@ -0,0 +1,9 @@ +angular.module("umbraco.directives.html") + .directive('umbEditorContainer', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-container.html' + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorections.directive.js new file mode 100644 index 0000000000..9373aab543 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorections.directive.js @@ -0,0 +1,14 @@ +angular.module("umbraco.directives.html") +.directive('umbEditorActions', function () { + return { + + scope: { + actions: "=", + }, + + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/navigation/umb-editor-actions.html' +}; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorfooter.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorfooter.directive.js new file mode 100644 index 0000000000..d23bbb247e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorfooter.directive.js @@ -0,0 +1,9 @@ +angular.module("umbraco.directives.html") + .directive('umbEditorFooter', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-footer.html' + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js new file mode 100644 index 0000000000..6405457e49 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -0,0 +1,15 @@ +angular.module("umbraco.directives.html") +.directive('umbEditorHeader', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + scope: { + tabs: "=", + actions: "=", + name: "=", + menu: "=" + }, + templateUrl: 'views/components/editor/umb-editor-header.html' + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js new file mode 100644 index 0000000000..b19e0ece24 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js @@ -0,0 +1,13 @@ +angular.module("umbraco.directives.html") +.directive('umbEditorMenu', function () { + return { + scope: { + menu: "=", + dimmed: "=" + }, + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/navigation/umb-editor-menu.html' +}; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorview.directive.js new file mode 100644 index 0000000000..163f7f6977 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorview.directive.js @@ -0,0 +1,9 @@ +angular.module("umbraco.directives.html") + .directive('umbEditorView', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-view.html' + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/field/umbfield.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/field/umbfield.directive.js new file mode 100644 index 0000000000..8ea2261d3a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/field/umbfield.directive.js @@ -0,0 +1,53 @@ +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbField +* @restrict E +* +* Used to constryct propety like form html, but without an actual property object +**/ +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbProperty +* @restrict E +**/ +angular.module("umbraco.directives") + .directive('umbField', function (localizationService) { + return { + scope: { + label: "@label", + description: "@", + hideLabel: "@", + alias: "@" + }, + require: '?^form', + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/field/umb-field.html', + link: function (scope, element, attr, formCtrl) { + + scope.formValid = function() { + if (formCtrl) { + return formCtrl.$valid; + } + //there is no form. + return true; + }; + + if (scope.label && scope.label[0] === "@") { + scope.labelstring = localizationService.localize(scope.label.substring(1)); + } + else { + scope.labelstring = scope.label; + } + + if (scope.description && scope.description[0] === "@") { + scope.descriptionstring = localizationService.localize(scope.description.substring(1)); + } + else { + scope.descriptionstring = scope.description; + } + + } + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js new file mode 100644 index 0000000000..512036dff0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js @@ -0,0 +1,54 @@ +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbProperty +* @restrict E +**/ +angular.module("umbraco.directives") + .directive('umbOverlay', function () { + return { + + scope: { + model: "=", + view: "=", + position: "@", + animation: "@", + shadow: "@" + }, + + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/overlays/umb-overlay.html', + link: function(scope, element, attrs) { + + var cssClass = "umb-overlay-center"; + if(scope.position) + { + cssClass = "umb-overlay-" + scope.position; + } + + if(scope.animation){ + cssClass += " " + scope.animation; + } + + var shadow = "shadow-depth-3"; + if(scope.shadow){ + shadow = "shadow-depth-" + scope.shadow; + } + cssClass += " " + shadow; + + + scope.overlayCssClass = cssClass; + scope.closeOverLay = function(){ + if(scope.model.close){ + scope.model.close(scope.model); + }else{ + scope.model = null; + } + }; + + } + + + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js similarity index 90% rename from src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js rename to src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index d84bb8e24d..7c3d59dfba 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -1,30 +1,30 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbProperty -* @restrict E -**/ -angular.module("umbraco.directives") - .directive('umbProperty', function (umbPropEditorHelper) { - return { - scope: { - property: "=" - }, - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/directives/umb-property.html', - - //Define a controller for this directive to expose APIs to other directives - controller: function ($scope, $timeout) { - - var self = this; - - //set the API properties/methods - - self.property = $scope.property; - self.setPropertyError = function(errorMsg) { - $scope.property.propertyErrorMessage = errorMsg; - }; - } - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbProperty +* @restrict E +**/ +angular.module("umbraco.directives") + .directive('umbProperty', function (umbPropEditorHelper) { + return { + scope: { + property: "=" + }, + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/property/umb-property.html', + + //Define a controller for this directive to expose APIs to other directives + controller: function ($scope, $timeout) { + + var self = this; + + //set the API properties/methods + + self.property = $scope.property; + self.setPropertyError = function(errorMsg) { + $scope.property.propertyErrorMessage = errorMsg; + }; + } + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js new file mode 100644 index 0000000000..3bca2f9c06 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js @@ -0,0 +1,36 @@ +/** +* @ngdoc directive +* @function +* @name umbraco.directives.directive:umbPropertyEditor +* @requires formController +* @restrict E +**/ +angular.module("umbraco.directives") + .directive('umbPropertyEditor', function (umbPropEditorHelper) { + return { + scope: { + model: "=", + isPreValue: "@" + }, + + require: "^form", + restrict: 'E', + replace: true, + templateUrl: 'views/components/property/umb-property-editor.html', + link: function (scope, element, attrs, ctrl) { + + //we need to copy the form controller val to our isolated scope so that + //it get's carried down to the child scopes of this! + //we'll also maintain the current form name. + scope[ctrl.$name] = ctrl; + + if(!scope.model.alias){ + scope.model.alias = Math.random().toString(36).slice(2); + } + + scope.$watch("model.view", function(val){ + scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue); + }); + } + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertygroup.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertygroup.directive.js new file mode 100644 index 0000000000..fd2be97b42 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertygroup.directive.js @@ -0,0 +1,9 @@ +angular.module("umbraco.directives.html") + .directive('umbPropertyGroup', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/property/umb-property-group.html' + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabcontainer.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabcontainer.directive.js new file mode 100644 index 0000000000..7635b5d23e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabcontainer.directive.js @@ -0,0 +1,9 @@ +angular.module("umbraco.directives.html") + .directive('umbTabContainer', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/tabs/umb-tab-container.html' + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtablist.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtablist.directive.js new file mode 100644 index 0000000000..a2fd6cdcae --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtablist.directive.js @@ -0,0 +1,9 @@ +angular.module("umbraco.directives.html") + .directive('umbTabList', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/tabs/umb-tab-list.html' + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/editors/contenteditable.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/editors/contenteditable.directive.js new file mode 100644 index 0000000000..08eae2f378 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/editors/contenteditable.directive.js @@ -0,0 +1,35 @@ +angular.module("umbraco.directives") +.directive("contenteditable", function() { + + return { + require: "ngModel", + link: function(scope, element, attrs, ngModel) { + + function read() { + ngModel.$setViewValue(element.html()); + } + + ngModel.$render = function() { + element.html(ngModel.$viewValue || ""); + }; + + + element.bind("focus", function(){ + + var range = document.createRange(); + range.selectNodeContents(element[0]); + + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + + }); + + element.bind("blur keyup change", function() { + scope.$apply(read); + }); + } + + }; + +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbAutoResize.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbAutoResize.directive.js index 7e7d5a7c5a..502030bcb5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbAutoResize.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/editors/umbAutoResize.directive.js @@ -7,11 +7,17 @@ angular.module("umbraco.directives") if(force === true){ element.height(0); + element.width(0); } if(domEl.scrollHeight !== domEl.clientHeight){ element.height(domEl.scrollHeight); } + + if(domEl.scrollWidth !== domEl.clientWidth) { + element.width(domEl.scrollWidth); + } + }; element.bind('keyup keydown keypress change', update); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/events/events.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/events/events.directive.js new file mode 100644 index 0000000000..0077853ac5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/events/events.directive.js @@ -0,0 +1,90 @@ +/** +* @description Utillity directives for key and field events +**/ +angular.module('umbraco.directives') + +.directive('onKeyup', function () { + return function (scope, elm, attrs) { + elm.bind("keyup", function () { + scope.$apply(attrs.onKeyup); + }); + }; +}) + +.directive('onKeydown', function () { + return { + link: function (scope, elm, attrs) { + scope.$apply(attrs.onKeydown); + } + }; +}) + +.directive('onBlur', function () { + return function (scope, elm, attrs) { + elm.bind("blur", function () { + scope.$apply(attrs.onBlur); + }); + }; +}) + +.directive('onFocus', function () { + return function (scope, elm, attrs) { + elm.bind("focus", function () { + scope.$apply(attrs.onFocus); + }); + }; +}) + +.directive('onOutsideClick', function ($timeout) { + return function (scope, element, attrs) { + + function oneTimeClick(event) { + var el = event.target.nodeName; + //ignore link and button clicks + var els = ["INPUT","A","BUTTON"]; + if(els.indexOf(el) >= 0){return;} + + //ignore children of links and buttons + var parents = $(event.target).parents("a,button"); + if(parents.length > 0){ + return; + } + + //ignore clicks inside this element + if( $(element).has( $(event.target) ).length > 0 ){ + return; + } + + scope.$apply(attrs.onOutsideClick); + } + + $timeout(function(){ + $(document).on("click", oneTimeClick); + + scope.$on("$destroy", function() { + $(document).off("click", oneTimeClick); + }); + }, 1000); + + }; +}) + +.directive('onRightClick',function(){ + + document.oncontextmenu = function (e) { + if(e.target.hasAttribute('on-right-click')) { + e.preventDefault(); + e.stopPropagation(); + return false; + } + }; + + return function(scope,el,attrs){ + el.bind('contextmenu',function(e){ + e.preventDefault(); + e.stopPropagation(); + scope.$apply(attrs.onRightClick); + return false; + }); + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index deaa857e94..8408705dd7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -68,6 +68,27 @@ function contentTypeResource($q, $http, umbRequestHelper) { "contentTypeApiBaseUrl", "GetAllPropertyTypeAliases")), 'Failed to retrieve property type aliases'); + }, + + getPropertyTypeScaffold : function (id) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentTypeApiBaseUrl", + "GetPropertyTypeScaffold", + [{ id: id }])), + 'Failed to retrieve property type scaffold'); + }, + + getById: function (id) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentTypeApiBaseUrl", + "GetById", + [{ id: id }])), + 'Failed to retrieve content type'); } }; diff --git a/src/Umbraco.Web.UI.Client/src/less/animations.less b/src/Umbraco.Web.UI.Client/src/less/animations.less deleted file mode 100644 index ee17d37194..0000000000 --- a/src/Umbraco.Web.UI.Client/src/less/animations.less +++ /dev/null @@ -1,191 +0,0 @@ -// Animations -// ------------------------- - -.fade-hide, .fade-show , .fade-leave, .fade-enter { - -webkit-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; - -moz-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; - -o-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; - transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.3s; -} -.fade-enter { - opacity: 1; -} - -.fade-hide.fade-hide-active { - opacity: 0; -} -.fade-leave { - opacity: 0; -} - -.fade-show.fade-show-active { - opacity: 1; -} - -.slide-hide, .slide-show { - -webkit-transition: all cubic-bezier(0.770, 0.000, 0.175, 1.000) 0.5s; - -moz-transition: all cubic-bezier(0.770, 0.000, 0.175, 1.000) 0.5s; - -o-transition: all cubic-bezier(0.770, 0.000, 0.175, 1.000) 0.5s; - transition: all cubic-bezier(0.770, 0.000, 0.175, 1.000) 0.5s; -} - -.slide-hide { - -webkit-transform: translateX(0%); - -moz-transform: translateX(0%); - -ms-transform: translateX(0%); - transform: translateX(0%); -} - -.slide-hide.slide-hide-active { - -webkit-transform: translateX(-100%); - -moz-transform: translateX(-100%); - -ms-transform: translateX(-100%); - transform: translateX(-100%); -} - -.slide-show { - -webkit-transform: translateX(-100%); - -moz-transform: translateX(-100%); - -ms-transform: translateX(-100%); - transform: translateX(-100%); -} - -.slide-show.slide-show-active { - -webkit-transform: translateX(0%); - -moz-transform: translateX(0%); - -ms-transform: translateX(0%); - transform: translateX(0%); -} - -.tree-node-delete-leave { - -webkit-animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); - -moz-animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); - -o-animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); - animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); - display: block; - position: relative; -} - -@-webkit-keyframes leave { - to { - opacity: 0; - height: 0px; - bottom: -70px; - } - 25% { - bottom: 15px; - } - from { - opacity: 1; - height: 30px; - bottom: 0px; - } -} -@-moz-keyframes leave { - to { - opacity: 0; - height: 0px; - bottom: -70px; - } - 25% { - bottom: 15px; - } - from { - opacity: 1; - height: 30px; - bottom: 0px; - } -} -@-ms-keyframes leave { - to { - opacity: 0; - height: 0px; - bottom: -70px; - } - 25% { - bottom: 15px; - } - from { - opacity: 1; - height: 30px; - bottom: 0px; - } -} -@-o-keyframes leave { - to { - opacity: 0; - height: 0px; - bottom: -70px; - } - 25% { - bottom: 15px; - } - from { - opacity: 1; - height: 30px; - bottom: 0px; - } -} -@keyframes leave { - to { - opacity: 0; - height: 0px; - bottom: -70px; - } - 25% { - bottom: 15px; - } - from { - opacity: 1; - height: 30px; - bottom: 0px; - } -} - -.tree-node-delete-leave * { - color:@red !important; -} - - -.tree-node-slide-up -{ - opacity:1; - top: 0px; - -webkit-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; - -moz-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; - -ms-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; - -o-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; - transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; -} -.tree-node-slide-up * { - font-size:100%; - -webkit-transition:font-size 700ms; - -moz-transition:font-size 700ms; - -ms-transition:font-size 700ms; - -o-transition:font-size 700ms; - transition:font-size 700ms; -} -.tree-node-slide-up.tree-node-slide-up-hide-active { - opacity: 0; - top: -100px; -} -.tree-node-slide-up.tree-node-slide-up-hide-active * { - font-size:120%; -} - -.tree-fade-out-hide , -.tree-fade-out-show, -.tree-fade-out-hide div:not(.tree-node-slide-up-hide-active), -.tree-fade-out-show div:not(.tree-node-slide-up-hide-active) { - -webkit-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; - -moz-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; - -ms-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; - -o-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; - transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; -} -.tree-fade-out-show.tree-fade-out-show-active div:not(.tree-node-slide-up-hide-active){ - opacity: 1; -} -.tree-fade-out-hide.tree-fade-out-hide-active div:not(.tree-node-slide-up-hide-active){ - opacity: 0; -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/application/animations.less b/src/Umbraco.Web.UI.Client/src/less/application/animations.less new file mode 100644 index 0000000000..680a0a7f71 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/application/animations.less @@ -0,0 +1,177 @@ +// Animations +// ------------------------- + +//Animate.css - http://daneden.me/animate +//Licensed under the MIT license - http://opensource.org/licenses/MIT +//Copyright (c) 2013 Daniel Eden +.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}40%,43%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-30px,0);-ms-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}70%{-webkit-transition-timing-function:cubic-bezier(0.755,.050,.855,.060);transition-timing-function:cubic-bezier(0.755,.050,.855,.060);-webkit-transform:translate3d(0,-15px,0);-ms-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);-ms-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);-ms-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);-ms-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);-ms-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);-ms-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);-ms-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);-ms-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);-ms-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);-ms-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);-ms-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);-ms-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-transform-origin:top center;-ms-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{0%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg);transform:scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);-ms-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;-ms-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);-ms-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);-ms-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);-ms-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);-ms-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);-ms-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{0%,100%,20%,40%,60%,80%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);-ms-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);-ms-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);-ms-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);-ms-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);-ms-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);-ms-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);-ms-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);-ms-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);-ms-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);-ms-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);-ms-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);-ms-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);-ms-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);-ms-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;-ms-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{0%,100%,60%,75%,90%{-webkit-transition-timing-function:cubic-bezier(0.215,.61,.355,1);transition-timing-function:cubic-bezier(0.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);-ms-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);-ms-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);-ms-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);-ms-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);-ms-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);-ms-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);-ms-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);-ms-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);-ms-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);-ms-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);-ms-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);-ms-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);-ms-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);-ms-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);-ms-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-ms-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);-ms-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;-ms-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);-ms-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);-ms-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);-ms-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);-ms-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);-ms-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);-ms-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg);opacity:1}100%{-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);-ms-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY;-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);-ms-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);-ms-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);-ms-transform:skewX(-5deg);transform:skewX(-5deg);opacity:1}100%{-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{0%{opacity:1}100%{-webkit-transform:translate3d(100%,0,0) skewX(30deg);-ms-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,-200deg);-ms-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);opacity:0}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,45deg);-ms-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);opacity:0}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-90deg);-ms-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);opacity:0}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:none;-ms-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{-webkit-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}@keyframes rotateOut{0%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;opacity:1}100%{-webkit-transform-origin:center;-ms-transform-origin:center;transform-origin:center;-webkit-transform:rotate3d(0,0,1,200deg);-ms-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate(0,0,1,45deg);-ms-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;opacity:1}100%{-webkit-transform-origin:left bottom;-ms-transform-origin:left bottom;transform-origin:left bottom;-webkit-transform:rotate3d(0,0,1,-45deg);-ms-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}@keyframes rotateOutUpRight{0%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;opacity:1}100%{-webkit-transform-origin:right bottom;-ms-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate3d(0,0,1,90deg);-ms-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);-ms-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate3d(0,0,1,60deg);-ms-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-transform-origin:top left;-ms-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}100%{-webkit-transform:translate3d(0,700px,0);-ms-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);-ms-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;-ms-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);-ms-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-ms-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);-ms-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}100%{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);-ms-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;-ms-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);-ms-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);-ms-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;-ms-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-ms-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,.055,.675,.19);animation-timing-function:cubic-bezier(0.55,.055,.675,.19)}100%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-ms-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;-ms-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(0.175,.885,.32,1);animation-timing-function:cubic-bezier(0.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp} + + +.slide-in-left.ng-hide-remove { + -webkit-animation: fadeInLeft 0.6s; + animation: fadeInLeft 0.6s; +} + +.slide-in-left.ng-hide-add { + -webkit-animation: fadeOutLeft 0.6s; + animation: fadeOutLeft 0.6s; + display: block !important; +} + +.slide-in-right.ng-hide-remove { + -webkit-animation: fadeInRight 0.6s; + animation: fadeInRight 0.6s; +} + +.slide-in-right.ng-hide-add { + -webkit-animation: fadeOutRight 0.6s; + animation: fadeOutRight 0.6s; + display: block !important; +} + +.slide-in-up.ng-hide-remove { + -webkit-animation: fadeInUp 0.6s; + animation: fadeInUp 0.6s; +} + +.slide-in-up.ng-hide-add { + -webkit-animation: fadeOutDown 0.6s; + animation: fadeOutDown 0.6s; + display: block !important; +} + + +// TREE ANIMATION + +.tree-node-delete-leave { + -webkit-animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); + -moz-animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); + -o-animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); + animation: leave 600ms cubic-bezier(0.445, 0.050, 0.550, 0.950); + display: block; + position: relative; +} + +@-webkit-keyframes leave { + to { + opacity: 0; + height: 0px; + bottom: -70px; + } + 25% { + bottom: 15px; + } + from { + opacity: 1; + height: 30px; + bottom: 0px; + } +} +@-moz-keyframes leave { + to { + opacity: 0; + height: 0px; + bottom: -70px; + } + 25% { + bottom: 15px; + } + from { + opacity: 1; + height: 30px; + bottom: 0px; + } +} +@-ms-keyframes leave { + to { + opacity: 0; + height: 0px; + bottom: -70px; + } + 25% { + bottom: 15px; + } + from { + opacity: 1; + height: 30px; + bottom: 0px; + } +} +@-o-keyframes leave { + to { + opacity: 0; + height: 0px; + bottom: -70px; + } + 25% { + bottom: 15px; + } + from { + opacity: 1; + height: 30px; + bottom: 0px; + } +} +@keyframes leave { + to { + opacity: 0; + height: 0px; + bottom: -70px; + } + 25% { + bottom: 15px; + } + from { + opacity: 1; + height: 30px; + bottom: 0px; + } +} + +.tree-node-delete-leave * { + color:@red !important; +} + + +.tree-node-slide-up +{ + opacity:1; + top: 0px; + -webkit-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; + -moz-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; + -ms-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; + -o-transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; + transition: 700ms cubic-bezier(0.000, 0.000, 0.580, 1.000) all; +} +.tree-node-slide-up * { + font-size:100%; + -webkit-transition:font-size 700ms; + -moz-transition:font-size 700ms; + -ms-transition:font-size 700ms; + -o-transition:font-size 700ms; + transition:font-size 700ms; +} +.tree-node-slide-up.tree-node-slide-up-hide-active { + opacity: 0; + top: -100px; +} +.tree-node-slide-up.tree-node-slide-up-hide-active * { + font-size:120%; +} + +.tree-fade-out-hide , +.tree-fade-out-show, +.tree-fade-out-hide div:not(.tree-node-slide-up-hide-active), +.tree-fade-out-show div:not(.tree-node-slide-up-hide-active) { + -webkit-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; + -moz-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; + -ms-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; + -o-transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; + transition: 700ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; +} +.tree-fade-out-show.tree-fade-out-show-active div:not(.tree-node-slide-up-hide-active){ + opacity: 1; +} +.tree-fade-out-hide.tree-fade-out-hide-active div:not(.tree-node-slide-up-hide-active){ + opacity: 0; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/grid.less b/src/Umbraco.Web.UI.Client/src/less/application/grid.less similarity index 93% rename from src/Umbraco.Web.UI.Client/src/less/grid.less rename to src/Umbraco.Web.UI.Client/src/less/application/grid.less index 74c2d17f7e..de26bb92c7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/application/grid.less @@ -1,187 +1,187 @@ -// Grid -// ------------------------- - -/* CONTAINS BASIC APPLICATION LAYOUT, POSITIONING AND AREA DIMENSIONS */ - -html, body { - height: 100%; - overflow: hidden; -} - -body { - margin: 0; - padding: 0; - height: 100%; - width: 100%; - - font-family: @baseFontFamily; - font-size: @baseFontSize; - line-height: @baseLineHeight; - color: @textColor; - background-color: @bodyBackground; -} - - -.padded { - padding: 20px -} - - -#layout { - position: relative; - height: 100%; - padding: 0; - z-index: 1; -} - -#mainwrapper { - height: 100%; - width: 100%; - margin: 0; -} - -#contentwrapper, #contentcolumn { - position: absolute; - top: 0px; bottom: 0px; right: 0px; left: 80px; - z-index: 10; - margin: 0 -} - -#contentcolumn { - left: 0px; -} - -#contentcolumn iframe#right { - display: block; - position: relative; - height: 100%; - width: 100%; - border: none; -} - -#leftcolumn { - height: 100%; - z-index: 20; - width: 80px; - float: left; - position: absolute; -} - -#applications { - z-index: 1000; - height: 100%; - left: 0px; - top: 0; - bottom: 0; - position: absolute; - text-align: center -} - -#applications-tray { - z-index: 900; - left: 80px; - top: 0; - bottom: 0; - position: absolute; - height: 100%; - text-align: center; -} - -#search-form { - display: block; - margin: 0px; - z-index: 100; - position: absolute; - top: 0; - left: 0; - right: 0; -} - -#search-form form{ - margin-top: 10px; -} - -#navigation { - left: 80px; - top: 0; - bottom: 0; - position: absolute; - z-index: 100; - background: @white; - height: 100%; - -} - -.navigation-inner-container{ - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - padding-top: 100px; - border-right: 1px solid @grayLight; -} - -#dialog { - min-width: 500px; - left: 100%; - top: 0; - position: absolute; - z-index: 50; - display: inline-block; -} - -#tree { - padding: 0px; - z-index: 100 !important; - overflow: auto; -} - -#tree .umb-tree { - padding: 0px 0px 20px 0px; -} - -#search-results { - z-index: 200; -} - -#contextMenu { - z-index: 50; - position: absolute; - top: 0px; - left: 100%; - min-width: 250px; -} - -#speechbubble { - z-index: 1000; - position: absolute; - bottom: 100px; - left: 0; - right: 0; - border-bottom: none; - margin: auto; - padding: 0px; - border: none; - background: none; - border-radius: 0; -} - -@media (min-width: 1101px) { - #contentwrapper {left: 440px;} - #speechbubble {left: 360px;} -} - -//empty section modification -.emptySection #contentwrapper {left: 80px;} -.emptySection #speechbubble {left: 0;} -.emptySection #navigation {display: none} - - -.login-only #speechbubble { - z-index: 10000; - left: 0 !important; -} -.login-only #speechbubble ul { - padding-left:20px +// Grid +// ------------------------- + +/* CONTAINS BASIC APPLICATION LAYOUT, POSITIONING AND AREA DIMENSIONS */ + +html, body { + height: 100%; + overflow: hidden; +} + +body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + + font-family: @baseFontFamily; + font-size: @baseFontSize; + line-height: @baseLineHeight; + color: @textColor; + background-color: @bodyBackground; +} + + +.padded { + padding: 20px +} + + +#layout { + position: relative; + height: 100%; + padding: 0; + z-index: 1; +} + +#mainwrapper { + height: 100%; + width: 100%; + margin: 0; +} + +#contentwrapper, #contentcolumn { + position: absolute; + top: 0px; bottom: 0px; right: 0px; left: 80px; + z-index: 10; + margin: 0 +} + +#contentcolumn { + left: 0px; +} + +#contentcolumn iframe#right { + display: block; + position: relative; + height: 100%; + width: 100%; + border: none; +} + +#leftcolumn { + height: 100%; + z-index: 20; + width: 80px; + float: left; + position: absolute; +} + +#applications { + z-index: 1000; + height: 100%; + left: 0px; + top: 0; + bottom: 0; + position: absolute; + text-align: center +} + +#applications-tray { + z-index: 900; + left: 80px; + top: 0; + bottom: 0; + position: absolute; + height: 100%; + text-align: center; +} + +#search-form { + display: block; + margin: 0px; + z-index: 100; + position: absolute; + top: 0; + left: 0; + right: 0; +} + +#search-form form{ + margin-top: 10px; +} + +#navigation { + left: 80px; + top: 0; + bottom: 0; + position: absolute; + z-index: 100; + background: @white; + height: 100%; + +} + +.navigation-inner-container{ + position: absolute; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + padding-top: 100px; + border-right: 1px solid @grayLight; +} + +#dialog { + min-width: 500px; + left: 100%; + top: 0; + position: absolute; + z-index: 50; + display: inline-block; +} + +#tree { + padding: 0px; + z-index: 100 !important; + overflow: auto; +} + +#tree .umb-tree { + padding: 0px 0px 20px 0px; +} + +#search-results { + z-index: 200; +} + +#contextMenu { + z-index: 50; + position: absolute; + top: 0px; + left: 100%; + min-width: 250px; +} + +#speechbubble { + z-index: 1000; + position: absolute; + bottom: 100px; + left: 0; + right: 0; + border-bottom: none; + margin: auto; + padding: 0px; + border: none; + background: none; + border-radius: 0; +} + +@media (min-width: 1101px) { + #contentwrapper {left: 440px;} + #speechbubble {left: 360px;} +} + +//empty section modification +.emptySection #contentwrapper {left: 80px;} +.emptySection #speechbubble {left: 0;} +.emptySection #navigation {display: none} + + +.login-only #speechbubble { + z-index: 10000; + left: 0 !important; +} +.login-only #speechbubble ul { + padding-left:20px } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/application/shadows.less b/src/Umbraco.Web.UI.Client/src/less/application/shadows.less new file mode 100644 index 0000000000..3df8ca5758 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/application/shadows.less @@ -0,0 +1,15 @@ +.shadow-depth-1{ + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); +} +.shadow-depth-2{ + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +.shadow-depth-3{ + box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); +} +.shadow-depth-4{ + box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); +} +.shadow-depth-5{ + box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22); +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 79b16605ad..358825c8a3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -7,6 +7,7 @@ * */ + // Core variables and mixins @import "fonts.less"; // Loading fonts @import "variables.less"; // Modify this for custom colors, font-sizes, etc @@ -43,7 +44,6 @@ @import "../../lib/bootstrap/less/pagination.less"; @import "../../lib/bootstrap/less/pager.less"; - // Components: Popovers @import "../../lib/bootstrap/less/modals.less"; @import "../../lib/bootstrap/less/tooltip.less"; @@ -59,12 +59,17 @@ @import "../../lib/bootstrap/less/carousel.less"; @import "../../lib/bootstrap/less/hero-unit.less"; + // Utility classes @import "../../lib/bootstrap/less/utilities.less"; // Has to be last to override when necessary + +// Application wide styles (refactor is WIP) +@import "application/grid.less"; +@import "application/shadows.less"; +@import "application/animations.less"; + // Belle styles -@import "grid.less"; -@import "login.less"; @import "buttons.less"; @import "forms.less"; @import "modals.less"; @@ -76,12 +81,24 @@ @import "listview.less"; @import "gridview.less"; @import "footer.less"; -@import "animations.less"; @import "dragdrop.less"; @import "dashboards.less"; + +// Umbraco Components +@import "components/editor.less"; +@import "components/overlays.less"; +@import "components/card.less"; + + +//page specific styles +@import "pages/documentTypeEditor.less"; +@import "login.less"; + + //used for property editors @import "property-editors.less"; + @import "typeahead.less"; @import "hacks.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/card.less b/src/Umbraco.Web.UI.Client/src/less/components/card.less new file mode 100644 index 0000000000..9fc3500600 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/card.less @@ -0,0 +1,160 @@ +/* + Library of card related compoents, like the right-hand icon list on the grid "cards" +*/ + +.umb-card{ + position: relative; + padding: 5px 10px 5px 10px; + background: white; + + .title{padding: 12px; color: @gray; border-bottom: 1px solid @grayLight; font-weight: 400; font-size: 16px; text-transform: none; margin: 0 -10px 10px -10px;} + +} + +.umb-card-thumb{ + text-align: center; + + i{ + text-align: center; + font-size: 20px; + line-height: 40px; + color: @blue; + display: block; + padding-top: 5px + } +} + +.umb-card-content{ + .item-title{color: @blackLight; font-weight: 400; border: none; font-size: 16px; text-transform: none; margin-bottom: 3px;} + p{color: @gray; margin-bottom: 1px;} +} + +.umb-card-actions{ + padding-top: 10px; + border-top: @grayLighter 1px solid; + clear: both; +} + +.umb-card-icons{ + text-align: center; + vertical-align: center; + display: block; + list-style: none; + margin: 0; + padding: 0; +} + +.umb-card-icons.vertical{ + position: absolute; + top: 7px; + right: 7px; + text-align: right; + width: 1px; +} + +.umb-card-icons li{ + display: inline-block; + margin: 0 2px 0 2px; +} + +.umb-card-icons.vertical li{ + float: right; + display: block; + margin-bottom: 3px; +} + +//card iocn list +.umb-card-list{ + display: block; + padding: 0; + margin: 0; + } + +.umb-card-list li{ + border-bottom: @grayLighter 1px solid; + padding-bottom: 3px; + display: block; +} + + + + +//Card icon grid for picking items off a card +.umb-card-grid{ + display: block; + padding: 0; + margin: 0; + } + +.umb-card-grid li{ + display: inline-block; + width: 90px; + height: 80px; + margin: 5px; + padding: 5px; + overflow: hidden; + font-size: 11px; + text-align: center; + } + +.umb-card-grid li:hover, .umb-card-grid li:hover *{ + background: @blue; + color: white; + } + +.umb-card-grid a{ + color: #222; + text-decoration: none; + } + +.umb-card-grid i{ + font-size: 30px; + line-height: 50px; + color: #999; + display: block + } + + + + + +//Round icon-like button - this should be somewhere else +.umb-btn-round{ + padding: 4px 6px 4px 6px; + display: inline-block; + cursor: pointer; + border-radius: 200px; + background: rgba(255,255,255, 1); + border:1px solid rgb(182, 182, 182); + margin: 2px; +} + +.umb-btn-round:hover, .umb-btn-round:hover *{ + background: @blue !important; + color: white !important; + border-color: @blue !important; + text-decoration:none; + } + + .umb-btn-round a:hover { + text-decoration:none; + color: white !important; + } + + .umb-btn-round i { + font-size:16px !important; + color: #grayLight; + display:block; + } + + .umb-btn-round.alert:hover, .umb-btn-round.alert:hover *{ + background: @red !important; + color: white !important; + border-color: @red !important; + text-decoration:none; + } + +.umb-btn-round.no-border{ + border: none !important; + background: none !important; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less new file mode 100644 index 0000000000..9b585fe53e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -0,0 +1,53 @@ +/* + contains styling for all main editor directives +*/ + +.umb-editor-wrapper{ + background: white; + position: absolute; + top: 0px; bottom: 0px; left: 0px; right: 0px; +} + + +.umb-editor-header{ + background: @grayLighter; + border-bottom: 1px solid @grayLight; + position: absolute; + height: 99px; + top: 0px; + right: 0px; + left: 0px; +} + + +.umb-editor-container{ + top: 101px; + left: 0px; + right: 0px; + bottom: 60px; + position: absolute; + clear: both; + overflow: auto; +} + + +.umb-editor-drawer{ + margin: 0; + padding: 20px; + z-index: 999; + position: absolute; + bottom: 0px; + left: 0px; + right: 0px; + height: 20px; +} + + +.umb-editor-actions{ + list-style: none; + margin: 0; padding: 0; +} + +.umb-editor-actions li{ + display: inline-block; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less new file mode 100644 index 0000000000..d9ae70afbd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -0,0 +1,65 @@ + +// CENTERED OVERLAY +.umb-overlay-center{ + position: absolute; + width: 460px; + height: 400px; + overflow: hidden; + border: 1px solid #ccc; + margin-top: -200px; + margin-left: -230px; + background: white; + padding: 7px; + top: 50%; + left: 50%; + z-index: 996660; + + .umb-overlay-header{ + border-bottom: 1px solid #d9d9d9; + color: #999; + padding: 10px; + margin-top: 0; + } + + .umb-overlay-container{ + top: 50px; left: 7px; right: 7px; bottom: 7px; position: absolute; overflow: auto; + } + } + + + + +// RIGHT SIDE OVERLAY +.umb-overlay-right{ + position: fixed; + width: 460px; + overflow: hidden; + background: white; + top: 0; + right: 0; + bottom: 0; + z-index: 996660; + + .umb-overlay-header{ + height: 64px; + background: @grayLighter; + border-bottom: 1px solid @grayLight; + padding: 15px 10px 20px 20px; + } + + .umb-overlay-container{ + top: 100px; left: 0px; right: 0px; bottom: 50px; position: absolute; overflow: auto; + } + + .umb-overlay-drawer{ + height: 50px; left: 0px; right: 0px; bottom: 0px; position: absolute; overflow: auto; + padding: 10px; + text-align: right; + } + + .umb-overlay-body{ + padding: 20px; + } +} + + diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less b/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less new file mode 100644 index 0000000000..18bc9d8441 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less @@ -0,0 +1,387 @@ + +.editors-document-type-container{ + + .handle{visibility: hidden;} + .item-title{color: @blackLight; text-transform: none; font-size: 16px; padding-right: 30px;} + .item-alias{color: @grayLight; font-weight: 400; font-size: 14px;} + + li{list-style: none} + + + .fieldset-title{ + border-bottom: 1px solid @grayLight; + border-top: 1px solid @grayLight; + background: @grayLighter; + font-size: 16px; + text-transform: none; + padding: 7px 10px 7px 10px; + margin-left: -30px; + margin-right: -10px; + font-weight: 400 + } + + + .tab-title{ + padding: 0 25px 0 25px; + position: absolute; + left: -2px; + top: -36px; + height: 34px; + background: white; + border: 2px solid #cccccc; + border-bottom: none; + border-radius: 5px 5px 0 0; + -webkit-transition: width 0.5s; /* Safari */ + transition: width 0.5s; + font-weight: bold; + .tab-title-input { + border-color: #ffffff; + width: 27px; + font-weight: bold; + &:hover { + border-color: @inputBorder; + } + } + .tab-title-text { + position: relative; + top: 10px; + font-size: 14px; + } + } + + .editors-document-type-canvas{ + /* + min-height: 200px; + position: absolute; + top: 0; + right: 0; + left: 0; + + bottom: 0; + overflow: auto; + */ + padding: 40px 80px; + + } + + .editors-document-type-sidebar{ + padding: 20px; + top: 0; + left: 0; + width: 340px; + bottom: 0; + overflow: auto; + border-right: 1px solid #d9d9d9; + background: rgba(248, 248, 248, 0.06); + position: absolute; + display: none; + } + + .no-style-list{ + padding: 0; + margin: 0; + list-style: none; + } + + .edt-props-sortable{min-height: 5px} + + .properties-placeholder{ + //text-align: center; + background: @grayLighter; + border: 1px dashed @grayLight; + padding: 20px; + margin: 30px 20px 30px 20px; + } + +} + +/* ---------- TABS ---------- */ + +.edt-tab{ + margin: 50px 20px 70px 20px; + border: 2px solid #cccccc; + padding: 0; + box-shadow: 0 5px 1px 0 rgba(0,0,0,0.1); + border-radius: 0 5px 5px 5px; + &:hover { + .edt-tab-actions { + visibility: visible; + opacity: 1; + -webkit-transition: opacity 0.5s; /* Safari */ + transition: opacity 0.5s; + } + } + .edt-tab-actions { + position: absolute; + top: 5px; + right: 5px; + visibility: hidden; + opacity: 0; + z-index: 10; + .tab-action { + display: inline-block; + } + } +} + +.edt-tab-center-text { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + text-align: center; +} + +.edt-tab:hover > h4 > .handle{ + visibility: visible; +} + +.edt-property-group{ + position: relative; + padding: 60px 20px 0 20px; +} + +.edt-property-group:hover > h5 > .handle{ + visibility: visible; +} + +.edt-property-group-sizer{ + bottom: 0; + top: 0; + right: -10px; + width: 15px; + position: absolute; + cursor: e-resize; +} + +.span12 .edt-property-group-sizer{ + cursor: w-resize; +} + + +.edt-props-sortable{ + margin-left: 0px; +} + +// tab is in init state +.edt-tab.tab-state-init { + cursor: pointer; + border: 2px dashed @grayLight; + height: 100px; + box-shadow: none !important; + .tab-title{ + border: 2px dashed @grayLight; + border-bottom: none; + } + &:hover { + //border-style: solid; + .tab-title { + //border-style: solid; + //border-bottom-style: none; + } + } +} + + +// tab is active +.edt-tab.tab-state-active { + + .edt-property-group{ + padding-top: 60px; + } + + .edt-property { + margin-bottom: 20px + } +} + + +// tab is inactive +.edt-tab.tab-state-inactive { + + cursor: pointer; + + .edt-property-group{ + padding-top: 20px; + } + + .edt-property { + height: 50px; + margin-bottom: 10px; + background: @grayLighter; + color: @grayDarker; + border-radius: @baseBorderRadius; + padding-top: 0; + padding-bottom: 0; + } + + .edt-property-meta { + margin: 15px 0 0 10px; + } + + .edt-property-actions { + width: 150px; + text-align: right; + margin: 7px 10px 0 0; + .property-action { + display: inline-block; + } + } + + .ui-sortable-placeholder { + height: 50px !important; + padding: 0 !important; + margin-bottom: 10px; + } + + .edt-property-actions { + .property-action { + display: inline-block; + } + } + +} + +/* ---------- PROPERTY ---------- */ + +.edt-property{ + position: relative; + display: flex; + flex-flow: row; + padding: 20px 30px; + /* + &:hover, + &.active { + background: @grayLighter; + -webkit-transition: background 0.5s; + transition: background 0.5s; + } + */ +} + +// property state init - property placeholder +.edt-property.property-state-init { + border: 2px dashed @grayLight; + border-radius: 5px; + cursor: pointer; +} + +.edt-property-meta { + width: 300px; + .property-meta-group { + display: inline-block; + } +} + +.edt-property-inner{ + flex: 2; + max-height: 100px; + overflow: hidden; + position: relative; + /* + opacity: 0.7; + padding: 10px; + border: 1px dashed @grayLight; + */ + /* + &:hover { + cursor: pointer; + .edt-property-inner-overlay { + display: block; + } + } + */ + div { + padding-top: 10px; + } +} + +.edt-property-actions { + width: 50px; + text-align: center; + visibility: hidden; + opacity: 0; +} + +.edt-property:hover { + .edt-property-actions { + visibility: visible; + opacity: 1; + -webkit-transition: opacity 0.5s; /* Safari */ + transition: opacity 0.5s; + } +} + +.edt-property-inner-overlay { + background-color: @grayLighter; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10; + display: none; + .overlay-text { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: @grayDark; + } +} + +.edt-property-label{ + font-size: 11px; + position: absolute; + top: 0px; + left: 0px; + text-transform: uppercase; + z-index: 15; + background: @grayLighter; + padding: 3px; + line-height: 12px; + opacity: 0.8 +} + +.edt-property-inner:after { + content:""; + position:absolute; + top:0px; + left:0; + height:100%; + width:100%; + background: @grayLighter; + opacity: 0.3; +} + +.invisible-input { + border-color: transparent; + &:hover { + border-color: @inputBorder; + } +} + +/* ---------- PLACEHOLDERS ----------- */ + +.placeholder { + background: #e5e5e5; + display: block; + width: 100%; + margin-bottom: 5px; + height: 50px; +} + +.placeholder-input-small { + background: #e5e5e5; + display: block; + height: 15px; + width: 200px; + margin-bottom: 5px; +} + +.placeholder-input { + background: #cccccc; + display: block; + height: 25px; + width: 200px; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html new file mode 100644 index 0000000000..5dd97b28a5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html @@ -0,0 +1,5 @@ +
+
+
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-footer.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-footer.html new file mode 100644 index 0000000000..8b798b5c77 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-footer.html @@ -0,0 +1,7 @@ +
+ + +
+ +
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html new file mode 100644 index 0000000000..5c7bfd9e89 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -0,0 +1,19 @@ +
+
+
+ +
+ +
+
+ + +
+
+ +
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-view.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-view.html new file mode 100644 index 0000000000..4babd85937 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-view.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/field/umb-field.html b/src/Umbraco.Web.UI.Client/src/views/components/field/umb-field.html new file mode 100644 index 0000000000..0cc3a6ec1c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/field/umb-field.html @@ -0,0 +1,13 @@ +
+
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-actions.html b/src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-actions.html new file mode 100644 index 0000000000..231f56b0a4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-actions.html @@ -0,0 +1,5 @@ +
    +
  • + +
  • +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-menu.html b/src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-menu.html new file mode 100644 index 0000000000..ba9a6276a2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/navigation/umb-editor-menu.html @@ -0,0 +1,22 @@ +
+ + + + Actions + + + + + +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html new file mode 100644 index 0000000000..1d579c88cb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html @@ -0,0 +1,16 @@ +
+ +
+

{{model.title}}

+
+ +
+
+
+
+ +
+ + +
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html new file mode 100644 index 0000000000..b191662ad1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-group.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-group.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html new file mode 100644 index 0000000000..7de6130230 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -0,0 +1,19 @@ +
+ +
+ + + +
+ + + +
+
+
+
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js index 55af70f287..6254894075 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js @@ -1,65 +1,371 @@ /** * @ngdoc controller - * @name Umbraco.Editors.ContentType.EditController + * @name Umbraco.Editors.DocumentType.EditController * @function - * + * * @description * The controller for the content type editor */ -function ContentTypeEditController($scope, $routeParams, $log, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, entityResource) { - - $scope.tabs = []; - $scope.page = {}; - $scope.contentType = {tabs: [], name: "My content type", alias:"myType", icon:"icon-folder", allowedChildren: [], allowedTemplate: []}; - $scope.contentType.tabs = [ - {name: "Content", properties:[ {name: "test"}]}, - {name: "Generic Properties", properties:[]} - ]; +function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, dataTypeResource) { + $scope.page = {action: [], menu: [] }; + //$rootScope.emptySection = true; + contentTypeResource.getById($routeParams.id).then(function(dt){ - - $scope.dataTypesOptions ={ - group: "properties", - onDropHandler: function(item, args){ - args.sourceScope.move(args); - }, - onReleaseHandler: function(item, args){ - var a = args; - } - }; + $scope.contentType = dt; - $scope.tabOptions ={ - group: "tabs", - drop: false, - nested: true, - onDropHandler: function(item, args){ - - }, - onReleaseHandler: function(item, args){ - - } - }; + // set all tab to active + if( $scope.contentType.groups.length !== 0 ) { + angular.forEach($scope.contentType.groups, function(group){ + group.tabState = "active"; + }); + } - $scope.propertiesOptions ={ - group: "properties", - onDropHandler: function(item, args){ - //alert("dropped on properties"); - //args.targetScope.ngModel.$modelValue.push({name: "bong"}); - }, - onReleaseHandler: function(item, args){ - //alert("released from properties"); - //args.targetScope.ngModel.$modelValue.push({name: "bong"}); - }, - }; + addInitTab(); - - $scope.omg = function(){ - alert("wat"); - }; - - entityResource.getAll("Datatype").then(function(data){ - $scope.page.datatypes = data; }); + + //hacking datatypes and their icons + dataTypeResource.getAll().then(function(data){ + + data = _.groupBy(data, function(dt){ + dt.icon = "icon-autofill"; + + if(dt.name.indexOf("Dropdown") > -1 || dt.name.indexOf("Checkbox") > -1){ + dt.icon = "icon-bulleted-list"; + return "Lists"; + } + + if(dt.name.indexOf("Grid") > -1 || dt.name.indexOf("List View") > -1){ + dt.icon = "icon-item-arrangement"; + return "Collections"; + } + + if(dt.name.indexOf("picker") > -1){ + dt.icon ="icon-hand-pointer-alt"; + return "Pickers"; + } + + if(dt.name.indexOf("media") > -1 || dt.name.indexOf("Upload") > -1 || dt.name.indexOf("Crop") > -1){ + dt.icon ="icon-picture"; + return "Media"; + } + + return "Fields"; + }); + + $scope.dataTypes = data; + }); + + $scope.actions = [{name: "Structure", cssClass: "list"},{name: "Structure", cssClass: "list"},{name: "Structure", cssClass: "list"}]; + + + /* ---------- TABS ---------- */ + + $scope.addTab = function(tab){ + + tab.tabState = "active"; + + // push new init tab to the scope + addInitTab; + + }; + + $scope.deleteTab = function(tabIndex) { + $scope.contentType.groups.splice(tabIndex, 1); + }; + + $scope.activateTab = function(tab) { + tab.tabState = "active"; + }; + + $scope.updateTabTitle = function(tab) { + if(tab.properties.length === 0) { + addInitProperty(tab); + } + }; + + function addInitTab() { + + // check i init tab already exists + var addTab = true; + + angular.forEach($scope.contentType.groups, function(group){ + if(group.tabState === "init") { + addTab = false; + } + }); + + if(addTab) { + $scope.contentType.groups.push({ + groups: [], + properties:[], + tabState: "init" + }); + } + } + + function addInitProperty(tab) { + tab.properties.push({ + propertyState: "init" + }); + } + + /* ---------- PROPERTIES ---------- */ + + /* + $scope.addProperty = function(properties){ + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add property type"; + $scope.dialogModel.datatypes = $scope.dataTypes; + $scope.dialogModel.addNew = true; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + $scope.dialogModel.close = function(model){ + properties.push(model.property); + $scope.dialogModel = null; + }; + }; + */ + + $scope.changePropertyName = function(property) { + + var str = property.label; + + // capitalize all words + str = str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); + + // remove spaces + str = str.replace(/\s/g, ''); + + property.alias = str; + + }; + + $scope.toggleGroupSize = function(group){ + if(group.columns !== 12){ + group.columns = 12; + }else{ + group.columns = 6; + } + }; + + $scope.editPropertyTypeSettings = function(property) { + $scope.dialogModel = {}; + $scope.dialogModel.title = "Edit property type settings"; + $scope.dialogModel.property = property; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/editPropertySettings/editPropertySettings.html"; + $scope.showDialog = true; + + // set indicator on property to tell the dialog is open - is used to set focus on the element + property.dialogIsOpen = true; + + $scope.dialogModel.submit = function(dt){ + + /* + contentTypeResource.getPropertyTypeScaffold(dt.id) + .then(function(pt){ + property.config = pt.config; + property.editor = pt.editor; + property.view = pt.view; + $scope.dialogModel = null; + $scope.showDialog = false; + }); + */ + + property.dialogIsOpen = false; + + $scope.showDialog = false; + $scope.dialogModel = null; + + }; + + /* + $scope.dialogModel.submit = function(){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + */ + + $scope.dialogModel.close = function(model){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + + }; + + $scope.choosePropertyType = function(property, tab) { + + console.log(tab); + + $scope.showDialog = true; + $scope.dialogModel = {}; + $scope.dialogModel.title = "Choose property type"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + $scope.showDialog = false; + }; + + $scope.dialogModel.submit = function(dt){ + contentTypeResource.getPropertyTypeScaffold(dt.id).then(function(pt){ + + property.config = pt.config; + property.editor = pt.editor; + property.view = pt.view; + property.dataType = dt; + + property.propertyState = "active"; + + // open settings dialog + $scope.editPropertyTypeSettings(property); + + // push new init property to scope + //addInitProperty(tab); + + // push new init tab to scope + addInitTab(); + + }); + }; + + }; + + $scope.addItems = function(tab){ + + $scope.showDialog = true; + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add some stuff"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + var target = tab; + if(tab.groups && tab.groups.length > 0){ + target = _.last(tab.groups); + } + + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + $scope.showDialog = false; + }; + + $scope.dialogModel.submit = function(dt){ + contentTypeResource.getPropertyTypeScaffold(dt.id).then(function(pt){ + + pt.label = dt.name + " field"; + pt.dataType = dt; + target.properties.push(pt); + + // open settings dialog + $scope.editPropertyTypeSettings(pt); + + }); + }; + }; + + $scope.deleteProperty = function(tab, propertyIndex) { + tab.properties.splice(propertyIndex, 1); + }; + + /* + $scope.addProperty = function(group){ + $log.log("open dialog"); + + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add property type"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + }; + }; + */ + + + + + + $scope.sortableOptionsFieldset = { + distance: 10, + revert: true, + tolerance: "pointer", + opacity: 0.7, + scroll:true, + cursor:"move", + placeholder: "ui-sortable-placeholder", + connectWith: ".edt-tabs", + handle: ".handle", + zIndex: 6000, + start: function (e, ui) { + ui.placeholder.addClass( ui.item.attr("class") ); + }, + stop: function(e, ui){ + ui.placeholder.remove(); + } + }; + + + $scope.sortableOptionsEditor = { + distance: 10, + revert: true, + tolerance: "pointer", + connectWith: ".edt-props-sortable", + opacity: 0.7, + scroll: true, + cursor: "move", + handle: ".edt-property-handle", + placeholder: "ui-sortable-placeholder", + zIndex: 6000, + start: function (e, ui) { + + // set all tabs to inactive to collapse all content + angular.forEach($scope.contentType.groups, function(tab){ + $scope.$apply(function () { + + tab.tabIsActive = false; + + }); + }); + + }, + stop: function(e, ui){ + console.log(e); + console.log(ui); + } + }; + + $scope.sortableOptionsTab = { + distance: 10, + revert: true, + tolerance: "pointer", + opacity: 0.7, + scroll:true, + cursor:"move", + placeholder: "ui-sortable-placeholder", + zIndex: 6000, + handle: ".edt-tab-handle", + start: function (e, ui) { + + // set all tabs to inactive to collapse all content + angular.forEach($scope.contentType.groups, function(tab){ + $scope.$apply(function () { + + tab.tabIsActive = false; + + }); + }); + + }, + stop: function(e, ui){ + console.log(e); + console.log(ui); + } + }; + } -angular.module("umbraco").controller("Umbraco.Editors.ContentType.EditController", ContentTypeEditController); \ No newline at end of file +angular.module("umbraco").controller("Umbraco.Editors.DocumentType.EditController", DocumentTypeEditController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html b/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html index b99e2bd3fd..3ac3922a29 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html @@ -1,74 +1,318 @@ -
- - - -
- - -
-
-
- -
- -
-
-
-
+ - - -
-
- -
-
    -
  • -
    {{tab.name}}
    + + -
      -
    • - {{property.name}} -
    • -
    + -
  • -
+
+ +
    + +
  • + + + +
    + +
    + Tab +
    + +
    + Add first tab + Add new tab +
    - {{page.datatypes | json}}
    -
    -
    -
- - -
- + - -
-
- - - +
  • +
    {{ contentType | json }}
    +
  • + + + +
    + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.controller.js new file mode 100644 index 0000000000..bc3a363e63 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.controller.js @@ -0,0 +1,64 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ +function EditDataTypeController($scope, dataTypeResource) { + + + editDataType($scope.model.property.dataType.id); + + function editDataType(dataTypeId) { + + dataTypeResource.getById(dataTypeId) + .then(function(data) { + + console.log(data); + + //$scope.loaded = true; + //$scope.preValuesLoaded = true; + //$scope.content = data; + + $scope.model.property.dataType.preValues = data.preValues; + + createPreValueProps($scope.model.property.dataType.preValues); + + //share state + //editorState.set($scope.content); + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + //serverValidationManager.executeAndClearAllSubscriptions(); + + /* + navigationService.syncTree({ tree: "datatype", path: [String(data.id)] }).then(function (syncArgs) { + $scope.currentNode = syncArgs.node; + }); + */ + }); + + } + + function createPreValueProps(preVals) { + $scope.preValues = []; + for (var i = 0; i < preVals.length; i++) { + $scope.preValues.push({ + hideLabel: preVals[i].hideLabel, + alias: preVals[i].key, + description: preVals[i].description, + label: preVals[i].label, + view: preVals[i].view, + value: preVals[i].value + }); + } + } + + +} + +angular.module("umbraco").controller("Umbraco.Editors.DocumentType.EditDataTypeController", EditDataTypeController); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.html new file mode 100644 index 0000000000..b5761d16e9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editDataType/editDataType.html @@ -0,0 +1,16 @@ +
    +
    +
    Change configuration for data types here
    + + + + + + + +
    {{ model | json }}
    +
    +
    + diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.controller.js new file mode 100644 index 0000000000..31ebafda0a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.controller.js @@ -0,0 +1,68 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ +function EditPropertySettingsController($scope, contentTypeResource) { + + $scope.changePropertyEditor = function(property){ + + $scope.dialogModel = {}; + $scope.dialogModel.title = "Change property type"; + $scope.dialogModel.dataTypes = $scope.model.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + $scope.showDialog = true; + + $scope.dialogModel.submit = function(dt){ + contentTypeResource.getPropertyTypeScaffold(dt.id) + .then(function(pt){ + + // save data to property + property.config = pt.config; + property.editor = pt.editor; + property.view = pt.view; + property.dataType = dt; + + // close dialog + $scope.dialogModel = null; + $scope.showDialog = false; + + }); + }; + + $scope.dialogModel.close = function(model){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + }; + + $scope.editDataType = function(property) { + + $scope.dialogModel = {}; + $scope.dialogModel.title = "Edit data type"; + $scope.dialogModel.property = property; + $scope.dialogModel.view = "views/documentType/dialogs/editDataType/editDataType.html"; + $scope.showDialog = true; + + $scope.dialogModel.submit = function(dt){ + alert("submit from edit data type"); + $scope.showDialog = false; + $scope.dialogModel = null; + }; + + $scope.dialogModel.close = function(model){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + + } + + + + +} + +angular.module("umbraco").controller("Umbraco.Editors.DocumentType.EditPropertySettingsController", EditPropertySettingsController); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.html new file mode 100644 index 0000000000..e8aabd9ce7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/editPropertySettings/editPropertySettings.html @@ -0,0 +1,83 @@ +
    +
    + +
    + +
    + +
    Property settings
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    Validation
    + +
    + + +
    + +
    + +
    + +
    +
    + +
    Property type
    + +
    +
    + + {{ model.property.dataType.name }} + {{ model.property.view }} +
    + +
    + +
    {{ model | json }}
    + +
    + +
    + +
    + + + + + +
    + diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js new file mode 100644 index 0000000000..183681b294 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js @@ -0,0 +1,24 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ +function DocumentTypePropertyController($scope, contentTypeResource) { + + /* + $scope.selectDataType = function(dataType, model){ + contentTypeResource.getPropertyTypeScaffold(dataType.id) + .then(function(pt){ + model.property.config = pt.config; + model.property.editor = pt.config; + model.property.view = pt.view; + $scope.closeOverLay(); + }); + }; + */ +} + +angular.module("umbraco").controller("Umbraco.Editors.DocumentType.PropertyController", DocumentTypePropertyController); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html new file mode 100644 index 0000000000..2733f7f439 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html @@ -0,0 +1,34 @@ +
    + + + +
    +
    {{key}}
    + +
    +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js new file mode 100644 index 0000000000..897061f59d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js @@ -0,0 +1,315 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.EditController + * @function + * + * @description + * The controller for the content type editor + */ +function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, dataTypeResource) { + $scope.page = {action: [], menu: [] }; + //$rootScope.emptySection = true; + + contentTypeResource.getById($routeParams.id).then(function(dt){ + + $scope.contentType = dt; + + // set first tab to active + if( $scope.contentType.groups.length !== 0 ) { + $scope.contentType.groups[0].tabIsActive = true; + } + + }); + + //hacking datatypes and their icons + dataTypeResource.getAll().then(function(data){ + + data = _.groupBy(data, function(dt){ + dt.icon = "icon-autofill"; + + if(dt.name.indexOf("Dropdown") > -1 || dt.name.indexOf("Checkbox") > -1){ + dt.icon = "icon-bulleted-list"; + return "Lists"; + } + + if(dt.name.indexOf("Grid") > -1 || dt.name.indexOf("List View") > -1){ + dt.icon = "icon-item-arrangement"; + return "Collections"; + } + + if(dt.name.indexOf("picker") > -1){ + dt.icon ="icon-hand-pointer-alt"; + return "Pickers"; + } + + if(dt.name.indexOf("media") > -1 || dt.name.indexOf("Upload") > -1 || dt.name.indexOf("Crop") > -1){ + dt.icon ="icon-picture"; + return "Media"; + } + + return "Fields"; + }); + + $scope.dataTypes = data; + }); + + $scope.actions = [{name: "Structure", cssClass: "list"},{name: "Structure", cssClass: "list"},{name: "Structure", cssClass: "list"}]; + + + /* ---------- TABS ---------- */ + + $scope.addTab = function(){ + + // set all tabs to inactive + angular.forEach($scope.contentType.groups, function(group){ + group.tabIsActive = false; + }); + + // push tab + $scope.contentType.groups.push({ + groups: [], + properties:[], + tabIsActive: true + }); + + }; + + $scope.deleteTab = function(tabIndex) { + + $scope.contentType.groups.splice(tabIndex, 1); + + // activate previous tab + if( $scope.contentType.groups.length === 1 ) { + $scope.contentType.groups[0].tabIsActive = true; + } + + }; + + $scope.activateTab = function(tab) { + + // set all tabs to inactive + angular.forEach($scope.contentType.groups, function(group){ + group.tabIsActive = false; + }); + + // activate tab + tab.tabIsActive = true; + + }; + + /* ---------- PROPERTIES ---------- */ + + $scope.addProperty = function(properties){ + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add property type"; + $scope.dialogModel.datatypes = $scope.dataTypes; + $scope.dialogModel.addNew = true; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + $scope.dialogModel.close = function(model){ + properties.push(model.property); + $scope.dialogModel = null; + }; + }; + + $scope.changePropertyName = function(property) { + + var str = property.label; + + // capitalize all words + str = str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); + + // remove spaces + str = str.replace(/\s/g, ''); + + property.alias = str; + + }; + + $scope.toggleGroupSize = function(group){ + if(group.columns !== 12){ + group.columns = 12; + }else{ + group.columns = 6; + } + }; + + $scope.editPropertyTypeSettings = function(property) { + $scope.dialogModel = {}; + $scope.dialogModel.title = "Edit property type settings"; + $scope.dialogModel.property = property; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/editPropertySettings/editPropertySettings.html"; + $scope.showDialog = true; + + // set indicator on property to tell the dialog is open - is used to set focus on the element + property.dialogIsOpen = true; + + $scope.dialogModel.submit = function(dt){ + + /* + contentTypeResource.getPropertyTypeScaffold(dt.id) + .then(function(pt){ + property.config = pt.config; + property.editor = pt.editor; + property.view = pt.view; + $scope.dialogModel = null; + $scope.showDialog = false; + }); + */ + + property.dialogIsOpen = false; + + $scope.showDialog = false; + $scope.dialogModel = null; + + }; + + /* + $scope.dialogModel.submit = function(){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + */ + + $scope.dialogModel.close = function(model){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + + }; + + $scope.addItems = function(tab){ + + $scope.showDialog = true; + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add some stuff"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + var target = tab; + if(tab.groups && tab.groups.length > 0){ + target = _.last(tab.groups); + } + + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + $scope.showDialog = false; + }; + + $scope.dialogModel.submit = function(dt){ + contentTypeResource.getPropertyTypeScaffold(dt.id).then(function(pt){ + + pt.label = dt.name + " field"; + pt.dataType = dt; + target.properties.push(pt); + + // open settings dialog + $scope.editPropertyTypeSettings(pt); + + }); + }; + }; + + $scope.deleteProperty = function(tab, propertyIndex) { + tab.properties.splice(propertyIndex, 1); + }; + + + $scope.addProperty = function(group){ + $log.log("open dialog"); + + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add property type"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + }; + }; + + + + + + $scope.sortableOptionsFieldset = { + distance: 10, + revert: true, + tolerance: "pointer", + opacity: 0.7, + scroll:true, + cursor:"move", + placeholder: "ui-sortable-placeholder", + connectWith: ".edt-tabs", + handle: ".handle", + zIndex: 6000, + start: function (e, ui) { + ui.placeholder.addClass( ui.item.attr("class") ); + }, + stop: function(e, ui){ + ui.placeholder.remove(); + } + }; + + + $scope.sortableOptionsEditor = { + distance: 10, + revert: true, + tolerance: "pointer", + connectWith: ".edt-props-sortable", + opacity: 0.7, + scroll: true, + cursor: "move", + handle: ".edt-property-handle", + placeholder: "ui-sortable-placeholder", + zIndex: 6000, + start: function (e, ui) { + + // set all tabs to inactive to collapse all content + angular.forEach($scope.contentType.groups, function(tab){ + $scope.$apply(function () { + + tab.tabIsActive = false; + + }); + }); + + }, + stop: function(e, ui){ + console.log(e); + console.log(ui); + } + }; + + $scope.sortableOptionsTab = { + distance: 10, + revert: true, + tolerance: "pointer", + opacity: 0.7, + scroll:true, + cursor:"move", + placeholder: "ui-sortable-placeholder", + zIndex: 6000, + handle: ".edt-tab-handle", + start: function (e, ui) { + + // set all tabs to inactive to collapse all content + angular.forEach($scope.contentType.groups, function(tab){ + $scope.$apply(function () { + + tab.tabIsActive = false; + + }); + }); + + }, + stop: function(e, ui){ + console.log(e); + console.log(ui); + } + }; + +} + +angular.module("umbraco").controller("Umbraco.Editors.DocumentType.EditController", DocumentTypeEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html new file mode 100644 index 0000000000..bee774a972 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html @@ -0,0 +1,271 @@ +
    + + + + + + + + +
    + +
      + +
    • + +
      + +
      + + + + + + + + {{ tab.name }} + +
      + +
      + +
      + + + +
      + +
      + + + +
      + +
      + + + + +
      + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis lectus arcu.
      + + + +
      + + +
      +
        +
      • + +
        + + + + +
        + + +
        + +
        + + +
        + +
        + + +
        + +
        + + +
        + {{ property.label }}: + {{ property.alias }} +
        + +
        + + +
        + + + Edit property + + + {{property.dataType.name}} + + + + +
        + + +
        + + +
        + + + + + + +
        + +
        + + + +
        + +
        + + +
        + +
        + +
        + +
      • + +
      • +
        + +
        +
      • +
      +
      + + + +
      +
      + +
    • + + +
    • +
      + +
      + Tab +
      + +
      + Add first tab + Add new tab +
      + +
      + +
    • + +
    • +
      {{ contentType | json }}
      +
    • + + +
    +
    + +
    + + + + + + +
    + + + + + + + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/property.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/property.html new file mode 100644 index 0000000000..ccd632246c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/property.html @@ -0,0 +1,4 @@ +
    + + {{property.description}} +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/propertygroup.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/propertygroup.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 4c2ea733b6..6bbab0a6d3 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -13,6 +13,7 @@ using System.Linq; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using Newtonsoft.Json; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Editors { @@ -56,6 +57,41 @@ namespace Umbraco.Web.Editors return ApplicationContext.Services.ContentTypeService.GetAllPropertyTypeAliases(); } + + public Umbraco.Web.Models.ContentEditing.ContentTypeDisplay GetById(int id) + { + var ct = Services.ContentTypeService.GetContentType(id); + if (ct == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var dto = Mapper.Map(ct); + return dto; + } + + + public ContentPropertyDisplay GetPropertyTypeScaffold(int id) + { + var dataTypeDiff = Services.DataTypeService.GetDataTypeDefinitionById(id); + + if (dataTypeDiff == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var preVals = UmbracoContext.Current.Application.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(id); + var editor = PropertyEditorResolver.Current.GetByAlias(dataTypeDiff.PropertyEditorAlias); + + return new ContentPropertyDisplay() + { + Editor = dataTypeDiff.PropertyEditorAlias, + Validation = new PropertyTypeValidation() { }, + View = editor.ValueEditor.View, + Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) + }; + } + /// /// Returns the allowed child content type objects for the content item id passed in /// diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 67b5619e3c..fed52aaa55 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -59,6 +59,22 @@ namespace Umbraco.Web.Editors return Mapper.Map(dataType); } + /// + /// Gets the content json for all data types + /// + /// + /// + /// + /// Permission is granted to this method if the user has access to any of these trees: DataTypes, Content or Media + /// + [UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.Content, Constants.Trees.Media)] + public IEnumerable GetAll() + { + return Services.DataTypeService + .GetAllDataTypeDefinitions() + .Select(x => Mapper.Map(x)).Where(x => x.IsSystemDataType == false); + } + /// /// Deletes a data type wth a given ID /// diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs new file mode 100644 index 0000000000..48c531970c --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "contentType", Namespace = "")] + public class ContentTypeDisplay : ContentTypeBasic + { + [DataMember(Name = "groups")] + public IEnumerable Groups { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs new file mode 100644 index 0000000000..4865d0d546 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "propertyType")] + public class PropertyTypeDisplay + { + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "description")] + public string Description { get; set; } + + [DataMember(Name = "editor")] + public string Editor { get; set; } + + [DataMember(Name = "validation")] + public PropertyTypeValidation Validation { get; set; } + + [DataMember(Name = "label")] + public string Label { get; set; } + + [DataMember(Name = "view")] + public string View { get; set; } + + [DataMember(Name = "config")] + public IDictionary Config { get; set; } + + [DataMember(Name = "value")] + public string Value { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs new file mode 100644 index 0000000000..6e31f811d1 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "propertyTypeGroup", Namespace = "")] + public class PropertyTypeGroupDisplay + { + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "parentGroupId")] + public int ParentGroupId { get; set; } + + [DataMember(Name = "sortOrder")] + public int SortOrder { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "properties")] + public IEnumerable Properties { get; set; } + + [DataMember(Name = "groups")] + public IEnumerable Groups { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index a09592a49d..09b53dc45d 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -16,6 +16,13 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap(); config.CreateMap(); config.CreateMap(); + + config.CreateMap() + .ForMember( + dto => dto.Groups, + expression => expression.ResolveUsing()); } + + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs new file mode 100644 index 0000000000..999b1fe824 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -0,0 +1,77 @@ +using AutoMapper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal class PropertyTypeGroupResolver : ValueResolver> + { + protected override IEnumerable ResolveCore(IContentType source) + { + var groups = new List(); + + var propGroups = source.CompositionPropertyGroups; + var genericGroup = new PropertyTypeGroupDisplay() { Name = "properties", Id = 0, ParentGroupId = 0 }; + genericGroup.Properties = MapProperties(source.PropertyTypes); + genericGroup.Groups = new List(); + + foreach (var group in propGroups.Where(pg => pg.ParentId.HasValue == false)) + { + var mapped = new PropertyTypeGroupDisplay() { Id = group.Id, ParentGroupId = 0, Name = group.Name, SortOrder = group.SortOrder }; + mapped.Properties = MapProperties(group.PropertyTypes); + mapped.Groups = MapChildGroups(mapped, propGroups); + groups.Add(mapped); + } + + groups.Add(genericGroup); + return groups; + } + + private IEnumerable MapChildGroups(PropertyTypeGroupDisplay parent, IEnumerable groups) + { + var mappedGroups = new List(); + + foreach (var child in groups.Where(x => x.ParentId == parent.Id)) + { + var mapped = new PropertyTypeGroupDisplay() { Id = child.Id, ParentGroupId = child.ParentId.Value, Name = child.Name, SortOrder = child.SortOrder }; + mapped.Name += child.PropertyTypes.Count.ToString(); + mapped.Properties = MapProperties(child.PropertyTypes); + mapped.Groups = MapChildGroups(mapped, groups); + mappedGroups.Add(mapped); + } + + return mappedGroups; + } + + private IEnumerable MapProperties(IEnumerable properties) + { + var mappedProperties = new List(); + foreach (var p in properties) + { + var editor = PropertyEditorResolver.Current.GetByAlias(p.PropertyEditorAlias); + var preVals = UmbracoContext.Current.Application.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(p.DataTypeDefinitionId); + + mappedProperties.Add( + new PropertyTypeDisplay() + { + Alias = p.Alias, + Description = p.Description, + Editor = p.PropertyEditorAlias, + Validation = new PropertyTypeValidation() { Mandatory = p.Mandatory, Pattern = p.ValidationRegExp }, + Label = p.Name, + View = editor.ValueEditor.View, + Config = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals) , + Value = "" + }); + } + + return mappedProperties; + } + } +} diff --git a/src/Umbraco.Web/Trees/DocumentTypeTreeController.cs b/src/Umbraco.Web/Trees/DocumentTypeTreeController.cs new file mode 100644 index 0000000000..d7d2cca335 --- /dev/null +++ b/src/Umbraco.Web/Trees/DocumentTypeTreeController.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Formatting; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core; +using Umbraco.Web.Models.Trees; +using Umbraco.Web.WebApi.Filters; + +namespace Umbraco.Web.Trees +{ + [UmbracoTreeAuthorize(Constants.Trees.DataTypes)] + [Tree(Constants.Applications.Settings, Constants.Trees.DocumentTypes, "Document Types")] + [Umbraco.Web.Mvc.PluginController("UmbracoTrees")] + [CoreTree] + public class DocumentTypeTreeController : TreeController + { + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) + { + var nodes = new TreeNodeCollection(); + nodes.AddRange( + Services.ContentTypeService.GetAllContentTypes() + .OrderBy(x => x.Name) + .Select(dt => CreateTreeNode(dt.Id.ToString(), id, queryStrings, dt.Name, "icon-item-arrangement", false))); + + return nodes; + } + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + return new MenuItemCollection(); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 793d0f7861..7aada5939e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -304,6 +304,11 @@ + + + + + From fbb46c9ffe27f5a04e6e82458d44a5281203ada0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 14 May 2015 14:42:53 +1000 Subject: [PATCH 0005/1710] Allows Courier core to see internals --- src/Umbraco.Core/Properties/AssemblyInfo.cs | 1 + src/Umbraco.Web/Properties/AssemblyInfo.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 1b913ca324..8782ce7b80 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -41,6 +41,7 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Concorde.Sync")] [assembly: InternalsVisibleTo("Umbraco.Belle")] [assembly: InternalsVisibleTo("Umbraco.VisualStudio")] +[assembly: InternalsVisibleTo("Concorde.Courier.Core")] [assembly: InternalsVisibleTo("umbraco.providers")] //allow this to be mocked in our unit tests diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs index ad54647119..b3d649d968 100644 --- a/src/Umbraco.Web/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Web/Properties/AssemblyInfo.cs @@ -33,6 +33,7 @@ using System.Security; [assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] [assembly: InternalsVisibleTo("umbraco.webservices")] [assembly: InternalsVisibleTo("Concorde.Sync")] +[assembly: InternalsVisibleTo("Concorde.Courier.Core")] [assembly: InternalsVisibleTo("Umbraco.Belle")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file From 65f2b431851cc1b888853f858598a90de59c17cc Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 14 May 2015 07:39:18 +0200 Subject: [PATCH 0006/1710] Latest files from mads --- .../src/less/pages/documentTypeEditor.less | 90 +++++++++---------- .../contenttype.edit.controller.js | 2 +- .../src/views/contenttype/edit.html | 3 +- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less b/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less index 18bc9d8441..b7285cb02b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less @@ -9,28 +9,28 @@ .fieldset-title{ - border-bottom: 1px solid @grayLight; - border-top: 1px solid @grayLight; - background: @grayLighter; - font-size: 16px; - text-transform: none; - padding: 7px 10px 7px 10px; - margin-left: -30px; - margin-right: -10px; - font-weight: 400 + border-bottom: 1px solid @grayLight; + border-top: 1px solid @grayLight; + background: @grayLighter; + font-size: 16px; + text-transform: none; + padding: 7px 10px 7px 10px; + margin-left: -30px; + margin-right: -10px; + font-weight: 400 } .tab-title{ - padding: 0 25px 0 25px; - position: absolute; - left: -2px; - top: -36px; - height: 34px; - background: white; - border: 2px solid #cccccc; - border-bottom: none; - border-radius: 5px 5px 0 0; + padding: 0 25px 0 25px; + position: absolute; + left: -2px; + top: -36px; + height: 34px; + background: white; + border: 2px solid #cccccc; + border-bottom: none; + border-radius: 5px 5px 0 0; -webkit-transition: width 0.5s; /* Safari */ transition: width 0.5s; font-weight: bold; @@ -51,46 +51,46 @@ .editors-document-type-canvas{ /* - min-height: 200px; - position: absolute; - top: 0; - right: 0; - left: 0; + min-height: 200px; + position: absolute; + top: 0; + right: 0; + left: 0; - bottom: 0; - overflow: auto; - */ + bottom: 0; + overflow: auto; + */ padding: 40px 80px; } .editors-document-type-sidebar{ - padding: 20px; - top: 0; - left: 0; - width: 340px; - bottom: 0; - overflow: auto; - border-right: 1px solid #d9d9d9; - background: rgba(248, 248, 248, 0.06); - position: absolute; - display: none; + padding: 20px; + top: 0; + left: 0; + width: 340px; + bottom: 0; + overflow: auto; + border-right: 1px solid #d9d9d9; + background: rgba(248, 248, 248, 0.06); + position: absolute; + display: none; } .no-style-list{ - padding: 0; - margin: 0; - list-style: none; + padding: 0; + margin: 0; + list-style: none; } .edt-props-sortable{min-height: 5px} .properties-placeholder{ - //text-align: center; - background: @grayLighter; - border: 1px dashed @grayLight; - padding: 20px; - margin: 30px 20px 30px 20px; + //text-align: center; + background: @grayLighter; + border: 1px dashed @grayLight; + padding: 20px; + margin: 30px 20px 30px 20px; } } @@ -361,8 +361,8 @@ } } -/* ---------- PLACEHOLDERS ----------- */ +/* ---------- PLACEHOLDERS ----------- */ .placeholder { background: #e5e5e5; display: block; diff --git a/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js index 6254894075..3d612fd894 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/contenttype/contenttype.edit.controller.js @@ -7,7 +7,7 @@ * The controller for the content type editor */ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, dataTypeResource) { - $scope.page = {action: [], menu: [] }; + $scope.page = {actions: [], menu: [] }; //$rootScope.emptySection = true; contentTypeResource.getById($routeParams.id).then(function(dt){ diff --git a/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html b/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html index 3ac3922a29..8bed036850 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/contenttype/edit.html @@ -8,7 +8,8 @@ + actions="page.actions"> + From 01de0bd6a13680ff6db6ac8d1a76d32a7fcca0df Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Hoffback Date: Fri, 15 May 2015 07:22:11 +0200 Subject: [PATCH 0007/1710] Swedish was using the wrong format for variables The swedish translation was using {0} instead of %0%. Leading to alot of texts being weirdly formated. --- src/Umbraco.Web.UI/umbraco/config/lang/sv.xml | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml index aed36d5d92..b619025624 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml @@ -40,11 +40,11 @@ Lägg till nytt domännamn Domännamn - Har skapat domännamnet {0} - Har tagit bort domännamnet {0} - Domänen {0} är redan tillagd + Har skapat domännamnet %0% + Har tagit bort domännamnet %0% + Domänen %0% är redan tillagd t.ex.: dittdomannamn.se, www.dittdomannamn.se - Domännamnet {0} har uppdaterats + Domännamnet %0% har uppdaterats Domänen är redan tilldelad Ärv Ogiltigt domännamn @@ -145,7 +145,7 @@ Sidnamn Ej medlem av grupp(er) Egenskaper - Detta dokument är publicerat men syns inte eftersom den överordnade sidan {0} inte är publicerad + Detta dokument är publicerat men syns inte eftersom den överordnade sidan %0% inte är publicerad Oops: detta dokument är publicerat men finns inte i cacheminnet (internt fel) Publicera Publiceringsstatus @@ -166,7 +166,7 @@ Länk till dokument - Var vill du skapa den nya {0} + Var vill du skapa den nya %0% Skapa innehåll under "dokumenttyper".]]> "mediatyper".]]> @@ -187,7 +187,7 @@ Stäng fönstret Är du säker på att du vill ta bort Är du säker på att du vill avaktivera - Kryssa i denna ruta för att bekräfta att {0} objekt tas bort + Kryssa i denna ruta för att bekräfta att %0% objekt tas bort Är du säker? Är du säker? Klipp ut @@ -228,7 +228,7 @@ Se cachat objekt - Redigera de olika översättningarna för ordboksinlägget {0} nedan. Du kan lägga till ytterligare språk under 'språk' i menyn till vänster. + Redigera de olika översättningarna för ordboksinlägget %0% nedan. Du kan lägga till ytterligare språk under 'språk' i menyn till vänster. Språknamn @@ -265,15 +265,15 @@ Informationen har sparats, men innan du kan publicera denna sida måste du åtgärda följande fel: Det går inte att byta lösenord i den medlemshanterare du har valt (EnablePasswordRetrieval måste vara satt till 'true'). - {0} redan finns + %0% redan finns Följande fel inträffade: Följande fel inträffade: - Lösenordet måste bestå av minst {0} tecken varav minst {1} är icke-alfanumeriska tecken (t.ex. %, #, !, @). - {0} måste vara ett heltal - {0} under {1} är ett obligatoriskt fält - {0} är ett obligatoriskt fält - {0} under {1} har ett felaktigt format - {0} har ett felaktigt format + Lösenordet måste bestå av minst %0% tecken varav minst %1% är icke-alfanumeriska tecken (t.ex. %, #, !, @). + %0% måste vara ett heltal + %0% under %1% är ett obligatoriskt fält + %0% är ett obligatoriskt fält + %0% under %1% har ett felaktigt format + %0% har ett felaktigt format Även om CodeMirror är aktiverad i konfigurationen, så är den avaktiverad i Internet Explorer på grund av att den inte är tillräckligt stabil @@ -408,12 +408,12 @@ Kunde inte spara filen web.config. Vänligen ändra databasanslutnings-inställningarna manuellt. Din databas har lokaliserats och är identifierad som Databaskonfiguration - installera]]> - Nästa för att fortsätta.]]> + installera]]> + Nästa för att fortsätta.]]> Databasen kunde inte hittas! Kontrollera att informationen i databasanslutnings-inställningarna i filen "web.config" är rätt.

    För att fortsätta måste du redigera filen "web.config" (du kan använda Visual Studio eller din favorit text-redigerare), bläddra till slutet, lägg till databasanslutnings-inställningarna för din databas i fältet som heter "umbracoDbDSN" och spara filen.

    Klicka på Försök igen knappen när du är klar.
    > Mer information om att redigera web.config hittar du här.

    ]]>
    Eventuellt kan du behöva kontakta ditt webb-hotell. Om du installerar på en lokal maskin eller server kan du få informationen från din systemadministratör.]]> - Tryck Uppgradera knappen för att uppgradera din databas till Umbraco {0}

    Du behöver inte vara orolig. Inget innehåll kommer att raderas och efteråt kommer allt att fungera som vanligt!

    ]]>
    - Tryck Nästa för att fortsätta.]]> + Tryck Uppgradera knappen för att uppgradera din databas till Umbraco %0%

    Du behöver inte vara orolig. Inget innehåll kommer att raderas och efteråt kommer allt att fungera som vanligt!

    ]]>
    + Tryck Nästa för att fortsätta.]]> Nästa för att fortsätta med konfigurationsguiden]]> Lösenordet på standardanvändaren måste bytas!]]> Standardanvändaren har avaktiverats eller har inte åtkomst till Umbraco!

    Du behöver inte göra något ytterligare här. Klicka Next för att fortsätta.]]> @@ -455,13 +455,13 @@ Tack för att du valde Umbraco Besök din nya webbplats Du installerade Runway, så varför inte se hur din nya webbplats ser ut.]]> Ytterligare hjälp och information Få hjälp från våra prisbelönta community, bläddra i dokumentationen eller titta på några gratis videor om hur man bygger en enkel webbplats, hur du använder paket eller en snabbguide till Umbracos terminologi]]> - Umbraco {0} är installerat och klart för användning - /web.config filen och ändra AppSettingsnyckeln UmbracoConfigurationStatus på slutet till {0}]]> + Umbraco %0% är installerat och klart för användning + /web.config filen och ändra AppSettingsnyckeln UmbracoConfigurationStatus på slutet till %0%]]> börja omedelbart genom att klicka på "Starta Umbraco"-knappen nedan.
    Om du är en ny Umbraco användarekan du hitta massor av resurser på våra kom igång sidor.]]>
    Starta Umbraco För att administrera din webbplats öppnar du bara Umbraco back office och börjar lägga till innehåll, uppdatera mallar och stilmallar eller lägga till nya funktioner.]]> Anslutningen till databasen misslyckades. Se - Umbraco {0} antingen för en ny installation eller en uppgradering från version 3.0.

    Tryck på "next" för att börja.]]>
    + Umbraco %0% antingen för en ny installation eller en uppgradering från version 3.0.

    Tryck på "next" för att börja.]]>
    Umbraco Version 3 Umbraco Version 4 @@ -474,7 +474,7 @@ Förnya nu för att spara ditt arbete - © 2001 - {0}
    umbraco.com

    ]]>
    + © 2001 - %0%
    umbraco.com

    ]]>
    Happy super Sunday Happy manic Monday Happy tremendous Tuesday @@ -496,10 +496,10 @@ Välj sida ovan... - {0} har kopierats till {1} - Ange mål att kopiera sidan {0} till nedan - {0} har flyttats till {1} - Ange vart sidan {0} skall flyttas till nedan + %0% har kopierats till %1% + Ange mål att kopiera sidan %0% till nedan + %0% har flyttats till %1% + Ange vart sidan %0% skall flyttas till nedan är nu roten för ditt nya innehåll. Klicka 'ok' nedan. Du har inte valt någon sida än. Välj en sida i listan ovan och klicka sedan 'fortsätt'. Aktuell nod får inte existera i roten @@ -550,7 +550,7 @@ Fyll i ett namn... Skriv för att filtrera... - Namnge {0}... + Namnge %0%... Fyll i ditt lösenord Skriv för att söka... Fyll i ditt lösenord @@ -562,8 +562,8 @@ Sida med felmeddelande Används när en användare är inloggad, men saknar rättigheter att se sidan Välj hur du vill lösenordsskydda sidan - {0} är nu lösenordsskyddad - Lösenordsskyddet är nu borttaget på {0} + %0% är nu lösenordsskyddad + Lösenordsskyddet är nu borttaget på %0% Inloggningssida Välj sidan med inloggningsformuläret Ta bort lösenordsskydd @@ -574,17 +574,17 @@ Välj detta alternativ om du vill skydda sidan med ett enkelt användarnamn och lösenord. Alla loggar då in med samma inloggningsuppgifter. - {0} kunde inte publiceras på grund av dess tidsinställda publicering. - {0} kunde inte publiceras på grund av att ett tredjepartstillägg avbröt publiceringen. - {0} kan inte publiceras, på grund av att överordnad nod inte är publicerad. - {0} kunde inte publiceras på grund av följande orsaker: {1} passerade inte valideringen. + %0% kunde inte publiceras på grund av dess tidsinställda publicering. + %0% kunde inte publiceras på grund av att ett tredjepartstillägg avbröt publiceringen. + %0% kan inte publiceras, på grund av att överordnad nod inte är publicerad. + %0% kunde inte publiceras på grund av följande orsaker: %1% passerade inte valideringen. Inkludera opublicerade undersidor Publicering pågår - vänligen vänta... - {0} av {1} sidor har publicerats... - {0} har publicerats - {0} och underliggande sidor har publicerats - Publicera {0} och alla dess underordnade sidor - ok för att publicera {0}. Därmed blir innehållet publikt.

    Du kan publicera denna sida och alla dess undersidor genom att kryssa i publicera alla undersidor. ]]>
    + %0% av %1% sidor har publicerats... + %0% har publicerats + %0% och underliggande sidor har publicerats + Publicera %0% och alla dess underordnade sidor + ok för att publicera %0%. Därmed blir innehållet publikt.

    Du kan publicera denna sida och alla dess undersidor genom att kryssa i publicera alla undersidor. ]]>
    Lägg till extern länk @@ -654,12 +654,12 @@ Publiceringen avbröts av ett tredjepartstillägg Egenskapstyp finns redan Egenskapstyp skapad - Datatyp: {1}]]> + Datatyp: %1%]]> Egenskapstypen har tagits bort Innehållstypen har sparats Ny flik skapad Fliken har tagits bort - Fliken med id: {0} har tagits bort + Fliken med id: %0% har tagits bort Innehållet är avpublicerat Stilmallen kunde inte sparas Stilmallen sparades @@ -790,13 +790,13 @@ Ladda hem DTD för XML Fält Inkludera undersidor - Hej {0}. Detta är ett automatisk mail skickat for att informera dig om att det finns en översättningsförfrågan på dokument '%1' till '{5}' skickad av {2}. För att redigere, besök http://{3}/translation/details.aspx?id={4}. För att få en översikt över dina översättningsuppgigter loggar du in i Umbraco på: http://{3} - [{0}] Översättningsuppgit för {1} + Hej %0%. Detta är ett automatisk mail skickat for att informera dig om att det finns en översättningsförfrågan på dokument '%1%' till '%5%' skickad av %2%. För att redigera, besök http://%3%/translation/details.aspx?id=%4%. För att få en översikt över dina översättningsuppgigter loggar du in i Umbraco på: http://%3% + [%0%] Översättningsuppgift för %1% Hittade inga användare som är översättare. Vänligen skapa en användare som är översättare innan du börjar skicka innehåll för översättning Arbetsuppgifter som du har skapat som du har skapat. För att se en detaljvy med kommentarer, klicka på "Detaljer" eller på sidans namn. Du kan också ladda ned sidan i XML-format genom att klicka på länken "Ladda ned XML".
    För att markera ett översättningsjobb som avslutat, gå till detaljvyn och klicka på knappen "Stäng".]]>
    - Sidan {0} har skickats för översättning - Skicka sidan {0} för översättning + Sidan %0% har skickats för översättning + Skicka sidan %0% för översättning Tilldelat av Arbetsuppgift öppnad Totalt antal ord @@ -841,7 +841,7 @@ Ny uppdatering tillgänglig - {0} är klart, klicka här för att ladda ner + %0% är klart, klicka här för att ladda ner Ingen kontakt med server Fel vid kontroll av uppdatering. Se trace-stack för mer information. From 1dda3ce45dfdfecc13495b5e38593eb977c4acbd Mon Sep 17 00:00:00 2001 From: Arjan Woldring Date: Sat, 16 May 2015 19:06:04 +0200 Subject: [PATCH 0008/1710] Updating Dutch Translations Missing keys added, deleted keys deleted, reorderded some keys based on the english one and corrected couple of wrong translations. --- src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 89 ++++++++++++------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 05e9de462e..0320df479e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -16,8 +16,6 @@ Uitschakelen Prullenbak leegmaken Documenttype exporteren - Exporteer naar .NET - Exporteer naar .NET Documenttype importeren Package importeren Aanpassen in Canvas @@ -51,11 +49,11 @@ Nieuw domein '%0%' is aangemaakt Domein '%0%' is verwijderd Domein '%0' is al aanwezig + Domein '%0%' is bijgewerkt + Bewerk huidige domeinen
    Zgn. 'one-level' paden in domeinen worden ondersteund, bijv. "example.com/en". Echter, ze zouden moeten worden vermeden. Gebruik bij voorkeur de cultuurinstelling hierboven.]]>
    - Domein '%0%' is bijgewerkt - Bewerk huidige domeinen Overerven Cultuur of erf de cultuur over van de ouder nodes. Zal ook van toepassing
    @@ -87,6 +85,7 @@ Macro invoegen Afbeelding invoegen Relaties wijzigen + Terug naar overzicht Opslaan Opslaan en publiceren Opslaan en verzenden voor goedkeuring @@ -117,6 +116,7 @@ Alleen alternatieve types geldig voor de huidige locatie worden weergegeven. + Is gepubliceerd Over deze pagina Alternatieve link (hoe zou jij de foto beschrijven via de telefoon) @@ -133,6 +133,7 @@ Dit item is gewijzigd na publicatie Dit item is niet gepubliceerd Laatst gepubliceerd op + Nog geen items om weer te geven. Mediatype Link naar media item(s) Ledengroep @@ -152,11 +153,12 @@ Om nodes te sorteren, sleep de nodes of klik op één van de kolomtitels. Je kan meerdere nodes tegelijk selecteren door de "shift"- of "control"knop in te drukken tijdens het selecteren. Statistieken Titel (optioneel) + Alternatieve tekst (optioneel) Type Depubliceren Laatst gewijzigd Date/time this document was edited - Bestand verwijderen + Bestand(en) verwijderen Link naar het document Lid van groep(en) Geen lid van groep(en) @@ -170,7 +172,7 @@ Waar wil je de nieuwe %0% aanmaken? - Aanmaken op + Aanmaken onder Kies een type en een titel "Documenttypes".]]> @@ -241,16 +243,20 @@ Cultuurnaam - Type je gebruikersnaam - Type je wachtwoord + Typ je gebruikersnaam + Typ je wachtwoord Benoem de %0%... - Type een naam... - Type om te zoeken... - Type om te filteren... + Typ een naam... + Typ om te zoeken... + Typ om te filteren... + Typ om tags toe te voegen (druk op enter na elke tag)... + Toestaan op root-niveau + Wanneer aangevinkt dan mag dit document type aangemaakt worden op het root-niveau van content of media trees. Toegelaten subnodetypes + Document Type Composities Nieuw Tab verwijderen Omschrijving @@ -258,6 +264,11 @@ Tab Miniatuur Lijstweergave inschakelen + Stelt het content item in zodat een sorteer- en zoekbare lijstweergave van onderliggende nodes wordt getoond. De onderliggende nodes worden niet in de tree getoond + Huidige lijstweergave + De actieve data type in lijstweergave + Maak een aangepaste lijstweergave + Verwijder aangepaste lijstweergave Prevalue toevoegen @@ -501,7 +512,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Sessie is verlopen © 2001 - %0%
    umbraco.com

    ]]>
    - Welkom bij Umbraco, geef je gebruikersnaam en wachtwoord op in de onderstaande velden: Dashboard @@ -662,6 +672,9 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Open in nieuw venster Verwijder link + + Reset + Huidige versie Rode tekst wordt niet getoond in de geselecteerde versie , groen betekent toegevoegd]]> @@ -690,6 +703,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Umbraco Contour Help + Formulieren + Analytics Standaard template @@ -708,6 +723,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Dit inhoudstype gebruikt als basis inhoudstype. Tabs van basis inhoudstypes worden niet getoond en kunnen alleen worden aangepast op het basis inhoudstype zelf Geen eigenschappen gedefinieerd op dit tabblad. Klik op de link "voeg een nieuwe eigenschap" aan de bovenkant om een ​​nieuwe eigenschap te creëren. + Master Document Type + Maak een bijbehorend template Sorteren gereed. @@ -788,36 +805,40 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Quick Guide voor Umbraco template tags Sjabloon - - Item toevoegen - Een rij aan de lay-out toevoegen - teken onderaan en voeg je eerste item toe]]> + + Item toevoegen + Een rij aan de lay-out toevoegen + teken onderaan en voeg je eerste item toe]]> - Grid lay-outs - Lay-outs zijn het globale werkgebied voor de grid editor. Je hebt meestal maar één of twee verschillende lay-outs nodig - Een grid layout toevoegen - De lay-out aanpassen door de kolombreedte aan te passen en extra kolommen toe te voegen + Klik om een item te embedden + Klik om een afbeelding in te voegen + Afbeelding ondertitel... + Typ hier...... + Grid lay-outs + Lay-outs zijn het globale werkgebied voor de grid editor. Je hebt meestal maar één of twee verschillende lay-outs nodig + Een grid layout toevoegen + De lay-out aanpassen door de kolombreedte aan te passen en extra kolommen toe te voegen - Rijconfiguratie - Rijen zijn voorgedefinieerde cellen die horizontaal zijn gerangschikt - Een rijconfiguratie toevoegen - De rijconfiguratie aanpassen door de breedte van de cel in te stellen en extra cellen toe te voegen + Rijconfiguratie + Rijen zijn voorgedefinieerde cellen die horizontaal zijn gerangschikt + Een rijconfiguratie toevoegen + De rijconfiguratie aanpassen door de breedte van de cel in te stellen en extra cellen toe te voegen - Kolommen - Het totaal aantal gecombineerde kolommen in de grid layout + Kolommen + Het totaal aantal gecombineerde kolommen in de grid layout - Instellingen - Configureren welke instellingen de editors kunnen aanpassen + Instellingen + Configureren welke instellingen de editors kunnen aanpassen - Styles - Configureren welke stijlen de editors kunnen aanpassen + Styles + Configureren welke stijlen de editors kunnen aanpassen - De instellingen worden enkel bewaard indien de ingevoerde Json geldig is + De instellingen worden enkel bewaard indien de ingevoerde Json geldig is - Alle editors toelaten - Alle rijconfiguraties toelaten - + Alle editors toelaten + Alle rijconfiguraties toelaten + Alternatief veld Alternatieve tekst From 7cbe2410e4556da10f160920bdf1482ad98e848d Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 15 May 2015 22:20:09 +0200 Subject: [PATCH 0009/1710] Do not flood the log when installing a new version --- src/Umbraco.Core/ApplicationContext.cs | 18 +++++++++++------- src/Umbraco.Web/UmbracoModule.cs | 12 ++++++++++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index f1bb929a94..382c3f29c4 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -165,6 +165,8 @@ namespace Umbraco.Core /// internal string OriginalRequestUrl { get; set; } + private bool _versionsDifferenceReported; + /// /// Checks if the version configured matches the assembly version /// @@ -174,17 +176,19 @@ namespace Umbraco.Core { try { - string configStatus = ConfigurationStatus; - string currentVersion = UmbracoVersion.Current.ToString(3); + var configStatus = ConfigurationStatus; + var currentVersion = UmbracoVersion.Current.ToString(3); + var ok = configStatus == currentVersion; - - if (currentVersion != configStatus) + if (ok == false && _versionsDifferenceReported == false) { - LogHelper.Info("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'"); + // remember it's been reported so we don't flood the log + // no thread-safety so there may be a few log entries, doesn't matter + _versionsDifferenceReported = true; + LogHelper.Info("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'"); } - - return (configStatus == currentVersion); + return ok; } catch { diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index f2d216f2fd..bc6037a683 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -409,15 +409,23 @@ namespace Umbraco.Web return false; } + private bool _notConfiguredReported; + // ensures Umbraco is configured // if not, redirect to install and return false // if yes, return true - private static bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri) + private bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri) { if (ApplicationContext.Current.IsConfigured) return true; - LogHelper.Warn("Umbraco is not configured"); + if (_notConfiguredReported) + { + // remember it's been reported so we don't flood the log + // no thread-safety so there may be a few log entries, doesn't matter + _notConfiguredReported = true; + LogHelper.Warn("Umbraco is not configured"); + } var installPath = UriUtility.ToAbsolute(SystemDirectories.Install); var installUrl = string.Format("{0}/?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString())); From 92f56ccedd298ae8ae72cd3463b40138ec4156d7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 15 May 2015 22:21:39 +0200 Subject: [PATCH 0010/1710] U4-6626 - XmlCache, bugfix locks --- .../umbraco.presentation/content.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 4e82e4cb63..edc71ef161 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -797,6 +797,7 @@ order by umbracoNode.level, umbracoNode.sortOrder"; private void LoadXmlLocked(SafeXmlReaderWriter safeXml, out bool registerXmlChange) { LogHelper.Debug("Loading Xml..."); + EnsureFileLock(); // get the lock asap // try to get it from the file if (XmlFileEnabled && (safeXml.Xml = LoadXmlFromFile()) != null) @@ -1056,12 +1057,14 @@ order by umbracoNode.level, umbracoNode.sortOrder"; internal void SaveXmlToFile() { LogHelper.Info("Save Xml to file..."); - EnsureFileLock(); - - var xml = _xmlContent; // capture (atomic + volatile), immutable anyway try { + var xml = _xmlContent; // capture (atomic + volatile), immutable anyway + if (xml == null) return; + + EnsureFileLock(); + // delete existing file, if any DeleteXmlFile(); @@ -1079,7 +1082,7 @@ order by umbracoNode.level, umbracoNode.sortOrder"; fs.Write(bytes, 0, bytes.Length); } - LogHelper.Debug("Saved Xml to file."); + LogHelper.Info("Saved Xml to file."); } catch (Exception e) { @@ -1094,12 +1097,14 @@ order by umbracoNode.level, umbracoNode.sortOrder"; internal async System.Threading.Tasks.Task SaveXmlToFileAsync() { LogHelper.Info("Save Xml to file..."); - EnsureFileLock(); - - var xml = _xmlContent; // capture (atomic + volatile), immutable anyway try { + var xml = _xmlContent; // capture (atomic + volatile), immutable anyway + if (xml == null) return; + + EnsureFileLock(); + // delete existing file, if any DeleteXmlFile(); @@ -1117,7 +1122,7 @@ order by umbracoNode.level, umbracoNode.sortOrder"; await fs.WriteAsync(bytes, 0, bytes.Length); } - LogHelper.Debug("Saved Xml to file."); + LogHelper.Info("Saved Xml to file."); } catch (Exception e) { @@ -1160,19 +1165,25 @@ order by umbracoNode.level, umbracoNode.sortOrder"; private XmlDocument LoadXmlFromFile() { LogHelper.Info("Load Xml from file..."); - EnsureFileLock(); try { + EnsureFileLock(); + var xml = new XmlDocument(); using (var fs = new FileStream(_xmlFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { xml.Load(fs); } _lastFileRead = DateTime.UtcNow; - LogHelper.Info("Successfully loaded Xml from file."); + LogHelper.Info("Loaded Xml from file."); return xml; } + catch (FileNotFoundException) + { + LogHelper.Warn("Failed to load Xml, file does not exist."); + return null; + } catch (Exception e) { LogHelper.Error("Failed to load Xml from file.", e); From c356ed1df8b1ae2e98ca4568e483dc5c661cd025 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 16 May 2015 10:29:21 +0200 Subject: [PATCH 0011/1710] Logger - add AppDomain.Id to log --- src/Umbraco.Core/Logging/LogHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Logging/LogHelper.cs b/src/Umbraco.Core/Logging/LogHelper.cs index 9291ffec1d..14aab56f0b 100644 --- a/src/Umbraco.Core/Logging/LogHelper.cs +++ b/src/Umbraco.Core/Logging/LogHelper.cs @@ -40,8 +40,8 @@ namespace Umbraco.Core.Logging /// private static string PrefixThreadId(string generateMessageFormat) { - return "[Thread " + Thread.CurrentThread.ManagedThreadId + "] " + generateMessageFormat; - } + return "[T" + Thread.CurrentThread.ManagedThreadId + "/D" + AppDomain.CurrentDomain.Id + "] " + generateMessageFormat; + } #region Error /// From 8121fc42e5a22fe45bba9ce5717faccfae213d29 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 17 May 2015 21:47:31 +0200 Subject: [PATCH 0012/1710] U4-6626 - fix log level --- src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index e48063ca22..46ea9c1146 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -531,7 +531,7 @@ namespace Umbraco.Web.Scheduling // processing, call the UnregisterObject method, and then return or it can return immediately and complete // processing asynchronously before calling the UnregisterObject method. - LogHelper.Debug>("Shutting down, waiting for tasks to complete."); + LogHelper.Info>("Shutting down, waiting for tasks to complete."); Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait lock (_locker) From 21a4dfe33eb553a6c332b98d2c90966af0676bac Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 17 May 2015 22:16:39 +0200 Subject: [PATCH 0013/1710] Fix changes in user section labels Fixed translation in da.xml according to change in en.xml from 7.2.4 to 7.2.5 --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index ad7737e5be..41e8760eea 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -863,7 +863,7 @@ Mange hilsner fra Umbraco robotten Redaktør Uddragsfelt Sprog - Login + Brugernavn Startnode i mediearkivet Moduler Deaktivér adgang til Umbraco @@ -881,12 +881,11 @@ Mange hilsner fra Umbraco robotten Vælg sider for at ændre deres rettigheder Søg alle 'børn' Start node - Brugernavn + Navn Bruger tilladelser Brugertype Bruger typer Forfatter - Din profil Din historik Session udløber From 76fb595a55d01c11d06cdb9a2043d7ddd6efd450 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 18 May 2015 13:38:14 +1000 Subject: [PATCH 0014/1710] Fixes: U4-6363 Expose Dictionary item access on UmbracoHelper --- .../CultureDictionaryFactoryResolver.cs | 17 ++- .../Dictionary/ICultureDictionary.cs | 8 ++ .../Dictionary/UmbracoCultureDictionary.cs | 112 ++++++++++++++++-- src/Umbraco.Web/UmbracoHelper.cs | 26 ++-- .../RazorCore/UmbracoCultureDictionary.cs | 9 +- 5 files changed, 146 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs b/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs index 80abfb0eaf..334c2fbe4b 100644 --- a/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs +++ b/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs @@ -1,3 +1,5 @@ +using System; +using System.ComponentModel; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.Dictionary @@ -12,15 +14,22 @@ namespace Umbraco.Core.Dictionary { } - /// - /// Can be used by developers at runtime to set their ICultureDictionaryFactory at app startup - /// - /// + [Obsolete("Use SetDictionaryFactory instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public void SetContentStore(ICultureDictionaryFactory factory) { Value = factory; } + /// + /// Can be used by developers at runtime to set their ICultureDictionaryFactory at app startup + /// + /// + public void SetDictionaryFactory(ICultureDictionaryFactory factory) + { + Value = factory; + } + /// /// Returns the ICultureDictionaryFactory /// diff --git a/src/Umbraco.Core/Dictionary/ICultureDictionary.cs b/src/Umbraco.Core/Dictionary/ICultureDictionary.cs index 57a739dbc6..8617d7440e 100644 --- a/src/Umbraco.Core/Dictionary/ICultureDictionary.cs +++ b/src/Umbraco.Core/Dictionary/ICultureDictionary.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; namespace Umbraco.Core.Dictionary @@ -19,5 +20,12 @@ namespace Umbraco.Core.Dictionary /// Returns the current culture /// CultureInfo Culture { get; } + + /// + /// Returns the child dictionary entries for a given key + /// + /// + /// + IDictionary GetChildren(string key); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs b/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs index e7ebb3cb6a..29954701ea 100644 --- a/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs +++ b/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs @@ -1,17 +1,61 @@ using System; +using System.Collections.Generic; using System.Dynamic; using System.Globalization; +using System.Linq; using System.Web; using Umbraco.Core.Logging; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.language; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Models; +using Umbraco.Core.Services; namespace Umbraco.Web.Dictionary { + /// + /// A culture dictionary that uses the Umbraco ILocalizationService + /// public class DefaultCultureDictionary : Umbraco.Core.Dictionary.ICultureDictionary { - /// + private readonly ILocalizationService _localizationService; + private readonly ICacheProvider _requestCacheProvider; + private readonly CultureInfo _specificCulture = null; + + public DefaultCultureDictionary() + : this(ApplicationContext.Current.Services.LocalizationService, ApplicationContext.Current.ApplicationCache.RequestCache) + { + + } + + public DefaultCultureDictionary(ILocalizationService localizationService, ICacheProvider requestCacheProvider) + { + if (localizationService == null) throw new ArgumentNullException("localizationService"); + if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider"); + _localizationService = localizationService; + _requestCacheProvider = requestCacheProvider; + } + + public DefaultCultureDictionary(CultureInfo specificCulture) + : this(ApplicationContext.Current.Services.LocalizationService, ApplicationContext.Current.ApplicationCache.RequestCache) + { + if (specificCulture == null) throw new ArgumentNullException("specificCulture"); + _specificCulture = specificCulture; + } + + public DefaultCultureDictionary(CultureInfo specificCulture, ILocalizationService localizationService, ICacheProvider requestCacheProvider) + { + if (specificCulture == null) throw new ArgumentNullException("specificCulture"); + if (localizationService == null) throw new ArgumentNullException("localizationService"); + if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider"); + _localizationService = localizationService; + _requestCacheProvider = requestCacheProvider; + _specificCulture = specificCulture; + } + + /// /// Returns the dictionary value based on the key supplied /// /// @@ -20,15 +64,19 @@ namespace Umbraco.Web.Dictionary { get { - try - { - return new global::umbraco.cms.businesslogic.Dictionary.DictionaryItem(key).Value(Language.id); - } - catch (Exception e) - { - LogHelper.WarnWithException("Error returning dictionary item '" + key + "'", true, e); - return string.Empty; - } + var found = _localizationService.GetDictionaryItemByKey(key); + if (found == null) + { + return string.Empty; + } + + var byLang = found.Translations.FirstOrDefault(x => x.Language.Equals(Language)); + if (byLang == null) + { + return string.Empty; + } + + return byLang.Value; } } @@ -37,12 +85,50 @@ namespace Umbraco.Web.Dictionary /// public CultureInfo Culture { - get { return System.Threading.Thread.CurrentThread.CurrentUICulture; } + get { return _specificCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture; } } - private Language Language + /// + /// Returns the child dictionary entries for a given key + /// + /// + /// + public IDictionary GetChildren(string key) + { + var result = new Dictionary(); + + var found = _localizationService.GetDictionaryItemByKey(key); + if (found == null) + { + return result; + } + + var children = _localizationService.GetDictionaryItemChildren(found.Key); + if (children == null) + { + return result; + } + + foreach (var dictionaryItem in children) + { + var byLang = dictionaryItem.Translations.FirstOrDefault((x => x.Language.Equals(Language))); + if (byLang != null) + { + result.Add(dictionaryItem.ItemKey, byLang.Value); + } + } + + return result; + } + + private ILanguage Language { - get { return Language.GetByCultureCode(System.Threading.Thread.CurrentThread.CurrentUICulture.Name); } + get + { + //ensure it's stored/retrieved from request cache + return _requestCacheProvider.GetCacheItem(typeof (DefaultCultureDictionary).Name + "Culture", + () => _localizationService.GetLanguageByIsoCode(Culture.Name)); + } } } diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 383897b2f2..d60630a58d 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -26,8 +26,7 @@ namespace Umbraco.Web private readonly UmbracoContext _umbracoContext; private readonly IPublishedContent _currentPage; private readonly ITypedPublishedContentQuery _typedQuery; - private readonly IDynamicPublishedContentQuery _dynamicQuery; - + private readonly IDynamicPublishedContentQuery _dynamicQuery; private readonly HtmlStringUtilities _stringUtilities = new HtmlStringUtilities(); private IUmbracoComponentRenderer _componentRenderer; @@ -362,14 +361,25 @@ namespace Umbraco.Web /// public string GetDictionaryValue(string key) { - if (_cultureDictionary == null) - { - var factory = CultureDictionaryFactoryResolver.Current.Factory; - _cultureDictionary = factory.CreateDictionary(); - } - return _cultureDictionary[key]; + return CultureDictionary[key]; } + /// + /// Returns the ICultureDictionary for access to dictionary items + /// + public ICultureDictionary CultureDictionary + { + get + { + if (_cultureDictionary == null) + { + var factory = CultureDictionaryFactoryResolver.Current.Factory; + _cultureDictionary = factory.CreateDictionary(); + } + return _cultureDictionary; + } + } + #endregion #region Membership diff --git a/src/umbraco.MacroEngines/RazorCore/UmbracoCultureDictionary.cs b/src/umbraco.MacroEngines/RazorCore/UmbracoCultureDictionary.cs index b228f0741e..8921925f9a 100644 --- a/src/umbraco.MacroEngines/RazorCore/UmbracoCultureDictionary.cs +++ b/src/umbraco.MacroEngines/RazorCore/UmbracoCultureDictionary.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Dynamic; using System.Globalization; using umbraco.cms.businesslogic; @@ -39,7 +40,13 @@ namespace umbraco.MacroEngines } } - public Language Language + [Obsolete("This returns an empty dictionary, it is not intended to be used by the legacy razor macros")] + public IDictionary GetChildren(string key) + { + return new Dictionary(); + } + + public Language Language { get { return Language.GetByCultureCode(System.Threading.Thread.CurrentThread.CurrentUICulture.Name); } } From b01f8799aea93360cd3f7e3d9bcb36eafa0602b9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 18 May 2015 16:03:25 +1000 Subject: [PATCH 0015/1710] oops! long time ago with the original 7.3 refactor of runtime caches, before we fixed deep cloning, the changes made just used normal http runtime cache, but with the deep cloning fixes these were not carried over. This fixes that by forcing the base RepositoryBase to use a DeepCloneRuntimeCacheProvider which is a wrapper for the normal runtime cache but ensures deep cloning in/out and resets dirty properties. --- .../DeepCloneRuntimeCacheProvider.cs | 126 ++++++++++++++++++ .../Repositories/RepositoryBase.cs | 6 +- 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs new file mode 100644 index 0000000000..fa998f06a2 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Caching; +using Umbraco.Core.Cache; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Persistence.Repositories +{ + /// + /// Ensures that all inserts and returns are a deep cloned copy of the item when + /// the item is IDeepCloneable + /// + internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider + { + private readonly IRuntimeCacheProvider _innerProvider; + + public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider) + { + _innerProvider = innerProvider; + } + + #region Clear - doesn't require any changes + public void ClearAllCache() + { + _innerProvider.ClearAllCache(); + } + + public void ClearCacheItem(string key) + { + _innerProvider.ClearCacheItem(key); + } + + public void ClearCacheObjectTypes(string typeName) + { + _innerProvider.ClearCacheObjectTypes(typeName); + } + + public void ClearCacheObjectTypes() + { + _innerProvider.ClearCacheObjectTypes(); + } + + public void ClearCacheObjectTypes(Func predicate) + { + _innerProvider.ClearCacheObjectTypes(predicate); + } + + public void ClearCacheByKeySearch(string keyStartsWith) + { + _innerProvider.ClearCacheByKeySearch(keyStartsWith); + } + + public void ClearCacheByKeyExpression(string regexString) + { + _innerProvider.ClearCacheByKeyExpression(regexString); + } + #endregion + + public IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + { + return _innerProvider.GetCacheItemsByKeySearch(keyStartsWith) + .Select(CheckCloneableAndTracksChanges); + } + + public IEnumerable GetCacheItemsByKeyExpression(string regexString) + { + return _innerProvider.GetCacheItemsByKeyExpression(regexString) + .Select(CheckCloneableAndTracksChanges); + } + + public object GetCacheItem(string cacheKey) + { + var item = _innerProvider.GetCacheItem(cacheKey); + return CheckCloneableAndTracksChanges(item); + } + + public object GetCacheItem(string cacheKey, Func getCacheItem) + { + return _innerProvider.GetCacheItem(cacheKey, () => + { + //Resolve the item but returned the cloned/reset item + var item = getCacheItem(); + return CheckCloneableAndTracksChanges(item); + }); + } + + public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) + { + return _innerProvider.GetCacheItem(cacheKey, () => + { + //Resolve the item but returned the cloned/reset item + var item = getCacheItem(); + return CheckCloneableAndTracksChanges(item); + }, timeout, isSliding, priority, removedCallback, dependentFiles); + } + + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) + { + _innerProvider.InsertCacheItem(cacheKey, () => + { + //Resolve the item but returned the cloned/reset item + var item = getCacheItem(); + return CheckCloneableAndTracksChanges(item); + }, timeout, isSliding, priority, removedCallback, dependentFiles); + } + + private static object CheckCloneableAndTracksChanges(object input) + { + var entity = input as IDeepCloneable; + if (entity == null) return input; + + var cloned = entity.DeepClone(); + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + var tracksChanges = cloned as TracksChangesEntityBase; + if (tracksChanges != null) + { + tracksChanges.ResetDirtyProperties(false); + return tracksChanges; + } + return cloned; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 0d3c7db7a7..4c5087674f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -22,7 +22,11 @@ namespace Umbraco.Core.Persistence.Repositories if (logger == null) throw new ArgumentNullException("logger"); Logger = logger; _work = work; - _cache = cache; + + //IMPORTANT: We will force the DeepCloneRuntimeCacheProvider to be used here which is a wrapper for the underlying + // runtime cache to ensure that anything that can be deep cloned in/out is done so, this also ensures that our tracks + // changes entities are reset. + _cache = new CacheHelper(new DeepCloneRuntimeCacheProvider(cache.RuntimeCache), cache.StaticCache, cache.RequestCache); } /// From 1d0b5f3251f72a603ea49b59eb51027b733caaa6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 18 May 2015 16:04:23 +1000 Subject: [PATCH 0016/1710] ensures courier has access to internals --- src/Umbraco.Core/Properties/AssemblyInfo.cs | 3 +-- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Web/Properties/AssemblyInfo.cs | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 8782ce7b80..d5c55c90d8 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -36,12 +36,11 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Umbraco.Web")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] [assembly: InternalsVisibleTo("UmbracoExamine")] -[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] [assembly: InternalsVisibleTo("Concorde.Sync")] [assembly: InternalsVisibleTo("Umbraco.Belle")] [assembly: InternalsVisibleTo("Umbraco.VisualStudio")] -[assembly: InternalsVisibleTo("Concorde.Courier.Core")] +[assembly: InternalsVisibleTo("Umbraco.Courier.Core")] [assembly: InternalsVisibleTo("umbraco.providers")] //allow this to be mocked in our unit tests diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 19cba49d14..4b1ec43f96 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -386,6 +386,7 @@ + diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs index b3d649d968..ab7b13898e 100644 --- a/src/Umbraco.Web/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Web/Properties/AssemblyInfo.cs @@ -30,10 +30,9 @@ using System.Security; [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("umbraco.MacroEngines")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] -[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] [assembly: InternalsVisibleTo("umbraco.webservices")] [assembly: InternalsVisibleTo("Concorde.Sync")] -[assembly: InternalsVisibleTo("Concorde.Courier.Core")] +[assembly: InternalsVisibleTo("Umbraco.Courier.Core")] [assembly: InternalsVisibleTo("Umbraco.Belle")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file From 76a10fc776f20b8d6ae4f470fee0ba2cd0f6342f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 18 May 2015 16:06:22 +1000 Subject: [PATCH 0017/1710] Works on: U4-1979 Some legacy business logic APIs do not wrap the new Service APIs. Deprecates the ctor passing in a parent id for the data type (since it's always -1). Wraps legacy DataTypeDefinition to use the new services layer. --- src/Umbraco.Core/Models/DataTypeDefinition.cs | 12 ++ .../Factories/DataTypeDefinitionFactory.cs | 2 +- .../LegacyPropertyEditorIdToAliasConverter.cs | 8 +- src/Umbraco.Core/Services/PackagingService.cs | 2 +- .../DataTypeDefinitionRepositoryTest.cs | 6 +- .../Cache/CacheRefresherEventHandler.cs | 14 +- .../Cache/DataTypeCacheRefresher.cs | 32 +-- .../Cache/DistributedCacheExtensions.cs | 14 +- src/Umbraco.Web/Editors/DataTypeController.cs | 2 +- .../Models/Mapping/DataTypeModelMapper.cs | 2 +- .../datatype/DataTypeDefinition.cs | 200 +++++++----------- 11 files changed, 104 insertions(+), 190 deletions(-) diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs index ba36c88a2a..e2a226af3d 100644 --- a/src/Umbraco.Core/Models/DataTypeDefinition.cs +++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs @@ -33,6 +33,7 @@ namespace Umbraco.Core.Models private DataTypeDatabaseType _databaseType; [Obsolete("Property editor's are defined by a string alias from version 7 onwards, use the alternative contructor that specifies an alias")] + [EditorBrowsable(EditorBrowsableState.Never)] public DataTypeDefinition(int parentId, Guid controlId) { _parentId = parentId; @@ -47,6 +48,9 @@ namespace Umbraco.Core.Models _additionalData = new Dictionary(); } + + [Obsolete("Don't use this, parentId is always -1 for data types")] + [EditorBrowsable(EditorBrowsableState.Never)] public DataTypeDefinition(int parentId, string propertyEditorAlias) { _parentId = parentId; @@ -55,6 +59,14 @@ namespace Umbraco.Core.Models _additionalData = new Dictionary(); } + public DataTypeDefinition(string propertyEditorAlias) + { + _parentId = -1; + _propertyEditorAlias = propertyEditorAlias; + + _additionalData = new Dictionary(); + } + private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId); private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder); diff --git a/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs b/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs index 6fd6500552..377d70e3cb 100644 --- a/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence.Factories public IDataTypeDefinition BuildEntity(DataTypeDto dto) { - var dataTypeDefinition = new DataTypeDefinition(dto.NodeDto.ParentId, dto.PropertyEditorAlias) + var dataTypeDefinition = new DataTypeDefinition(dto.PropertyEditorAlias) { CreateDate = dto.NodeDto.CreateDate, DatabaseType = dto.DbType.EnumParse(true), diff --git a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs index 3da704eaf9..41400f8787 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs @@ -134,6 +134,8 @@ namespace Umbraco.Core.PropertyEditors CreateMap(Guid.Parse(Constants.PropertyEditors.UploadField), Constants.PropertyEditors.UploadFieldAlias); CreateMap(Guid.Parse(Constants.PropertyEditors.XPathCheckBoxList), Constants.PropertyEditors.XPathCheckBoxListAlias); CreateMap(Guid.Parse(Constants.PropertyEditors.XPathDropDownList), Constants.PropertyEditors.XPathDropDownListAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.MacroContainerAlias); + CreateMap(Guid.Parse(Constants.PropertyEditors.ImageCropper), Constants.PropertyEditors.ImageCropperAlias); //Being mapped to different editors //TODO: Map this somewhere! @@ -144,10 +146,8 @@ namespace Umbraco.Core.PropertyEditors //Not being converted - convert to label CreateMap(Guid.Parse(Constants.PropertyEditors.DictionaryPicker), Constants.PropertyEditors.NoEditAlias); CreateMap(Guid.Parse(Constants.PropertyEditors.UmbracoUserControlWrapper), Constants.PropertyEditors.NoEditAlias); - - //Not ready for v7.0! - will need to create for 7.1 - CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.NoEditAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.ImageCropper), Constants.PropertyEditors.NoEditAlias); + + } } diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 5b44cbb3e0..11f383a0fe 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -828,7 +828,7 @@ namespace Umbraco.Core.Services else { //the Id field is actually the string property editor Alias - var dataTypeDefinition = new DataTypeDefinition(-1, dataTypeElement.Attribute("Id").Value.Trim()) + var dataTypeDefinition = new DataTypeDefinition(dataTypeElement.Attribute("Id").Value.Trim()) { Key = dataTypeDefinitionId, Name = dataTypeDefinitionName, diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index ec2d1b981c..f3d7aaa0f1 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -199,7 +199,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var repository = CreateRepository(unitOfWork)) { - var dataTypeDefinition = new DataTypeDefinition(-1, "Test.TestEditor") + var dataTypeDefinition = new DataTypeDefinition("Test.TestEditor") { DatabaseType = DataTypeDatabaseType.Integer, Name = "AgeDataType", @@ -226,7 +226,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var repository = CreateRepository(unitOfWork)) { - var dataTypeDefinition = new DataTypeDefinition(-1, "Test.blah") + var dataTypeDefinition = new DataTypeDefinition("Test.blah") { DatabaseType = DataTypeDatabaseType.Integer, Name = "AgeDataType", @@ -260,7 +260,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var repository = CreateRepository(unitOfWork)) { - var dataTypeDefinition = new DataTypeDefinition(-1, "Test.TestEditor") + var dataTypeDefinition = new DataTypeDefinition("Test.TestEditor") { DatabaseType = DataTypeDatabaseType.Integer, Name = "AgeDataType", diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index ff23c57976..48c7692dae 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -46,9 +46,7 @@ namespace Umbraco.Web.Cache //Bind to data type events //NOTE: we need to bind to legacy and new API events currently: http://issues.umbraco.org/issue/U4-1979 - //TODO: Wrap as much of this api as possible so that we don't need to listen to legacy events! - global::umbraco.cms.businesslogic.datatype.DataTypeDefinition.AfterDelete += DataTypeDefinitionDeleting; - global::umbraco.cms.businesslogic.datatype.DataTypeDefinition.Saving += DataTypeDefinitionSaving; + DataTypeService.Deleted += DataTypeServiceDeleted; DataTypeService.Saved += DataTypeServiceSaved; @@ -380,15 +378,7 @@ namespace Umbraco.Web.Cache e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveDataTypeCache(x)); } - static void DataTypeDefinitionSaving(global::umbraco.cms.businesslogic.datatype.DataTypeDefinition sender, System.EventArgs e) - { - DistributedCache.Instance.RefreshDataTypeCache(sender); - } - - static void DataTypeDefinitionDeleting(global::umbraco.cms.businesslogic.datatype.DataTypeDefinition sender, System.EventArgs e) - { - DistributedCache.Instance.RemoveDataTypeCache(sender); - } + #endregion #region Stylesheet and stylesheet property event handlers diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index c76d5413e2..86114d5f77 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -27,20 +27,7 @@ namespace Umbraco.Web.Cache var serializer = new JavaScriptSerializer(); var jsonObject = serializer.Deserialize(json); return jsonObject; - } - - /// - /// Creates the custom Json payload used to refresh cache amongst the servers - /// - /// - /// - internal static string SerializeToJsonPayload(params global::umbraco.cms.businesslogic.datatype.DataTypeDefinition[] dataTypes) - { - var serializer = new JavaScriptSerializer(); - var items = dataTypes.Select(FromDataTypeDefinition).ToArray(); - var json = serializer.Serialize(items); - return json; - } + } /// /// Creates the custom Json payload used to refresh cache amongst the servers @@ -54,22 +41,7 @@ namespace Umbraco.Web.Cache var json = serializer.Serialize(items); return json; } - - /// - /// Converts a macro to a jsonPayload object - /// - /// - /// - private static JsonPayload FromDataTypeDefinition(global::umbraco.cms.businesslogic.datatype.DataTypeDefinition dataType) - { - var payload = new JsonPayload - { - UniqueId = dataType.UniqueId, - Id = dataType.Id - }; - return payload; - } - + /// /// Converts a macro to a jsonPayload object /// diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index c3c1d64d89..84ed6f225e 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -125,19 +125,7 @@ namespace Umbraco.Web.Cache #endregion - #region Data type cache - - public static void RefreshDataTypeCache(this DistributedCache dc, global::umbraco.cms.businesslogic.datatype.DataTypeDefinition dataType) - { - if (dataType == null) return; - dc.RefreshByJson(DistributedCache.DataTypeCacheRefresherGuid, DataTypeCacheRefresher.SerializeToJsonPayload(dataType)); - } - - public static void RemoveDataTypeCache(this DistributedCache dc, global::umbraco.cms.businesslogic.datatype.DataTypeDefinition dataType) - { - if (dataType == null) return; - dc.RefreshByJson(DistributedCache.DataTypeCacheRefresherGuid, DataTypeCacheRefresher.SerializeToJsonPayload(dataType)); - } + #region Data type cache public static void RefreshDataTypeCache(this DistributedCache dc, IDataTypeDefinition dataType) { diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 67b5619e3c..0508e43fc1 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -81,7 +81,7 @@ namespace Umbraco.Web.Editors public DataTypeDisplay GetEmpty() { - var dt = new DataTypeDefinition(-1, ""); + var dt = new DataTypeDefinition(""); return Mapper.Map(dt); } diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs index 8652cfb8c6..8d0411dd1e 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs @@ -59,7 +59,7 @@ namespace Umbraco.Web.Models.Mapping }); config.CreateMap() - .ConstructUsing(save => new DataTypeDefinition(-1, save.SelectedEditor) {CreateDate = DateTime.Now}) + .ConstructUsing(save => new DataTypeDefinition(save.SelectedEditor) {CreateDate = DateTime.Now}) .ForMember(definition => definition.Id, expression => expression.MapFrom(save => Convert.ToInt32(save.Id))) //we have to ignore the Key otherwise this will reset the UniqueId field which should never change! // http://issues.umbraco.org/issue/U4-3911 diff --git a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs index da7005ff72..1e86544ab9 100644 --- a/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs +++ b/src/umbraco.cms/businesslogic/datatype/DataTypeDefinition.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.ComponentModel; using System.Globalization; using System.Data; using System.Linq; @@ -13,12 +14,11 @@ using umbraco.cms.businesslogic.media; using umbraco.interfaces; using PropertyType = umbraco.cms.businesslogic.propertytype.PropertyType; using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; namespace umbraco.cms.businesslogic.datatype { - - //TODO: Wrap as much of this api as possible so that we don't need to listen to legacy events! - /// /// Datatypedefinitions is the basic buildingblocks of umbraco's documents/medias/members generic datastructure /// @@ -30,17 +30,16 @@ namespace umbraco.cms.businesslogic.datatype [Obsolete("This class is no longer used and will be removed from the codebase in the future.")] public class DataTypeDefinition : CMSNode { - #region Private fields - - internal string PropertyEditorAlias { get; private set; } - - private static readonly Guid ObjectType = new Guid(Constants.ObjectTypes.DataType); - private string _text1; - - #endregion + internal IDataTypeDefinition DataTypeItem; #region Constructors + internal DataTypeDefinition(IDataTypeDefinition dataType) + : base(dataType) + { + DataTypeItem = dataType; + } + /// /// Initialization of the datatypedefinition /// @@ -59,8 +58,8 @@ namespace umbraco.cms.businesslogic.datatype public override string Text { - get { return _text1 ?? (_text1 = base.Text); } - set { _text1 = value; } + get { return DataTypeItem.Name; } + set { DataTypeItem.Name = value; } } /// @@ -70,7 +69,7 @@ namespace umbraco.cms.businesslogic.datatype { get { - if (PropertyEditorAlias.IsNullOrWhiteSpace()) + if (DataTypeItem.PropertyEditorAlias.IsNullOrWhiteSpace()) return null; //Attempt to resolve a legacy control id from the alias. If one is not found we'll generate one - @@ -79,7 +78,7 @@ namespace umbraco.cms.businesslogic.datatype //So, we'll generate an id for it based on the alias which will remain consistent, but then we'll try to resolve a legacy // IDataType which of course will not exist. In this case we'll have to create a new one on the fly for backwards compatibility but // this instance will have limited capabilities and will really only work for saving data so the legacy APIs continue to work. - var controlId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); + var controlId = LegacyPropertyEditorIdToAliasConverter.GetLegacyIdFromAlias(DataTypeItem.PropertyEditorAlias, LegacyPropertyEditorIdToAliasConverter.NotFoundLegacyIdResponseBehavior.GenerateId); var dt = DataTypesResolver.Current.GetById(controlId.Value); @@ -91,7 +90,7 @@ namespace umbraco.cms.businesslogic.datatype { //Ok so it was not found, we can only assume that this is because this is a new property editor that does not have a legacy predecessor. //we'll have to attempt to generate one at runtime. - dt = BackwardsCompatibleDataType.Create(PropertyEditorAlias, controlId.Value, Id); + dt = BackwardsCompatibleDataType.Create(DataTypeItem.PropertyEditorAlias, controlId.Value, Id); } @@ -99,23 +98,16 @@ namespace umbraco.cms.businesslogic.datatype } set { - if (SqlHelper == null) - throw new InvalidOperationException("Cannot execute a SQL command when the SqlHelper is null"); if (value == null) throw new InvalidOperationException("The value passed in is null. The DataType property cannot be set to a null value"); var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(value.Id, true); - SqlHelper.ExecuteNonQuery("update cmsDataType set propertyEditorAlias = @alias where nodeID = " + this.Id, - SqlHelper.CreateParameter("@alias", alias)); - - - - PropertyEditorAlias = alias; + DataTypeItem.PropertyEditorAlias = alias; } } - internal string DbType { get; private set; } + //internal string DbType { get; private set; } #endregion @@ -126,22 +118,8 @@ namespace umbraco.cms.businesslogic.datatype FireBeforeDelete(e); if (e.Cancel == false) { - //first clear the prevalues - PreValues.DeleteByDataTypeDefinition(this.Id); - - //next clear out the property types - var propTypes = PropertyType.GetByDataTypeDefinition(this.Id); - foreach (var p in propTypes) - { - p.delete(); - } - - //delete the cmsDataType role, then the umbracoNode - SqlHelper.ExecuteNonQuery("delete from cmsDataType where nodeId=@nodeId", - SqlHelper.CreateParameter("@nodeId", this.Id)); - base.delete(); - - + ApplicationContext.Current.Services.DataTypeService.Delete(DataTypeItem); + FireAfterDelete(e); } } @@ -157,58 +135,24 @@ namespace umbraco.cms.businesslogic.datatype /// public override void Save() { - //Cannot change to a duplicate alias - var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType -INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id -WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @"= @name -AND umbracoNode.id <> @id", - new { id = this.Id, name = this.Text }); - if (exists > 0) - { - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem( - string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, this.Id)); - - throw new DuplicateNameException("A data type with the name " + this.Text + " already exists"); - } - - //this actually does the persisting. - base.Text = _text1; + ApplicationContext.Current.Services.DataTypeService.Save(DataTypeItem); OnSaving(EventArgs.Empty); } public XmlElement ToXml(XmlDocument xd) { - //here we need to get the property editor alias from it's id - var alias = LegacyPropertyEditorIdToAliasConverter.GetAliasFromLegacyId(DataType.Id, true); + var serializer = new EntityXmlSerializer(); + var xml = serializer.Serialize(ApplicationContext.Current.Services.DataTypeService, DataTypeItem); + return (XmlElement)xml.GetXmlNode(xd); - var dt = xd.CreateElement("DataType"); - dt.Attributes.Append(xmlHelper.addAttribute(xd, "Name", Text)); - //The 'ID' when exporting is actually the property editor alias (in pre v7 it was the IDataType GUID id) - dt.Attributes.Append(xmlHelper.addAttribute(xd, "Id", alias)); - dt.Attributes.Append(xmlHelper.addAttribute(xd, "Definition", UniqueId.ToString())); - dt.Attributes.Append(xmlHelper.addAttribute(xd, "DatabaseType", DbType)); - - // templates - var prevalues = xd.CreateElement("PreValues"); - foreach (DictionaryEntry item in PreValues.GetPreValues(Id)) - { - var prevalue = xd.CreateElement("PreValue"); - prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Id", ((PreValue)item.Value).Id.ToString(CultureInfo.InvariantCulture))); - prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Value", ((PreValue)item.Value).Value)); - prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Alias", ((PreValue)item.Value).Alias)); - prevalues.AppendChild(prevalue); - } - - dt.AppendChild(prevalues); - - return dt; } #endregion #region Static methods [Obsolete("Do not use this method, it will not function correctly because legacy property editors are not supported in v7")] + [EditorBrowsable(EditorBrowsableState.Never)] public static DataTypeDefinition Import(XmlNode xmlData) { var name = xmlData.Attributes["Name"].Value; @@ -254,23 +198,8 @@ AND umbracoNode.id <> @id", /// A list of all datatypedefinitions public static DataTypeDefinition[] GetAll() { - var retvalSort = new SortedList(); - var tmp = getAllUniquesFromObjectType(ObjectType); - var retval = new DataTypeDefinition[tmp.Length]; - for (var i = 0; i < tmp.Length; i++) - { - var dt = GetDataTypeDefinition(tmp[i]); - retvalSort.Add(dt.Text + "|||" + Guid.NewGuid(), dt); - } - - var ide = retvalSort.GetEnumerator(); - var counter = 0; - while (ide.MoveNext()) - { - retval[counter] = (DataTypeDefinition)ide.Value; - counter++; - } - return retval; + var result = ApplicationContext.Current.Services.DataTypeService.GetAllDataTypeDefinitions(); + return result.Select(x => new DataTypeDefinition(x)).ToArray(); } /// @@ -293,21 +222,21 @@ AND umbracoNode.id <> @id", /// public static DataTypeDefinition MakeNew(BusinessLogic.User u, string Text, Guid UniqueId) { - //Cannot add a duplicate data type - var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsDataType -INNER JOIN umbracoNode ON cmsDataType.nodeId = umbracoNode.id -WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + "= @name", new { name = Text }); - if (exists > 0) + var found = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionByName(Text); + if (found != null) { throw new DuplicateNameException("A data type with the name " + Text + " already exists"); } - var newId = MakeNew(-1, ObjectType, u.Id, 1, Text, UniqueId).Id; + var created = new Umbraco.Core.Models.DataTypeDefinition("") + { + Name = Text, + Key = UniqueId + }; - //insert empty prop ed alias - SqlHelper.ExecuteNonQuery("Insert into cmsDataType (nodeId, propertyEditorAlias, dbType) values (" + newId + ",'','Ntext')"); + ApplicationContext.Current.Services.DataTypeService.Save(created); - var dtd = new DataTypeDefinition(newId); + var dtd = new DataTypeDefinition(created); dtd.OnNew(EventArgs.Empty); return dtd; @@ -353,37 +282,60 @@ WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("te public static DataTypeDefinition GetDataTypeDefinition(int id) { - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, id), - () => new DataTypeDefinition(id)); + var found = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(id); + return found == null ? null : new DataTypeDefinition(found); } [Obsolete("Use GetDataTypeDefinition(int id) instead", false)] public static DataTypeDefinition GetDataTypeDefinition(Guid id) { - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - string.Format("{0}{1}", CacheKeys.DataTypeCacheKey, id), - () => new DataTypeDefinition(id)); + var found = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(id); + return found == null ? null : new DataTypeDefinition(found); } #endregion #region Protected methods + protected override void setupNode() { - base.setupNode(); - - using (var dr = SqlHelper.ExecuteReader("select dbType, propertyEditorAlias from cmsDataType where nodeId = '" + this.Id.ToString() + "'")) + DataTypeItem = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(Id); + if (DataTypeItem == null) { - if (dr.Read()) - { - PropertyEditorAlias = dr.GetString("propertyEditorAlias"); - DbType = dr.GetString("dbType"); - } - else - throw new ArgumentException("No dataType with id = " + this.Id.ToString() + " found"); + throw new ArgumentException("No dataType with id = " + this.Id.ToString() + " found"); } + //base.setupNode(); + + //using (var dr = SqlHelper.ExecuteReader("select dbType, propertyEditorAlias from cmsDataType where nodeId = '" + this.Id.ToString() + "'")) + //{ + // if (dr.Read()) + // { + // PropertyEditorAlias = dr.GetString("propertyEditorAlias"); + // DbType = dr.GetString("dbType"); + // } + // else + // throw new ArgumentException("No dataType with id = " + this.Id.ToString() + " found"); + //} + } + + //protected void SetupNode(IDataTypeDefinition dtd) + //{ + // base.setupNode(); + + // using (var dr = SqlHelper.ExecuteReader("select dbType, propertyEditorAlias from cmsDataType where nodeId = '" + this.Id.ToString() + "'")) + // { + // if (dr.Read()) + // { + // PropertyEditorAlias = dr.GetString("propertyEditorAlias"); + // DbType = dr.GetString("dbType"); + // } + // else + // throw new ArgumentException("No dataType with id = " + this.Id.ToString() + " found"); + // } + + //} + #endregion #region Events @@ -425,7 +377,7 @@ WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("te /// Raises the event. /// /// The instance containing the event data. - protected new virtual void FireBeforeDelete(DeleteEventArgs e) + protected override void FireBeforeDelete(DeleteEventArgs e) { if (BeforeDelete != null) BeforeDelete(this, e); @@ -439,7 +391,7 @@ WHERE umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("te /// Raises the event. /// /// The instance containing the event data. - protected new virtual void FireAfterDelete(DeleteEventArgs e) + protected override void FireAfterDelete(DeleteEventArgs e) { if (AfterDelete != null) AfterDelete(this, e); From 25f9fecc165db9c5cb50c51ccda4782085d2ff26 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 18 May 2015 19:34:01 +1000 Subject: [PATCH 0018/1710] Completes: U4-1979 Some legacy business logic APIs do not wrap the new Service APIs - gets permissions wrapped too --- .../Interfaces/IUserRepository.cs | 8 ++ .../Repositories/PermissionRepository.cs | 36 ++++++ .../Repositories/UserRepository.cs | 12 ++ .../Services/ContentServiceExtensions.cs | 11 ++ src/Umbraco.Core/Services/IUserService.cs | 17 ++- src/Umbraco.Core/Services/UserService.cs | 15 +++ .../Services/UserServiceExtensions.cs | 21 ++++ .../Properties/AssemblyInfo.cs | 1 + src/umbraco.businesslogic/User.cs | 78 ++++++------- src/umbraco.cms/businesslogic/Permission.cs | 107 ++++++------------ 10 files changed, 189 insertions(+), 117 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index e40d996637..542cceac40 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -55,5 +55,13 @@ namespace Umbraco.Core.Persistence.Repositories /// /// void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); + + /// + /// Assigns the same permission set for a single user to any number of entities + /// + /// + /// + /// + void AssignUserPermission(int userId, char permission, params int[] entityIds); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index 8a2cd21acd..10c20663e4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -152,6 +152,42 @@ namespace Umbraco.Core.Persistence.Repositories AssignedPermissions.RaiseEvent( new SaveEventArgs(ConvertToPermissionList(toInsert), false), this); } + } + + /// + /// Assigns one permission for a user to many entities + /// + /// + /// + /// + public void AssignUserPermission(int userId, char permission, params int[] entityIds) + { + var db = _unitOfWork.Database; + using (var trans = db.GetTransaction()) + { + db.Execute("DELETE FROM umbracoUser2NodePermission WHERE userId=@userId AND permission=@permission AND nodeId in (@entityIds)", + new + { + userId = userId, + permission = permission.ToString(CultureInfo.InvariantCulture), + entityIds = entityIds + }); + + var actions = entityIds.Select(id => new User2NodePermissionDto + { + NodeId = id, + Permission = permission.ToString(CultureInfo.InvariantCulture), + UserId = userId + }).ToArray(); + + _unitOfWork.Database.BulkInsertRecords(actions, trans); + + trans.Complete(); + + //Raise the event + AssignedPermissions.RaiseEvent( + new SaveEventArgs(ConvertToPermissionList(actions), false), this); + } } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 670cb89c5d..86eb2964fc 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -389,6 +389,18 @@ namespace Umbraco.Core.Persistence.Repositories repo.ReplaceUserPermissions(userId, permissions, entityIds); } + /// + /// Assigns the same permission set for a single user to any number of entities + /// + /// + /// + /// + public void AssignUserPermission(int userId, char permission, params int[] entityIds) + { + var repo = new PermissionRepository(UnitOfWork, _cacheHelper, SqlSyntax); + repo.AssignUserPermission(userId, permission, entityIds); + } + #endregion private IEnumerable ConvertFromDtos(IEnumerable dtos) diff --git a/src/Umbraco.Core/Services/ContentServiceExtensions.cs b/src/Umbraco.Core/Services/ContentServiceExtensions.cs index 5180955542..4919a81217 100644 --- a/src/Umbraco.Core/Services/ContentServiceExtensions.cs +++ b/src/Umbraco.Core/Services/ContentServiceExtensions.cs @@ -1,9 +1,20 @@ using System.Linq; +using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Services { public static class ContentServiceExtensions { + /// + /// Remove all permissions for this user for all nodes + /// + /// + /// + public static void RemoveContentPermissions(this IContentService contentService, int contentId) + { + contentService.ReplaceContentPermissions(new EntityPermissionSet(contentId, Enumerable.Empty())); + } + /// /// Returns true if there is any content in the recycle bin /// diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index aa8250cf59..7353f44cb8 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -73,13 +73,24 @@ namespace Umbraco.Core.Services /// /// Replaces the same permission set for a single user to any number of entities - /// - /// If no 'entityIds' are specified all permissions will be removed for the specified user. + /// /// Id of the user - /// Permissions as enumerable list of + /// + /// Permissions as enumerable list of , + /// if no permissions are specified then all permissions for this node are removed for this user + /// /// Specify the nodes to replace permissions for. If nothing is specified all permissions are removed. + /// If no 'entityIds' are specified all permissions will be removed for the specified user. void ReplaceUserPermissions(int userId, IEnumerable permissions, params int[] entityIds); + /// + /// Assigns the same permission set for a single user to any number of entities + /// + /// Id of the user + /// + /// Specify the nodes to replace permissions for + void AssignUserPermission(int userId, char permission, params int[] entityIds); + #region User types /// diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 6ba20cd4fa..961175f455 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -559,6 +559,21 @@ namespace Umbraco.Core.Services } } + /// + /// Assigns the same permission set for a single user to any number of entities + /// + /// Id of the user + /// + /// Specify the nodes to replace permissions for + public void AssignUserPermission(int userId, char permission, params int[] entityIds) + { + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateUserRepository(uow)) + { + repository.AssignUserPermission(userId, permission, entityIds); + } + } + /// /// Gets all UserTypes or thosed specified as parameters /// diff --git a/src/Umbraco.Core/Services/UserServiceExtensions.cs b/src/Umbraco.Core/Services/UserServiceExtensions.cs index b97a40ace8..afb679afa1 100644 --- a/src/Umbraco.Core/Services/UserServiceExtensions.cs +++ b/src/Umbraco.Core/Services/UserServiceExtensions.cs @@ -6,6 +6,27 @@ namespace Umbraco.Core.Services { internal static class UserServiceExtensions { + /// + /// Remove all permissions for this user for all nodes specified + /// + /// + /// + /// + public static void RemoveUserPermissions(this IUserService userService, int userId, params int[] entityIds) + { + userService.ReplaceUserPermissions(userId, new char[] {}, entityIds); + } + + /// + /// Remove all permissions for this user for all nodes + /// + /// + /// + public static void RemoveUserPermissions(this IUserService userService, int userId) + { + userService.ReplaceUserPermissions(userId, new char[] { }); + } + /// /// Maps a custom provider's information to an umbraco user account /// diff --git a/src/umbraco.businesslogic/Properties/AssemblyInfo.cs b/src/umbraco.businesslogic/Properties/AssemblyInfo.cs index 73bfa02ee2..ca3992844b 100644 --- a/src/umbraco.businesslogic/Properties/AssemblyInfo.cs +++ b/src/umbraco.businesslogic/Properties/AssemblyInfo.cs @@ -18,4 +18,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("umbraco")] +[assembly: InternalsVisibleTo("cms")] [assembly: InternalsVisibleTo("Umbraco.LegacyTests")] \ No newline at end of file diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs index e3349f6af6..475a2614dd 100644 --- a/src/umbraco.businesslogic/User.cs +++ b/src/umbraco.businesslogic/User.cs @@ -23,7 +23,7 @@ namespace umbraco.BusinessLogic [Obsolete("Use the UserService instead")] public class User { - private IUser _user; + internal IUser UserEntity; private int? _lazyId; private bool? _defaultToLiveEditing; @@ -38,7 +38,7 @@ namespace umbraco.BusinessLogic internal User(IUser user) { - _user = user; + UserEntity = user; } /// @@ -81,8 +81,8 @@ namespace umbraco.BusinessLogic private void SetupUser(int ID) { - _user = ApplicationContext.Current.Services.UserService.GetUserById(ID); - if (_user == null) + UserEntity = ApplicationContext.Current.Services.UserService.GetUserById(ID); + if (UserEntity == null) { throw new ArgumentException("No User exists with ID " + ID); } @@ -95,7 +95,7 @@ namespace umbraco.BusinessLogic { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - ApplicationContext.Current.Services.UserService.Save(_user); + ApplicationContext.Current.Services.UserService.Save(UserEntity); OnSaving(EventArgs.Empty); } @@ -109,11 +109,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.Name; + return UserEntity.Name; } set { - _user.Name = value; + UserEntity.Name = value; } } @@ -127,11 +127,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.Email; + return UserEntity.Email; } set { - _user.Email = value; + UserEntity.Email = value; } } @@ -144,11 +144,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.Language; + return UserEntity.Language; } set { - _user.Language = value; + UserEntity.Language = value; } } @@ -164,7 +164,7 @@ namespace umbraco.BusinessLogic } set { - _user.RawPasswordValue = value; + UserEntity.RawPasswordValue = value; } } @@ -175,7 +175,7 @@ namespace umbraco.BusinessLogic public string GetPassword() { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.RawPasswordValue; + return UserEntity.RawPasswordValue; } /// @@ -233,7 +233,7 @@ namespace umbraco.BusinessLogic var allApps = Application.getAll(); var apps = new List(); - var sections = _user.AllowedSections; + var sections = UserEntity.AllowedSections; foreach (var s in sections) { @@ -254,14 +254,14 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.Username; + return UserEntity.Username; } set { if (EnsureUniqueLoginName(value, this) == false) throw new Exception(String.Format("A user with the login '{0}' already exists", value)); - _user.Username = value; + UserEntity.Username = value; } } @@ -327,11 +327,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return new UserType(_user.UserType); + return new UserType(UserEntity.UserType); } set { - _user.UserType = value.UserTypeItem; + UserEntity.UserType = value.UserTypeItem; } } @@ -576,7 +576,7 @@ namespace umbraco.BusinessLogic OnDeleting(EventArgs.Empty); - ApplicationContext.Current.Services.UserService.Delete(_user, true); + ApplicationContext.Current.Services.UserService.Delete(UserEntity, true); FlushFromCache(); } @@ -589,7 +589,7 @@ namespace umbraco.BusinessLogic OnDisabling(EventArgs.Empty); //delete without the true overload will perform the disable operation - ApplicationContext.Current.Services.UserService.Delete(_user); + ApplicationContext.Current.Services.UserService.Delete(UserEntity); } /// @@ -603,7 +603,7 @@ namespace umbraco.BusinessLogic var defaultPermissions = UserType.DefaultPermissions; - var cachedPermissions = ApplicationContext.Current.Services.UserService.GetPermissions(_user) + var cachedPermissions = ApplicationContext.Current.Services.UserService.GetPermissions(UserEntity) .ToArray(); // NH 4.7.1 changing default permission behavior to default to User Type permissions IF no specific permissions has been @@ -668,7 +668,7 @@ namespace umbraco.BusinessLogic { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - var notifications = ApplicationContext.Current.Services.NotificationService.GetUserNotifications(_user); + var notifications = ApplicationContext.Current.Services.NotificationService.GetUserNotifications(UserEntity); foreach (var n in notifications.OrderBy(x => x.EntityId)) { int nodeId = n.EntityId; @@ -688,7 +688,7 @@ namespace umbraco.BusinessLogic /// The id. public int Id { - get { return _user.Id; } + get { return UserEntity.Id; } } /// @@ -697,9 +697,9 @@ namespace umbraco.BusinessLogic public void ClearApplications() { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - foreach (var s in _user.AllowedSections.ToArray()) + foreach (var s in UserEntity.AllowedSections.ToArray()) { - _user.RemoveAllowedSection(s); + UserEntity.RemoveAllowedSection(s); } } @@ -711,13 +711,13 @@ namespace umbraco.BusinessLogic { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - foreach (var s in _user.AllowedSections.ToArray()) + foreach (var s in UserEntity.AllowedSections.ToArray()) { - _user.RemoveAllowedSection(s); + UserEntity.RemoveAllowedSection(s); } //For backwards compatibility this requires an implicit save - ApplicationContext.Current.Services.UserService.Save(_user); + ApplicationContext.Current.Services.UserService.Save(UserEntity); } /// @@ -727,7 +727,7 @@ namespace umbraco.BusinessLogic public void AddApplication(string appAlias) { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - _user.AddAllowedSection(appAlias); + UserEntity.AddAllowedSection(appAlias); } /// @@ -739,10 +739,10 @@ namespace umbraco.BusinessLogic { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - _user.AddAllowedSection(AppAlias); + UserEntity.AddAllowedSection(AppAlias); //For backwards compatibility this requires an implicit save - ApplicationContext.Current.Services.UserService.Save(_user); + ApplicationContext.Current.Services.UserService.Save(UserEntity); } /// @@ -754,11 +754,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.IsLockedOut; + return UserEntity.IsLockedOut; } set { - _user.IsLockedOut = value; + UserEntity.IsLockedOut = value; } } @@ -771,11 +771,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.IsApproved == false; + return UserEntity.IsApproved == false; } set { - _user.IsApproved = value == false; + UserEntity.IsApproved = value == false; } } @@ -789,11 +789,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.StartContentId; + return UserEntity.StartContentId; } set { - _user.StartContentId = value; + UserEntity.StartContentId = value; } } @@ -806,11 +806,11 @@ namespace umbraco.BusinessLogic get { if (_lazyId.HasValue) SetupUser(_lazyId.Value); - return _user.StartMediaId; + return UserEntity.StartMediaId; } set { - _user.StartMediaId = value; + UserEntity.StartMediaId = value; } } diff --git a/src/umbraco.cms/businesslogic/Permission.cs b/src/umbraco.cms/businesslogic/Permission.cs index ba0ad56b14..1a598452fe 100644 --- a/src/umbraco.cms/businesslogic/Permission.cs +++ b/src/umbraco.cms/businesslogic/Permission.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Specialized; using System.Data; -using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using Umbraco.Core; @@ -10,13 +9,12 @@ using Umbraco.Core.Events; using umbraco.DataLayer; using umbraco.cms.businesslogic; using System.Collections.Generic; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; using DeleteEventArgs = umbraco.cms.businesslogic.DeleteEventArgs; namespace umbraco.BusinessLogic { - - //TODO: Wrap this in the new services/repo layer! - /// /// Summary description for Permission. /// @@ -31,39 +29,17 @@ namespace umbraco.BusinessLogic /// Private constructor, this class cannot be directly instantiated /// private Permission() { } - - private static ISqlHelper SqlHelper - { - get { return Application.SqlHelper; } - } - - + public static void MakeNew(User User, CMSNode Node, char PermissionKey) { MakeNew(User, Node, PermissionKey, true); } - [MethodImpl(MethodImplOptions.Synchronized)] - internal static void MakeNew(User user, IEnumerable nodes, char permissionKey, bool raiseEvents) + private static void MakeNew(User user, IEnumerable nodes, char permissionKey, bool raiseEvents) { var asArray = nodes.ToArray(); - foreach (var node in asArray) - { - var parameters = new[] { SqlHelper.CreateParameter("@userId", user.Id), - SqlHelper.CreateParameter("@nodeId", node.Id), - SqlHelper.CreateParameter("@permission", permissionKey.ToString()) }; - // Method is synchronized so exists remains consistent (avoiding race condition) - var exists = SqlHelper.ExecuteScalar( - "SELECT COUNT(userId) FROM umbracoUser2nodePermission WHERE userId = @userId AND nodeId = @nodeId AND permission = @permission", - parameters) > 0; - - if (exists) return; - - SqlHelper.ExecuteNonQuery( - "INSERT INTO umbracoUser2nodePermission (userId, nodeId, permission) VALUES (@userId, @nodeId, @permission)", - parameters); - } + ApplicationContext.Current.Services.UserService.AssignUserPermission(user.Id, permissionKey, asArray.Select(x => x.Id).ToArray()); if (raiseEvents) { @@ -83,20 +59,17 @@ namespace umbraco.BusinessLogic /// public static IEnumerable GetUserPermissions(User user) { - var items = new List(); - using (IRecordsReader dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", user.Id))) - { - while (dr.Read()) + var permissions = ApplicationContext.Current.Services.UserService.GetPermissions(user.UserEntity); + + return permissions.SelectMany( + entityPermission => entityPermission.AssignedPermissions, + (entityPermission, assignedPermission) => new Permission { - items.Add(new Permission() - { - NodeId = dr.GetInt("nodeId"), - PermissionId = Convert.ToChar(dr.GetString("permission")), - UserId = dr.GetInt("userId") - }); - } - } - return items; + NodeId = entityPermission.EntityId, + PermissionId = assignedPermission[0], + UserId = entityPermission.UserId + }); + } /// @@ -106,20 +79,19 @@ namespace umbraco.BusinessLogic /// public static IEnumerable GetNodePermissions(CMSNode node) { - var items = new List(); - using (IRecordsReader dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where nodeId = @nodeId order by nodeId", SqlHelper.CreateParameter("@nodeId", node.Id))) - { - while (dr.Read()) + var content = ApplicationContext.Current.Services.ContentService.GetById(node.Id); + if (content == null) return Enumerable.Empty(); + + var permissions = ApplicationContext.Current.Services.ContentService.GetPermissionsForEntity(content); + + return permissions.SelectMany( + entityPermission => entityPermission.AssignedPermissions, + (entityPermission, assignedPermission) => new Permission { - items.Add(new Permission() - { - NodeId = dr.GetInt("nodeId"), - PermissionId = Convert.ToChar(dr.GetString("permission")), - UserId = dr.GetInt("userId") - }); - } - } - return items; + NodeId = entityPermission.EntityId, + PermissionId = assignedPermission[0], + UserId = entityPermission.UserId + }); } /// @@ -134,10 +106,7 @@ namespace umbraco.BusinessLogic internal static void DeletePermissions(User user, CMSNode node, bool raiseEvents) { - // delete all settings on the node for this user - SqlHelper.ExecuteNonQuery("delete from umbracoUser2NodePermission where userId = @userId and nodeId = @nodeId", - SqlHelper.CreateParameter("@userId", user.Id), SqlHelper.CreateParameter("@nodeId", node.Id)); - + ApplicationContext.Current.Services.UserService.RemoveUserPermissions(user.Id, node.Id); if (raiseEvents) { OnDeleted(new UserPermission(user, node, null), new DeleteEventArgs()); @@ -150,19 +119,14 @@ namespace umbraco.BusinessLogic /// public static void DeletePermissions(User user) { - // delete all settings on the node for this user - SqlHelper.ExecuteNonQuery("delete from umbracoUser2NodePermission where userId = @userId", - SqlHelper.CreateParameter("@userId", user.Id)); + ApplicationContext.Current.Services.UserService.RemoveUserPermissions(user.Id); OnDeleted(new UserPermission(user, Enumerable.Empty(), null), new DeleteEventArgs()); } public static void DeletePermissions(int iUserID, int[] iNodeIDs) { - var sql = "DELETE FROM umbracoUser2NodePermission WHERE nodeID IN ({0}) AND userID=@userID"; - var nodeIDs = string.Join(",", Array.ConvertAll(iNodeIDs, Converter)); - sql = string.Format(sql, nodeIDs); - SqlHelper.ExecuteNonQuery(sql, new[] { SqlHelper.CreateParameter("@userID", iUserID) }); + ApplicationContext.Current.Services.UserService.RemoveUserPermissions(iUserID, iNodeIDs); OnDeleted(new UserPermission(iUserID, iNodeIDs), new DeleteEventArgs()); } @@ -170,10 +134,6 @@ namespace umbraco.BusinessLogic { DeletePermissions(iUserID, new[] { iNodeID }); } - private static string Converter(int from) - { - return from.ToString(CultureInfo.InvariantCulture); - } /// /// delete all permissions for this node @@ -181,14 +141,11 @@ namespace umbraco.BusinessLogic /// public static void DeletePermissions(CMSNode node) { - SqlHelper.ExecuteNonQuery( - "delete from umbracoUser2NodePermission where nodeId = @nodeId", - SqlHelper.CreateParameter("@nodeId", node.Id)); - + ApplicationContext.Current.Services.ContentService.RemoveContentPermissions(node.Id); + OnDeleted(new UserPermission(null, node, null), new DeleteEventArgs()); } - [MethodImpl(MethodImplOptions.Synchronized)] public static void UpdateCruds(User user, CMSNode node, string permissions) { ApplicationContext.Current.Services.UserService.ReplaceUserPermissions( From adde7c2f500fd2dee4c96284076c4d02de117e0e Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Hoffback Date: Mon, 18 May 2015 11:34:13 +0200 Subject: [PATCH 0019/1710] Fixed login-view on mobile devices On smaller screens the login-view was too far to the right. Related issue: http://issues.umbraco.org/issue/U4-5020 --- src/Umbraco.Web.UI.Client/src/less/login.less | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/login.less b/src/Umbraco.Web.UI.Client/src/less/login.less index d46bb41317..110733ffee 100644 --- a/src/Umbraco.Web.UI.Client/src/less/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/login.less @@ -24,7 +24,7 @@ .login-overlay .form { display: block; padding-top: 100px; - padding-left: 165px; + padding-left: 21%; width: 370px; text-align: right } @@ -44,4 +44,12 @@ padding-left: 6px; margin-top: 10px; text-align: center; +} + +@media (max-width: 767px) { + // Remove padding on login-form on smaller devices + .login-overlay .form { + padding-left: 2%!important; + width: 90%; + } } \ No newline at end of file From 3b791c75f6cf289b055c1d608fad50f859b6e1a7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 18 May 2015 20:09:03 +1000 Subject: [PATCH 0020/1710] Fixes: U4-5020 Login view on mobile screen is too big --- src/Umbraco.Web.UI.Client/src/less/login.less | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/login.less b/src/Umbraco.Web.UI.Client/src/less/login.less index 110733ffee..491c842586 100644 --- a/src/Umbraco.Web.UI.Client/src/less/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/login.less @@ -22,9 +22,10 @@ } .login-overlay .form { + position:fixed; display: block; - padding-top: 100px; - padding-left: 21%; + top: 100px; + left: 165px; width: 370px; text-align: right } @@ -46,10 +47,10 @@ text-align: center; } -@media (max-width: 767px) { +@media (max-width: 565px) { // Remove padding on login-form on smaller devices .login-overlay .form { - padding-left: 2%!important; - width: 90%; + left: inherit; + right:25px; } } \ No newline at end of file From d1d9878ab6ea04a05960a52fe0fc6dab371831e4 Mon Sep 17 00:00:00 2001 From: Sebastian Claesson Hoffback Date: Mon, 18 May 2015 13:59:42 +0200 Subject: [PATCH 0021/1710] Fix content-edit-view on mobile devices The content editing view did not work at all on small screens. The label took to much space, leaving the controller with almost none. Now the label and the controller get its own row on smaller screens. Much cleaner look and you can acctually edit a page on a small screen. Note: Grids still don't work well. --- src/Umbraco.Web.UI.Client/src/less/forms.less | 14 ++++++++++++++ src/Umbraco.Web.UI.Client/src/less/main.less | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 1091a95ea8..dc59a4b6ab 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -794,3 +794,17 @@ legend + .control-group { position: relative; z-index: 1000; } + + + +@media (max-width: 767px) { + + // Labels on own row + .form-horizontal .control-label { + width: 100%; + } + .form-horizontal .controls { + margin-left: 0; + } + +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 379f65b10a..fcdc683182 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -146,7 +146,7 @@ h5{ .controls-row { padding-top: 5px; - margin-left: 240px !important; + margin-left: 240px; } .controls-row label { display: inline-block From 918bb761b20b1d8a681cbafe43bd6de7611828fe Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 7 May 2015 10:16:21 +0200 Subject: [PATCH 0022/1710] Bugfix property value editors null-checks --- src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs | 2 -- src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs | 3 +++ .../PropertyEditors/PublishValuesMultipleValueEditor.cs | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs index f33eac1928..8b00164059 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs @@ -345,9 +345,7 @@ namespace Umbraco.Core.PropertyEditors public virtual string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService) { if (property.Value == null) - { return string.Empty; - } switch (GetDatabaseType()) { diff --git a/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs index 053c349ccb..98a1ecefa1 100644 --- a/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs @@ -39,6 +39,9 @@ namespace Umbraco.Web.PropertyEditors /// public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService) { + if (property.Value == null) + return null; + var idAttempt = property.Value.TryConvertTo(); if (idAttempt.Success) { diff --git a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs index 3d68936118..044a958deb 100644 --- a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs @@ -39,6 +39,9 @@ namespace Umbraco.Web.PropertyEditors /// public override string ConvertDbToString(Property property, PropertyType propertyType, IDataTypeService dataTypeService) { + if (property.Value == null) + return null; + //publishing ids, so just need to return the value as-is if (_publishIds) { From 99ff86aefb334b486494d1504af9d8123a02e16d Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 8 Apr 2015 14:21:58 +0200 Subject: [PATCH 0023/1710] DistributedCache - don't serialize payload on local node --- .../Cache/IPayloadCacheRefresher.cs | 16 +++++++ .../Cache/JsonCacheRefresherBase.cs | 15 ++++--- .../Cache/PayloadCacheRefresherBase.cs | 27 ++++++++++++ .../Sync/DatabaseServerMessenger.cs | 37 +++++++++++----- src/Umbraco.Core/Sync/IServerMessenger.cs | 25 ++++------- src/Umbraco.Core/Sync/MessageType.cs | 3 +- src/Umbraco.Core/Sync/ServerMessengerBase.cs | 42 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 2 + .../DistributedCache/DistributedCacheTests.cs | 4 ++ src/Umbraco.Web/Cache/DistributedCache.cs | 10 +++++ 10 files changed, 144 insertions(+), 37 deletions(-) create mode 100644 src/Umbraco.Core/Cache/IPayloadCacheRefresher.cs create mode 100644 src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs diff --git a/src/Umbraco.Core/Cache/IPayloadCacheRefresher.cs b/src/Umbraco.Core/Cache/IPayloadCacheRefresher.cs new file mode 100644 index 0000000000..416cb223d7 --- /dev/null +++ b/src/Umbraco.Core/Cache/IPayloadCacheRefresher.cs @@ -0,0 +1,16 @@ +using umbraco.interfaces; + +namespace Umbraco.Core.Cache +{ + /// + /// A cache refresher that supports refreshing cache based on a custom payload + /// + interface IPayloadCacheRefresher : IJsonCacheRefresher + { + /// + /// Refreshes, clears, etc... any cache based on the information provided in the payload + /// + /// + void Refresh(object payload); + } +} diff --git a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs index 708e2e1605..48dd008a3d 100644 --- a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs @@ -4,17 +4,16 @@ using umbraco.interfaces; namespace Umbraco.Core.Cache { /// - /// A base class for json cache refreshers that ensures the correct events are raised when - /// cache refreshing occurs. + /// Provides a base class for "json" cache refreshers. /// - /// The real cache refresher type, this is used for raising strongly typed events - public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher - where TInstanceType : ICacheRefresher + /// The actual cache refresher type. + /// Ensures that the correct events are raised when cache refreshing occurs. + public abstract class JsonCacheRefresherBase : CacheRefresherBase, IJsonCacheRefresher + where TInstance : ICacheRefresher { - - public virtual void Refresh(string jsonPayload) + public virtual void Refresh(string json) { - OnCacheUpdated(Instance, new CacheRefresherEventArgs(jsonPayload, MessageType.RefreshByJson)); + OnCacheUpdated(Instance, new CacheRefresherEventArgs(json, MessageType.RefreshByJson)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs new file mode 100644 index 0000000000..b3ea2ff7b1 --- /dev/null +++ b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs @@ -0,0 +1,27 @@ +using Umbraco.Core.Sync; +using umbraco.interfaces; + +namespace Umbraco.Core.Cache +{ + /// + /// Provides a base class for "payload" cache refreshers. + /// + /// The actual cache refresher type. + /// Ensures that the correct events are raised when cache refreshing occurs. + public abstract class PayloadCacheRefresherBase : JsonCacheRefresherBase, IPayloadCacheRefresher + where TInstance : ICacheRefresher + { + protected abstract object Deserialize(string json); + + public override void Refresh(string json) + { + var payload = Deserialize(json); + Refresh(payload); + } + + public virtual void Refresh(object payload) + { + OnCacheUpdated(Instance, new CacheRefresherEventArgs(payload, MessageType.RefreshByPayload)); + } + } +} diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 956903d96d..e6ce565280 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -187,25 +187,42 @@ namespace Umbraco.Core.Sync // the local server as they've already been processed. We should NOT assume that the sequence of // instructions in the database makes any sense whatsoever, because it's all async. var localIdentity = GetLocalIdentity(); - var remoteDtos = dtos.Where(x => x.OriginIdentity != localIdentity); var lastId = 0; - foreach (var dto in remoteDtos) + foreach (var dto in dtos) { + if (dto.OriginIdentity == localIdentity) + { + // just skip that local one but update lastId nevertheless + lastId = dto.Id; + continue; + } + + // deserialize remote instructions & skip if it fails + JArray jsonA; try { - var jsonArray = JsonConvert.DeserializeObject(dto.Instructions); - NotifyRefreshers(jsonArray); - lastId = dto.Id; + jsonA = JsonConvert.DeserializeObject(dto.Instructions); } catch (JsonException ex) { - // FIXME - // if we cannot deserialize then it's OK to skip the instructions - // but what if NotifyRefreshers throws?! - - LogHelper.Error("Could not deserialize a distributed cache instruction (\"" + dto.Instructions + "\").", ex); + LogHelper.Error(string.Format("Failed to deserialize instructions ({0}: \"{1}\").", dto.Id, dto.Instructions), ex); + lastId = dto.Id; // skip + continue; } + + // execute remote instructions & update lastId + try + { + NotifyRefreshers(jsonA); + lastId = dto.Id; + } + catch (Exception ex) + { + LogHelper.Error(string.Format("Failed to execute instructions ({0}: \"{1}\").", dto.Id, dto.Instructions), ex); + LogHelper.Warn("BEWARE - DISTRIBUTED CACHE IS NOT UPDATED."); + throw; + } } if (lastId > 0) diff --git a/src/Umbraco.Core/Sync/IServerMessenger.cs b/src/Umbraco.Core/Sync/IServerMessenger.cs index 100638c202..1208ea3caf 100644 --- a/src/Umbraco.Core/Sync/IServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IServerMessenger.cs @@ -10,15 +10,13 @@ namespace Umbraco.Core.Sync /// Also ensures that the notification is processed on the local environment. public interface IServerMessenger { - // TODO - // everything we do "by JSON" means that data is serialized then deserialized on the local server - // we should stop using this, and instead use Notify() with an actual object that can be passed - // around locally, and serialized for remote messaging - but that would break backward compat ;-( - // - // and then ServerMessengerBase must be able to handle Notify(), and all messengers too - // and then ICacheRefresher (or INotifiableCacheRefresher?) must be able to handle it too - // - // >> v8 + /// + /// Notifies the distributed cache, for a specified . + /// + /// The servers that compose the load balanced environment. + /// The ICacheRefresher. + /// The notification content. + void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, object payload); /// /// Notifies the distributed cache, for a specified . @@ -28,15 +26,6 @@ namespace Umbraco.Core.Sync /// The notification content. void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload); - ///// - ///// Notifies the distributed cache, for a specified . - ///// - ///// The servers that compose the load balanced environment. - ///// The ICacheRefresher. - ///// The notification content. - ///// A custom Json serializer. - //void Notify(IEnumerable servers, ICacheRefresher refresher, object payload, Func serializer = null); - /// /// Notifies the distributed cache of specifieds item invalidation, for a specified . /// diff --git a/src/Umbraco.Core/Sync/MessageType.cs b/src/Umbraco.Core/Sync/MessageType.cs index 80e9bcae76..9cdf94bafa 100644 --- a/src/Umbraco.Core/Sync/MessageType.cs +++ b/src/Umbraco.Core/Sync/MessageType.cs @@ -10,6 +10,7 @@ RefreshByJson, RemoveById, RefreshByInstance, - RemoveByInstance + RemoveByInstance, + RefreshByPayload } } \ No newline at end of file diff --git a/src/Umbraco.Core/Sync/ServerMessengerBase.cs b/src/Umbraco.Core/Sync/ServerMessengerBase.cs index 5da0837c2d..573be67169 100644 --- a/src/Umbraco.Core/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Core/Sync/ServerMessengerBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using Umbraco.Core.Cache; using Umbraco.Core.Logging; using umbraco.interfaces; @@ -55,6 +56,15 @@ namespace Umbraco.Core.Sync #region IServerMessenger + public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, object payload) + { + if (servers == null) throw new ArgumentNullException("servers"); + if (refresher == null) throw new ArgumentNullException("refresher"); + if (payload == null) throw new ArgumentNullException("payload"); + + Deliver(servers, refresher, payload); + } + public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) { if (servers == null) throw new ArgumentNullException("servers"); @@ -144,6 +154,19 @@ namespace Umbraco.Core.Sync #region Deliver + protected void DeliverLocal(ICacheRefresher refresher, object payload) + { + if (refresher == null) throw new ArgumentNullException("refresher"); + + LogHelper.Debug("Invoking refresher {0} on local server for message type RefreshByPayload", + refresher.GetType); + + var payloadRefresher = refresher as IPayloadCacheRefresher; + if (payloadRefresher == null) + throw new InvalidOperationException("The cache refresher " + refresher.GetType() + " is not of type " + typeof(IPayloadCacheRefresher)); + payloadRefresher.Refresh(payload); + } + /// /// Executes the non strongly typed on the local/current server /// @@ -269,6 +292,25 @@ namespace Umbraco.Core.Sync //protected abstract void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, object payload); + protected virtual void Deliver(IEnumerable servers, ICacheRefresher refresher, object payload) + { + if (servers == null) throw new ArgumentNullException("servers"); + if (refresher == null) throw new ArgumentNullException("refresher"); + + var serversA = servers.ToArray(); + + // deliver local + DeliverLocal(refresher, payload); + + // distribute? + if (RequiresDistributed(serversA, refresher, MessageType.RefreshByJson) == false) + return; + + // deliver remote + var json = JsonConvert.SerializeObject(payload); + DeliverRemote(serversA, refresher, MessageType.RefreshByJson, null, json); + } + protected virtual void Deliver(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) { if (servers == null) throw new ArgumentNullException("servers"); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4b1ec43f96..7729357568 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -154,12 +154,14 @@ + + diff --git a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs index 06d3e6b27d..8873a72de4 100644 --- a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs @@ -138,6 +138,10 @@ namespace Umbraco.Tests.Cache.DistributedCache public List PayloadsRefreshed = new List(); public int CountOfFullRefreshes = 0; + public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, object payload) + { + // doing nothing + } public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) { diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs index eb46f91ad1..9788618bb5 100644 --- a/src/Umbraco.Web/Cache/DistributedCache.cs +++ b/src/Umbraco.Web/Cache/DistributedCache.cs @@ -145,6 +145,16 @@ namespace Umbraco.Web.Cache id); } + public void RefreshByPayload(Guid factoryGuid, object payload) + { + if (factoryGuid == Guid.Empty || payload == null) return; + + ServerMessengerResolver.Current.Messenger.PerformRefresh( + ServerRegistrarResolver.Current.Registrar.Registrations, + GetRefresherById(factoryGuid), + payload); + } + /// /// Notifies the distributed cache, for a specified . /// From 43f99e11a5736c25fdc0f3aa89a4fc5169fe8dfa Mon Sep 17 00:00:00 2001 From: Alain-es Date: Mon, 18 May 2015 15:54:50 +0100 Subject: [PATCH 0024/1710] Datepicker custom format --- .../datepicker/datepicker.controller.js | 21 ++++++++++++++++--- .../datepicker/datepicker.html | 2 +- .../PropertyEditors/DatePropertyEditor.cs | 13 +++++++++--- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 9ad177285c..eb49bc3b39 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -18,9 +18,16 @@ function dateTimePickerController($scope, notificationsService, assetsService, a //map the user config $scope.model.config = angular.extend(config, $scope.model.config); + $scope.datetimePickerValue = $scope.model.value; + //hide picker if clicking on the document $scope.hidePicker = function () { - $element.find("div:first").datetimepicker("hide"); + //$element.find("div:first").datetimepicker("hide"); + // Sometimes the statement above fails and generates errors in the browser console. The following statements fix that. + var dtp = $element.find("div:first"); + if (dtp && dtp.datetimepicker) { + dtp.datetimepicker("hide"); + } }; $(document).bind("click", $scope.hidePicker); @@ -67,8 +74,16 @@ function dateTimePickerController($scope, notificationsService, assetsService, a .datetimepicker($scope.model.config) .on("dp.change", applyDate); - //manually assign the date to the plugin - $element.find("div:first").datetimepicker("setValue", $scope.model.value ? $scope.model.value : null); + //manually assign the date to the plugin + if (!$scope.model.config.format) { + $element.find("div:first").datetimepicker("setValue", $scope.model.value ? $scope.model.value : null); + } + else { + $element.find("div:first").datetimepicker("setValue", $scope.model.value ? new Date($scope.model.value) : null); + if ($scope.model.value && $scope.model.config.format) { + $scope.datetimePickerValue = moment($scope.model.value).format($scope.model.config.format); + } + } //Ensure to remove the event handler when this instance is destroyted $scope.$on('$destroy', function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 95452e0f19..b3e503eb4a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -1,7 +1,7 @@
    diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs index 04a5890c2c..d49458d98e 100644 --- a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -23,9 +23,6 @@ namespace Umbraco.Web.PropertyEditors private IDictionary _defaultPreVals; - /// - /// Overridden because we ONLY support Date (no time) format and we don't have pre-values in the db. - /// public override IDictionary DefaultPreValues { get { return _defaultPreVals; } @@ -60,5 +57,15 @@ namespace Umbraco.Web.PropertyEditors } } + + protected override PreValueEditor CreatePreValueEditor() + { + return new DatePreValueEditor(); + } + + internal class DatePreValueEditor : PreValueEditor + { + public string DefaultValue { get; set; } + } } } \ No newline at end of file From 2abb3177afb405925e61c5158fd01fce43c5a15e Mon Sep 17 00:00:00 2001 From: Alain-es Date: Mon, 18 May 2015 15:57:27 +0100 Subject: [PATCH 0025/1710] Datepicker custom format --- src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs index d49458d98e..73fd5ca674 100644 --- a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -65,6 +65,7 @@ namespace Umbraco.Web.PropertyEditors internal class DatePreValueEditor : PreValueEditor { + [PreValueField("format", "Date format", "textstring", Description = "If left empty then the format is YYYY-MM-DD. (see momentjs.com for supported formats)")] public string DefaultValue { get; set; } } } From 0e6b663eeda9033070d39a50565dfcb24be95c2e Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 18 May 2015 20:07:20 +0200 Subject: [PATCH 0026/1710] Models for content type display --- .../ContentEditing/ContentTypeDisplay.cs | 34 +++++++++++++++++++ .../ContentEditing/PropertyTypeDisplay.cs | 4 +++ .../PropertyTypeGroupDisplay.cs | 5 +++ 3 files changed, 43 insertions(+) diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs index 48c531970c..549143355c 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs @@ -10,6 +10,40 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "contentType", Namespace = "")] public class ContentTypeDisplay : ContentTypeBasic { + //name, alias, icon, thumb, desc, inherited from basic + + + // Templates + [DataMember(Name = "allowedTemplates")] + public IEnumerable AllowedTemplates { get; set; } + + [DataMember(Name = "availableTemplates")] + public IEnumerable AvailableTemplates { get; set; } + + [DataMember(Name = "defaultTemplate")] + public string DefaultTemplate { get; set; } + + + // Allowed parent node types (can include root) + [DataMember(Name = "allowedParentNodeTypes")] + public IEnumerable AllowedParentNodeTypes { get; set; } + + + //List view + [DataMember(Name = "enableListView")] + public bool EnableListView { get; set; } + + + //Compositions + [DataMember(Name = "compositedContentTypes")] + public IEnumerable CompositedContentTypes { get; set; } + + [DataMember(Name = "availableContentTypes")] + public IEnumerable AvailableContentTypes { get; set; } + + + //Tabs + [DataMember(Name = "groups")] public IEnumerable Groups { get; set; } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs index 4865d0d546..7e988039ff 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs @@ -33,5 +33,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "value")] public string Value { get; set; } + + //indicates if this property was inherited + [DataMember(Name = "inherited")] + public bool Inherited { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs index 6e31f811d1..56a2ea506b 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs @@ -27,5 +27,10 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "groups")] public IEnumerable Groups { get; set; } + + //Indicate if this tab was inherited + [DataMember(Name = "inherited")] + public bool Inherited { get; set; } + } } From 6c218c3ab5ca6258d4ae3db3b7fca0e64fa8e16d Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 18 May 2015 20:08:00 +0200 Subject: [PATCH 0027/1710] datatype controller methods for ct editor --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 +- src/Umbraco.Web.UI/config/trees.config | 5 ++- src/Umbraco.Web/Editors/DataTypeController.cs | 39 +++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index cbc564b796..639f47ecb3 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -39,7 +39,7 @@ 4.0 v4.5 - true + false @@ -2575,7 +2575,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" True 7300 / - http://localhost:7300 + http://umbraco7.local False False diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 8a85f26788..eec71c2706 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -9,7 +9,7 @@ - + @@ -37,5 +37,6 @@ + iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="10" />--> + \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index fed52aaa55..1a6d1e0e7a 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -75,6 +75,45 @@ namespace Umbraco.Web.Editors .Select(x => Mapper.Map(x)).Where(x => x.IsSystemDataType == false); } + /// + /// Gets the content json for all data types added by the user + /// + /// + /// + /// + /// Permission is granted to this method if the user has access to any of these trees: DataTypes, Content or Media + /// + [UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.Content, Constants.Trees.Media)] + public IEnumerable GetAllUserConfigured() + { + //find all user configured for re-reference + return Services.DataTypeService + .GetAllDataTypeDefinitions() + .Where(x => x.Id > 1045) + .Select(x => Mapper.Map(x)).Where(x => x.IsSystemDataType == false); + + //find all custom editors added by non-core manifests + + //find the rest + } + + /// + /// Gets the content json for all user added property editors + /// + /// + /// + /// + /// Permission is granted to this method if the user has access to any of these trees: DataTypes, Content or Media + /// + [UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.Content, Constants.Trees.Media)] + public IEnumerable GetAllUserPropertyEditors() + { + return PropertyEditorResolver.Current.PropertyEditors + .OrderBy(x => x.Name) + .Where(x => x.ValueEditor.View.IndexOf("app_plugins", StringComparison.InvariantCultureIgnoreCase) >= 0) + .Select(Mapper.Map); + } + /// /// Deletes a data type wth a given ID /// From 71c24b7bf3bbfadee12820011102b43f8bb4bb43 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 19 May 2015 09:01:54 +1000 Subject: [PATCH 0028/1710] reverts proj change --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 639f47ecb3..cbc564b796 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -39,7 +39,7 @@ 4.0 v4.5 - false + true @@ -2575,7 +2575,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" True 7300 / - http://umbraco7.local + http://localhost:7300 False False From b9120698ba4bd1d6e9b80558471ca1d640b404a5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 19 May 2015 11:17:23 +1000 Subject: [PATCH 0029/1710] Adds more mapping logic for content type mappers, creates unit tests for them, adds some missing required properties that will be needed for editing, adds lots of notes. --- .../PropertyEditors/PropertyEditorResolver.cs | 7 +- .../Models/Mapping/AutoMapperTests.cs | 24 ++ .../Mapping/ContentTypeModelMappingTests.cs | 208 ++++++++++++++++++ .../TestHelpers/BaseDatabaseFactoryTest.cs | 3 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../ContentEditing/ContentTypeDisplay.cs | 19 +- .../ContentEditing/PropertyTypeDisplay.cs | 6 + .../PropertyTypeGroupDisplay.cs | 7 + .../Models/Mapping/ContentTypeModelMapper.cs | 99 ++++++++- .../Mapping/PropertyTypeGroupResolver.cs | 18 +- 10 files changed, 376 insertions(+), 16 deletions(-) create mode 100644 src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs index 02834660ee..5c0227247e 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Umbraco.Core.Cache; using Umbraco.Core.Logging; using Umbraco.Core.IO; using Umbraco.Core.Manifest; @@ -17,12 +18,12 @@ namespace Umbraco.Core.PropertyEditors /// public class PropertyEditorResolver : LazyManyObjectsResolverBase { - public PropertyEditorResolver(IServiceProvider serviceProvider, ILogger logger, Func> typeListProducerList) + public PropertyEditorResolver(IServiceProvider serviceProvider, ILogger logger, Func> typeListProducerList, IRuntimeCacheProvider runtimeCache) : base(serviceProvider, logger, typeListProducerList, ObjectLifetimeScope.Application) { var builder = new ManifestBuilder( - ApplicationContext.Current.ApplicationCache.RuntimeCache, - new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationContext.Current.ApplicationCache.RuntimeCache)); + runtimeCache, + new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), runtimeCache)); _unioned = new Lazy>(() => Values.Union(builder.PropertyEditors).ToList()); } diff --git a/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs b/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs index 22aadd744e..f385bf4954 100644 --- a/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs @@ -1,5 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; using AutoMapper; +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; +using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Models.Mapping @@ -8,6 +14,24 @@ namespace Umbraco.Tests.Models.Mapping [TestFixture] public class AutoMapperTests : BaseUmbracoApplicationTest { + /// + /// Inheritors can override this to setup any resolvers before resolution is frozen + /// + protected override void FreezeResolution() + { + Func> typeListProducerList = Enumerable.Empty; + var propertyEditorResolver = new Mock( + //ctor args + Mock.Of(), + Mock.Of(), + typeListProducerList, + Umbraco.Core.CacheHelper.CreateDisabledCacheHelper().RuntimeCache); + + PropertyEditorResolver.Current = propertyEditorResolver.Object; + + base.FreezeResolution(); + } + [Test] public void Assert_Valid_Mappings() { diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs new file mode 100644 index 0000000000..5195b15dc5 --- /dev/null +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Moq; +using NUnit.Framework; +using umbraco.cms.presentation; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Profiling; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Tests.Models.Mapping +{ + [TestFixture] + public class ContentTypeModelMappingTests : BaseUmbracoConfigurationTest + { + //Mocks of services that can be setup on a test by test basis to return whatever we want + private Mock _contentTypeService = new Mock(); + private Mock _contentService = new Mock(); + private Mock _dataTypeService = new Mock(); + private Mock _propertyEditorResolver; + + [SetUp] + public void Setup() + { + var nullCacheHelper = CacheHelper.CreateDisabledCacheHelper(); + + //Create an app context using mocks + var appContext = new ApplicationContext( + new DatabaseContext(Mock.Of(), Mock.Of(), Mock.Of(), "test"), + + //Create service context using mocks + new ServiceContext( + + contentService: _contentService.Object, + contentTypeService:_contentTypeService.Object, + dataTypeService:_dataTypeService.Object), + + nullCacheHelper, + new ProfilingLogger(Mock.Of(), Mock.Of())); + + //create a fake property editor resolver to return fake property editors + Func> typeListProducerList = Enumerable.Empty; + _propertyEditorResolver = new Mock( + //ctor args + Mock.Of(), Mock.Of(), typeListProducerList, nullCacheHelper.RuntimeCache); + + Mapper.Initialize(configuration => + { + //initialize our content type mapper + var mapper = new ContentTypeModelMapper(new Lazy(() => _propertyEditorResolver.Object)); + mapper.ConfigureMappings(configuration, appContext); + }); + } + + [Test] + public void ContentTypeDisplay_To_IContentType() + { + //Arrange + + // setup the mocks to return the data we want to test against... + + _dataTypeService.Setup(x => x.GetDataTypeDefinitionById(It.IsAny())) + .Returns(Mock.Of( + definition => + definition.Id == 555 + && definition.PropertyEditorAlias == "myPropertyType" + && definition.DatabaseType == DataTypeDatabaseType.Nvarchar)); + + var display = CreateSimpleContentTypeDisplay(); + + //Act + + var result = Mapper.Map(display); + + //Assert + + Assert.AreEqual(display.Alias, result.Alias); + Assert.AreEqual(display.Description, result.Description); + Assert.AreEqual(display.Icon, result.Icon); + Assert.AreEqual(display.Id, result.Id); + Assert.AreEqual(display.Name, result.Name); + Assert.AreEqual(display.ParentId, result.ParentId); + Assert.AreEqual(display.Path, result.Path); + Assert.AreEqual(display.Thumbnail, result.Thumbnail); + Assert.AreEqual(display.AllowedAsRoot, result.AllowedAsRoot); + Assert.AreEqual(display.EnableListView, result.IsContainer); + + //TODO: Now we need to assert all of the more complicated parts + Assert.AreEqual(1, result.PropertyGroups.Count); + Assert.AreEqual(1, result.PropertyGroups[0].PropertyTypes.Count); + } + + [Test] + public void IContentType_To_ContentTypeDisplay() + { + //Arrange + + // setup the mocks to return the data we want to test against... + + // for any call to GetPreValuesCollectionByDataTypeId just return an empty dictionary for now + // TODO: but we'll need to change this to return some pre-values to test the mappings + _dataTypeService.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny())) + .Returns(new PreValueCollection(new Dictionary())); + + //return a textbox property editor for any requested editor by alias + _propertyEditorResolver.Setup(resolver => resolver.GetByAlias(It.IsAny())) + .Returns(new TextboxPropertyEditor()); + //for testing, just return a list of whatever property editors we want + _propertyEditorResolver.Setup(resolver => resolver.PropertyEditors) + .Returns(new[] { new TextboxPropertyEditor() }); + + var contentType = MockedContentTypes.CreateTextpageContentType(); + + //Act + + var result = Mapper.Map(contentType); + + //Assert + + Assert.AreEqual(contentType.Alias, result.Alias); + Assert.AreEqual(contentType.Description, result.Description); + Assert.AreEqual(contentType.Icon, result.Icon); + Assert.AreEqual(contentType.Id, result.Id); + Assert.AreEqual(contentType.Name, result.Name); + Assert.AreEqual(contentType.ParentId, result.ParentId); + Assert.AreEqual(contentType.Path, result.Path); + Assert.AreEqual(contentType.Thumbnail, result.Thumbnail); + Assert.AreEqual(contentType.AllowedAsRoot, result.AllowedAsRoot); + Assert.AreEqual(contentType.IsContainer, result.EnableListView); + + //TODO: Now we need to assert all of the more complicated parts + Assert.AreEqual(2, result.Groups.Count()); + Assert.AreEqual(2, result.Groups.ElementAt(0).Properties.Count()); + Assert.AreEqual(2, result.Groups.ElementAt(1).Properties.Count()); + } + + private ContentTypeDisplay CreateSimpleContentTypeDisplay() + { + return new ContentTypeDisplay + { + Alias = "test", + AllowedParentNodeTypes = new List(), + AllowedTemplates = new List(), + AvailableContentTypes = new List(), + AvailableTemplates = new List(), + DefaultTemplate = "template1", + Description = "hello world", + Icon = "tree-icon", + Id = 1234, + Key = new Guid("8A60656B-3866-46AB-824A-48AE85083070"), + Name = "My content type", + Path = "-1,1234", + ParentId = -1, + Thumbnail = "tree-thumb", + EnableListView = true, + Groups = new List() + { + new PropertyTypeGroupDisplay + { + Id = 987, + Name = "Tab 1", + ParentGroupId = -1, + SortOrder = 0, + Inherited = false, + Properties = new List + { + new PropertyTypeDisplay + { + Alias = "property1", + Description = "this is property 1", + Inherited = false, + Label = "Property 1", + Validation = new PropertyTypeValidation + { + Mandatory = false, + Pattern = "" + }, + Editor = "myPropertyType", + Value = "value 1", + //View = ??? - isn't this the same as editor? + Config = new Dictionary + { + {"item1", "value1"}, + {"item2", "value2"} + }, + SortOrder = 0, + DataTypeId = 555, + View = "blah" + } + } + } + } + + }; + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index d89e645c1f..f7e68a7ba9 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -202,7 +202,8 @@ namespace Umbraco.Tests.TestHelpers { PropertyEditorResolver.Current = new PropertyEditorResolver( new ActivatorServiceProvider(), Logger, - () => PluginManager.Current.ResolvePropertyEditors()); + () => PluginManager.Current.ResolvePropertyEditors(), + ApplicationContext.ApplicationCache.RuntimeCache); DataTypesResolver.Current = new DataTypesResolver( new ActivatorServiceProvider(), Logger, diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9190056d66..4e931be7e5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -171,6 +171,7 @@ + diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs index 549143355c..d1ec50d9a9 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs @@ -10,6 +10,17 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "contentType", Namespace = "")] public class ContentTypeDisplay : ContentTypeBasic { + public ContentTypeDisplay() + { + //initialize collections so at least their never null + AllowedTemplates = new List(); + AvailableTemplates = new List(); + AvailableContentTypes = new List(); + AllowedParentNodeTypes = new List(); + CompositeContentTypes = new List(); + Groups = new List(); + } + //name, alias, icon, thumb, desc, inherited from basic @@ -30,13 +41,15 @@ namespace Umbraco.Web.Models.ContentEditing //List view - [DataMember(Name = "enableListView")] + [DataMember(Name = "enableListView")] public bool EnableListView { get; set; } + [DataMember(Name = "allowedAtRoot")] + public bool AllowedAsRoot { get; set; } //Compositions - [DataMember(Name = "compositedContentTypes")] - public IEnumerable CompositedContentTypes { get; set; } + [DataMember(Name = "compositeContentTypes")] + public IEnumerable CompositeContentTypes { get; set; } [DataMember(Name = "availableContentTypes")] public IEnumerable AvailableContentTypes { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs index 7e988039ff..f5cc2437b8 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeDisplay.cs @@ -34,8 +34,14 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "value")] public string Value { get; set; } + [DataMember(Name = "sortOrder")] + public int SortOrder { get; set; } + //indicates if this property was inherited [DataMember(Name = "inherited")] public bool Inherited { get; set; } + + [DataMember(Name = "dataTypeId")] + public int DataTypeId { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs index 56a2ea506b..20f7d1b4b5 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs @@ -25,6 +25,13 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "properties")] public IEnumerable Properties { get; set; } + //TODO: Why is there recursive list of PropertyTypeGroupDisplay? Not sure how this + // is intended to work but seems like it will become very complicated. This will also + // mean that serialization won't work very well because you cannot serialize a recursive + // property. These models should just be flat lists of properties and groups with properties + // indicating where they've come from. These models don't have to be an exact representation + // of their data structures, they should be structured in the simplest format in order for + // us to pass data to and from the editor, and that's it. [DataMember(Name = "groups")] public IEnumerable Groups { get; set; } diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index 09b53dc45d..4c364c9f72 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -1,7 +1,9 @@ -using AutoMapper; +using System; +using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Mapping; +using Umbraco.Core.PropertyEditors; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -10,17 +12,102 @@ namespace Umbraco.Web.Models.Mapping /// Defines mappings for content/media/members type mappings /// internal class ContentTypeModelMapper : MapperConfiguration - { + { + private readonly Lazy _propertyEditorResolver; + + //default ctor + public ContentTypeModelMapper() + { + _propertyEditorResolver = new Lazy(() => PropertyEditorResolver.Current); + } + + //ctor can be used for testing + public ContentTypeModelMapper(Lazy propertyEditorResolver) + { + _propertyEditorResolver = propertyEditorResolver; + } + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap(); config.CreateMap(); config.CreateMap(); - config.CreateMap() - .ForMember( - dto => dto.Groups, - expression => expression.ResolveUsing()); + + config.CreateMap() + .ConstructUsing((ContentTypeDisplay source) => new ContentType(source.ParentId)) + //Ignore these since they are 'read only' and only updated with the repository layer + .ForMember(dto => dto.CreatorId, expression => expression.Ignore()) + .ForMember(dto => dto.Level, expression => expression.Ignore()) + .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) + .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) + .ForMember(dto => dto.SortOrder, expression => expression.Ignore()) + .ForMember(dto => dto.IsContainer, expression => expression.MapFrom(display => display.EnableListView)) + //ignore, we'll do this in after map + .ForMember(dto => dto.PropertyGroups, expression => expression.Ignore()) + .AfterMap((source, dest) => + { + dest.PropertyGroups = new PropertyGroupCollection(); + foreach (var groupDisplay in source.Groups) + { + dest.PropertyGroups.Add(Mapper.Map(groupDisplay)); + } + }); + + config.CreateMap() + //Ignore because this is not actually used for content types + .ForMember(display => display.Trashed, expression => expression.Ignore()) + //Ignore for now, this will need to be manually mapped or mapped with a resolver + // since there is no source property to map from + .ForMember(display => display.AvailableContentTypes, expression => expression.Ignore()) + //Ignore for now, this will need to be manually mapped or mapped with a resolver + // since there is no source property to map from + .ForMember(display => display.AvailableTemplates, expression => expression.Ignore()) + .ForMember(display => display.EnableListView, expression => expression.MapFrom(type => type.IsContainer)) + .ForMember( + dto => dto.Groups, + expression => expression.ResolveUsing(new PropertyTypeGroupResolver(applicationContext, _propertyEditorResolver))); + + config.CreateMap() + .ForMember(g => g.CreateDate, expression => expression.Ignore()) + .ForMember(g => g.UpdateDate, expression => expression.Ignore()) + .ForMember(g => g.ParentId, expression => expression.MapFrom(display => display.ParentGroupId)) + + //NOTE: We don't actually need to map these because auto-mapper will automatically do that since they are the same name! + //.ForMember(g => g.SortOrder, expression => expression.MapFrom(display => display.SortOrder)) + //.ForMember(g => g.Id, expression => expression.MapFrom(display => display.Id)) + //.ForMember(g => g.Name, expression => expression.MapFrom(display => display.Name)) + + //ignore these, we'll do this in after map + .ForMember(g => g.PropertyTypes, expression => expression.Ignore()) + .AfterMap((source, destination) => + { + destination.PropertyTypes = new PropertyTypeCollection(); + foreach (var propertyTypeDisplay in source.Properties) + { + destination.PropertyTypes.Add(Mapper.Map(propertyTypeDisplay)); + } + }); + + + config.CreateMap() + .ConstructUsing((PropertyTypeDisplay propertyTypeDisplay) => + { + var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeDisplay.DataTypeId); + if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeDisplay.DataTypeId); + return new PropertyType(dataType, propertyTypeDisplay.Alias); + }) + //ignore because this is set in the ctor + .ForMember(type => type.Alias, expression => expression.Ignore()) + //ignore because this is obsolete and shouldn't be used + .ForMember(type => type.DataTypeId, expression => expression.Ignore()) + //ignore because these are 'readonly' + .ForMember(type => type.CreateDate, expression => expression.Ignore()) + .ForMember(type => type.UpdateDate, expression => expression.Ignore()) + .ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory)) + .ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern)) + .ForMember(type => type.PropertyEditorAlias, expression => expression.MapFrom(display => display.Editor)) + .ForMember(type => type.DataTypeDefinitionId, expression => expression.MapFrom(display => display.DataTypeId)); } diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index 999b1fe824..030e7872de 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -4,19 +4,31 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { + internal class PropertyTypeGroupResolver : ValueResolver> { + private readonly ApplicationContext _applicationContext; + private readonly Lazy _propertyEditorResolver; + + public PropertyTypeGroupResolver(ApplicationContext applicationContext, Lazy propertyEditorResolver) + { + _applicationContext = applicationContext; + _propertyEditorResolver = propertyEditorResolver; + } + protected override IEnumerable ResolveCore(IContentType source) { var groups = new List(); - var propGroups = source.CompositionPropertyGroups; + var propGroups = source.CompositionPropertyGroups.ToArray(); + var genericGroup = new PropertyTypeGroupDisplay() { Name = "properties", Id = 0, ParentGroupId = 0 }; genericGroup.Properties = MapProperties(source.PropertyTypes); genericGroup.Groups = new List(); @@ -54,8 +66,8 @@ namespace Umbraco.Web.Models.Mapping var mappedProperties = new List(); foreach (var p in properties) { - var editor = PropertyEditorResolver.Current.GetByAlias(p.PropertyEditorAlias); - var preVals = UmbracoContext.Current.Application.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(p.DataTypeDefinitionId); + var editor = _propertyEditorResolver.Value.GetByAlias(p.PropertyEditorAlias); + var preVals = _applicationContext.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(p.DataTypeDefinitionId); mappedProperties.Add( new PropertyTypeDisplay() From 9d8f33e98eb7f710aea38545a0336cdad568db74 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 19 May 2015 14:25:31 +1000 Subject: [PATCH 0030/1710] Fixes: U4-6597 Umbraco fails to reconnect to a SQL Server if it started up when SQL server was offline - however this requires some review. --- src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index c5870f26e8..b65c6da840 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -129,11 +129,13 @@ namespace Umbraco.Core.Cache if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem); var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); lck.UpgradeToWriteLock(); + + result = new Lazy(getCacheItem, LazyThreadSafetyMode.PublicationOnly); + _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); } } @@ -173,7 +175,7 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem); + var result = new Lazy(getCacheItem, LazyThreadSafetyMode.PublicationOnly); var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache if (value == null) return; // do not store null values (backward compat) From 0e5a46957779f72e2552e13f103f2b71e6075244 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 19 May 2015 14:29:25 +1000 Subject: [PATCH 0031/1710] Fixes: U4-6597 Umbraco fails to reconnect to a SQL Server if it started up when SQL server was offline - however this requires some review. --- src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs | 13 +++++++++++-- .../Cache/ObjectCacheRuntimeCacheProvider.cs | 12 ++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index b65c6da840..00c2008da0 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -134,7 +134,11 @@ namespace Umbraco.Core.Cache lck.UpgradeToWriteLock(); - result = new Lazy(getCacheItem, LazyThreadSafetyMode.PublicationOnly); + result = new Lazy(getCacheItem, + //NOTE: This is required to not cache any exceptions that throw when the callback is executed. + // we want to ensure the callback is re-executed and not cached in the case that it might no + // longer throw an exception if runtime circumstances have changed. + LazyThreadSafetyMode.PublicationOnly); _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); } @@ -175,7 +179,12 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem, LazyThreadSafetyMode.PublicationOnly); + var result = new Lazy(getCacheItem, + //NOTE: This is required to not cache any exceptions that throw when the callback is executed. + // we want to ensure the callback is re-executed and not cached in the case that it might no + // longer throw an exception if runtime circumstances have changed. + LazyThreadSafetyMode.PublicationOnly); + var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache if (value == null) return; // do not store null values (backward compat) diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index edf1ba5aa6..13b987df63 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -207,10 +207,14 @@ namespace Umbraco.Core.Cache result = MemoryCache.Get(cacheKey) as Lazy; if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem); var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); lck.UpgradeToWriteLock(); + result = new Lazy(getCacheItem, + //NOTE: This is required to not cache any exceptions that throw when the callback is executed. + // we want to ensure the callback is re-executed and not cached in the case that it might no + // longer throw an exception if runtime circumstances have changed. + LazyThreadSafetyMode.PublicationOnly); MemoryCache.Set(cacheKey, result, policy); } } @@ -227,7 +231,11 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem); + var result = new Lazy(getCacheItem, + //NOTE: This is required to not cache any exceptions that throw when the callback is executed. + // we want to ensure the callback is re-executed and not cached in the case that it might no + // longer throw an exception if runtime circumstances have changed. + LazyThreadSafetyMode.PublicationOnly); var value = result.Value; // force evaluation now if (value == null) return; // do not store null values (backward compat) From 444a028e30e664cef22bb3bbe52676f61214cdf9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 19 May 2015 15:57:11 +1000 Subject: [PATCH 0032/1710] adds tests for caching exceptions and only caching one time and fixes HttpRequestCacheProvider to work with them. --- .../Cache/HttpRequestCacheProvider.cs | 3 +- src/Umbraco.Tests/Cache/CacheProviderTests.cs | 56 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs index 0a95ff6fd2..0ade5d55f4 100644 --- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Web; namespace Umbraco.Core.Cache @@ -105,7 +106,7 @@ namespace Umbraco.Core.Cache if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem); + result = new Lazy(getCacheItem, LazyThreadSafetyMode.PublicationOnly); ContextItems[cacheKey] = result; } } diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs index b0a8aec68c..8eab3b4bde 100644 --- a/src/Umbraco.Tests/Cache/CacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Web.UI; using NUnit.Framework; using Umbraco.Core.Cache; @@ -23,6 +24,59 @@ namespace Umbraco.Tests.Cache Provider.ClearAllCache(); } + [Test] + public void Does_Not_Cache_Exceptions() + { + var counter = 0; + + object result; + try + { + result = Provider.GetCacheItem("Blah", () => + { + counter++; + throw new Exception("Do not cache this"); + }); + } + catch (Exception){} + + try + { + result = Provider.GetCacheItem("Blah", () => + { + counter++; + throw new Exception("Do not cache this"); + }); + } + catch (Exception){} + + Assert.Greater(counter, 1); + + } + + [Test] + public void Ensures_Delegate_Result_Is_Cached_Once() + { + var counter = 0; + + object result; + + result = Provider.GetCacheItem("Blah", () => + { + counter++; + return ""; + }); + + result = Provider.GetCacheItem("Blah", () => + { + counter++; + return ""; + }); + + Assert.AreEqual(counter, 1); + + } + [Test] public void Can_Get_By_Search() { From 16e06b6d4dae7a9eb7c468670edea308fb9be77a Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 19 May 2015 09:24:25 +0200 Subject: [PATCH 0033/1710] Revert 444a028, 0e5a469 and 9d8f33e --- .../Cache/HttpRequestCacheProvider.cs | 3 +- .../Cache/HttpRuntimeCacheProvider.cs | 15 +---- .../Cache/ObjectCacheRuntimeCacheProvider.cs | 12 +--- src/Umbraco.Tests/Cache/CacheProviderTests.cs | 56 +------------------ 4 files changed, 6 insertions(+), 80 deletions(-) diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs index 0ade5d55f4..0a95ff6fd2 100644 --- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Web; namespace Umbraco.Core.Cache @@ -106,7 +105,7 @@ namespace Umbraco.Core.Cache if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem, LazyThreadSafetyMode.PublicationOnly); + result = new Lazy(getCacheItem); ContextItems[cacheKey] = result; } } diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index 00c2008da0..c5870f26e8 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -129,17 +129,11 @@ namespace Umbraco.Core.Cache if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { + result = new Lazy(getCacheItem); var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); lck.UpgradeToWriteLock(); - - result = new Lazy(getCacheItem, - //NOTE: This is required to not cache any exceptions that throw when the callback is executed. - // we want to ensure the callback is re-executed and not cached in the case that it might no - // longer throw an exception if runtime circumstances have changed. - LazyThreadSafetyMode.PublicationOnly); - _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); } } @@ -179,12 +173,7 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem, - //NOTE: This is required to not cache any exceptions that throw when the callback is executed. - // we want to ensure the callback is re-executed and not cached in the case that it might no - // longer throw an exception if runtime circumstances have changed. - LazyThreadSafetyMode.PublicationOnly); - + var result = new Lazy(getCacheItem); var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache if (value == null) return; // do not store null values (backward compat) diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index 13b987df63..edf1ba5aa6 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -207,14 +207,10 @@ namespace Umbraco.Core.Cache result = MemoryCache.Get(cacheKey) as Lazy; if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { + result = new Lazy(getCacheItem); var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); lck.UpgradeToWriteLock(); - result = new Lazy(getCacheItem, - //NOTE: This is required to not cache any exceptions that throw when the callback is executed. - // we want to ensure the callback is re-executed and not cached in the case that it might no - // longer throw an exception if runtime circumstances have changed. - LazyThreadSafetyMode.PublicationOnly); MemoryCache.Set(cacheKey, result, policy); } } @@ -231,11 +227,7 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem, - //NOTE: This is required to not cache any exceptions that throw when the callback is executed. - // we want to ensure the callback is re-executed and not cached in the case that it might no - // longer throw an exception if runtime circumstances have changed. - LazyThreadSafetyMode.PublicationOnly); + var result = new Lazy(getCacheItem); var value = result.Value; // force evaluation now if (value == null) return; // do not store null values (backward compat) diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs index 8eab3b4bde..b0a8aec68c 100644 --- a/src/Umbraco.Tests/Cache/CacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using System.Web.UI; using NUnit.Framework; using Umbraco.Core.Cache; @@ -24,59 +23,6 @@ namespace Umbraco.Tests.Cache Provider.ClearAllCache(); } - [Test] - public void Does_Not_Cache_Exceptions() - { - var counter = 0; - - object result; - try - { - result = Provider.GetCacheItem("Blah", () => - { - counter++; - throw new Exception("Do not cache this"); - }); - } - catch (Exception){} - - try - { - result = Provider.GetCacheItem("Blah", () => - { - counter++; - throw new Exception("Do not cache this"); - }); - } - catch (Exception){} - - Assert.Greater(counter, 1); - - } - - [Test] - public void Ensures_Delegate_Result_Is_Cached_Once() - { - var counter = 0; - - object result; - - result = Provider.GetCacheItem("Blah", () => - { - counter++; - return ""; - }); - - result = Provider.GetCacheItem("Blah", () => - { - counter++; - return ""; - }); - - Assert.AreEqual(counter, 1); - - } - [Test] public void Can_Get_By_Search() { From dbaf7c0190d217be89444bcffee8817884f27ced Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 19 May 2015 09:16:05 +0200 Subject: [PATCH 0034/1710] U4-6597 - fix issue with caches that would cache exceptions --- .../Cache/DictionaryCacheProviderBase.cs | 50 +++++++++++++---- .../Cache/HttpRequestCacheProvider.cs | 17 ++++-- .../Cache/HttpRuntimeCacheProvider.cs | 19 +++++-- .../Cache/ObjectCacheRuntimeCacheProvider.cs | 43 +++++--------- src/Umbraco.Tests/Cache/CacheProviderTests.cs | 56 ++++++++++++++++++- .../Cache/HttpRuntimeCacheProviderTests.cs | 27 ++++++++- .../config/umbracoSettings.config | 29 +--------- 7 files changed, 164 insertions(+), 77 deletions(-) diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs index a8307044a1..64eb8a3d35 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Cache protected const string CacheItemPrefix = "umbrtmche"; // an object that represent a value that has not been created yet - protected readonly object ValueNotCreated = new object(); + protected internal static readonly object ValueNotCreated = new object(); // manupulate the underlying cache entries // these *must* be called from within the appropriate locks @@ -30,19 +30,49 @@ namespace Umbraco.Core.Cache return string.Format("{0}-{1}", CacheItemPrefix, key); } - protected object GetSafeLazyValue(Lazy lazy, bool onlyIfValueIsCreated = false) + protected internal static Lazy GetSafeLazy(Func getCacheItem) { - try + // try to generate the value and if it fails, + // wrap in an ExceptionHolder - would be much simpler + // to just use lazy.IsValueFaulted alas that field is + // internal + return new Lazy(() => { - // if onlyIfValueIsCreated, do not trigger value creation - // must return something, though, to differenciate from null values - if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated; - return lazy.Value; - } - catch + try + { + return getCacheItem(); + } + catch (Exception e) + { + return new ExceptionHolder(e); + } + }); + } + + protected internal static object GetSafeLazyValue(Lazy lazy, bool onlyIfValueIsCreated = false) + { + // if onlyIfValueIsCreated, do not trigger value creation + // must return something, though, to differenciate from null values + if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated; + + // if execution has thrown then lazy.IsValueCreated is false + // and lazy.IsValueFaulted is true (but internal) so we use our + // own exception holder (see Lazy source code) to return null + if (lazy.Value is ExceptionHolder) return null; + + // we have a value and execution has not thrown so returning + // here does not throw + return lazy.Value; + } + + internal class ExceptionHolder + { + public ExceptionHolder(Exception e) { - return null; + Exception = e; } + + public Exception Exception { get; private set; } } #region Clear diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs index 0a95ff6fd2..a4a6938fc0 100644 --- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -105,15 +105,22 @@ namespace Umbraco.Core.Cache if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem); + result = GetSafeLazy(getCacheItem); ContextItems[cacheKey] = result; } } - // this may throw if getCacheItem throws, but this is the only place where - // it would throw as everywhere else we use GetLazySaveValue() to hide exceptions - // and pretend exceptions were never inserted into cache to begin with. - return result.Value; + // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache + // exceptions (but try again and again) and silently eat them - however at + // some point we have to report them - so need to re-throw here + + // this does not throw anymore + //return result.Value; + + var value = result.Value; // will not throw (safe lazy) + var eh = value as ExceptionHolder; + if (eh != null) throw eh.Exception; // throw once! + return value; } #endregion diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index c5870f26e8..748912b9f1 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -129,7 +129,7 @@ namespace Umbraco.Core.Cache if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem); + result = GetSafeLazy(getCacheItem); var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); @@ -138,10 +138,17 @@ namespace Umbraco.Core.Cache } } - // this may throw if getCacheItem throws, but this is the only place where - // it would throw as everywhere else we use GetLazySaveValue() to hide exceptions - // and pretend exceptions were never inserted into cache to begin with. - return result.Value; + // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache + // exceptions (but try again and again) and silently eat them - however at + // some point we have to report them - so need to re-throw here + + // this does not throw anymore + //return result.Value; + + value = result.Value; // will not throw (safe lazy) + var eh = value as ExceptionHolder; + if (eh != null) throw eh.Exception; // throw once! + return value; } public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) @@ -173,7 +180,7 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem); + var result = GetSafeLazy(getCacheItem); var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache if (value == null) return; // do not store null values (backward compat) diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index edf1ba5aa6..d4f2c33ed1 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -19,29 +19,11 @@ namespace Umbraco.Core.Cache private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); internal ObjectCache MemoryCache; - // an object that represent a value that has not been created yet - protected readonly object ValueNotCreated = new object(); - public ObjectCacheRuntimeCacheProvider() { MemoryCache = new MemoryCache("in-memory"); } - protected object GetSafeLazyValue(Lazy lazy, bool onlyIfValueIsCreated = false) - { - try - { - // if onlyIfValueIsCreated, do not trigger value creation - // must return something, though, to differenciate from null values - if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated; - return lazy.Value; - } - catch - { - return null; - } - } - #region Clear public virtual void ClearAllCache() @@ -72,7 +54,7 @@ namespace Umbraco.Core.Cache // x.Value is Lazy and not null, its value may be null // remove null values as well, does not hurt // get non-created as NonCreatedValue & exceptions as null - var value = GetSafeLazyValue((Lazy)x.Value, true); + var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value, true); return value == null || value.GetType().ToString().InvariantEquals(typeName); }) .Select(x => x.Key) @@ -92,7 +74,7 @@ namespace Umbraco.Core.Cache // x.Value is Lazy and not null, its value may be null // remove null values as well, does not hurt // get non-created as NonCreatedValue & exceptions as null - var value = GetSafeLazyValue((Lazy)x.Value, true); + var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value, true); return value == null || value.GetType() == typeOfT; }) .Select(x => x.Key) @@ -112,7 +94,7 @@ namespace Umbraco.Core.Cache // x.Value is Lazy and not null, its value may be null // remove null values as well, does not hurt // get non-created as NonCreatedValue & exceptions as null - var value = GetSafeLazyValue((Lazy)x.Value, true); + var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value, true); if (value == null) return true; return value.GetType() == typeOfT && predicate(x.Key, (T) value); @@ -161,7 +143,7 @@ namespace Umbraco.Core.Cache .ToArray(); // evaluate while locked } return entries - .Select(x => GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null + .Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null .Where(x => x != null) // backward compat, don't store null values in the cache .ToList(); } @@ -176,7 +158,7 @@ namespace Umbraco.Core.Cache .ToArray(); // evaluate while locked } return entries - .Select(x => GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null + .Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null .Where(x => x != null) // backward compat, don't store null values in the cache .ToList(); } @@ -188,7 +170,7 @@ namespace Umbraco.Core.Cache { result = MemoryCache.Get(cacheKey) as Lazy; // null if key not found } - return result == null ? null : GetSafeLazyValue(result); // return exceptions as null + return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null } public object GetCacheItem(string cacheKey, Func getCacheItem) @@ -205,9 +187,9 @@ namespace Umbraco.Core.Cache using (var lck = new UpgradeableReadLock(_locker)) { result = MemoryCache.Get(cacheKey) as Lazy; - if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null + if (result == null || DictionaryCacheProviderBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { - result = new Lazy(getCacheItem); + result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem); var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); lck.UpgradeToWriteLock(); @@ -215,7 +197,12 @@ namespace Umbraco.Core.Cache } } - return result.Value; + //return result.Value; + + var value = result.Value; // will not throw (safe lazy) + var eh = value as DictionaryCacheProviderBase.ExceptionHolder; + if (eh != null) throw eh.Exception; // throw once! + return value; } #endregion @@ -227,7 +214,7 @@ namespace Umbraco.Core.Cache // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. - var result = new Lazy(getCacheItem); + var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem); var value = result.Value; // force evaluation now if (value == null) return; // do not store null values (backward compat) diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs index b0a8aec68c..8eab3b4bde 100644 --- a/src/Umbraco.Tests/Cache/CacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Web.UI; using NUnit.Framework; using Umbraco.Core.Cache; @@ -23,6 +24,59 @@ namespace Umbraco.Tests.Cache Provider.ClearAllCache(); } + [Test] + public void Does_Not_Cache_Exceptions() + { + var counter = 0; + + object result; + try + { + result = Provider.GetCacheItem("Blah", () => + { + counter++; + throw new Exception("Do not cache this"); + }); + } + catch (Exception){} + + try + { + result = Provider.GetCacheItem("Blah", () => + { + counter++; + throw new Exception("Do not cache this"); + }); + } + catch (Exception){} + + Assert.Greater(counter, 1); + + } + + [Test] + public void Ensures_Delegate_Result_Is_Cached_Once() + { + var counter = 0; + + object result; + + result = Provider.GetCacheItem("Blah", () => + { + counter++; + return ""; + }); + + result = Provider.GetCacheItem("Blah", () => + { + counter++; + return ""; + }); + + Assert.AreEqual(counter, 1); + + } + [Test] public void Can_Get_By_Search() { diff --git a/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs index 1fc8ab1327..87e6d4366e 100644 --- a/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs @@ -1,4 +1,5 @@ -using System.Web; +using System; +using System.Web; using NUnit.Framework; using Umbraco.Core.Cache; @@ -29,5 +30,29 @@ namespace Umbraco.Tests.Cache { get { return _provider; } } + + [Test] + public void DoesNotCacheExceptions() + { + string value; + Assert.Throws(() => { value = (string)_provider.GetCacheItem("key", () => GetValue(1)); }); + Assert.Throws(() => { value = (string)_provider.GetCacheItem("key", () => GetValue(2)); }); + + // does not throw + value = (string)_provider.GetCacheItem("key", () => GetValue(3)); + Assert.AreEqual("succ3", value); + + // cache + value = (string)_provider.GetCacheItem("key", () => GetValue(4)); + Assert.AreEqual("succ3", value); + } + + private static string GetValue(int i) + { + Console.WriteLine("get" + i); + if (i < 3) + throw new Exception("fail"); + return "succ" + i; + } } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 616b5ba424..255dce6542 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -118,33 +118,10 @@ false true - + - - - - - - - - - - - plus - star - - - ae - oe - aa - ae - oe - ue - ss - ae - oe - - - - + oxxx + From e0ecb3291d1ef46346a80f1e23037299893aa720 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 19 May 2015 10:20:36 +0200 Subject: [PATCH 0035/1710] XmlCache - bugfix running with file cache disabled --- .../umbraco.presentation/content.cs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index edc71ef161..b5a3d98758 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -40,20 +40,24 @@ namespace umbraco private content() { - if (SyncToXmlFile == false) return; - - // there's always be one task keeping a ref to the runner - // so it's safe to just create it as a local var here - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions + if (XmlFileEnabled) { - LongRunning = true, - KeepAlive = true - }); + InitializeFileLock(); + } - // create (and add to runner) - _persisterTask = new XmlCacheFilePersister(runner, this); + if (SyncToXmlFile) + { + // there's always be one task keeping a ref to the runner + // so it's safe to just create it as a local var here + var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions + { + LongRunning = true, + KeepAlive = true + }); - InitializeFileLock(); + // create (and add to runner) + _persisterTask = new XmlCacheFilePersister(runner, this); + } // initialize content - populate the cache using (var safeXml = GetSafeXmlWriter(false)) @@ -797,7 +801,6 @@ order by umbracoNode.level, umbracoNode.sortOrder"; private void LoadXmlLocked(SafeXmlReaderWriter safeXml, out bool registerXmlChange) { LogHelper.Debug("Loading Xml..."); - EnsureFileLock(); // get the lock asap // try to get it from the file if (XmlFileEnabled && (safeXml.Xml = LoadXmlFromFile()) != null) From b43f0a098648a6b2b1eb9018a91ad6aeaa8de9bd Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Tue, 19 May 2015 11:57:49 +0200 Subject: [PATCH 0036/1710] Fixed up a couple of mapping errors --- .../Models/Mapping/ContentTypeModelMappingTests.cs | 4 +++- .../Models/ContentEditing/ContentTypeDisplay.cs | 2 +- .../Models/ContentEditing/PropertyTypeGroupDisplay.cs | 4 ++-- src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs | 3 +++ src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs | 7 +++++++ .../Models/Mapping/PropertyTypeGroupResolver.cs | 6 +++--- 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 5195b15dc5..4f92a1bc4e 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -139,6 +139,8 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(contentType.AllowedAsRoot, result.AllowedAsRoot); Assert.AreEqual(contentType.IsContainer, result.EnableListView); + Assert.AreEqual(contentType.DefaultTemplate.Alias, result.DefaultTemplate.Alias); + //TODO: Now we need to assert all of the more complicated parts Assert.AreEqual(2, result.Groups.Count()); Assert.AreEqual(2, result.Groups.ElementAt(0).Properties.Count()); @@ -154,7 +156,7 @@ namespace Umbraco.Tests.Models.Mapping AllowedTemplates = new List(), AvailableContentTypes = new List(), AvailableTemplates = new List(), - DefaultTemplate = "template1", + DefaultTemplate = new EntityBasic(){ Alias = "test" }, Description = "hello world", Icon = "tree-icon", Id = 1234, diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs index d1ec50d9a9..70eeb63339 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeDisplay.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.Models.ContentEditing public IEnumerable AvailableTemplates { get; set; } [DataMember(Name = "defaultTemplate")] - public string DefaultTemplate { get; set; } + public EntityBasic DefaultTemplate { get; set; } // Allowed parent node types (can include root) diff --git a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs index 20f7d1b4b5..4a95ba556a 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PropertyTypeGroupDisplay.cs @@ -32,8 +32,8 @@ namespace Umbraco.Web.Models.ContentEditing // indicating where they've come from. These models don't have to be an exact representation // of their data structures, they should be structured in the simplest format in order for // us to pass data to and from the editor, and that's it. - [DataMember(Name = "groups")] - public IEnumerable Groups { get; set; } + // [DataMember(Name = "groups")] + // public IEnumerable Groups { get; set; } //Indicate if this tab was inherited [DataMember(Name = "inherited")] diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs index 4c364c9f72..b61676722a 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs @@ -57,12 +57,15 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap() //Ignore because this is not actually used for content types .ForMember(display => display.Trashed, expression => expression.Ignore()) + //Ignore for now, this will need to be manually mapped or mapped with a resolver // since there is no source property to map from .ForMember(display => display.AvailableContentTypes, expression => expression.Ignore()) + //Ignore for now, this will need to be manually mapped or mapped with a resolver // since there is no source property to map from .ForMember(display => display.AvailableTemplates, expression => expression.Ignore()) + .ForMember(display => display.EnableListView, expression => expression.MapFrom(type => type.IsContainer)) .ForMember( dto => dto.Groups, diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs index fb0a809c24..2c04365e91 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs @@ -45,6 +45,13 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dto => dto.Trashed, expression => expression.Ignore()) .ForMember(x => x.AdditionalData, expression => expression.Ignore()); + config.CreateMap() + .ForMember(basic => basic.Icon, expression => expression.UseValue("icon-layout")) + .ForMember(basic => basic.Path, expression => expression.UseValue("")) + .ForMember(basic => basic.ParentId, expression => expression.UseValue(-1)) + .ForMember(dto => dto.Trashed, expression => expression.Ignore()) + .ForMember(x => x.AdditionalData, expression => expression.Ignore()); + config.CreateMap() //default to document icon .ForMember(x => x.Icon, expression => expression.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index 030e7872de..1a1259b452 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -31,13 +31,13 @@ namespace Umbraco.Web.Models.Mapping var genericGroup = new PropertyTypeGroupDisplay() { Name = "properties", Id = 0, ParentGroupId = 0 }; genericGroup.Properties = MapProperties(source.PropertyTypes); - genericGroup.Groups = new List(); + //genericGroup.Groups = new List(); foreach (var group in propGroups.Where(pg => pg.ParentId.HasValue == false)) { var mapped = new PropertyTypeGroupDisplay() { Id = group.Id, ParentGroupId = 0, Name = group.Name, SortOrder = group.SortOrder }; mapped.Properties = MapProperties(group.PropertyTypes); - mapped.Groups = MapChildGroups(mapped, propGroups); + //mapped.Groups = MapChildGroups(mapped, propGroups); groups.Add(mapped); } @@ -54,7 +54,7 @@ namespace Umbraco.Web.Models.Mapping var mapped = new PropertyTypeGroupDisplay() { Id = child.Id, ParentGroupId = child.ParentId.Value, Name = child.Name, SortOrder = child.SortOrder }; mapped.Name += child.PropertyTypes.Count.ToString(); mapped.Properties = MapProperties(child.PropertyTypes); - mapped.Groups = MapChildGroups(mapped, groups); + //mapped.Groups = MapChildGroups(mapped, groups); mappedGroups.Add(mapped); } From f1dfa2822865e0a3e3ecf390a44a9382e2c40061 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 19 May 2015 12:31:59 +0200 Subject: [PATCH 0037/1710] Add the last changes from the old branche --- .../src/views/documenttype/edit.controller.js | 195 +++++++++++------- .../src/views/documenttype/edit.html | 150 +++++++++----- 2 files changed, 224 insertions(+), 121 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js index 897061f59d..c1b62b0034 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js @@ -7,18 +7,21 @@ * The controller for the content type editor */ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, dataTypeResource) { - $scope.page = {action: [], menu: [] }; - //$rootScope.emptySection = true; + $scope.page = {actions: [], menu: [] }; contentTypeResource.getById($routeParams.id).then(function(dt){ $scope.contentType = dt; - // set first tab to active + // set all tab to active if( $scope.contentType.groups.length !== 0 ) { - $scope.contentType.groups[0].tabIsActive = true; + angular.forEach($scope.contentType.groups, function(group){ + group.tabState = "active"; + }); } + addInitTab(); + }); //hacking datatypes and their icons @@ -58,59 +61,71 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont /* ---------- TABS ---------- */ - $scope.addTab = function(){ + $scope.addTab = function(tab){ - // set all tabs to inactive - angular.forEach($scope.contentType.groups, function(group){ - group.tabIsActive = false; - }); + tab.tabState = "active"; - // push tab - $scope.contentType.groups.push({ - groups: [], - properties:[], - tabIsActive: true - }); + // push new init tab to the scope + addInitTab; }; $scope.deleteTab = function(tabIndex) { - $scope.contentType.groups.splice(tabIndex, 1); - - // activate previous tab - if( $scope.contentType.groups.length === 1 ) { - $scope.contentType.groups[0].tabIsActive = true; - } - }; $scope.activateTab = function(tab) { + tab.tabState = "active"; + }; + + $scope.updateTabTitle = function(tab) { + if(tab.properties.length === 0) { + addInitProperty(tab); + } + }; + + function addInitTab() { + + // check i init tab already exists + var addTab = true; - // set all tabs to inactive angular.forEach($scope.contentType.groups, function(group){ - group.tabIsActive = false; + if(group.tabState === "init") { + addTab = false; + } }); - // activate tab - tab.tabIsActive = true; + if(addTab) { + $scope.contentType.groups.push({ + groups: [], + properties:[], + tabState: "init" + }); + } + } - }; + function addInitProperty(tab) { + tab.properties.push({ + propertyState: "init" + }); + } /* ---------- PROPERTIES ---------- */ - $scope.addProperty = function(properties){ - $scope.dialogModel = {}; - $scope.dialogModel.title = "Add property type"; - $scope.dialogModel.datatypes = $scope.dataTypes; - $scope.dialogModel.addNew = true; - $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + /* + $scope.addProperty = function(properties){ + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add property type"; + $scope.dialogModel.datatypes = $scope.dataTypes; + $scope.dialogModel.addNew = true; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; - $scope.dialogModel.close = function(model){ - properties.push(model.property); - $scope.dialogModel = null; - }; - }; + $scope.dialogModel.close = function(model){ + properties.push(model.property); + $scope.dialogModel = null; + }; + }; + */ $scope.changePropertyName = function(property) { @@ -148,15 +163,15 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.dialogModel.submit = function(dt){ /* - contentTypeResource.getPropertyTypeScaffold(dt.id) - .then(function(pt){ - property.config = pt.config; - property.editor = pt.editor; - property.view = pt.view; - $scope.dialogModel = null; - $scope.showDialog = false; - }); - */ + contentTypeResource.getPropertyTypeScaffold(dt.id) + .then(function(pt){ + property.config = pt.config; + property.editor = pt.editor; + property.view = pt.view; + $scope.dialogModel = null; + $scope.showDialog = false; + }); + */ property.dialogIsOpen = false; @@ -166,11 +181,11 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont }; /* - $scope.dialogModel.submit = function(){ - $scope.showDialog = false; - $scope.dialogModel = null; - }; - */ + $scope.dialogModel.submit = function(){ + $scope.showDialog = false; + $scope.dialogModel = null; + }; + */ $scope.dialogModel.close = function(model){ $scope.showDialog = false; @@ -179,6 +194,45 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont }; + $scope.choosePropertyType = function(property, tab) { + + console.log(tab); + + $scope.showDialog = true; + $scope.dialogModel = {}; + $scope.dialogModel.title = "Choose property type"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; + + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + $scope.showDialog = false; + }; + + $scope.dialogModel.submit = function(dt){ + contentTypeResource.getPropertyTypeScaffold(dt.id).then(function(pt){ + + property.config = pt.config; + property.editor = pt.editor; + property.view = pt.view; + property.dataType = dt; + + property.propertyState = "active"; + + // open settings dialog + $scope.editPropertyTypeSettings(property); + + // push new init property to scope + //addInitProperty(tab); + + // push new init tab to scope + addInitTab(); + + }); + }; + + }; + $scope.addItems = function(tab){ $scope.showDialog = true; @@ -215,19 +269,20 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont tab.properties.splice(propertyIndex, 1); }; + /* + $scope.addProperty = function(group){ + $log.log("open dialog"); - $scope.addProperty = function(group){ - $log.log("open dialog"); + $scope.dialogModel = {}; + $scope.dialogModel.title = "Add property type"; + $scope.dialogModel.dataTypes = $scope.dataTypes; + $scope.dialogModel.view = "views/documentType/dialogs/property.html"; - $scope.dialogModel = {}; - $scope.dialogModel.title = "Add property type"; - $scope.dialogModel.dataTypes = $scope.dataTypes; - $scope.dialogModel.view = "views/documentType/dialogs/property.html"; - - $scope.dialogModel.close = function(model){ - $scope.dialogModel = null; - }; - }; + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + }; + }; + */ @@ -245,11 +300,11 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont handle: ".handle", zIndex: 6000, start: function (e, ui) { - ui.placeholder.addClass( ui.item.attr("class") ); - }, - stop: function(e, ui){ - ui.placeholder.remove(); - } + ui.placeholder.addClass( ui.item.attr("class") ); + }, + stop: function(e, ui){ + ui.placeholder.remove(); + } }; @@ -309,7 +364,7 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont console.log(ui); } }; - + } -angular.module("umbraco").controller("Umbraco.Editors.DocumentType.EditController", DocumentTypeEditController); +angular.module("umbraco").controller("Umbraco.Editors.DocumentType.EditController", DocumentTypeEditController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html index bee774a972..deb4877630 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html @@ -8,7 +8,8 @@ + actions="page.actions"> + @@ -19,8 +20,25 @@
  • -
    - + + +
    + +
    + Tab +
    + +
    + Add first tab + Add new tab +
    + +
    + + + +
    +
    - + - {{ tab.name }} + {{ tab.name }}
    -
    + @@ -57,13 +75,33 @@ --> - -
    + + +
    @@ -71,7 +109,30 @@ ng-model="tab.properties" class="no-style-list">
  • -
    + +
    + +
    +
    +
    +
    + +
    + +
    + + Preview + + Click to add property +
    + +
    + +
    + +
    + +
    -
    +
    - +
    @@ -96,7 +157,7 @@
    -
    +
    {{ property.label }}: {{ property.alias }}
    @@ -104,28 +165,29 @@
    -
    +
    - Edit property + Choose property type - {{property.dataType.name}} + + {{property.dataType.name}} + Preview + - - +
    -
    +
    - +
    - +
    +
    @@ -220,23 +285,6 @@
  • - -
  • -
    - -
    - Tab -
    - -
    - Add first tab - Add new tab -
    - -
    - -
  • -
  • {{ contentType | json }}
  • From 57127460f90f525cc8f1d2762cf9625fc981353d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 19 May 2015 13:46:38 +0200 Subject: [PATCH 0038/1710] Added getAllUsersConfigured and getAllUserPropertyEditors as resource --- .../src/common/resources/datatype.resource.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js index 3bd8548990..2492ac3309 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/datatype.resource.js @@ -84,6 +84,26 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to retrieve data'); }, + getAllUserConfigured: function () { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "dataTypeApiBaseUrl", + "GetAllUserConfigured")), + 'Failed to retrieve data'); + }, + + getAllUserPropertyEditors: function () { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "dataTypeApiBaseUrl", + "GetAllUserPropertyEditors")), + 'Failed to retrieve data'); + }, + /** * @ngdoc method * @name umbraco.resources.contentResource#getScaffold From f7e079f0d8b36645296b46b3a9108e75e11ca491 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 19 May 2015 13:47:48 +0200 Subject: [PATCH 0039/1710] get configured data types, custom editors and systems data types in dialog. Make it possible to select one. --- .../dialogs/property.controller.js | 38 ++++++++++- .../views/documenttype/dialogs/property.html | 65 ++++++++++--------- .../src/views/documenttype/edit.controller.js | 52 ++++----------- 3 files changed, 83 insertions(+), 72 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js index 183681b294..05393da9a0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.controller.js @@ -6,7 +6,7 @@ * @description * The controller for the content type editor property dialog */ -function DocumentTypePropertyController($scope, contentTypeResource) { +function DocumentTypePropertyController($scope, dataTypeResource) { /* $scope.selectDataType = function(dataType, model){ @@ -19,6 +19,42 @@ function DocumentTypePropertyController($scope, contentTypeResource) { }); }; */ + + $scope.dataTypes = { + "userConfigured": [], + "userPropertyEditors": [], + "system": [] + }; + + getAllUserConfiguredDataTypes(); + getAllUserPropertyEditors(); + getAllDatatypes(); + + + function getAllDatatypes() { + + dataTypeResource.getAll().then(function(data){ + $scope.dataTypes.system = data; + }); + + } + + function getAllUserConfiguredDataTypes() { + + dataTypeResource.getAllUserConfigured().then(function(data){ + $scope.dataTypes.userConfigured = data; + }); + + } + + function getAllUserPropertyEditors() { + + dataTypeResource.getAllUserPropertyEditors().then(function(data){ + $scope.dataTypes.userPropertyEditors = data; + }); + + } + } angular.module("umbraco").controller("Umbraco.Editors.DocumentType.PropertyController", DocumentTypePropertyController); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html index 2733f7f439..e68b562c5c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/dialogs/property.html @@ -1,34 +1,39 @@
    - - +
    +
    Your configurations
    + +
    + +
    +
    Custom property editors
    + +
    + +
    +
    Standard
    + +
    -
    -
    {{key}}
    - -
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js index c1b62b0034..7a431a521f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js @@ -7,7 +7,9 @@ * The controller for the content type editor */ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, dataTypeResource) { + $scope.page = {actions: [], menu: [] }; + contentTypeResource.getById($routeParams.id).then(function(dt){ @@ -24,38 +26,6 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont }); - //hacking datatypes and their icons - dataTypeResource.getAll().then(function(data){ - - data = _.groupBy(data, function(dt){ - dt.icon = "icon-autofill"; - - if(dt.name.indexOf("Dropdown") > -1 || dt.name.indexOf("Checkbox") > -1){ - dt.icon = "icon-bulleted-list"; - return "Lists"; - } - - if(dt.name.indexOf("Grid") > -1 || dt.name.indexOf("List View") > -1){ - dt.icon = "icon-item-arrangement"; - return "Collections"; - } - - if(dt.name.indexOf("picker") > -1){ - dt.icon ="icon-hand-pointer-alt"; - return "Pickers"; - } - - if(dt.name.indexOf("media") > -1 || dt.name.indexOf("Upload") > -1 || dt.name.indexOf("Crop") > -1){ - dt.icon ="icon-picture"; - return "Media"; - } - - return "Fields"; - }); - - $scope.dataTypes = data; - }); - $scope.actions = [{name: "Structure", cssClass: "list"},{name: "Structure", cssClass: "list"},{name: "Structure", cssClass: "list"}]; @@ -196,8 +166,6 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.choosePropertyType = function(property, tab) { - console.log(tab); - $scope.showDialog = true; $scope.dialogModel = {}; $scope.dialogModel.title = "Choose property type"; @@ -209,13 +177,14 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.showDialog = false; }; - $scope.dialogModel.submit = function(dt){ - contentTypeResource.getPropertyTypeScaffold(dt.id).then(function(pt){ + $scope.dialogModel.selectDataType = function(selectedDataType) { - property.config = pt.config; - property.editor = pt.editor; - property.view = pt.view; - property.dataType = dt; + contentTypeResource.getPropertyTypeScaffold(selectedDataType.id).then(function(propertyType){ + + property.config = propertyType.config; + property.editor = propertyType.editor; + property.view = propertyType.view; + property.dataType = selectedDataType; property.propertyState = "active"; @@ -223,12 +192,13 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.editPropertyTypeSettings(property); // push new init property to scope - //addInitProperty(tab); + addInitProperty(tab); // push new init tab to scope addInitTab(); }); + }; }; From 009f071f486f26630451de86337702e5cdc87a21 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 19 May 2015 14:04:32 +0200 Subject: [PATCH 0040/1710] XmlCache - comments --- src/Umbraco.Web/umbraco.presentation/content.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index b5a3d98758..64380b1484 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -42,11 +42,14 @@ namespace umbraco { if (XmlFileEnabled) { + // if we use the file, prepare the lock InitializeFileLock(); } if (SyncToXmlFile) { + // if we write to file, prepare the persister task + // there's always be one task keeping a ref to the runner // so it's safe to just create it as a local var here var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions @@ -63,7 +66,12 @@ namespace umbraco using (var safeXml = GetSafeXmlWriter(false)) { bool registerXmlChange; + + // if we don't use the file then LoadXmlLocked will not even + // read from the file and will go straight to database LoadXmlLocked(safeXml, out registerXmlChange); + // if we use the file and registerXmlChange is true this will + // write to file, else it will not safeXml.Commit(registerXmlChange); } } From bea7387459516fadff8c1a2c5c326fe7110c8410 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 19 May 2015 15:06:22 +0200 Subject: [PATCH 0041/1710] bugfix LegacyPropertyEditorIdToAliasConverterTests --- .../PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs | 1 - .../LegacyPropertyEditorIdToAliasConverterTests.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs index 41400f8787..c15b2d0576 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyPropertyEditorIdToAliasConverter.cs @@ -134,7 +134,6 @@ namespace Umbraco.Core.PropertyEditors CreateMap(Guid.Parse(Constants.PropertyEditors.UploadField), Constants.PropertyEditors.UploadFieldAlias); CreateMap(Guid.Parse(Constants.PropertyEditors.XPathCheckBoxList), Constants.PropertyEditors.XPathCheckBoxListAlias); CreateMap(Guid.Parse(Constants.PropertyEditors.XPathDropDownList), Constants.PropertyEditors.XPathDropDownListAlias); - CreateMap(Guid.Parse(Constants.PropertyEditors.MacroContainer), Constants.PropertyEditors.MacroContainerAlias); CreateMap(Guid.Parse(Constants.PropertyEditors.ImageCropper), Constants.PropertyEditors.ImageCropperAlias); //Being mapped to different editors diff --git a/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs index 432973d13b..e763d72af2 100644 --- a/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/LegacyPropertyEditorIdToAliasConverterTests.cs @@ -64,7 +64,7 @@ namespace Umbraco.Tests.PropertyEditors { LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors(); - Assert.AreEqual(37, LegacyPropertyEditorIdToAliasConverter.Count()); + Assert.AreEqual(36, LegacyPropertyEditorIdToAliasConverter.Count()); } } } \ No newline at end of file From ac7ff31a674ae1f8668cbe71c362f5089f0ffb1b Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 19 May 2015 20:08:52 +0200 Subject: [PATCH 0042/1710] U4-6626 - XmlCache, bugfix locks --- src/Umbraco.Core/AsyncLock.cs | 9 ++++ src/Umbraco.Core/CoreBootManager.cs | 2 +- .../XmlCacheFilePersister.cs | 3 +- .../Scheduling/BackgroundTaskRunner.cs | 15 ++++++- .../umbraco.presentation/content.cs | 41 ++++++++++++++----- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/AsyncLock.cs b/src/Umbraco.Core/AsyncLock.cs index 426984ab9f..0a9c79a80e 100644 --- a/src/Umbraco.Core/AsyncLock.cs +++ b/src/Umbraco.Core/AsyncLock.cs @@ -11,6 +11,15 @@ namespace Umbraco.Core // - this is NOT a reader/writer lock // - this is NOT a recursive lock // + // using a named Semaphore here and not a Mutex because mutexes have thread + // affinity which does not work with async situations + // + // it is important that managed code properly release the Semaphore before + // going down else it will maintain the lock - however note that when the + // whole process (w3wp.exe) goes down and all handles to the Semaphore have + // been closed, the Semaphore system object is destroyed - so in any case + // an iisreset should clean up everything + // internal class AsyncLock { private readonly SemaphoreSlim _semaphore; diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 16ffc828a9..6e00d3d682 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -66,7 +66,7 @@ namespace Umbraco.Core InitializeProfilerResolver(); - _timer = DisposableTimer.DebugDuration("Umbraco application starting", "Umbraco application startup complete"); + _timer = DisposableTimer.TraceDuration("Umbraco application starting", "Umbraco application startup complete"); CreateApplicationCache(); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs index fced0ce5d7..46b8eed789 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs @@ -130,7 +130,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } if (runNow) - Run(); + //Run(); + LogHelper.Warn("Cannot write now because we are going down, changes may be lost."); return ret; // this, by default, unless we created a new one } diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index 46ea9c1146..a11606937e 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -541,11 +541,13 @@ namespace Umbraco.Web.Scheduling { HostingEnvironment.UnregisterObject(this); LogHelper.Info>("Down, tasks completed."); + Stopped.RaiseEvent(new StoppedEventArgs(false), this); }); else { HostingEnvironment.UnregisterObject(this); LogHelper.Info>("Down, tasks completed."); + Stopped.RaiseEvent(new StoppedEventArgs(false), this); } } } @@ -560,9 +562,20 @@ namespace Umbraco.Web.Scheduling Shutdown(true, true); // cancel all tasks, wait for the current one to end HostingEnvironment.UnregisterObject(this); LogHelper.Info>("Down."); + Stopped.RaiseEvent(new StoppedEventArgs(true), this); } } - + public class StoppedEventArgs : EventArgs + { + public StoppedEventArgs(bool immediate) + { + Immediate = immediate; + } + + public bool Immediate { get; private set; } + } + + public event TypedEventHandler, StoppedEventArgs> Stopped; } } diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 64380b1484..34ecccd4ed 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -40,16 +40,13 @@ namespace umbraco private content() { - if (XmlFileEnabled) - { - // if we use the file, prepare the lock - InitializeFileLock(); - } - if (SyncToXmlFile) { - // if we write to file, prepare the persister task + // if we write to file, prepare the lock + // (if we don't use the file, or just read from it, no need to lock) + InitializeFileLock(); + // and prepare the persister task // there's always be one task keeping a ref to the runner // so it's safe to just create it as a local var here var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions @@ -58,6 +55,28 @@ namespace umbraco KeepAlive = true }); + // when the runner has stopped we know we will not be writing + // to the file anymore, so we can release the lock now - and + // not wait for the AppDomain unload + runner.Stopped += (sender, args) => + { + if (_fileLock == null) return; // not locking (testing?) + if (_fileLocked == null) return; // not locked + + // thread-safety + // lock something that's readonly and not null.. + lock (_xmlFileName) + { + // double-check + if (_fileLocked == null) return; + + LogHelper.Debug("Release file lock."); + _fileLocked.Dispose(); + _fileLocked = null; + _fileLock = null; // ensure we don't lock again + } + }; + // create (and add to runner) _persisterTask = new XmlCacheFilePersister(runner, this); } @@ -1019,8 +1038,8 @@ order by umbracoNode.level, umbracoNode.sortOrder"; private void OnDomainUnloadReleaseFileLock(object sender, EventArgs args) { // the unload event triggers AFTER all hosted objects (eg the file persister - // background task runner) have been stopped, so we should NOT be accessing - // the file from now one - release the lock + // background task runner) have been stopped, so we should have released the + // lock already - this is for safety - might be possible to get rid of it // NOTE // trying to write to the log via LogHelper at that point is a BAD idea @@ -1179,7 +1198,9 @@ order by umbracoNode.level, umbracoNode.sortOrder"; try { - EnsureFileLock(); + // if we're not writing back to the file, no need to lock + if (SyncToXmlFile) + EnsureFileLock(); var xml = new XmlDocument(); using (var fs = new FileStream(_xmlFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) From a3b5996372984c9848cd67c643eaf6d13551c353 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 20 May 2015 16:01:44 +1000 Subject: [PATCH 0043/1710] Fixes save/publish button placement on small screens --- src/Umbraco.Web.UI.Client/src/less/panel.less | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 4fed599ddc..d14e76bfc6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -37,11 +37,6 @@ bottom: 31px !important; } -/* -.umb-tab-buttons.umb-bottom-bar { - bottom: 50px !important; -}*/ - .umb-panel-header .umb-headline, .umb-panel-header h1 { font-size: 16px; border: none; @@ -153,27 +148,34 @@ /* tab buttons */ .umb-bottom-bar{ background: white; - -webkit-box-shadow: 0px -18px 20px rgba(255, 255, 255, 1); -moz-box-shadow: 0px -18px 20px rgba(255, 255, 255, 1); box-shadow: 0px -18px 20px rgba(255, 255, 255, 1); - border-top: 1px solid @grayLighter; - padding: 10px 0 10px 0; - position: fixed; bottom: 0px; left: 100px; - right: 20px; z-index: 6010; -}; - -@media (min-width: 1101px) { - .umb-bottom-bar {left: 460px;} } -.umb-tab-buttons{padding-left: 240px;} +.umb-tab-buttons{ + padding-left: 240px; +} + +@media (min-width: 1101px) { + .umb-bottom-bar { + left: 460px; + } +} + +@media (max-width: 767px) { + .umb-tab-buttons{ + padding-left:0px; + } +} + + .umb-tab-pane{padding-bottom: 90px} .tab-content{overflow: visible; } From 6cfb5bc678dbc7c500d825b0b1ff94aaecdb0dae Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 20 May 2015 16:13:36 +1000 Subject: [PATCH 0044/1710] Have made the buttons always left aligned with the editing section, seems better since it's consistent on all screens and less right hand motion to click a button --- src/Umbraco.Web.UI.Client/src/less/panel.less | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index d14e76bfc6..2a6abef6d7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -160,7 +160,7 @@ } .umb-tab-buttons{ - padding-left: 240px; + padding-left: 0px; } @media (min-width: 1101px) { @@ -169,13 +169,6 @@ } } -@media (max-width: 767px) { - .umb-tab-buttons{ - padding-left:0px; - } -} - - .umb-tab-pane{padding-bottom: 90px} .tab-content{overflow: visible; } From 2b2ed0029890b1604050f8d7888e52c59814aedf Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 20 May 2015 09:31:40 +0200 Subject: [PATCH 0045/1710] New tab and placeholder styling --- .../src/less/pages/documentTypeEditor.less | 601 +++++++++--------- .../src/views/documenttype/edit.controller.js | 25 +- .../src/views/documenttype/edit.html | 121 +--- 3 files changed, 352 insertions(+), 395 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less b/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less index b7285cb02b..53c96eb246 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/documentTypeEditor.less @@ -1,4 +1,3 @@ - .editors-document-type-container{ .handle{visibility: hidden;} @@ -20,48 +19,12 @@ font-weight: 400 } - - .tab-title{ - padding: 0 25px 0 25px; - position: absolute; - left: -2px; - top: -36px; - height: 34px; - background: white; - border: 2px solid #cccccc; - border-bottom: none; - border-radius: 5px 5px 0 0; - -webkit-transition: width 0.5s; /* Safari */ - transition: width 0.5s; - font-weight: bold; - .tab-title-input { - border-color: #ffffff; - width: 27px; - font-weight: bold; - &:hover { - border-color: @inputBorder; - } - } - .tab-title-text { - position: relative; - top: 10px; - font-size: 14px; - } - } - .editors-document-type-canvas{ - /* - min-height: 200px; - position: absolute; - top: 0; - right: 0; - left: 0; - - bottom: 0; - overflow: auto; - */ padding: 40px 80px; - + @media screen and (max-width: 1500px) { + padding-left: 0; + padding-right: 0; + } } .editors-document-type-sidebar{ @@ -83,305 +46,349 @@ list-style: none; } - .edt-props-sortable{min-height: 5px} + /* ---------- TABS ---------- */ - .properties-placeholder{ - //text-align: center; - background: @grayLighter; - border: 1px dashed @grayLight; - padding: 20px; - margin: 30px 20px 30px 20px; - } - -} - -/* ---------- TABS ---------- */ - -.edt-tab{ - margin: 50px 20px 70px 20px; - border: 2px solid #cccccc; - padding: 0; - box-shadow: 0 5px 1px 0 rgba(0,0,0,0.1); - border-radius: 0 5px 5px 5px; - &:hover { + .edt-tab{ + margin: 50px 20px 70px 20px; + min-height: 145px; + border: 2px solid #454A52; + padding: 0; + box-shadow: 0 3px 0px 0 rgba(0,0,0,0.1); + border-radius: 0 10px 10px 10px; + &:hover { + .edt-tab-actions { + visibility: visible; + opacity: 1; + -webkit-transition: opacity 0.5s; /* Safari */ + transition: opacity 0.5s; + } + } .edt-tab-actions { - visibility: visible; - opacity: 1; - -webkit-transition: opacity 0.5s; /* Safari */ - transition: opacity 0.5s; + position: absolute; + top: 5px; + right: 5px; + visibility: hidden; + opacity: 0; + z-index: 10; + .tab-action { + display: inline-block; + } } } - .edt-tab-actions { - position: absolute; - top: 5px; - right: 5px; - visibility: hidden; - opacity: 0; - z-index: 10; - .tab-action { - display: inline-block; - } - } -} -.edt-tab-center-text { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - text-align: center; -} - -.edt-tab:hover > h4 > .handle{ - visibility: visible; -} - -.edt-property-group{ - position: relative; - padding: 60px 20px 0 20px; -} - -.edt-property-group:hover > h5 > .handle{ - visibility: visible; -} - -.edt-property-group-sizer{ - bottom: 0; - top: 0; - right: -10px; - width: 15px; - position: absolute; - cursor: e-resize; -} - -.span12 .edt-property-group-sizer{ - cursor: w-resize; -} - - -.edt-props-sortable{ - margin-left: 0px; -} - -// tab is in init state -.edt-tab.tab-state-init { - cursor: pointer; - border: 2px dashed @grayLight; - height: 100px; - box-shadow: none !important; .tab-title{ - border: 2px dashed @grayLight; + padding: 10px 25px 0 25px; + position: absolute; + left: -2px; + top: -50px; + height: 38px; + background: white; + border: 2px solid #454A52; border-bottom: none; - } - &:hover { - //border-style: solid; - .tab-title { - //border-style: solid; - //border-bottom-style: none; + border-radius: 10px 10px 0 0; + font-weight: bold; + .tab-title-input { + border-color: #ffffff; + min-width: 80px; + font-weight: bold; + color: #515151; + margin-bottom: 0; + height: 25px !important; + &:hover { + border-color: @inputBorder; + } + &.tab-title-placeholder { + border: 1px dashed #979797 !important; + } + } + .tab-title-text { + position: relative; + top: 7px; + font-size: 14px; + } + } + + .edt-tab-center-text { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + text-align: center; + font-weight: bold; + color: #00759D; + } + + // tab is in init state + .edt-tab.tab-state-init { + cursor: pointer; + border: 2px dashed @grayLight; + box-shadow: none !important; + -webkit-transition: all 0.5s; + transition: all 0.5s; + .tab-title{ + border: 2px dashed @grayLight; + border-bottom: none; + width: 70px; + } + &:hover { + border-color: @blueLight; + .tab-title { + border-color: @blueLight; + } } } -} -// tab is active -.edt-tab.tab-state-active { + // tab is active + .edt-tab.tab-state-active { + border-color: @blue; + + .tab-title{ + border-color: @blue; + } + + .edt-property-group{ + padding-top: 60px; + } + + .edt-property { + margin-bottom: 20px + } + } + + + // tab is inactive + .edt-tab.tab-state-inactive { + + cursor: pointer; + + .edt-property-group{ + padding-top: 20px; + } + + .edt-property { + height: 50px; + margin-bottom: 10px; + background: @grayLighter; + color: @grayDarker; + border-radius: @baseBorderRadius; + padding-top: 0; + padding-bottom: 0; + } + + .edt-property-meta { + margin: 15px 0 0 10px; + } + + .edt-property-actions { + width: 150px; + text-align: right; + margin: 7px 10px 0 0; + .property-action { + display: inline-block; + } + } + + .ui-sortable-placeholder { + height: 50px !important; + padding: 0 !important; + margin-bottom: 10px; + } + + .edt-property-actions { + .property-action { + display: inline-block; + } + } + + } + + /* ---------- PROPERTY ---------- */ .edt-property-group{ - padding-top: 60px; + position: relative; + padding: 60px 20px 0 20px; } - .edt-property { - margin-bottom: 20px - } -} - - -// tab is inactive -.edt-tab.tab-state-inactive { - - cursor: pointer; - - .edt-property-group{ - padding-top: 20px; - } - - .edt-property { - height: 50px; - margin-bottom: 10px; - background: @grayLighter; - color: @grayDarker; - border-radius: @baseBorderRadius; - padding-top: 0; - padding-bottom: 0; + .edt-property{ + position: relative; + display: flex; + flex-flow: row; + padding: 20px 30px; } .edt-property-meta { - margin: 15px 0 0 10px; - } - - .edt-property-actions { - width: 150px; - text-align: right; - margin: 7px 10px 0 0; - .property-action { + width: 300px; + margin-right: 50px; + .property-meta-group { display: inline-block; } } - .ui-sortable-placeholder { - height: 50px !important; - padding: 0 !important; - margin-bottom: 10px; + .edt-property-preview{ + flex: 2; + max-height: 100px; + overflow: hidden; + position: relative; } - .edt-property-actions { - .property-action { - display: inline-block; - } - } - -} - -/* ---------- PROPERTY ---------- */ - -.edt-property{ - position: relative; - display: flex; - flex-flow: row; - padding: 20px 30px; - /* - &:hover, - &.active { + .edt-property-preview:after { + content:""; + position:absolute; + top:0px; + left:0; + height:100%; + width:100%; background: @grayLighter; - -webkit-transition: background 0.5s; - transition: background 0.5s; + opacity: 0.3; } - */ -} -// property state init - property placeholder -.edt-property.property-state-init { - border: 2px dashed @grayLight; - border-radius: 5px; - cursor: pointer; -} - -.edt-property-meta { - width: 300px; - .property-meta-group { - display: inline-block; - } -} - -.edt-property-inner{ - flex: 2; - max-height: 100px; - overflow: hidden; - position: relative; - /* - opacity: 0.7; - padding: 10px; - border: 1px dashed @grayLight; - */ - /* - &:hover { - cursor: pointer; - .edt-property-inner-overlay { - display: block; + .edt-property-preview-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10; + background: rgba(0,0,0,0.1); + .overlay-text { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: @grayDark; + display: none; } } - */ - div { - padding-top: 10px; + + .edt-property-preview-label{ + font-size: 11px; + position: absolute; + top: 0; + left: 0; + text-transform: uppercase; + z-index: 15; + background: @grayLighter; + padding: 3px; + line-height: 12px; + opacity: 0.8 } -} -.edt-property-actions { - width: 50px; - text-align: center; - visibility: hidden; - opacity: 0; -} -.edt-property:hover { .edt-property-actions { - visibility: visible; - opacity: 1; - -webkit-transition: opacity 0.5s; /* Safari */ - transition: opacity 0.5s; + width: 50px; + text-align: center; + margin: 15px 0 0 0; + .property-action { + margin: 0 0 5px 0; + display: block; + &:hover { + text-decoration: none; + } + .icon { + font-size: 25px; + color: @gray; + } + } } -} -.edt-property-inner-overlay { - background-color: @grayLighter; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10; - display: none; - .overlay-text { + // property state init - property placeholder + .edt-property.property-state-init { + border: 2px dashed @grayLight; + border-radius: 5px; + cursor: pointer; + &:hover { + border-color: @blueLight; + } + } + + + /* ---------- INPUTS ---------- */ + + .invisible-field { + input, + textarea { + border-color: transparent; + background: transparent; + } + } + + .edt-property-meta-alias { + input { + font-size: 12px; + color: @gray; + border-color: #fff; + margin-bottom: 0; + height: 10px; + } + } + + .edt-property-meta-label { + input { + font-size: 16px; + font-weight: bold; + border-color: #fff; + margin-bottom: 0; + color: @grayDarker; + } + } + + .edt-property-meta-description { + textarea { + font-size: 12px; + color: @grayDark; + } + } + + + /* ---------- PLACEHOLDERS ----------- */ + + .placeholder { + background: @grayLight; + display: block; + width: 100%; + margin-bottom: 5px; + height: 60px; + } + + .placeholder-text { + color: @gray; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); - color: @grayDark; + font-weight: bold; } -} -.edt-property-label{ - font-size: 11px; - position: absolute; - top: 0px; - left: 0px; - text-transform: uppercase; - z-index: 15; - background: @grayLighter; - padding: 3px; - line-height: 12px; - opacity: 0.8 -} - -.edt-property-inner:after { - content:""; - position:absolute; - top:0px; - left:0; - height:100%; - width:100%; - background: @grayLighter; - opacity: 0.3; -} - -.invisible-input { - border-color: transparent; - &:hover { - border-color: @inputBorder; + .placeholder-input-small { + background: @grayLight; + display: block; + height: 10px; + margin-bottom: 5px; + width: 25%; } -} + .placeholder-input { + background: @grayLight; + display: block; + height: 20px; + margin-bottom: 5px; + } -/* ---------- PLACEHOLDERS ----------- */ -.placeholder { - background: #e5e5e5; - display: block; - width: 100%; - margin-bottom: 5px; - height: 50px; -} + .placeholder-text-group { + .placeholder-text-line-full { + background: @grayLight; + display: block; + height: 5px; + margin-bottom: 5px; + } + .placeholder-text-line-short { + background: @grayLight; + display: block; + height: 5px; + margin-bottom: 5px; + width: 75%; + } + } -.placeholder-input-small { - background: #e5e5e5; - display: block; - height: 15px; - width: 200px; - margin-bottom: 5px; -} - -.placeholder-input { - background: #cccccc; - display: block; - height: 25px; - width: 200px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js index 7a431a521f..09461bf339 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.controller.js @@ -33,7 +33,7 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.addTab = function(tab){ - tab.tabState = "active"; + $scope.activateTab(tab); // push new init tab to the scope addInitTab; @@ -45,7 +45,17 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont }; $scope.activateTab = function(tab) { + + // set all other tabs that are inactive to active + angular.forEach($scope.contentType.groups, function(group){ + // skip init tab + if(group.tabState !== "init") { + group.tabState = "inActive"; + } + }); + tab.tabState = "active"; + }; $scope.updateTabTitle = function(tab) { @@ -69,6 +79,7 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.contentType.groups.push({ groups: [], properties:[], + name: "", tabState: "init" }); } @@ -97,7 +108,7 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont }; */ - $scope.changePropertyName = function(property) { + $scope.changePropertyLabel = function(property) { var str = property.label; @@ -172,10 +183,7 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont $scope.dialogModel.dataTypes = $scope.dataTypes; $scope.dialogModel.view = "views/documentType/dialogs/property.html"; - $scope.dialogModel.close = function(model){ - $scope.dialogModel = null; - $scope.showDialog = false; - }; + property.dialogIsOpen = true; $scope.dialogModel.selectDataType = function(selectedDataType) { @@ -201,6 +209,11 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont }; + $scope.dialogModel.close = function(model){ + $scope.dialogModel = null; + $scope.showDialog = false; + }; + }; $scope.addItems = function(tab){ diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html index deb4877630..ce6d307bea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttype/edit.html @@ -25,7 +25,7 @@
    - Tab +
    @@ -41,12 +41,8 @@
    - - - + {{ tab.name }} @@ -69,43 +65,9 @@
    - - - - - -
    -