diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index 2d4042fad0..b53a2b8eaf 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -77,7 +77,7 @@ /// alias for the macro tree. /// public const string Macros = "macros"; - + /// /// alias for the datatype tree. /// @@ -92,7 +92,7 @@ /// alias for the dictionary tree. /// public const string Dictionary = "dictionary"; - + public const string Stylesheets = "stylesheets"; /// @@ -121,7 +121,7 @@ public const string Templates = "templates"; public const string RelationTypes = "relationTypes"; - + public const string Languages = "languages"; /// diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index c404e0404d..a868245940 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -228,6 +228,7 @@ namespace Umbraco.Tests.Testing .Append() .Append() .Append() + .Append() .Append(); Composition.RegisterUnique(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js index edf54ca034..40def728ed 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js @@ -29,6 +29,8 @@ let typeahead; let tagsHound; + let initLoad = true; + vm.$onInit = onInit; vm.$onChanges = onChanges; vm.$onDestroy = onDestroy; @@ -53,7 +55,7 @@ vm.isLoading = false; //ensure that the models are formatted correctly - configureViewModel(); + configureViewModel(true); // Set the visible prompt to -1 to ensure it will not be visible vm.promptIsVisible = "-1"; @@ -139,8 +141,7 @@ if (!changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) { configureViewModel(); - reValidate() - + reValidate(); } } } @@ -154,13 +155,19 @@ $element.find('.tags-' + vm.htmlId).typeahead('destroy'); } - function configureViewModel() { + function configureViewModel(isInitLoad) { if (vm.value) { if (angular.isString(vm.value) && vm.value.length > 0) { if (vm.config.storageType === "Json") { //json storage vm.viewModel = JSON.parse(vm.value); - updateModelValue(vm.viewModel); + + //if this is the first load, we are just re-formatting the underlying model to be consistent + //we don't want to notify the component parent of any changes, that will occur if the user actually + //changes a value. If we notify at this point it will signal a form dirty change which we don't want. + if (!isInitLoad) { + updateModelValue(vm.viewModel); + } } else { //csv storage @@ -174,8 +181,12 @@ return self.indexOf(v) === i; }); - updateModelValue(vm.viewModel); - + //if this is the first load, we are just re-formatting the underlying model to be consistent + //we don't want to notify the component parent of any changes, that will occur if the user actually + //changes a value. If we notify at this point it will signal a form dirty change which we don't want. + if (!isInitLoad) { + updateModelValue(vm.viewModel); + } } } else if (angular.isArray(vm.value)) { diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index ed3d6ded33..e801e2cc58 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -192,7 +192,6 @@ function FormsController($scope, $route, $cookies, packageResource, localization function Video_player (videoId) { // Get dom elements this.container = document.getElementById(videoId); - this.video = this.container.getElementsByTagName('video')[0]; //Create controls this.controls = document.createElement('div'); @@ -215,104 +214,6 @@ function FormsController($scope, $route, $cookies, packageResource, localization this.controls.appendChild(this.loader); this.loader.appendChild(this.progress_bar); } - - - Video_player.prototype - .seeking = function() { - // get the value of the seekbar (hidden input[type="range"]) - var time = this.video.duration * (this.seek_bar.value / 100); - - // Update video to seekbar value - this.video.currentTime = time; - }; - - // Stop video when user initiates seeking - Video_player.prototype - .start_seek = function() { - this.video.pause(); - }; - - // Start video when user stops seeking - Video_player.prototype - .stop_seek = function() { - this.video.play(); - }; - - // Update the progressbar (span.loader) according to video.currentTime - Video_player.prototype - .update_progress_bar = function() { - // Get video progress in % - var value = (100 / this.video.duration) * this.video.currentTime; - - // Update progressbar - this.progress_bar.style.width = value + '%'; - }; - - // Bind progressbar to mouse when seeking - Video_player.prototype - .handle_mouse_move = function(event) { - // Get position of progressbar relative to browser window - var pos = this.progress_bar.getBoundingClientRect().left; - - // Make sure event is reckonized cross-browser - event = event || window.event; - - // Update progressbar - this.progress_bar.style.width = (event.clientX - pos) + "px"; - }; - - // Eventlisteners for seeking - Video_player.prototype - .video_event_handler = function(videoPlayer, interval) { - // Update the progress bar - var animate_progress_bar = setInterval(function () { - videoPlayer.update_progress_bar(); - }, interval); - - // Fire when input value changes (user seeking) - videoPlayer.seek_bar - .addEventListener("change", function() { - videoPlayer.seeking(); - }); - - // Fire when user clicks on seekbar - videoPlayer.seek_bar - .addEventListener("mousedown", function (clickEvent) { - // Pause video playback - videoPlayer.start_seek(); - - // Stop updating progressbar according to video progress - clearInterval(animate_progress_bar); - - // Update progressbar to where user clicks - videoPlayer.handle_mouse_move(clickEvent); - - // Bind progressbar to cursor - window.onmousemove = function(moveEvent){ - videoPlayer.handle_mouse_move(moveEvent); - }; - }); - - // Fire when user releases seekbar - videoPlayer.seek_bar - .addEventListener("mouseup", function () { - - // Unbind progressbar from cursor - window.onmousemove = null; - - // Start video playback - videoPlayer.stop_seek(); - - // Animate the progressbar - animate_progress_bar = setInterval(function () { - videoPlayer.update_progress_bar(); - }, interval); - }); - }; - - - var videoPlayer = new Video_player('video_1'); - videoPlayer.video_event_handler(videoPlayer, 17); } angular.module("umbraco").controller("Umbraco.Dashboard.FormsDashboardController", FormsController); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html index 3b382367c4..c18e7f4ccf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/forms/formsdashboardintro.html @@ -8,14 +8,6 @@
-
- -
-

Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it!

Styles The CSS that should be applied in the rich text editor, e.g. "color:red;" Code - Rich Text Editor + Rich Text Editor Failed to delete template with ID %0% diff --git a/src/Umbraco.Web/Dashboards/FormsDashboard.cs b/src/Umbraco.Web/Dashboards/FormsDashboard.cs index a3e1123369..867e8af3aa 100644 --- a/src/Umbraco.Web/Dashboards/FormsDashboard.cs +++ b/src/Umbraco.Web/Dashboards/FormsDashboard.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Dashboards; @@ -9,7 +10,7 @@ namespace Umbraco.Web.Dashboards { public string Alias => "formsInstall"; - public string[] Sections => new [] { "forms" }; + public string[] Sections => new [] { Constants.Applications.Forms }; public string View => "views/dashboard/forms/formsdashboardintro.html"; diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs index 0a2f17cd15..52034b9c95 100644 --- a/src/Umbraco.Web/Editors/SectionController.cs +++ b/src/Umbraco.Web/Editors/SectionController.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.Editors // this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that // since tree's by nature are controllers and require request contextual data - var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContext, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService, Umbraco) + var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContext, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService, _sectionService, Umbraco) { ControllerContext = ControllerContext }; diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs index 3afe6aa397..52373184b4 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComposer.cs @@ -220,6 +220,7 @@ namespace Umbraco.Web.Runtime .Append() .Append() .Append() + .Append() .Append(); // register core CMS dashboards and 3rd party types - will be ordered by weight attribute & merged with package.manifest dashboards diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index 162d001e96..b8fe709738 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -32,13 +32,15 @@ namespace Umbraco.Web.Trees public class ApplicationTreeController : UmbracoAuthorizedApiController { private readonly ITreeService _treeService; + private readonly ISectionService _sectionService; public ApplicationTreeController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, - IRuntimeState runtimeState, ITreeService treeService, UmbracoHelper umbracoHelper) + IRuntimeState runtimeState, ITreeService treeService, ISectionService sectionService, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _treeService = treeService; + _sectionService = sectionService; } /// @@ -56,12 +58,21 @@ namespace Umbraco.Web.Trees if (string.IsNullOrEmpty(application)) throw new HttpResponseException(HttpStatusCode.NotFound); + var section = _sectionService.GetByAlias(application); + if (section == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + //find all tree definitions that have the current application alias var groupedTrees = _treeService.GetBySectionGrouped(application, use); var allTrees = groupedTrees.Values.SelectMany(x => x).ToList(); if (allTrees.Count == 0) - throw new HttpResponseException(HttpStatusCode.NotFound); + { + //if there are no trees defined for this section but the section is defined then we can have a simple + //full screen section without trees + var name = Services.TextService.Localize("sections/" + application); + return TreeRootNode.CreateSingleTreeRoot(Constants.System.Root.ToInvariantString(), null, null, name, TreeNodeCollection.Empty, true); + } // handle request for a specific tree / or when there is only one tree if (!tree.IsNullOrWhiteSpace() || allTrees.Count == 1) @@ -101,8 +112,8 @@ namespace Umbraco.Web.Trees return treeRootNode; } - // otherwise it's a section with no tree, aka a fullscreen section - // todo is this true? what if we just failed to TryGetRootNode on all of them? + // otherwise it's a section with all empty trees, aka a fullscreen section + // todo is this true? what if we just failed to TryGetRootNode on all of them? SD: Yes it's true but we should check the result of TryGetRootNode and throw? return TreeRootNode.CreateSingleTreeRoot(Constants.System.Root.ToInvariantString(), null, null, name, TreeNodeCollection.Empty, true); } diff --git a/src/Umbraco.Web/Trees/FormsBackOfficeSection.cs b/src/Umbraco.Web/Trees/FormsBackOfficeSection.cs new file mode 100644 index 0000000000..048ed47f11 --- /dev/null +++ b/src/Umbraco.Web/Trees/FormsBackOfficeSection.cs @@ -0,0 +1,14 @@ +using Umbraco.Core; +using Umbraco.Core.Models.Trees; + +namespace Umbraco.Web.Trees +{ + /// + /// Defines the back office media section + /// + public class FormsBackOfficeSection : IBackOfficeSection + { + public string Alias => Constants.Applications.Forms; + public string Name => "Forms"; + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 11d2be5b70..fa2ac81761 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -214,6 +214,7 @@ +