From 68f05e05d1ecb2e5676439750212381040521268 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sat, 27 Oct 2018 21:18:03 +0200 Subject: [PATCH 01/18] Initial create and edit --- src/Umbraco.Core/IO/SystemDirectories.cs | 2 + src/Umbraco.Core/Models/Stylesheet.cs | 2 +- .../views/stylesheets/create.controller.js | 18 ++ .../src/views/stylesheets/create.html | 23 ++ .../src/views/stylesheets/edit.controller.js | 205 ++++++++++++++++++ .../src/views/stylesheets/edit.html | 58 +++++ .../Umbraco/config/lang/en_us.xml | 1 + src/Umbraco.Web/Editors/CodeFileController.cs | 79 ++++++- .../Models/Mapping/CodeFileMapperProfile.cs | 7 + 9 files changed, 389 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/create.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/create.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs index a0fa732c8c..46c5bf851c 100644 --- a/src/Umbraco.Core/IO/SystemDirectories.cs +++ b/src/Umbraco.Core/IO/SystemDirectories.cs @@ -36,6 +36,8 @@ namespace Umbraco.Core.IO public static string Scripts => IOHelper.ReturnPath("umbracoScriptsPath", "~/scripts"); + public static string StyleSheets => IOHelper.ReturnPath("umbracoStylesheetsPath", "~/css"); + public static string Umbraco => IOHelper.ReturnPath("umbracoPath", "~/umbraco"); //TODO: Consider removing this diff --git a/src/Umbraco.Core/Models/Stylesheet.cs b/src/Umbraco.Core/Models/Stylesheet.cs index a228b70105..87632fac27 100644 --- a/src/Umbraco.Core/Models/Stylesheet.cs +++ b/src/Umbraco.Core/Models/Stylesheet.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core.Models { } internal Stylesheet(string path, Func getFileContent) - : base(path.EnsureEndsWith(".css"), getFileContent) + : base(string.IsNullOrEmpty(path) ? path : path.EnsureEndsWith(".css"), getFileContent) { InitializeProperties(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/stylesheets/create.controller.js new file mode 100644 index 0000000000..229587cc63 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/create.controller.js @@ -0,0 +1,18 @@ +(function () { + "use strict"; + + function StyleSheetsCreateController($scope, $location, navigationService) { + + var vm = this; + var node = $scope.dialogOptions.currentNode; + + vm.createFile = createFile; + + function createFile() { + $location.path("/settings/stylesheets/edit/" + node.id).search("create", "true"); + navigationService.hideMenu(); + } + } + + angular.module("umbraco").controller("Umbraco.Editors.StyleSheets.CreateController", StyleSheetsCreateController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/create.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/create.html new file mode 100644 index 0000000000..82854635f9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/create.html @@ -0,0 +1,23 @@ + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js new file mode 100644 index 0000000000..33aa0f979b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js @@ -0,0 +1,205 @@ +(function () { + "use strict"; + + function StyleSheetsEditController($scope, $routeParams, $timeout, appState, editorState, navigationService, assetsService, codefileResource, contentEditingHelper, notificationsService, localizationService, templateHelper, angularHelper) { + + var vm = this; + var currentPosition = null; + + vm.page = {}; + vm.page.loading = true; + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; + vm.page.saveButtonState = "init"; + + //Used to toggle the keyboard shortcut modal + //From a custom keybinding in ace editor - that conflicts with our own to show the dialog + vm.showKeyboardShortcut = false; + + //Keyboard shortcuts for help dialog + vm.page.keyboardShortcutsOverview = []; + + templateHelper.getGeneralShortcuts().then(function(shortcuts){ + vm.page.keyboardShortcutsOverview.push(shortcuts); + }); + + templateHelper.getEditorShortcuts().then(function(shortcuts){ + vm.page.keyboardShortcutsOverview.push(shortcuts); + }); + + vm.stylesheet = {}; + + // bind functions to view model + vm.save = save; + + /* Function bound to view model */ + + function save() { + + vm.page.saveButtonState = "busy"; + + vm.stylesheet.content = vm.editor.getValue(); + + contentEditingHelper.contentEditorPerformSave({ + saveMethod: codefileResource.save, + scope: $scope, + content: vm.stylesheet, + // We do not redirect on failure for style sheets - this is because it is not possible to actually save the style sheet + // when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, + rebindCallback: function (orignal, saved) {} + }).then(function (saved) { + + localizationService.localizeMany(["speechBubbles_fileSavedHeader", "speechBubbles_fileSavedText"]).then(function(data){ + var header = data[0]; + var message = data[1]; + notificationsService.success(header, message); + }); + + //check if the name changed, if so we need to redirect + if (vm.stylesheet.id !== saved.id) { + contentEditingHelper.redirectToRenamedContent(saved.id); + } + else { + vm.page.saveButtonState = "success"; + vm.stylesheet = saved; + + //sync state + editorState.set(vm.stylesheet); + + // sync tree + navigationService.syncTree({ tree: "stylesheets", path: vm.stylesheet.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + + }, function (err) { + + vm.page.saveButtonState = "error"; + + localizationService.localizeMany(["speechBubbles_validationFailedHeader", "speechBubbles_validationFailedMessage"]).then(function(data){ + var header = data[0]; + var message = data[1]; + notificationsService.error(header, message); + }); + + }); + + + } + + /* Local functions */ + + function init() { + + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope); + + if ($routeParams.create) { + codefileResource.getScaffold("stylesheets", $routeParams.id).then(function (stylesheet) { + ready(stylesheet, false); + }); + } else { + codefileResource.getByPath('stylesheets', $routeParams.id).then(function (stylesheet) { + ready(stylesheet, true); + }); + } + + } + + function ready(stylesheet, syncTree) { + + vm.page.loading = false; + + vm.stylesheet = stylesheet; + + //sync state + editorState.set(vm.stylesheet); + + if (syncTree) { + navigationService.syncTree({ tree: "stylesheets", path: vm.stylesheet.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + + vm.aceOption = { + mode: "stylesheet", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px', + enableSnippets: true, + enableBasicAutocompletion: true, + enableLiveAutocompletion: false + }, + onLoad: function(_editor) { + + vm.editor = _editor; + + //Update the auto-complete method to use ctrl+alt+space + _editor.commands.bindKey("ctrl-alt-space", "startAutocomplete"); + + //Unassigns the keybinding (That was previously auto-complete) + //As conflicts with our own tree search shortcut + _editor.commands.bindKey("ctrl-space", null); + + //TODO: Move all these keybinding config out into some helper/service + _editor.commands.addCommands([ + //Disable (alt+shift+K) + //Conflicts with our own show shortcuts dialog - this overrides it + { + name: 'unSelectOrFindPrevious', + bindKey: 'Alt-Shift-K', + exec: function() { + //Toggle the show keyboard shortcuts overlay + $scope.$apply(function(){ + vm.showKeyboardShortcut = !vm.showKeyboardShortcut; + }); + }, + readOnly: true + } + ]); + + // initial cursor placement + // Keep cursor in name field if we are create a new style sheet + // else set the cursor at the bottom of the code editor + if(!$routeParams.create) { + $timeout(function(){ + vm.editor.navigateFileEnd(); + vm.editor.focus(); + }); + } + + vm.editor.on("change", changeAceEditor); + + } + } + + function changeAceEditor() { + setFormState("dirty"); + } + + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if(state === "dirty") { + currentForm.$setDirty(); + } else if(state === "pristine") { + currentForm.$setPristine(); + } + } + + + } + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.StyleSheets.EditController", StyleSheetsEditController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html new file mode 100644 index 0000000000..61252f5a54 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html @@ -0,0 +1,58 @@ +
+ + + +
+ + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index f2bf1c2c60..6dbcd0d870 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -315,6 +315,7 @@ New partial view from snippet New partial view macro from snippet New partial view macro (without macro) + New style sheet file Browse your website diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index edcd71b2e8..6bd1030dc3 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -17,6 +17,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Umbraco.Web.Trees; +using Stylesheet = Umbraco.Core.Models.Stylesheet; namespace Umbraco.Web.Editors { @@ -168,6 +169,18 @@ namespace Umbraco.Web.Editors return display; } throw new HttpResponseException(HttpStatusCode.NotFound); + + case Core.Constants.Trees.Stylesheets: + var stylesheet = Services.FileService.GetStylesheetByName(virtualPath); + if (stylesheet != null) + { + var display = Mapper.Map(stylesheet); + display.FileType = Core.Constants.Trees.Stylesheets; + display.Path = Url.GetTreePathFromFilePath(stylesheet.Path); + display.Id = System.Web.HttpUtility.UrlEncode(stylesheet.Path); + return display; + } + throw new HttpResponseException(HttpStatusCode.NotFound); } throw new HttpResponseException(HttpStatusCode.NotFound); @@ -235,6 +248,10 @@ namespace Umbraco.Web.Editors codeFileDisplay = Mapper.Map(new Script(string.Empty)); codeFileDisplay.VirtualPath = SystemDirectories.Scripts; break; + case Core.Constants.Trees.Stylesheets: + codeFileDisplay = Mapper.Map(new Stylesheet(string.Empty)); + codeFileDisplay.VirtualPath = SystemDirectories.StyleSheets; + break; default: throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Unsupported editortype")); } @@ -369,13 +386,17 @@ namespace Umbraco.Web.Editors display.Id = System.Web.HttpUtility.UrlEncode(scriptResult.Path); return display; - //display.AddErrorNotification( - // Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), - // Services.TextService.Localize("speechBubbles/partialViewErrorText")); - - + //display.AddErrorNotification( + // Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), + // Services.TextService.Localize("speechBubbles/partialViewErrorText")); + case Core.Constants.Trees.Stylesheets: + var stylesheetResult = CreateOrUpdateStylesheet(display); + display = Mapper.Map(stylesheetResult, display); + display.Path = Url.GetTreePathFromFilePath(stylesheetResult.Path); + display.Id = System.Web.HttpUtility.UrlEncode(stylesheetResult.Path); + return display; default: throw new HttpResponseException(HttpStatusCode.NotFound); @@ -431,6 +452,54 @@ namespace Umbraco.Web.Editors return script; } + private Stylesheet CreateOrUpdateStylesheet(CodeFileDisplay display) + { + return CreateOrUpdateFile(display, ".css", Current.FileSystems.StylesheetsFileSystem, + name => Services.FileService.GetStylesheetByName(name), + (stylesheet, userId) => Services.FileService.SaveStylesheet(stylesheet, userId), + name => new Stylesheet(name) + ); + } + + private T CreateOrUpdateFile(CodeFileDisplay display, string extension, IFileSystem fileSystem, + Func getFileByName, Action saveFile, Func createFile) where T : Core.Models.File + { + //must always end with the correct extension + display.Name = EnsureCorrectFileExtension(display.Name, extension); + + var virtualPath = display.VirtualPath ?? string.Empty; + // this is all weird, should be using relative paths everywhere! + var relPath = fileSystem.GetRelativePath(virtualPath); + + if (relPath.EndsWith(extension) == false) + { + //this would typically mean it's new + relPath = relPath.IsNullOrWhiteSpace() + ? relPath + display.Name + : relPath.EnsureEndsWith('/') + display.Name; + } + + var file = getFileByName(relPath); + if (file != null) + { + // might need to find the path + var orgPath = file.OriginalPath.Substring(0, file.OriginalPath.IndexOf(file.Name)); + file.Path = orgPath + display.Name; + + file.Content = display.Content; + //try/catch? since this doesn't return an Attempt? + saveFile(file, Security.CurrentUser.Id); + } + else + { + file = createFile(relPath); + file.Content = display.Content; + saveFile(file, Security.CurrentUser.Id); + } + + return file; + } + private Attempt CreateOrUpdatePartialView(CodeFileDisplay display) { return CreateOrUpdatePartialView(display, SystemDirectories.PartialViews, diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/CodeFileMapperProfile.cs index 082abfdace..94c43f8f11 100644 --- a/src/Umbraco.Web/Models/Mapping/CodeFileMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/CodeFileMapperProfile.cs @@ -1,6 +1,7 @@ using AutoMapper; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; +using Stylesheet = Umbraco.Core.Models.Stylesheet; namespace Umbraco.Web.Models.Mapping { @@ -20,6 +21,12 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Path, opt => opt.Ignore()) .ForMember(dest => dest.Snippet, opt => opt.Ignore()); + CreateMap() + .ForMember(dest => dest.FileType, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.Snippet, opt => opt.Ignore()); + CreateMap() .IgnoreEntityCommonProperties() .ForMember(dest => dest.Id, opt => opt.Ignore()) From a3232bb55c73d4193f1dc3150af01f8e3c6a3cd1 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 31 Oct 2018 21:33:32 +0100 Subject: [PATCH 02/18] WIP: split editing into tabs --- src/Umbraco.Web.UI.Client/gulpfile.js | 1 + src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-stylesheet.less | 38 ++++++++++++ .../src/views/stylesheets/edit.controller.js | 61 ++++++++++++++++++- .../src/views/stylesheets/edit.html | 16 +++-- .../views/stylesheets/views/code/code.html | 9 +++ .../views/rules/rules.controller.js | 26 ++++++++ .../views/stylesheets/views/rules/rules.html | 51 ++++++++++++++++ .../Editors/StylesheetController.cs | 61 ++++++++++++++++++- .../Models/ContentEditing/StylesheetRule.cs | 2 + 10 files changed, 253 insertions(+), 13 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index d1bbfe65db..3e37f902d9 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -162,6 +162,7 @@ gulp.task('dependencies', function () { "./node_modules/ace-builds/src-min-noconflict/theme-chrome.js", "./node_modules/ace-builds/src-min-noconflict/mode-razor.js", "./node_modules/ace-builds/src-min-noconflict/mode-javascript.js", + "./node_modules/ace-builds/src-min-noconflict/mode-css.js", "./node_modules/ace-builds/src-min-noconflict/worker-javascript.js" ], "base": "./node_modules/ace-builds" diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 3100433a4a..4fb16c08ec 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -149,6 +149,7 @@ @import "components/umb-box.less"; @import "components/umb-number-badge.less"; @import "components/umb-progress-circle.less"; +@import "components/umb-stylesheet.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less new file mode 100644 index 0000000000..fd976bba32 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less @@ -0,0 +1,38 @@ +.umb-stylesheet-rules { + width: 600px; +} + +.umb-stylesheet-rules__listitem { + display: flex; + padding: 6px; + margin: 10px 0 !important; + background: @gray-10; + cursor: move; +} + +.umb-stylesheet-rules__listitem i { + display: flex; + align-items: center; + margin-right: 5px +} + +.umb-stylesheet-rules__listitem a { + cursor: pointer; + margin-left: auto; +} + +.umb-stylesheet-rules__listitem input { + width: 295px; +} + +.umb-styleheet-rules__left { + display: block; + flex: 1 1 auto; + overflow: hidden; +} + +.umb-styleheet-rules__right { + display: flex; + flex: 0 0 auto; + align-items: center; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js index 33aa0f979b..09278a5521 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function StyleSheetsEditController($scope, $routeParams, $timeout, appState, editorState, navigationService, assetsService, codefileResource, contentEditingHelper, notificationsService, localizationService, templateHelper, angularHelper) { + function StyleSheetsEditController($scope, $routeParams, $timeout, $http, appState, editorState, navigationService, assetsService, codefileResource, contentEditingHelper, notificationsService, localizationService, templateHelper, angularHelper, umbRequestHelper) { var vm = this; var currentPosition = null; @@ -12,6 +12,22 @@ vm.page.menu.currentSection = appState.getSectionState("currentSection"); vm.page.menu.currentNode = null; vm.page.saveButtonState = "init"; + // TODO: localization + vm.page.navigation = [ + { + "name": "Code", + "alias": "code", + "icon": "icon-brackets", + "view": "views/stylesheets/views/code/code.html", + "active": true + }, + { + "name": "Styles", + "alias": "rules", + "icon": "icon-font", + "view": "views/stylesheets/views/rules/rules.html" + } + ]; //Used to toggle the keyboard shortcut modal //From a custom keybinding in ace editor - that conflicts with our own to show the dialog @@ -125,7 +141,7 @@ } vm.aceOption = { - mode: "stylesheet", + mode: "css", theme: "chrome", showPrintMargin: false, advanced: { @@ -193,8 +209,49 @@ currentForm.$setPristine(); } } + } + $scope.selectApp = function (app) { + vm.page.loading = true; + var payload = { + content: vm.stylesheet.content, + rules: vm.stylesheet.rules + }; + if (app.alias === "code") { + umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "stylesheetApiBaseUrl", + "PostInterpolateStylesheetRules"), + payload), + "Failed to interpolate sheet rules") + .then( + function(content) { + vm.page.loading = false; + vm.stylesheet.content = content; + }, + function(err) { + } + ); + } + else { + umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "stylesheetApiBaseUrl", + "PostExtractStylesheetRules"), + payload), + "Failed to extract style sheet rules") + .then( + function (rules) { + vm.page.loading = false; + vm.stylesheet.rules = rules; + }, + function(err) { + } + ); + } } init(); diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html index 61252f5a54..7daac56b38 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html @@ -13,19 +13,17 @@ hide-alias="true" description="vm.stylesheet.virtualPath" description-locked="true" + navigation="vm.page.navigation" + on-select-navigation-item="selectApp(item)" hide-icon="true"> - - -
-
-
-
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html new file mode 100644 index 0000000000..ce8b2c8b2f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html @@ -0,0 +1,9 @@ + + +
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js new file mode 100644 index 0000000000..59293c1b9e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js @@ -0,0 +1,26 @@ +angular.module("umbraco").controller("Umbraco.Editors.StyleSheets.RulesController", + function ($scope) { + $scope.sortableOptions = { + axis: 'y', + containment: 'parent', + cursor: 'move', + items: '> div.control-group', + tolerance: 'pointer', + update: function (e, ui) { + // TODO + console.log("TODO: set dirty") + } + }; + + $scope.add = function (evt) { + evt.preventDefault(); + + $scope.model.stylesheet.rules.push({}); + } + + $scope.remove = function (rule, evt) { + evt.preventDefault(); + + $scope.model.stylesheet.rules = _.without($scope.model.stylesheet.rules, rule); + } + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html new file mode 100644 index 0000000000..4e6ba8c8a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html @@ -0,0 +1,51 @@ + + + + +
+
+
Rich text editor styles
+ Some explanatory text about rich text editor styles goes here +
+
+
+
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ Remove +
+
+
+ +
+
+
+
+
diff --git a/src/Umbraco.Web/Editors/StylesheetController.cs b/src/Umbraco.Web/Editors/StylesheetController.cs index 99ab0add34..28b1930fb4 100644 --- a/src/Umbraco.Web/Editors/StylesheetController.cs +++ b/src/Umbraco.Web/Editors/StylesheetController.cs @@ -1,8 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Strings.Css; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using StylesheetRule = Umbraco.Web.Models.ContentEditing.StylesheetRule; namespace Umbraco.Web.Editors { @@ -30,6 +33,60 @@ namespace Umbraco.Web.Editors return css.Properties.Select(x => new StylesheetRule() { Name = x.Name, Selector = x.Alias }); } - } + public StylesheetRule[] PostExtractStylesheetRules(StylesheetData data) + { + if (data.Content.IsNullOrWhiteSpace()) + { + return new StylesheetRule[0]; + } + + return StylesheetHelper.ParseRules(data.Content)?.Select(rule => new StylesheetRule + { + Name = rule.Name, + Selector = rule.Selector, + Styles = rule.Styles + }).ToArray(); + } + + public string PostInterpolateStylesheetRules(StylesheetData data) + { + // first remove all existing rules + var existingRules = data.Content.IsNullOrWhiteSpace() + ? new Core.Strings.Css.StylesheetRule[0] + : StylesheetHelper.ParseRules(data.Content).ToArray(); + foreach (var rule in existingRules) + { + data.Content = StylesheetHelper.ReplaceRule(data.Content, rule.Name, null); + } + + data.Content = data.Content.TrimEnd('\n', '\r'); + + // now add all the posted rules + if (data.Rules != null && data.Rules.Any()) + { + foreach (var rule in data.Rules) + { + data.Content = StylesheetHelper.AppendRule(data.Content, new Core.Strings.Css.StylesheetRule + { + Name = rule.Name, + Selector = rule.Selector, + Styles = rule.Styles + }); + } + + data.Content += Environment.NewLine; + } + + return data.Content; + } + + // this is an internal class for passing stylesheet data from the client to the controller while editing + public class StylesheetData + { + public string Content { get; set; } + + public StylesheetRule[] Rules { get; set; } + } + } } diff --git a/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs b/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs index 9219cce4f5..b3212445ae 100644 --- a/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs +++ b/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs @@ -16,5 +16,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "selector")] public string Selector { get; set; } + [DataMember(Name = "styles")] + public string Styles { get; set; } } } From e726e6dffe6e394d0c2d278d2316bfc018ecff98 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 1 Nov 2018 11:08:41 +0100 Subject: [PATCH 03/18] Remove a bunch of inline styles --- .../src/less/components/umb-stylesheet.less | 20 ++++++++++++++-- .../views/stylesheets/views/rules/rules.html | 23 +++++++++---------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less index fd976bba32..b74e13719b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less @@ -25,13 +25,29 @@ width: 295px; } -.umb-styleheet-rules__left { +.umb-stylesheet-rules__left { display: block; flex: 1 1 auto; overflow: hidden; + + .umb-el-wrap { + margin: 5px auto; + + .control-label { + text-align: left + } + + .controls { + textarea { + width: 295px; + height: 80px; + resize: none; + } + } + } } -.umb-styleheet-rules__right { +.umb-stylesheet-rules__right { display: flex; flex: 0 0 auto; align-items: center; diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html index 4e6ba8c8a1..adb355a03c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html @@ -1,5 +1,4 @@ -
@@ -8,37 +7,37 @@ Some explanatory text about rich text editor styles goes here
-
+
-
+
-
-
-