From afd91f8adbad23d2b39ca4cb7b177b5be12a9131 Mon Sep 17 00:00:00 2001 From: David Peck Date: Thu, 3 Nov 2016 16:25:45 +0000 Subject: [PATCH 001/342] Added ability to only allow templates what are allowed by the document type --- .../UmbracoSettings/IWebRoutingSection.cs | 2 + .../UmbracoSettings/WebRoutingElement.cs | 9 +- src/Umbraco.Core/Models/ContentType.cs | 22 ++ src/Umbraco.Core/Models/IContentType.cs | 14 + .../WebRoutingElementDefaultTests.cs | 6 + .../config/umbracoSettings.config | 362 +++++++++--------- .../ContentFinderByNiceUrlAndTemplate.cs | 33 +- .../Routing/ContentFinderByPageIdQuery.cs | 3 +- .../Routing/PublishedContentRequestEngine.cs | 86 +++-- src/Umbraco.Web/Templates/TemplateRenderer.cs | 30 +- .../umbraco.presentation/NotFoundHandlers.cs | 15 +- src/Umbraco.Web/umbraco.presentation/page.cs | 24 +- 12 files changed, 386 insertions(+), 220 deletions(-) diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs index 9eb6d02aa7..aedfac7aa5 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs @@ -8,6 +8,8 @@ bool DisableAlternativeTemplates { get; } + bool DisableNotPermittedAlternativeTemplates { get; } + bool DisableFindContentByIdPath { get; } bool DisableRedirectUrlTracking { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs index 82f5d46b28..35348b0338 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs @@ -1,4 +1,5 @@ -using System.Configuration; +using System; +using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { @@ -21,6 +22,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { return (bool) base["disableAlternativeTemplates"]; } } + + [ConfigurationProperty("disableNotPermittedAlternativeTemplates", DefaultValue = "false")] + public bool DisableNotPermittedAlternativeTemplates + { + get { return (bool)base["disableNotPermittedAlternativeTemplates"]; } + } [ConfigurationProperty("disableFindContentByIdPath", DefaultValue = "false")] public bool DisableFindContentByIdPath { diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index 88c498a147..d05fc74ac8 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -98,6 +98,28 @@ namespace Umbraco.Core.Models } } + /// + /// Determines if AllowedTemplates contains templateId + /// + /// The template id to check + /// True if AllowedTemplates contains the templateId else False + public bool IsAllowedTemplate(int templateId) + { + var allowedTemplates = AllowedContentTypes ?? new ContentTypeSort[0]; + return allowedTemplates.Any(t => t.Id.Value == templateId); + } + + /// + /// Determines if AllowedTemplates contains templateId + /// + /// The template alias to check + /// True if AllowedTemplates contains the templateAlias else False + public bool IsAllowedTemplate(string templateAlias) + { + var allowedTemplates = AllowedContentTypes ?? new ContentTypeSort[0]; + return allowedTemplates.Any(t => t.Alias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase)); + } + /// /// Sets the default template for the ContentType /// diff --git a/src/Umbraco.Core/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs index 766a8eec81..356a5acccd 100644 --- a/src/Umbraco.Core/Models/IContentType.cs +++ b/src/Umbraco.Core/Models/IContentType.cs @@ -17,6 +17,20 @@ namespace Umbraco.Core.Models /// IEnumerable AllowedTemplates { get; set; } + /// + /// Determines if AllowedTemplates contains templateId + /// + /// The template id to check + /// True if AllowedTemplates contains the templateId else False + bool IsAllowedTemplate(int templateId); + + /// + /// Determines if AllowedTemplates contains templateId + /// + /// The template alias to check + /// True if AllowedTemplates contains the templateAlias else False + bool IsAllowedTemplate(string templateAlias); + /// /// Sets the default template for the ContentType /// diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs index 1e568c608e..eb27126e57 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs @@ -23,6 +23,12 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings Assert.IsTrue(SettingsSection.WebRouting.DisableAlternativeTemplates == false); } + [Test] + public void DisableNotPermittedAlternativeTemplates() + { + Assert.IsTrue(SettingsSection.WebRouting.DisableNotPermittedAlternativeTemplates == false); + } + [Test] public void DisableFindContentByIdPath() { diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 9eb3f135e9..0d0ad45d83 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -28,162 +28,163 @@ - True - - - + - - 1 - 1079 - 1080 - - - - - your@email.here - + + 1 + 1079 + 1080 + + + + + your@email.here + - - True + +True - - False + +False - - UTF8 + +UTF8 - - false + +false - - - true + + +true - - True + +True - - True + +True - - False + +False - - False + +False - - text + +text - - - In Preview Mode - click to end - ]]> + + + In Preview Mode - click to end + ]]> + - - - 1800 + + +1800 - - - - false + + + +false - - throw - - - ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,html,htm,svg,php,htaccess +throw - - Textstring - - + +ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,html,htm,svg,php,htaccess - - - true + +Textstring - - false - - - true - - + - - - false - - true - - - - - - - - - - - - - plus - star - - - ae - oe - aa - ae - oe - ue - ss - ae - oe - - - - - - + + +true - - - - true + +false - - - Mvc - + +true + + + + + +false + +true + + - + + + + + + + + + + plus + star + + + ae + oe + aa + ae + oe + ue + ss + ae + oe + - + + + + + + + + +true + + + +Mvc + - - - - cs - vb - - + + + + cs + vb + + - - - - - p - div - ul - span - - - + + p + div + ul + span + + + - - - + + + - - + + - - - true - true - - - - - - - + +true +true + + + + + + + - - - - + + + + - - - - - 0 + + + + +0 - - + - - - - - - - - - - - - - - - UsersMembershipProvider - - + + - - - - - + + + + + - + + UsersMembershipProvider + + + + + + + + + + - diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs index 246b109dea..570f0d00c5 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs @@ -22,6 +22,8 @@ namespace Umbraco.Web.Routing /// If successful, also assigns the template. public override bool TryFindContent(PublishedContentRequest docRequest) { + const string tracePrefix = "ContentFinderByNiceUrlAndTemplate: "; + IPublishedContent node = null; string path = docRequest.Uri.GetAbsolutePathDecoded(); @@ -43,7 +45,36 @@ namespace Umbraco.Web.Routing node = FindContent(docRequest, route); if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false && node != null) - docRequest.TemplateModel = template; + { + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == true) + { + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(node.DocumentTypeId); + if (publishedContentContentType == null) + { + LogHelper.Warn("Content type ({1}) of published content({2}) can't be fetched ={1} alias=\"{2}\"", () => tracePrefix, () => node.Id, () => node.DocumentTypeId); + } + else + { + var isAllowedTemplate = publishedContentContentType.IsAllowedTemplate(template.Id); + if (isAllowedTemplate == false) + { + //If it is not allowed then default to as if no altTemplate had been supplied + LogHelper.Warn("{0}Content type '{1}' requested altTemplate of '{2}' but this is not an allowed template", () => tracePrefix, () => node.DocumentTypeAlias, () => template.Alias); + + docRequest.PublishedContent = null; + node = null; + } + else + { + docRequest.TemplateModel = template; + } + } + } + else + { + docRequest.TemplateModel = template; + } + } } else { diff --git a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs index 4732895311..9ff3f25ae1 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs @@ -12,7 +12,8 @@ namespace Umbraco.Web.Routing public bool TryFindContent(PublishedContentRequest docRequest) { int pageId; - if (int.TryParse(docRequest.RoutingContext.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) + + if (int.TryParse(docRequest.RoutingContext.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) { var doc = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(pageId); diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 03a50a4b99..3acabdcbec 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -20,6 +20,7 @@ using umbraco.cms.businesslogic.member; using Umbraco.Core.Services; using Umbraco.Web.Security; using RenderingEngine = Umbraco.Core.RenderingEngine; +using Umbraco.Core.Models; namespace Umbraco.Web.Routing { @@ -630,20 +631,11 @@ namespace Umbraco.Web.Routing var templateId = _pcr.PublishedContent.TemplateId; - if (templateId > 0) - { - ProfilingLogger.Logger.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateId); - var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateId); - if (template == null) - throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render"); - _pcr.TemplateModel = template; - ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias); - } - else - { - ProfilingLogger.Logger.Debug("{0}No specified template.", () => tracePrefix); - } - } + // This code was moved to GetTemplateModel to allow the same functionality on a failed altTemplate (U4-8550) + // The only change is a diffent logger prefix and null will be set to _pcr.TemplateModel if the templateId is <= 0 + // rather than no set taking place + _pcr.TemplateModel = GetTemplateModel(_pcr.PublishedContent.TemplateId); + } else { // we have an alternate template specified. lookup the template with that alias @@ -657,15 +649,33 @@ namespace Umbraco.Web.Routing ProfilingLogger.Logger.Debug("{0}Look for alternate template alias=\"{1}\"", () => tracePrefix, () => altTemplate); var template = ApplicationContext.Current.Services.FileService.GetTemplate(altTemplate); - if (template != null) - { - _pcr.TemplateModel = template; - ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias); - } - else - { - ProfilingLogger.Logger.Debug("{0}The template with alias=\"{1}\" does not exist, ignoring.", () => tracePrefix, () => altTemplate); - } + if (template != null) + { + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates) + { + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(_pcr.PublishedContent.DocumentTypeId); + if (publishedContentContentType == null) + { + ProfilingLogger.Logger.Warn("{0}Content type ({1}) of published content({2}) can't be fetched ={1} alias=\"{2}\"", () => tracePrefix, () => _pcr.PublishedContent.Id, () => _pcr.PublishedContent.DocumentTypeId); + } + else + { + var isAllowedTemplate = publishedContentContentType.IsAllowedTemplate(template.Id); + if(isAllowedTemplate == false) + { + //If it is not allowed then default to as if no altTemplate had been supplied + ProfilingLogger.Logger.Warn("{0}Content type '{1}' requested altTemplate of '{2}' but this is not an allowed template", () => tracePrefix, () => _pcr.PublishedContent.DocumentTypeAlias, () => altTemplate); + template = GetTemplateModel(_pcr.PublishedContent.TemplateId); + } + } + } + _pcr.TemplateModel = template; + ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias); + } + else + { + ProfilingLogger.Logger.Debug("{0}The template with alias=\"{1}\" does not exist, ignoring.", () => tracePrefix, () => altTemplate); + } } if (_pcr.HasTemplate == false) @@ -687,11 +697,31 @@ namespace Umbraco.Web.Routing } } - /// - /// Follows external redirection through umbracoRedirect document property. - /// - /// As per legacy, if the redirect does not work, we just ignore it. - private void FollowExternalRedirect() + private ITemplate GetTemplateModel(int templateId) + { + const string tracePrefix = "GetTemplateModel: "; + + if (templateId > 0) + { + ProfilingLogger.Logger.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateId); + var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateId); + if (template == null) + throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render"); + ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias); + return template; + } + else + { + ProfilingLogger.Logger.Debug("{0}No specified template.", () => tracePrefix); + } + return null; + } + + /// + /// Follows external redirection through umbracoRedirect document property. + /// + /// As per legacy, if the redirect does not work, we just ignore it. + private void FollowExternalRedirect() { if (_pcr.HasPublishedContent == false) return; diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index d7d331d887..a409e2da2b 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -80,10 +80,32 @@ namespace Umbraco.Web.Templates //set the doc that was found by id contentRequest.PublishedContent = doc; - //set the template, either based on the AltTemplate found or the standard template of the doc - contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false - ? _umbracoContext.Application.Services.FileService.GetTemplate(doc.TemplateId) - : _umbracoContext.Application.Services.FileService.GetTemplate(AltTemplate.Value); + //set the template, either based on the AltTemplate found or the standard template of the doc + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false) + { + contentRequest.TemplateModel = _umbracoContext.Application.Services.FileService.GetTemplate(doc.TemplateId); + } + else + { + var template = _umbracoContext.Application.Services.FileService.GetTemplate(AltTemplate.Value); + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == false) + { + contentRequest.TemplateModel = template; + } + else + { + //Check if we're allowed to show the selected template + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(contentRequest.PublishedContent.DocumentTypeId); + if (publishedContentContentType != null) + { + var isAllowedTemplate = publishedContentContentType.IsAllowedTemplate(template.Id); + if (isAllowedTemplate == true) + { + contentRequest.TemplateModel = template; + } + } + } + } //if there is not template then exit if (!contentRequest.HasTemplate) diff --git a/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs b/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs index 27e62888d4..de8483b4d0 100644 --- a/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs +++ b/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs @@ -281,7 +281,20 @@ namespace umbraco { { _redirectID = int.Parse(urlNode.Attributes.GetNamedItem("id").Value); - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) + var altTemplateAllowed = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false; + + //Query: Is it worth the overhead of the DB request here? The usage of AltTemplate is checked later. + if (altTemplateAllowed && UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == true) + { + var nodeTypeAlias = urlNode.Attributes.GetNamedItem("nodeTypeAlias").Value; + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(nodeTypeAlias); + if (publishedContentContentType != null) + { + altTemplateAllowed = publishedContentContentType.IsAllowedTemplate(templateAlias); + } + } + + if (altTemplateAllowed) { HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] = templateAlias; HttpContext.Current.Trace.Write("umbraco.altTemplateHandler", diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index e71a290754..2172be88e5 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -152,9 +152,10 @@ namespace umbraco public page(XmlNode node) { populatePageData(node); - - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) + + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) { + var templateSet = false; // Check for alternative template if (HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] != null && HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString() != String.Empty) @@ -162,16 +163,29 @@ namespace umbraco _template = umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString()); - _elements.Add("template", _template.ToString()); + templateSet = true; } else if (helper.Request(Constants.Conventions.Url.AltTemplate) != String.Empty) { _template = umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( helper.Request(Constants.Conventions.Url.AltTemplate).ToLower()); - _elements.Add("template", _template.ToString()); + templateSet = true; } - } + + if (templateSet == true && UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == true) + { + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(_nodeTypeAlias); + if (publishedContentContentType != null && publishedContentContentType.IsAllowedTemplate(_template)) + { + _elements.Add("template", _template.ToString()); + } + } + else if(templateSet == true) + { + _elements.Add("template", _template.ToString()); + } + } if (_template == 0) { From 69728fb5c4ac0f7737914106de01cd684461519e Mon Sep 17 00:00:00 2001 From: David Peck Date: Tue, 8 Nov 2016 08:27:18 +0000 Subject: [PATCH 002/342] Changed Allowed Templates setting to be validate templates which checks url only --- .../UmbracoSettings/IWebRoutingSection.cs | 2 +- .../UmbracoSettings/WebRoutingElement.cs | 7 ++-- src/Umbraco.Core/Models/ContentType.cs | 6 +-- .../WebRoutingElementDefaultTests.cs | 4 +- .../config/umbracoSettings.config | 11 +++-- src/Umbraco.Web/PublishedContentExtensions.cs | 39 +++++++++++++++++ .../ContentFinderByNiceUrlAndTemplate.cs | 37 ++++------------ .../Routing/PublishedContentRequestEngine.cs | 36 +++++----------- src/Umbraco.Web/Templates/TemplateRenderer.cs | 40 ++++-------------- .../umbraco.presentation/NotFoundHandlers.cs | 15 +------ src/Umbraco.Web/umbraco.presentation/page.cs | 42 +++++++------------ 11 files changed, 98 insertions(+), 141 deletions(-) diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs index aedfac7aa5..c59d63d42e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs @@ -8,7 +8,7 @@ bool DisableAlternativeTemplates { get; } - bool DisableNotPermittedAlternativeTemplates { get; } + bool ValidateAlternativeTemplates { get; } bool DisableFindContentByIdPath { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs index 35348b0338..ed133e5f61 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs @@ -23,11 +23,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return (bool) base["disableAlternativeTemplates"]; } } - [ConfigurationProperty("disableNotPermittedAlternativeTemplates", DefaultValue = "false")] - public bool DisableNotPermittedAlternativeTemplates + [ConfigurationProperty("validateAlternativeTemplates", DefaultValue = "false")] + public bool ValidateAlternativeTemplates { - get { return (bool)base["disableNotPermittedAlternativeTemplates"]; } + get { return (bool)base["validateAlternativeTemplates"]; } } + [ConfigurationProperty("disableFindContentByIdPath", DefaultValue = "false")] public bool DisableFindContentByIdPath { diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs index d05fc74ac8..5849fcda04 100644 --- a/src/Umbraco.Core/Models/ContentType.cs +++ b/src/Umbraco.Core/Models/ContentType.cs @@ -105,8 +105,8 @@ namespace Umbraco.Core.Models /// True if AllowedTemplates contains the templateId else False public bool IsAllowedTemplate(int templateId) { - var allowedTemplates = AllowedContentTypes ?? new ContentTypeSort[0]; - return allowedTemplates.Any(t => t.Id.Value == templateId); + var allowedTemplates = AllowedTemplates ?? new ITemplate[0]; + return allowedTemplates.Any(t => t.Id == templateId); } /// @@ -116,7 +116,7 @@ namespace Umbraco.Core.Models /// True if AllowedTemplates contains the templateAlias else False public bool IsAllowedTemplate(string templateAlias) { - var allowedTemplates = AllowedContentTypes ?? new ContentTypeSort[0]; + var allowedTemplates = AllowedTemplates ?? new ITemplate[0]; return allowedTemplates.Any(t => t.Alias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase)); } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs index eb27126e57..4c77fff5bc 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs @@ -24,9 +24,9 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings } [Test] - public void DisableNotPermittedAlternativeTemplates() + public void ValidateAlternativeTemplates() { - Assert.IsTrue(SettingsSection.WebRouting.DisableNotPermittedAlternativeTemplates == false); + Assert.IsTrue(SettingsSection.WebRouting.ValidateAlternativeTemplates == false); } [Test] diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 0d0ad45d83..522da20c9c 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -302,9 +302,12 @@ will make Umbraco render the content on the current page with the template you requested, for example: http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the "About Us" page with a template with the alias Home. Setting this setting to true stops that behavior - @disableNotPermittedAlternativeTemplates - Similar to @disableAlternativeTemplates; setting this settings prevents alternative templates which have not been - permitted for the document type. Setting will not have any effect if disableNotPermittedAlternativeTemplates is set. + @validateAlternativeTemplates + By default you can add a altTemplate querystring or append a template name to the current URL which + will make Umbraco render the content on the current page with the template you requested, for example: + http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the + "About Us" page with a template with the alias Home. Setting this setting to true will ensure that + only templates that have been permitted on the document type will be allowed @disableFindContentByIdPath By default you can call any content Id in the url and show the content with that id, for example: http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting @@ -316,7 +319,7 @@ --> diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 5caa08729a..189dda4934 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -12,6 +12,7 @@ using Umbraco.Web.Models; using Umbraco.Core; using Umbraco.Web.Routing; using ContentType = umbraco.cms.businesslogic.ContentType; +using Umbraco.Core.Configuration; namespace Umbraco.Web { @@ -114,6 +115,44 @@ namespace Umbraco.Web return template == null ? string.Empty : template.Alias; } + public static bool IsTemplateAllowed(this IPublishedContent content, int templateId) + { + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true) + return content.TemplateId == templateId; + + if (content.TemplateId != templateId && UmbracoConfig.For.UmbracoSettings().WebRouting.ValidateAlternativeTemplates == true) + { + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(content.ContentType.Id); + if (publishedContentContentType == null) + throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')"); + + return publishedContentContentType.IsAllowedTemplate(templateId); + } + + return true; + } + public static bool IsTemplateAllowed(this IPublishedContent content, string templateAlias) + { + var contentTemplateAlias = content.GetTemplateAlias(); + //I assume it is enough to compare aliases, as you can not have different templates with the same alias + var matchingAlias = contentTemplateAlias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase); + + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true) + return matchingAlias; + + if (matchingAlias == false && UmbracoConfig.For.UmbracoSettings().WebRouting.ValidateAlternativeTemplates == true) + { + var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(content.ContentType.Id); + if (publishedContentContentType == null) + throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')"); + + //This doesn't appear to be working because new templates aren't in AllowedTemplates + return publishedContentContentType.IsAllowedTemplate(templateAlias); + } + + return true; + } + #endregion #region IsComposedOf diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs index 570f0d00c5..50696f6867 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs @@ -44,36 +44,15 @@ namespace Umbraco.Web.Routing var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; node = FindContent(docRequest, route); - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false && node != null) + if (node.IsTemplateAllowed(template.Id)) { - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == true) - { - var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(node.DocumentTypeId); - if (publishedContentContentType == null) - { - LogHelper.Warn("Content type ({1}) of published content({2}) can't be fetched ={1} alias=\"{2}\"", () => tracePrefix, () => node.Id, () => node.DocumentTypeId); - } - else - { - var isAllowedTemplate = publishedContentContentType.IsAllowedTemplate(template.Id); - if (isAllowedTemplate == false) - { - //If it is not allowed then default to as if no altTemplate had been supplied - LogHelper.Warn("{0}Content type '{1}' requested altTemplate of '{2}' but this is not an allowed template", () => tracePrefix, () => node.DocumentTypeAlias, () => template.Alias); - - docRequest.PublishedContent = null; - node = null; - } - else - { - docRequest.TemplateModel = template; - } - } - } - else - { - docRequest.TemplateModel = template; - } + docRequest.TemplateModel = template; + } + else + { + LogHelper.Warn("Configuration settings prevent template \"{0}\" from showing for node \"{1}\"", () => templateAlias, () => node.Id); + docRequest.PublishedContent = null; + node = null; } } else diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 3acabdcbec..796ef9c1ce 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -607,9 +607,8 @@ namespace Umbraco.Web.Routing // only if the published content is the initial once, else the alternate template // does not apply // + optionnally, apply the alternate template on internal redirects - var useAltTemplate = _webRoutingSection.DisableAlternativeTemplates == false - && (_pcr.IsInitialPublishedContent - || (_webRoutingSection.InternalRedirectPreservesTemplate && _pcr.IsInternalRedirectPublishedContent)); + var useAltTemplate = _pcr.IsInitialPublishedContent + || (_webRoutingSection.InternalRedirectPreservesTemplate && _pcr.IsInternalRedirectPublishedContent); string altTemplate = useAltTemplate ? _routingContext.UmbracoContext.HttpContext.Request[Constants.Conventions.Url.AltTemplate] : null; @@ -648,33 +647,18 @@ namespace Umbraco.Web.Routing ProfilingLogger.Logger.Debug("{0}Has a template already, but also an alternate template.", () => tracePrefix); ProfilingLogger.Logger.Debug("{0}Look for alternate template alias=\"{1}\"", () => tracePrefix, () => altTemplate); - var template = ApplicationContext.Current.Services.FileService.GetTemplate(altTemplate); - if (template != null) + if (_pcr.PublishedContent.IsTemplateAllowed(altTemplate)) { - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates) - { - var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(_pcr.PublishedContent.DocumentTypeId); - if (publishedContentContentType == null) - { - ProfilingLogger.Logger.Warn("{0}Content type ({1}) of published content({2}) can't be fetched ={1} alias=\"{2}\"", () => tracePrefix, () => _pcr.PublishedContent.Id, () => _pcr.PublishedContent.DocumentTypeId); - } - else - { - var isAllowedTemplate = publishedContentContentType.IsAllowedTemplate(template.Id); - if(isAllowedTemplate == false) - { - //If it is not allowed then default to as if no altTemplate had been supplied - ProfilingLogger.Logger.Warn("{0}Content type '{1}' requested altTemplate of '{2}' but this is not an allowed template", () => tracePrefix, () => _pcr.PublishedContent.DocumentTypeAlias, () => altTemplate); - template = GetTemplateModel(_pcr.PublishedContent.TemplateId); - } - } - } - _pcr.TemplateModel = template; - ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias); + var template = ApplicationContext.Current.Services.FileService.GetTemplate(altTemplate); + if (template != null) + _pcr.TemplateModel = template; + else + ProfilingLogger.Logger.Debug("{0}The template with alias=\"{1}\" does not exist, ignoring.", () => tracePrefix, () => altTemplate); } else { - ProfilingLogger.Logger.Debug("{0}The template with alias=\"{1}\" does not exist, ignoring.", () => tracePrefix, () => altTemplate); + LogHelper.Warn("{0}Configuration settings prevent template \"{1}\" from showing for node \"{2}\"", () => tracePrefix, () => altTemplate, () => _pcr.PublishedContent.Id); + _pcr.TemplateModel = GetTemplateModel(_pcr.PublishedContent.TemplateId); } } diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index a409e2da2b..2eb00a9c75 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -77,38 +77,16 @@ namespace Umbraco.Web.Templates { contentRequest.Culture = _umbracoContext.PublishedContentRequest.Culture; } - - //set the doc that was found by id - contentRequest.PublishedContent = doc; - //set the template, either based on the AltTemplate found or the standard template of the doc - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false) - { - contentRequest.TemplateModel = _umbracoContext.Application.Services.FileService.GetTemplate(doc.TemplateId); - } - else - { - var template = _umbracoContext.Application.Services.FileService.GetTemplate(AltTemplate.Value); - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == false) - { - contentRequest.TemplateModel = template; - } - else - { - //Check if we're allowed to show the selected template - var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(contentRequest.PublishedContent.DocumentTypeId); - if (publishedContentContentType != null) - { - var isAllowedTemplate = publishedContentContentType.IsAllowedTemplate(template.Id); - if (isAllowedTemplate == true) - { - contentRequest.TemplateModel = template; - } - } - } - } - //if there is not template then exit - if (!contentRequest.HasTemplate) + //set the doc that was found by id + contentRequest.PublishedContent = doc; + //set the template, either based on the AltTemplate found or the standard template of the doc + contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false + ? _umbracoContext.Application.Services.FileService.GetTemplate(doc.TemplateId) + : _umbracoContext.Application.Services.FileService.GetTemplate(AltTemplate.Value); + + //if there is not template then exit + if (!contentRequest.HasTemplate) { if (!AltTemplate.HasValue) { diff --git a/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs b/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs index de8483b4d0..27e62888d4 100644 --- a/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs +++ b/src/Umbraco.Web/umbraco.presentation/NotFoundHandlers.cs @@ -281,20 +281,7 @@ namespace umbraco { { _redirectID = int.Parse(urlNode.Attributes.GetNamedItem("id").Value); - var altTemplateAllowed = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false; - - //Query: Is it worth the overhead of the DB request here? The usage of AltTemplate is checked later. - if (altTemplateAllowed && UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == true) - { - var nodeTypeAlias = urlNode.Attributes.GetNamedItem("nodeTypeAlias").Value; - var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(nodeTypeAlias); - if (publishedContentContentType != null) - { - altTemplateAllowed = publishedContentContentType.IsAllowedTemplate(templateAlias); - } - } - - if (altTemplateAllowed) + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) { HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] = templateAlias; HttpContext.Current.Trace.Write("umbraco.altTemplateHandler", diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 2172be88e5..ed4254198e 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -152,42 +152,28 @@ namespace umbraco public page(XmlNode node) { populatePageData(node); - - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) - { - var templateSet = false; - // Check for alternative template - if (HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] != null && - HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString() != String.Empty) - { - _template = - umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( - HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString()); - templateSet = true; - } - else if (helper.Request(Constants.Conventions.Url.AltTemplate) != String.Empty) - { - _template = - umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( - helper.Request(Constants.Conventions.Url.AltTemplate).ToLower()); - templateSet = true; - } - if (templateSet == true && UmbracoConfig.For.UmbracoSettings().WebRouting.DisableNotPermittedAlternativeTemplates == true) + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) + { + // Check for alternative template + if (HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] != null && + HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString() != String.Empty) { - var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(_nodeTypeAlias); - if (publishedContentContentType != null && publishedContentContentType.IsAllowedTemplate(_template)) - { - _elements.Add("template", _template.ToString()); - } + _template = + umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( + HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString()); + _elements.Add("template", _template.ToString()); } - else if(templateSet == true) + else if (helper.Request(Constants.Conventions.Url.AltTemplate) != String.Empty) { + _template = + umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( + helper.Request(Constants.Conventions.Url.AltTemplate).ToLower()); _elements.Add("template", _template.ToString()); } } - if (_template == 0) + if (_template == 0) { try { From a031551fb2ba734b2a40be2e42a1720617ad2fa0 Mon Sep 17 00:00:00 2001 From: David Peck Date: Tue, 8 Nov 2016 17:59:30 +0000 Subject: [PATCH 003/342] Rationalised changes in whitespace, to reduce the differences in the PR --- .../config/umbracoSettings.config | 357 +++++++++--------- src/Umbraco.Web/PublishedContentExtensions.cs | 4 +- .../Routing/ContentFinderByPageIdQuery.cs | 3 +- .../Routing/PublishedContentRequestEngine.cs | 2 +- src/Umbraco.Web/Templates/TemplateRenderer.cs | 14 +- src/Umbraco.Web/umbraco.presentation/page.cs | 38 +- 6 files changed, 208 insertions(+), 210 deletions(-) diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 522da20c9c..3b037fa6f0 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -28,163 +28,162 @@ -True - - - + - - 1 - 1079 - 1080 - - - - - your@email.here - + + 1 + 1079 + 1080 + + + + + your@email.here + - -True + + True - -False + + False - -UTF8 + + UTF8 - -false + + false - - -true + + + true - -True + + True - -True + + True - -False + + False - -False + + False - -text + + text - - - In Preview Mode - click to end - ]]> - + + + In Preview Mode - click to end + ]]> - - -1800 + + + 1800 - - - -false + + + + false - -throw + throw + + + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,html,htm,svg,php,htaccess - -ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,html,htm,svg,php,htaccess + + Textstring + + - -Textstring + + + true - + + false + + + true + + - - -true + + + false + + true + + - + + + + + + + + + + plus + star + + + ae + oe + aa + ae + oe + ue + ss + ae + oe + - + + + + - -false + + + + true - -true - - - - - -false - -true - - - - - - - - - - - - - plus - star - - - ae - oe - aa - ae - oe - ue - ss - ae - oe - - - - - - - - - - -true - - - -Mvc - + + + Mvc + - - - - cs - vb - - + + + + cs + vb + + - - - - - p - div - ul - span - - - + + p + div + ul + span + + + - - - + + + - - + + - - -true -true - - - - - - - + + true + true + + + + + + + - - - - + + + + - - - - -0 + + + + + 0 - - + - + + + + + + + + + - - + + + + + UsersMembershipProvider + + - - - - - + + + + + - - - - - UsersMembershipProvider - - - - - - - - - - - diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 189dda4934..c08de157b9 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -5,14 +5,14 @@ using System.Globalization; using System.Linq; using System.Web; using Examine.LuceneEngine.SearchCriteria; +using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Web.Models; -using Umbraco.Core; using Umbraco.Web.Routing; using ContentType = umbraco.cms.businesslogic.ContentType; -using Umbraco.Core.Configuration; namespace Umbraco.Web { diff --git a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs index 9ff3f25ae1..4732895311 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByPageIdQuery.cs @@ -12,8 +12,7 @@ namespace Umbraco.Web.Routing public bool TryFindContent(PublishedContentRequest docRequest) { int pageId; - - if (int.TryParse(docRequest.RoutingContext.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) + if (int.TryParse(docRequest.RoutingContext.UmbracoContext.HttpContext.Request["umbPageID"], out pageId)) { var doc = docRequest.RoutingContext.UmbracoContext.ContentCache.GetById(pageId); diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 796ef9c1ce..4f40af4b59 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Models; using Umbraco.Core.Security; using umbraco; @@ -20,7 +21,6 @@ using umbraco.cms.businesslogic.member; using Umbraco.Core.Services; using Umbraco.Web.Security; using RenderingEngine = Umbraco.Core.RenderingEngine; -using Umbraco.Core.Models; namespace Umbraco.Web.Routing { diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index 2eb00a9c75..d7d331d887 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -77,16 +77,16 @@ namespace Umbraco.Web.Templates { contentRequest.Culture = _umbracoContext.PublishedContentRequest.Culture; } - - //set the doc that was found by id - contentRequest.PublishedContent = doc; - //set the template, either based on the AltTemplate found or the standard template of the doc - contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false + + //set the doc that was found by id + contentRequest.PublishedContent = doc; + //set the template, either based on the AltTemplate found or the standard template of the doc + contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false ? _umbracoContext.Application.Services.FileService.GetTemplate(doc.TemplateId) : _umbracoContext.Application.Services.FileService.GetTemplate(AltTemplate.Value); - //if there is not template then exit - if (!contentRequest.HasTemplate) + //if there is not template then exit + if (!contentRequest.HasTemplate) { if (!AltTemplate.HasValue) { diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index ed4254198e..e71a290754 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -153,27 +153,27 @@ namespace umbraco { populatePageData(node); - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) - { + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false) + { // Check for alternative template - if (HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] != null && - HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString() != String.Empty) - { - _template = - umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( - HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString()); - _elements.Add("template", _template.ToString()); - } - else if (helper.Request(Constants.Conventions.Url.AltTemplate) != String.Empty) - { - _template = - umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( - helper.Request(Constants.Conventions.Url.AltTemplate).ToLower()); - _elements.Add("template", _template.ToString()); - } - } + if (HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate] != null && + HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString() != String.Empty) + { + _template = + umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( + HttpContext.Current.Items[Constants.Conventions.Url.AltTemplate].ToString()); + _elements.Add("template", _template.ToString()); + } + else if (helper.Request(Constants.Conventions.Url.AltTemplate) != String.Empty) + { + _template = + umbraco.cms.businesslogic.template.Template.GetTemplateIdFromAlias( + helper.Request(Constants.Conventions.Url.AltTemplate).ToLower()); + _elements.Add("template", _template.ToString()); + } + } - if (_template == 0) + if (_template == 0) { try { From 7aeea96e2353414eb69ee63f831be80fd5b6d73b Mon Sep 17 00:00:00 2001 From: Tim Geyssens Date: Fri, 11 Nov 2016 11:45:25 +0100 Subject: [PATCH 004/342] First take localized text into account before the title property when working with tree headers --- src/Umbraco.Web/Trees/TreeController.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Trees/TreeController.cs b/src/Umbraco.Web/Trees/TreeController.cs index 0f975a9860..167de70cf7 100644 --- a/src/Umbraco.Web/Trees/TreeController.cs +++ b/src/Umbraco.Web/Trees/TreeController.cs @@ -43,16 +43,18 @@ namespace Umbraco.Web.Trees get { - //if title is defined, return that - if(string.IsNullOrEmpty(_attribute.Title) == false) - return _attribute.Title; + //try to look up a tree header matching the tree alias var localizedLabel = Services.TextService.Localize("treeHeaders/" + _attribute.Alias); - if (string.IsNullOrEmpty(localizedLabel) == false) + if (!string.IsNullOrEmpty(localizedLabel)) return localizedLabel; + //if title is defined, return that + if (!string.IsNullOrEmpty(_attribute.Title)) + return _attribute.Title; + //is returned to signal that a label was not found return "[" + _attribute.Alias + "]"; } From e501f0f612ec8e1d22d8b97b6e833ba50073202c Mon Sep 17 00:00:00 2001 From: bjarnef Date: Tue, 15 Nov 2016 21:26:57 +0100 Subject: [PATCH 005/342] Fix positioning of file icon extension label --- src/Umbraco.Web.UI.Client/src/less/hacks.less | 15 ++++--- .../src/less/property-editors.less | 41 ++++++++++++------- .../fileupload/fileupload.html | 8 ++-- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index 716cb40256..50a50b598d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -20,12 +20,17 @@ } .thumbnail { - border-radius: 0px; -} + border-radius: 0; + min-width: 150px; -.thumbnail img { - max-width: 100% !important; - width: 100%; + > a { + display: block; + } + + img { + max-width: 100% !important; + width: 100%; + } } #mapCanvas img { diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 573f218fb1..93abc39987 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -408,6 +408,7 @@ ul.color-picker li a { .gravity-container .viewport { max-width: 600px; + min-width: 250px; } .gravity-container .viewport:hover { @@ -704,28 +705,38 @@ ul.color-picker li a { padding-top: 27px; } -.umb-fileupload .file-icon { +.umb-fileupload .file-icon-wrap { text-align: center; display: block; position: relative; padding: 5px 0; - > .icon { - font-size: 70px; - line-height: 110%; - color: #666; - text-align: center; + /* fit text within container */ + & + span { + word-wrap: break-word; } - > span { - color: #fff; - background: #666; - padding: 1px 3px; - font-size: 12px; - line-height: 130%; - position: absolute; - top: 45px; - left: 110px; + .file-icon { + position: relative; + display: inline-block; + + > .icon { + font-size: 70px; + line-height: 110%; + color: #666; + text-align: center; + } + + > span { + color: #fff; + background: #666; + padding: 1px 3px; + font-size: 12px; + line-height: 130%; + position: absolute; + top: 40px; + left: 35px; + } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html index 0905de07f9..77a52381a9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html @@ -6,9 +6,11 @@ {{file.file}} {{file.file}} - - - .{{file.extension}} + + + + .{{file.extension}} + {{file.file}} From a2611ce35f7335e6d62ec64aa67c88c29fa38f60 Mon Sep 17 00:00:00 2001 From: David Peck Date: Tue, 30 May 2017 22:39:26 +0100 Subject: [PATCH 006/342] Changed the name of IsTemplateAllowed to IsAllowedTemplate to reflect the usage elsewhere and changed the overload of IsAllowedTemplate with the alias parameter to use the template id version to be more DRY --- src/Umbraco.Web/PublishedContentExtensions.cs | 26 +++++-------------- .../ContentFinderByNiceUrlAndTemplate.cs | 2 +- .../Routing/PublishedContentRequestEngine.cs | 2 +- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index c08de157b9..b75547fca0 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -115,7 +115,7 @@ namespace Umbraco.Web return template == null ? string.Empty : template.Alias; } - public static bool IsTemplateAllowed(this IPublishedContent content, int templateId) + public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) { if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true) return content.TemplateId == templateId; @@ -131,26 +131,14 @@ namespace Umbraco.Web return true; } - public static bool IsTemplateAllowed(this IPublishedContent content, string templateAlias) + public static bool IsAllowedTemplate(this IPublishedContent content, string templateAlias) { - var contentTemplateAlias = content.GetTemplateAlias(); - //I assume it is enough to compare aliases, as you can not have different templates with the same alias - var matchingAlias = contentTemplateAlias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase); + var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateAlias); + var isAllowedTemplate = (template != null) ? + content.IsAllowedTemplate(template.Id) : + false; - if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true) - return matchingAlias; - - if (matchingAlias == false && UmbracoConfig.For.UmbracoSettings().WebRouting.ValidateAlternativeTemplates == true) - { - var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(content.ContentType.Id); - if (publishedContentContentType == null) - throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')"); - - //This doesn't appear to be working because new templates aren't in AllowedTemplates - return publishedContentContentType.IsAllowedTemplate(templateAlias); - } - - return true; + return isAllowedTemplate; } #endregion diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs index 50696f6867..f19886c4f2 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs @@ -44,7 +44,7 @@ namespace Umbraco.Web.Routing var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; node = FindContent(docRequest, route); - if (node.IsTemplateAllowed(template.Id)) + if (node.IsAllowedTemplate(template.Id)) { docRequest.TemplateModel = template; } diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 4f40af4b59..5ab62137ee 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -647,7 +647,7 @@ namespace Umbraco.Web.Routing ProfilingLogger.Logger.Debug("{0}Has a template already, but also an alternate template.", () => tracePrefix); ProfilingLogger.Logger.Debug("{0}Look for alternate template alias=\"{1}\"", () => tracePrefix, () => altTemplate); - if (_pcr.PublishedContent.IsTemplateAllowed(altTemplate)) + if (_pcr.PublishedContent.IsAllowedTemplate(altTemplate)) { var template = ApplicationContext.Current.Services.FileService.GetTemplate(altTemplate); if (template != null) From d980ffe7837f3f9779d43fd46f290fa1c4cafd7a Mon Sep 17 00:00:00 2001 From: Lennard Fonteijn Date: Wed, 23 Aug 2017 23:14:49 +0200 Subject: [PATCH 007/342] Added possibilty to ignore collisions from an IContentFinder --- src/Umbraco.Web/Routing/PublishedContentRequest.cs | 5 ++++- src/Umbraco.Web/Routing/UrlProviderExtensions.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Routing/PublishedContentRequest.cs b/src/Umbraco.Web/Routing/PublishedContentRequest.cs index 9c21958603..4a4968e4e3 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequest.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequest.cs @@ -614,6 +614,9 @@ namespace Umbraco.Web.Routing set { _headers = value; } } - + /// + /// Gets of sets a value indicating whether the Umbraco Backoffice should ignore a collision for this request. + /// + public bool IgnorePublishedContentCollisions { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index d464b8962e..accd7018fe 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -78,7 +78,7 @@ namespace Umbraco.Web.Routing { urls.Add(ui.Text("content", "routeError", "(error)", umbracoContext.Security.CurrentUser)); } - else if (pcr.PublishedContent.Id != content.Id) + else if (pcr.IgnorePublishedContentCollisions == false && pcr.PublishedContent.Id != content.Id) { var o = pcr.PublishedContent; string s; From 647d3e87926a2a849a9f259e58b5752550e10e92 Mon Sep 17 00:00:00 2001 From: Stefano Chiodino Date: Thu, 5 Oct 2017 19:17:06 +0100 Subject: [PATCH 008/342] Normalises whitespace (replace tabs) and add EOF line --- .../tools/Views.Web.config.install.xdt | 70 +- build/NuSpecs/tools/Web.config.install.xdt | 793 +++++++++--------- src/Umbraco.Web.UI/Media/Web.config | 2 +- src/Umbraco.Web.UI/Views/Web.config | 98 +-- .../umbraco/Install/Views/Web.config | 96 +-- src/umbraco.MacroEngines/Resources/Web.config | 92 +- 6 files changed, 571 insertions(+), 580 deletions(-) diff --git a/build/NuSpecs/tools/Views.Web.config.install.xdt b/build/NuSpecs/tools/Views.Web.config.install.xdt index a5797232b8..4d660301a8 100644 --- a/build/NuSpecs/tools/Views.Web.config.install.xdt +++ b/build/NuSpecs/tools/Views.Web.config.install.xdt @@ -1,42 +1,32 @@ - - -
-
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + +
+
+ + + + + + + + + + + + + + + + + + + + + diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt index 5ee5ed32c6..a381a58c3e 100644 --- a/build/NuSpecs/tools/Web.config.install.xdt +++ b/build/NuSpecs/tools/Web.config.install.xdt @@ -1,419 +1,420 @@ - - -
-
-
+ + +
+
+
- -
- - - -
-
-
-
-
- - + +
+ + + +
+
+
+
+
+ + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - + + - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - + - - - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + - - - - - + + + + + + + + - - - - - - - - - - + + + + + - - - - - - - - + + + + + + + + + + - - - - - - - - - - - + + + + + + + + - - - - + + + + + + + + + + + - - - - - + + + + - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI/Media/Web.config b/src/Umbraco.Web.UI/Media/Web.config index 6cbb733ea7..cd48da3840 100644 --- a/src/Umbraco.Web.UI/Media/Web.config +++ b/src/Umbraco.Web.UI/Media/Web.config @@ -6,4 +6,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/Views/Web.config b/src/Umbraco.Web.UI/Views/Web.config index 479445713d..bc9ac2bdef 100644 --- a/src/Umbraco.Web.UI/Views/Web.config +++ b/src/Umbraco.Web.UI/Views/Web.config @@ -1,65 +1,65 @@ - - -
-
- - + + +
+
+ + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + + + - - - - + + + + - - - - - - - + + + + + + - - + + - - - - - + + + + + diff --git a/src/Umbraco.Web.UI/umbraco/Install/Views/Web.config b/src/Umbraco.Web.UI/umbraco/Install/Views/Web.config index d7f2ec9848..a95df8b9d7 100644 --- a/src/Umbraco.Web.UI/umbraco/Install/Views/Web.config +++ b/src/Umbraco.Web.UI/umbraco/Install/Views/Web.config @@ -1,58 +1,58 @@ - - -
-
- - + + +
+
+ + - - - - - - - - - - - + + + + + + + + + + + - - - + + + - - - - + + + + - - - - - - - + + + + + + + - - + + - - - - - + + + + + diff --git a/src/umbraco.MacroEngines/Resources/Web.config b/src/umbraco.MacroEngines/Resources/Web.config index d96172c0e4..c98c89349d 100644 --- a/src/umbraco.MacroEngines/Resources/Web.config +++ b/src/umbraco.MacroEngines/Resources/Web.config @@ -1,56 +1,56 @@ - - -
-
- - + + +
+
+ + - - - - - - - - - + + + + + + + + + - - - + + + - - - - + + + + - - - - - - - + + + + + + + - - + + - - - - - + + + + + From 404fc40127180ff157189e50ecd245d3a735395c Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sun, 15 Oct 2017 10:28:16 +0100 Subject: [PATCH 009/342] Skips the Macro Selection stage if only one macro exists, and it has parameters to be entered --- .../macropicker/macropicker.controller.js | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js index 69f9851926..3915282876 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.controller.js @@ -17,14 +17,17 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi $scope.model.selectedMacro = macro; if ($scope.wizardStep === "macroSelect") { - editParams(); + editParams(true); } else { $scope.model.submit($scope.model); } }; /** changes the view to edit the params of the selected macro */ - function editParams() { + /** if there is pnly one macro, and it has parameters - editor can skip selecting the Macro **/ + function editParams(insertIfNoParameters) { + //whether to insert the macro in the rich text editor when editParams is called and there are no parameters see U4-10537 + insertIfNoParameters = (typeof insertIfNoParameters !== 'undefined') ? insertIfNoParameters : true; //get the macro params if there are any macroResource.getMacroParameters($scope.model.selectedMacro.id) .then(function (data) { @@ -34,7 +37,11 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi //go to next page if there are params otherwise we can just exit if (!angular.isArray(data) || data.length === 0) { - $scope.model.submit($scope.model); + if (insertIfNoParameters) { + $scope.model.submit($scope.model); + } else { + $scope.wizardStep = 'macroSelect'; + } } else { @@ -113,12 +120,19 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi if (found) { //select the macro and go to next screen $scope.model.selectedMacro = found; - editParams(); + editParams(true); return; } } - //we don't have a pre-selected macro so ensure the correct step is set - $scope.wizardStep = "macroSelect"; + //if there is only one macro in the site and it has parameters, let's not make the editor choose it from a selection of one macro (unless there are no parameters - then weirdly it's a better experience to make that selection) + if ($scope.macros.length == 1) { + $scope.model.selectedMacro = $scope.macros[0]; + editParams(false); + } + else { + //we don't have a pre-selected macro so ensure the correct step is set + $scope.wizardStep = 'macroSelect'; + } }); From 0752ef06b120373ea0fea214acebf4f53a262fe0 Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sun, 15 Oct 2017 10:36:26 +0100 Subject: [PATCH 010/342] Enables the editor to skip the macro selection stage, when there is only one Macro and it has parameters (is this a controller still used? ) --- .../common/dialogs/insertmacro.controller.js | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js index dbb86e87ec..1fd870b42d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js @@ -10,16 +10,22 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper) { /** changes the view to edit the params of the selected macro */ - function editParams() { + /** if there is pnly one macro, and it has parameters - editor can skip selecting the Macro **/ + function editParams(insertIfNoParameters) { + //whether to insert the macro in the rich text editor when editParams is called and there are no parameters see U4-10537 + insertIfNoParameters = (typeof insertIfNoParameters !== 'undefined') ? insertIfNoParameters : true; //get the macro params if there are any macroResource.getMacroParameters($scope.selectedMacro.id) .then(function (data) { //go to next page if there are params otherwise we can just exit if (!angular.isArray(data) || data.length === 0) { - //we can just exist! - submitForm(); - + //we can just exist! + if (insertIfNoParameters) { + submitForm(); + } else { + $scope.wizardStep = 'macroSelect'; + } } else { $scope.wizardStep = "paramSelect"; $scope.macroParams = data; @@ -117,7 +123,7 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi formHelper.resetForm({ scope: $scope }); if ($scope.wizardStep === "macroSelect") { - editParams(); + editParams(true); } else { submitForm(); @@ -155,12 +161,19 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi if (found) { //select the macro and go to next screen $scope.selectedMacro = found; - editParams(); + editParams(true); return; } } - //we don't have a pre-selected macro so ensure the correct step is set - $scope.wizardStep = "macroSelect"; + //if there is only one macro in the site and it has parameters, let's not make the editor choose it from a selection of one macro (unless there are no parameters - then weirdly it's a better experience to make that selection) + if ($scope.macros.length == 1) { + $scope.selectedMacro = $scope.macros[0]; + editParams(false); + } + else { + //we don't have a pre-selected macro so ensure the correct step is set + $scope.wizardStep = 'macroSelect'; + } }); From 899265c5c54d913f7b505c0614089a5f039bad00 Mon Sep 17 00:00:00 2001 From: Andy Felton Date: Sat, 21 Oct 2017 14:44:09 +0100 Subject: [PATCH 011/342] Use the underlying Peta Poco DB type to get the Type --- src/Umbraco.Core/DatabaseContext.cs | 34 ++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index 1409016da6..b78d35a17f 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -279,16 +279,34 @@ namespace Umbraco.Core { get { - string dbtype = Database.Connection == null ? ProviderName : Database.Connection.GetType().Name; + if (Database.Connection == null) + { + string dbtype = ProviderName; - if (dbtype.StartsWith("MySql")) return DatabaseProviders.MySql; - if (dbtype.StartsWith("SqlCe") || dbtype.Contains("SqlServerCe")) return DatabaseProviders.SqlServerCE; - if (dbtype.StartsWith("Npgsql")) return DatabaseProviders.PostgreSQL; - if (dbtype.StartsWith("Oracle") || dbtype.Contains("OracleClient")) return DatabaseProviders.Oracle; - if (dbtype.StartsWith("SQLite")) return DatabaseProviders.SQLite; - if (dbtype.Contains("Azure")) return DatabaseProviders.SqlAzure; + if (dbtype.StartsWith("MySql")) return DatabaseProviders.MySql; + if (dbtype.StartsWith("SqlCe") || dbtype.Contains("SqlServerCe")) return DatabaseProviders.SqlServerCE; + if (dbtype.StartsWith("Npgsql")) return DatabaseProviders.PostgreSQL; + if (dbtype.StartsWith("Oracle") || dbtype.Contains("OracleClient")) return DatabaseProviders.Oracle; + if (dbtype.StartsWith("SQLite")) return DatabaseProviders.SQLite; + if (dbtype.Contains("Azure")) return DatabaseProviders.SqlAzure; - return DatabaseProviders.SqlServer; + return DatabaseProviders.SqlServer; + } + else + { + Database.DBType dbType = Database.DatabaseType; + + switch (dbType) + { + case Persistence.Database.DBType.SqlServer: return DatabaseProviders.SqlServer; + case Persistence.Database.DBType.SqlServerCE: return DatabaseProviders.SqlServerCE; + case Persistence.Database.DBType.MySql: return DatabaseProviders.MySql; + case Persistence.Database.DBType.PostgreSQL: return DatabaseProviders.PostgreSQL; + case Persistence.Database.DBType.Oracle: return DatabaseProviders.Oracle; + case Persistence.Database.DBType.SQLite: return DatabaseProviders.SQLite; + default: return (ProviderName.Contains("Azure"))? DatabaseProviders.SqlAzure : DatabaseProviders.SqlServer; + } + } } } From 802a4386c362b2efb764d3a6d76490c0ff80930e Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 2 Nov 2017 10:58:44 +0000 Subject: [PATCH 012/342] Renames the true false field to be more descriptive, adds migration to change existing name. --- .../Migrations/Initial/BaseDataCreation.cs | 12 ++++++------ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PropertyEditors/TrueFalsePropertyEditor.cs | 2 +- src/umbraco.editorControls/yesno/YesNoDataType.cs | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 1f94d30b0f..2c46b031e7 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -122,7 +122,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -88, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-88", SortOrder = 32, UniqueId = new Guid("0cc0eba1-9960-42c9-bf9b-60e150b429ae"), Text = "Textstring", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -87, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-87", SortOrder = 4, UniqueId = new Guid("ca90c950-0aff-4e72-b976-a30b1ac57dad"), Text = "Richtext editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = new Guid("2e6d3631-066e-44b8-aec4-96f09099b2b5"), Text = "Numeric", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); - _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -49, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-49", SortOrder = 2, UniqueId = new Guid("92897bc6-a5f3-4ffe-ae27-f2e7e33dda49"), Text = "True/false", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); + _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -49, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-49", SortOrder = 2, UniqueId = new Guid("92897bc6-a5f3-4ffe-ae27-f2e7e33dda49"), Text = "Single Checkbox", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -43, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-43", SortOrder = 2, UniqueId = new Guid("fbaf13a8-4036-41f2-93a3-974f678c312a"), Text = "Checkbox list", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -42, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-42", SortOrder = 2, UniqueId = new Guid("0b6a45e7-44ba-430d-9da5-4e46060b9e03"), Text = "Dropdown", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = new Guid("5046194e-4237-453c-a547-15db3a07c4e1"), Text = "Date Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); @@ -188,18 +188,18 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateUmbracoUserGroup2AppData() { _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Content }); - _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Developer }); + _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Developer }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Media }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Members }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Settings }); _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Users }); - _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Forms }); - - _database.Insert(new UserGroup2AppDto { UserGroupId = 2, AppAlias = Constants.Applications.Content }); + _database.Insert(new UserGroup2AppDto { UserGroupId = 1, AppAlias = Constants.Applications.Forms }); + + _database.Insert(new UserGroup2AppDto { UserGroupId = 2, AppAlias = Constants.Applications.Content }); _database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Content }); _database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Media }); - _database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Forms }); + _database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Forms }); _database.Insert(new UserGroup2AppDto { UserGroupId = 4, AppAlias = Constants.Applications.Translation }); } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 585b9c8682..b16ad46e48 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -545,6 +545,7 @@ + diff --git a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs index 0dc181cbc3..d63bdb9af7 100644 --- a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.TrueFalseAlias, "True/False", PropertyEditorValueTypes.Integer, "boolean", IsParameterEditor = true, Group = "Common", Icon="icon-checkbox")] + [PropertyEditor(Constants.PropertyEditors.TrueFalseAlias, "Single Checkbox", PropertyEditorValueTypes.Integer, "boolean", IsParameterEditor = true, Group = "Common", Icon="icon-checkbox")] public class TrueFalsePropertyEditor : PropertyEditor { protected override PreValueEditor CreatePreValueEditor() diff --git a/src/umbraco.editorControls/yesno/YesNoDataType.cs b/src/umbraco.editorControls/yesno/YesNoDataType.cs index 699af1a364..3e949d1878 100644 --- a/src/umbraco.editorControls/yesno/YesNoDataType.cs +++ b/src/umbraco.editorControls/yesno/YesNoDataType.cs @@ -35,7 +35,7 @@ namespace umbraco.editorControls.yesno public override string DataTypeName { - get {return "True/False (Ja/Nej)";} + get {return "Single Checkbox";} } public override Guid Id { From a861d306c96cb3353227523313d5067bef5134ba Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 7 Nov 2017 09:39:18 +0000 Subject: [PATCH 013/342] Makes changes recommended by Sebastian, includes missing Migration. --- .../Migrations/Initial/BaseDataCreation.cs | 2 +- .../RenameTrueFalseField.cs | 28 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../TrueFalsePropertyEditor.cs | 2 +- .../yesno/YesNoDataType.cs | 2 +- 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenEightZero/RenameTrueFalseField.cs diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 2c46b031e7..f0a9d80593 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -122,7 +122,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -88, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-88", SortOrder = 32, UniqueId = new Guid("0cc0eba1-9960-42c9-bf9b-60e150b429ae"), Text = "Textstring", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -87, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-87", SortOrder = 4, UniqueId = new Guid("ca90c950-0aff-4e72-b976-a30b1ac57dad"), Text = "Richtext editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = new Guid("2e6d3631-066e-44b8-aec4-96f09099b2b5"), Text = "Numeric", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); - _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -49, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-49", SortOrder = 2, UniqueId = new Guid("92897bc6-a5f3-4ffe-ae27-f2e7e33dda49"), Text = "Single Checkbox", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); + _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -49, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-49", SortOrder = 2, UniqueId = new Guid("92897bc6-a5f3-4ffe-ae27-f2e7e33dda49"), Text = "Checkbox", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -43, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-43", SortOrder = 2, UniqueId = new Guid("fbaf13a8-4036-41f2-93a3-974f678c312a"), Text = "Checkbox list", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -42, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-42", SortOrder = 2, UniqueId = new Guid("0b6a45e7-44ba-430d-9da5-4e46060b9e03"), Text = "Dropdown", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = new Guid("5046194e-4237-453c-a547-15db3a07c4e1"), Text = "Date Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now }); diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenEightZero/RenameTrueFalseField.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenEightZero/RenameTrueFalseField.cs new file mode 100644 index 0000000000..b425086502 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenEightZero/RenameTrueFalseField.cs @@ -0,0 +1,28 @@ +using System.Data; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero +{ + [MigrationAttribute("7.8.0", 0, Constants.System.UmbracoMigrationName)] + public class RenameTrueFalseField : MigrationBase + { + public RenameTrueFalseField(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) + { + } + + public override void Up() + { + //rename the existing true/false field + Update.Table("umbracoNode").Set(new { text = "Checkbox" }).Where(new { id = -49 }); + } + + public override void Down() + { + //set the field back to true/false + Update.Table("umbracoNode").Set(new { text = "True/false" }).Where(new { id = -49 }); + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b16ad46e48..12648df9d3 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -545,7 +545,7 @@ - + diff --git a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs index d63bdb9af7..6a8e726911 100644 --- a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.TrueFalseAlias, "Single Checkbox", PropertyEditorValueTypes.Integer, "boolean", IsParameterEditor = true, Group = "Common", Icon="icon-checkbox")] + [PropertyEditor(Constants.PropertyEditors.TrueFalseAlias, "Checkbox", PropertyEditorValueTypes.Integer, "boolean", IsParameterEditor = true, Group = "Common", Icon="icon-checkbox")] public class TrueFalsePropertyEditor : PropertyEditor { protected override PreValueEditor CreatePreValueEditor() diff --git a/src/umbraco.editorControls/yesno/YesNoDataType.cs b/src/umbraco.editorControls/yesno/YesNoDataType.cs index 3e949d1878..ee9d9d84bf 100644 --- a/src/umbraco.editorControls/yesno/YesNoDataType.cs +++ b/src/umbraco.editorControls/yesno/YesNoDataType.cs @@ -35,7 +35,7 @@ namespace umbraco.editorControls.yesno public override string DataTypeName { - get {return "Single Checkbox";} + get {return "Checkbox";} } public override Guid Id { From a388657dbbc1a494b5be9ce9a81f54755abfbd30 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Wed, 15 Nov 2017 19:11:35 +0100 Subject: [PATCH 014/342] Change types of listview properties --- .../PropertyEditors/ListViewPropertyEditor.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs index 2ee208ed48..3a0066f9fc 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs @@ -37,14 +37,16 @@ namespace Umbraco.Web.PropertyEditors new {name = "Grid", path = "views/propertyeditors/listview/layouts/grid/grid.html", icon = "icon-thumbnails-small", isSystem = 1, selected = true} } }, - {"bulkActionPermissions", new + { + "bulkActionPermissions", new { allowBulkPublish = true, allowBulkUnpublish = true, allowBulkCopy = true, allowBulkMove = false, allowBulkDelete = true - }} + } + } }; } } @@ -52,7 +54,7 @@ namespace Umbraco.Web.PropertyEditors internal class ListViewPreValueEditor : PreValueEditor { [PreValueField("tabName", "Tab Name", "textstring", Description = "The name of the listview tab (default if empty: 'Child Items')")] - public int TabName { get; set; } + public string TabName { get; set; } [PreValueField("displayAtTabNumber", "Display At Tab Number", "number", Description = "Which tab position that the list of child items will be displayed")] public int DisplayAtTabNumber { get; set; } @@ -61,7 +63,7 @@ namespace Umbraco.Web.PropertyEditors public int PageSize { get; set; } [PreValueField("layouts", "Layouts", "views/propertyeditors/listview/layouts.prevalues.html")] - public int Layouts { get; set; } + public object Layouts { get; set; } [PreValueField("includeProperties", "Columns Displayed", "views/propertyeditors/listview/includeproperties.prevalues.html", Description = "The properties that will be displayed for each column")] @@ -69,10 +71,10 @@ namespace Umbraco.Web.PropertyEditors [PreValueField("orderBy", "Order By", "views/propertyeditors/listview/sortby.prevalues.html", Description = "The default sort order for the list")] - public int OrderBy { get; set; } + public string OrderBy { get; set; } [PreValueField("orderDirection", "Order Direction", "views/propertyeditors/listview/orderdirection.prevalues.html")] - public int OrderDirection { get; set; } + public string OrderDirection { get; set; } [PreValueField("bulkActionPermissions", "Bulk Action Permissions", "views/propertyeditors/listview/bulkactionpermissions.prevalues.html", Description = "The bulk actions that are allowed from the list view")] From 3d326158ca0a465950bde0a1cacff3021a774024 Mon Sep 17 00:00:00 2001 From: Matt Darby Date: Wed, 6 Dec 2017 19:58:58 +0700 Subject: [PATCH 015/342] Check node permissions only on content list view --- .../listview/listview.controller.js | 37 ++++++++++--------- .../propertyeditors/listview/listview.html | 2 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 8f9279fb02..03b95b6883 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -57,24 +57,27 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie totalPages: 0, items: [] }; + + //when this is null, we don't check permissions + $scope.currentNodePermissions = null; + + if ($scope.entityType === "content") { + //Just ensure we do have an editorState + if (editorState.current) { + //Fetch current node allowed actions for the current user + //This is the current node & not each individual child node in the list + var currentUserPermissions = editorState.current.allowedActions; - $scope.currentNodePermissions = {} - - //Just ensure we do have an editorState - if (editorState.current) { - //Fetch current node allowed actions for the current user - //This is the current node & not each individual child node in the list - var currentUserPermissions = editorState.current.allowedActions; - - //Create a nicer model rather than the funky & hard to remember permissions strings - $scope.currentNodePermissions = { - "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O - "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C - "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D - "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M - "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U - "canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) - }; + //Create a nicer model rather than the funky & hard to remember permissions strings + $scope.currentNodePermissions = { + "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O + "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C + "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D + "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M + "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U + "canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) + }; + } } //when this is null, we don't check permissions diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 97500fd7c1..2683317dc1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -10,7 +10,7 @@ - +
[PluginController("UmbracoApi")] - [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] + [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] public class MemberTypeController : ContentTypeControllerBase { /// @@ -48,6 +48,7 @@ namespace Umbraco.Web.Editors private readonly MembershipProvider _provider; + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public MemberTypeDisplay GetById(int id) { var ct = Services.MemberTypeService.Get(id); @@ -67,6 +68,7 @@ namespace Umbraco.Web.Editors /// [HttpDelete] [HttpPost] + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public HttpResponseMessage DeleteById(int id) { var foundType = Services.MemberTypeService.Get(id); @@ -93,6 +95,8 @@ namespace Umbraco.Web.Editors /// be looked up via the db, they need to be passed in. /// /// + + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public HttpResponseMessage GetAvailableCompositeMemberTypes(int contentTypeId, [FromUri]string[] filterContentTypes, [FromUri]string[] filterPropertyTypes) @@ -106,6 +110,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(result); } + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public MemberTypeDisplay GetEmpty() { var ct = new MemberType(-1); @@ -129,6 +134,7 @@ namespace Umbraco.Web.Editors return Enumerable.Empty(); } + [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] public MemberTypeDisplay PostSave(MemberTypeSave contentTypeSave) { var savedCt = PerformPostSave( From ebae69d00cb29a7e3dc028d3fc09eb141542dd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20=C3=96hman?= Date: Sun, 11 Feb 2018 16:58:46 +0100 Subject: [PATCH 017/342] U4-10962 - Disable stickyheader when nested, fix margins when disabled --- .../components/umbstickybar.directive.js | 5 +++++ .../subheader/umb-editor-sub-header.less | 22 ++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js index 91a0a41a10..e8d8315b7a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbstickybar.directive.js @@ -50,6 +50,11 @@ Use this directive make an element sticky and follow the page when scrolling. function activate() { + if (bar.parents(".umb-property").length > 1) { + bar.addClass("nested"); + return; + } + if (attr.scrollableContainer) { scrollableContainer = $(attr.scrollableContainer); } else { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index b94055a0d2..fb71016f90 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -1,12 +1,18 @@ .umb-editor-sub-header { - padding: 15px 0; - margin-bottom: 30px; - background: @white; - display: flex; - justify-content: space-between; - margin-top: -30px; - position: relative; - top: 0; + padding: 15px 0; + margin-bottom: 30px; + background: @white; + display: flex; + justify-content: space-between; + margin-top: -30px; + position: relative; + top: 0; + + &.nested { + margin-top: 0; + margin-bottom: 0; + background: @gray-10; + } } .umb-editor-sub-header.-umb-sticky-bar { From c040cfefcdf413a096d643171fba0b4c0be4cb88 Mon Sep 17 00:00:00 2001 From: Rebecca Date: Thu, 22 Feb 2018 13:02:34 +1000 Subject: [PATCH 018/342] Failed Logon Attempts resets to 0 after maximum number of attempts --- .../Security/BackOfficeUserManager.cs | 62 ++++++++++++++----- .../src/views/users/user.controller.js | 2 + .../src/views/users/views/user/details.html | 2 +- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Core/Security/BackOfficeUserManager.cs b/src/Umbraco.Core/Security/BackOfficeUserManager.cs index 4969fa5f2f..38861f300c 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserManager.cs @@ -57,7 +57,7 @@ namespace Umbraco.Core.Security [Obsolete("Use the overload specifying all dependencies instead")] public static BackOfficeUserManager Create( IdentityFactoryOptions options, - IUserService userService, + IUserService userService, IExternalLoginService externalLoginService, MembershipProviderBase membershipProvider) { @@ -94,7 +94,7 @@ namespace Umbraco.Core.Security manager.InitUserManager(manager, membershipProvider, contentSectionConfig, options); return manager; } - + [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use the overload specifying all dependencies instead")] public static BackOfficeUserManager Create( @@ -129,7 +129,7 @@ namespace Umbraco.Core.Security [Obsolete("Use the overload specifying all dependencies instead")] protected void InitUserManager( BackOfficeUserManager manager, - MembershipProviderBase membershipProvider, + MembershipProviderBase membershipProvider, IdentityFactoryOptions options) { InitUserManager(manager, membershipProvider, UmbracoConfig.For.UmbracoSettings().Content, options); @@ -216,8 +216,8 @@ namespace Umbraco.Core.Security /// /// protected void InitUserManager( - BackOfficeUserManager manager, - MembershipProviderBase membershipProvider, + BackOfficeUserManager manager, + MembershipProviderBase membershipProvider, IDataProtectionProvider dataProtectionProvider, IContentSection contentSectionConfig) { @@ -233,7 +233,7 @@ namespace Umbraco.Core.Security //use a custom hasher based on our membership provider manager.PasswordHasher = GetDefaultPasswordHasher(membershipProvider); - + if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")); @@ -373,7 +373,7 @@ namespace Umbraco.Core.Security } #region Overrides for password logic - + /// /// Logic used to validate a username and password /// @@ -469,7 +469,7 @@ namespace Umbraco.Core.Security await UpdateSecurityStampInternal(user); return IdentityResult.Success; - + } /// @@ -507,15 +507,22 @@ namespace Umbraco.Core.Security #endregion - public override Task SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd) + public override async Task SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd) { - var result = base.SetLockoutEndDateAsync(userId, lockoutEnd); + var result = await base.SetLockoutEndDateAsync(userId, lockoutEnd); // The way we unlock is by setting the lockoutEnd date to the current datetime - if (result.Result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow) + if (result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow) + { RaiseAccountLockedEvent(userId); + } else + { RaiseAccountUnlockedEvent(userId); + //Resets the login attempt fails back to 0 when unlock is clicked + await ResetAccessFailedCountAsync(userId); + + } return result; } @@ -537,16 +544,39 @@ namespace Umbraco.Core.Security RaiseResetAccessFailedCountEvent(userId); return await UpdateAsync(user); } - - - public override Task AccessFailedAsync(int userId) + + /// + /// Overides the microsoft ASP.NET user managment method + /// + /// + /// + /// returns a Async Task + /// + /// + /// Doesnt set fail attempts back to 0 + /// + public override async Task AccessFailedAsync(int userId) { - var result = base.AccessFailedAsync(userId); + var lockoutStore = (IUserLockoutStore)Store; + var user = await FindByIdAsync(userId); + if (user == null) + throw new InvalidOperationException("No user found by user id " + userId); + + var count = await lockoutStore.IncrementAccessFailedCountAsync(user); + + if (count >= MaxFailedAccessAttemptsBeforeLockout) + { + await lockoutStore.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(DefaultAccountLockoutTimeSpan)); + //NOTE: in normal aspnet identity this would do set the number of failed attempts back to 0 + //here we are persisting the value for the back office + } + + var result = await UpdateAsync(user); //Slightly confusing: this will return a Success if we successfully update the AccessFailed count - if (result.Result.Succeeded) + if (result.Succeeded) RaiseLoginFailedEvent(userId); return result; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index 0f3519c7ba..3a59a50c2c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -341,8 +341,10 @@ vm.unlockUserButtonState = "busy"; usersResource.unlockUsers([vm.user.id]).then(function (data) { vm.user.userState = 0; + vm.user.failedPasswordAttempts = 0; setUserDisplayState(); vm.unlockUserButtonState = "success"; + formHelper.showNotifications(data); }, function (error) { vm.unlockUserButtonState = "error"; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index 67f204aa2f..83c28f9f99 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -358,4 +358,4 @@ - \ No newline at end of file + From 863679b3864889ca31b2316b949e28eab0554b07 Mon Sep 17 00:00:00 2001 From: Samuel Sperling Date: Thu, 22 Feb 2018 14:06:26 +1100 Subject: [PATCH 019/342] U4-10849 fixed Umbraco user field "User Last updated" and "last locked date" not updating after password change / account locking.. --- .../Models/Identity/BackOfficeIdentityUser.cs | 15 +++++++++++++-- .../Models/Identity/IdentityModelMappings.cs | 3 ++- src/Umbraco.Core/Models/Identity/IdentityUser.cs | 8 +++++++- .../Security/BackOfficeUserManager.cs | 1 + src/Umbraco.Core/Security/BackOfficeUserStore.cs | 11 +++++++++-- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs index 1a8cdc446f..3151492106 100644 --- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs +++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs @@ -120,13 +120,22 @@ namespace Umbraco.Core.Models.Identity set { _tracker.SetPropertyValueAndDetectChanges(value, ref _userName, Ps.Value.UserNameSelector); } } + /// + /// LastPasswordChangeDateUtc so we can track changes to it + /// + public override DateTime? LastPasswordChangeDateUtc + { + get { return _lastPasswordChangeDateUtc; } + set { _tracker.SetPropertyValueAndDetectChanges(value, ref _lastPasswordChangeDateUtc, Ps.Value.LastPasswordChangeDateUtcSelector); } + } + /// /// Override LastLoginDateUtc so we can track changes to it /// public override DateTime? LastLoginDateUtc { get { return _lastLoginDateUtc; } - set { _tracker.SetPropertyValueAndDetectChanges(value, ref _lastLoginDateUtc, Ps.Value.LastLoginDateUtcSelector); } + set { _tracker.SetPropertyValueAndDetectChanges(value, ref _lastLoginDateUtc, Ps.Value.LastLoginDateUtcSelector); } } /// @@ -392,6 +401,7 @@ namespace Umbraco.Core.Models.Identity public readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email); public readonly PropertyInfo UserNameSelector = ExpressionHelper.GetPropertyInfo(x => x.UserName); public readonly PropertyInfo LastLoginDateUtcSelector = ExpressionHelper.GetPropertyInfo(x => x.LastLoginDateUtc); + public readonly PropertyInfo LastPasswordChangeDateUtcSelector = ExpressionHelper.GetPropertyInfo(x => x.LastPasswordChangeDateUtc); public readonly PropertyInfo EmailConfirmedSelector = ExpressionHelper.GetPropertyInfo(x => x.EmailConfirmed); public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public readonly PropertyInfo AccessFailedCountSelector = ExpressionHelper.GetPropertyInfo(x => x.AccessFailedCount); @@ -419,6 +429,7 @@ namespace Umbraco.Core.Models.Identity private int _id; private bool _hasIdentity = false; private DateTime? _lastLoginDateUtc; + private DateTime? _lastPasswordChangeDateUtc; private bool _emailConfirmed; private string _name; private int _accessFailedCount; @@ -449,4 +460,4 @@ namespace Umbraco.Core.Models.Identity } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs index 954705d544..5ef270c789 100644 --- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs @@ -19,6 +19,7 @@ namespace Umbraco.Core.Models.Identity }) .ConstructUsing(user => new BackOfficeIdentityUser(user.Id, user.Groups)) .ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime())) + .ForMember(user => user.LastPasswordChangeDateUtc, expression => expression.MapFrom(user => user.LastPasswordChangeDate.ToUniversalTime())) .ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email)) .ForMember(user => user.EmailConfirmed, expression => expression.MapFrom(user => user.EmailConfirmedDate.HasValue)) .ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id)) @@ -47,4 +48,4 @@ namespace Umbraco.Core.Models.Identity return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/Identity/IdentityUser.cs b/src/Umbraco.Core/Models/Identity/IdentityUser.cs index c867dcf622..81c7c56d13 100644 --- a/src/Umbraco.Core/Models/Identity/IdentityUser.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityUser.cs @@ -83,6 +83,12 @@ namespace Umbraco.Core.Models.Identity /// public virtual DateTime? LockoutEndDateUtc { get; set; } + /// + /// DateTime in UTC when the password was last changed. + /// + /// + public virtual DateTime? LastPasswordChangeDateUtc { get; set; } + /// /// Is lockout enabled for this user /// @@ -127,4 +133,4 @@ namespace Umbraco.Core.Models.Identity } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Security/BackOfficeUserManager.cs b/src/Umbraco.Core/Security/BackOfficeUserManager.cs index 4969fa5f2f..fff97f145c 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserManager.cs @@ -457,6 +457,7 @@ namespace Umbraco.Core.Security /// protected override async Task UpdatePassword(IUserPasswordStore passwordStore, T user, string newPassword) { + user.LastPasswordChangeDateUtc = DateTime.UtcNow; var userAwarePasswordHasher = PasswordHasher as IUserAwarePasswordHasher; if (userAwarePasswordHasher == null) return await base.UpdatePassword(passwordStore, user, newPassword); diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index 9db8347040..e0b91ce175 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -622,7 +622,7 @@ namespace Umbraco.Core.Security private bool UpdateMemberProperties(IUser user, BackOfficeIdentityUser identityUser) { var anythingChanged = false; - + //don't assign anything if nothing has changed as this will trigger the track changes of the model if (identityUser.IsPropertyDirty("LastLoginDateUtc") @@ -632,6 +632,13 @@ namespace Umbraco.Core.Security anythingChanged = true; user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime(); } + if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc") + || (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false) + || identityUser.LastPasswordChangeDateUtc.HasValue && user.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value) + { + anythingChanged = true; + user.LastPasswordChangeDate = identityUser.LastPasswordChangeDateUtc.Value.ToLocalTime(); + } if (identityUser.IsPropertyDirty("EmailConfirmed") || (user.EmailConfirmedDate.HasValue && user.EmailConfirmedDate.Value != default(DateTime) && identityUser.EmailConfirmed == false) || ((user.EmailConfirmedDate.HasValue == false || user.EmailConfirmedDate.Value == default(DateTime)) && identityUser.EmailConfirmed)) @@ -765,4 +772,4 @@ namespace Umbraco.Core.Security return Task.FromResult(false); } } -} \ No newline at end of file +} From 5bfac6c5b484cd1fc8db5959feb2357d883fe616 Mon Sep 17 00:00:00 2001 From: Maciej Sadowski Date: Tue, 27 Feb 2018 09:23:35 +0100 Subject: [PATCH 020/342] fix x alignment in colorpicker in umbraco skin --- .../lib/tinymce/skins/umbraco/skin.min.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/skin.min.css b/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/skin.min.css index c17e10b917..6fa010bce0 100755 --- a/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/skin.min.css +++ b/src/Umbraco.Web.UI.Client/lib/tinymce/skins/umbraco/skin.min.css @@ -1 +1 @@ -.mce-container,.mce-container *,.mce-widget,.mce-widget *{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#000;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-container ::-webkit-scrollbar{width:8px;height:8px;-webkit-border-radius:4px}.mce-container ::-webkit-scrollbar-track,.mce-container ::-webkit-scrollbar-track-piece{background-color:transparent}.mce-container ::-webkit-scrollbar-thumb{background-color:rgba(53,57,71,0.3);width:6px;height:6px;-webkit-border-radius:4px}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:visible!important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;border-radius:2px}.mce-wordcount{float:right;padding:8px}.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #c5c5c5;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:center;padding:2px}.mce-charmap td:hover{background:#d9d9d9}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover{border-color:#c5c5c5}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#e8e8e8;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#c4daff;background:#deeafa}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:#ccc 5px 5px 5px;-moz-box-shadow:#ccc 5px 5px 5px;box-shadow:#ccc 5px 5px 5px}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{position:absolute;top:0;left:0;background:#fff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#FFF;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #EEE;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#CCC;text-shadow:0 1px 0 white;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#AAA}.mce-window-head .mce-title{display:inline-block;*display:inline;*zoom:1;line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:whiteSmoke;border-top:1px solid #DDD;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000;box-shadow:0 0 5px #000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:0;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #c5c5c5;position:relative;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-btn:hover,.mce-btn:focus{text-decoration:none;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn.mce-disabled,.mce-btn.mce-disabled:hover{cursor:default;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);zoom:1;border-color:#04c #04c #002b80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary:hover,.mce-primary:focus{color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#005fb3;background-image:-moz-linear-gradient(top,#0077b3,#003cb3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#0077b3),to(#003cb3));background-image:-webkit-linear-gradient(top,#0077b3,#003cb3);background-image:-o-linear-gradient(top,#0077b3,#003cb3);background-image:linear-gradient(to bottom,#0077b3,#003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3',endColorstr='#ff003cb3',GradientType=0);zoom:1;border-color:#003cb3 #003cb3 #026;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary button{color:#333}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:3px 5px;font-size:12px;line-height:15px}.mce-btn-small i{margin-top:0}.mce-btn .mce-caret{margin-top:8px;*margin-top:6px;margin-left:0}.mce-btn-small .mce-caret{margin-top:6px;*margin-top:4px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #444;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#999}.mce-caret.mce-up{border-bottom:4px solid #444;border-top:0}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-btn:hover,.mce-btn-group .mce-btn:focus{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-disabled,.mce-btn-group .mce-btn.mce-disabled:hover{-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-active,.mce-btn-group .mce-btn.mce-active:hover,.mce-btn-group .mce-btn:active{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn-group .mce-btn.mce-disabled button{opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn-group .mce-first{border-left:1px solid #c5c5c5;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #c5c5c5;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0}.mce-checked i.mce-i-checkbox{color:#000;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox{border:1px solid #59a5e1;border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-colorbutton .mce-ico{position:relative}.mce-colorpicker{background:#FFF}.mce-colorbutton-grid{margin:4px}.mce-grid td div{border:1px solid #808080;width:12px;height:12px;margin:2px;cursor:pointer}.mce-grid td div:hover{border-color:black}.mce-grid td div:focus{border-color:#59a5e1;outline:1px solid rgba(82,168,236,0.8);border-color:rgba(82,168,236,0.8)}.mce-colorbutton{position:relative}.mce-colorbutton .mce-preview{display:block;position:absolute;left:50%;top:50%;margin-left:-8px;margin-top:7px;background:gray;width:16px;height:2px;overflow:hidden}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;width:100px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-combobox input{border-color:1px solid #c5c5c5;border-right-color:rgba(0,0,0,0.15);height:28px}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox *:focus{border-color:#59a5e1;border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#000}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:gray;color:white}.mce-path .mce-divider{display:inline}.mce-fieldset{border:0 solid #9e9e9e;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-iframe{border:0 solid #c5c5c5;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);border:0 solid #c5c5c5;overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label-disabled .mce-text{color:#999}.mce-label.mce-multiline{white-space:pre-wrap}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #ddd}.mce-menubar .mce-menubtn button{color:#000}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubtn:focus{border-color:transparent;background:#ddd;filter:none}.mce-menubtn.mce-disabled span{color:#999}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-listbox span{width:100%;display:block;overflow:hidden}.mce-menu-item{display:block;padding:6px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal}.mce-menu-item.mce-disabled .mce-text{color:#999}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0);zoom:1}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:white}.mce-menu-shortcut{display:inline-block;color:#999}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 20px 0 20px}.mce-menu-item .mce-caret{margin-top:6px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #666}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret{border-left-color:#FFF}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item-sep,.mce-menu-item-sep:hover{padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#e5e5e5;border-bottom:1px solid white;cursor:default;filter:none}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item.mce-active{background-color:#c8def4;outline:1px solid #c5c5c5}.mce-menu-item-checkbox.mce-active{background-color:#FFF;outline:0}.mce-menu{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#FFF;border:1px solid #CCC;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline;*zoom:1}.mce-menu-sub{margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}i.mce-radio{padding:1px;margin:0 3px 0 0;background-color:#fafafa;border:1px solid #cacece;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}i.mce-radio:after{font-family:Arial;font-size:12px;color:#000;content:'\25cf'}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#000}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#c5c5c5;border-right-color:#c5c5c5}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #ccc}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #ccc;border-width:1px 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-textbox{background:#FFF;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);display:inline-block;-webkit-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:normal;color:#000}.mce-textbox:focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-inserttime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-untitled:before{content:"\e029"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e026"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#BBB}.mce-i-tablerowprops:before{content:"\e604"}.mce-i-tablecellprops:before{content:"\e605"}.mce-i-table2:before{content:"\e606"}.mce-i-tablemergecells:before{content:"\e607"}.mce-i-tableinsertcolbefore:before{content:"\e608"}.mce-i-tableinsertcolafter:before{content:"\e609"}.mce-i-tableinsertrowbefore:before{content:"\e60a"}.mce-i-tableinsertrowafter:before{content:"\e60b"}.mce-i-tablesplitcells:before{content:"\e60d"}.mce-i-tabledelete:before{content:"\e60e"}.mce-i-tableleftheader:before{content:"\e62a"}.mce-i-tabletopheader:before{content:"\e62b"}.mce-i-tabledeleterow:before{content:"\e800"}.mce-i-tabledeletecol:before{content:"\e801"} \ No newline at end of file +.mce-container,.mce-container *,.mce-widget,.mce-widget *{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#000;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-container ::-webkit-scrollbar{width:8px;height:8px;-webkit-border-radius:4px}.mce-container ::-webkit-scrollbar-track,.mce-container ::-webkit-scrollbar-track-piece{background-color:transparent}.mce-container ::-webkit-scrollbar-thumb{background-color:rgba(53,57,71,0.3);width:6px;height:6px;-webkit-border-radius:4px}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:visible!important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;border-radius:2px}.mce-wordcount{float:right;padding:8px}.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #c5c5c5;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:center;padding:2px}.mce-charmap td:hover{background:#d9d9d9}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover{border-color:#c5c5c5}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#e8e8e8;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#c4daff;background:#deeafa}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:#ccc 5px 5px 5px;-moz-box-shadow:#ccc 5px 5px 5px;box-shadow:#ccc 5px 5px 5px}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{position:absolute;top:0;left:0;background:#fff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#FFF;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #EEE;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#CCC;text-shadow:0 1px 0 white;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#AAA}.mce-window-head .mce-title{display:inline-block;*display:inline;*zoom:1;line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:whiteSmoke;border-top:1px solid #DDD;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000;box-shadow:0 0 5px #000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:0;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #c5c5c5;position:relative;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-btn:hover,.mce-btn:focus{text-decoration:none;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn.mce-disabled,.mce-btn.mce-disabled:hover{cursor:default;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);zoom:1;border-color:#04c #04c #002b80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary:hover,.mce-primary:focus{color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#005fb3;background-image:-moz-linear-gradient(top,#0077b3,#003cb3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#0077b3),to(#003cb3));background-image:-webkit-linear-gradient(top,#0077b3,#003cb3);background-image:-o-linear-gradient(top,#0077b3,#003cb3);background-image:linear-gradient(to bottom,#0077b3,#003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3',endColorstr='#ff003cb3',GradientType=0);zoom:1;border-color:#003cb3 #003cb3 #026;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary button{color:#333}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:3px 5px;font-size:12px;line-height:15px}.mce-btn-small i{margin-top:0}.mce-btn .mce-caret{margin-top:8px;*margin-top:6px;margin-left:0}.mce-btn-small .mce-caret{margin-top:6px;*margin-top:4px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #444;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#999}.mce-caret.mce-up{border-bottom:4px solid #444;border-top:0}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-btn:hover,.mce-btn-group .mce-btn:focus{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-disabled,.mce-btn-group .mce-btn.mce-disabled:hover{-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-active,.mce-btn-group .mce-btn.mce-active:hover,.mce-btn-group .mce-btn:active{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn-group .mce-btn.mce-disabled button{opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn-group .mce-first{border-left:1px solid #c5c5c5;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #c5c5c5;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0}.mce-checked i.mce-i-checkbox{color:#000;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox{border:1px solid #59a5e1;border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-colorbutton .mce-ico{position:relative}.mce-colorpicker{background:#FFF}.mce-colorbutton-grid{margin:4px}.mce-grid td div{border:1px solid #808080;width:12px;height:12px;margin:2px;cursor:pointer}.mce-grid td div:hover{border-color:black}.mce-grid td div:focus{border-color:#59a5e1;outline:1px solid rgba(82,168,236,0.8);border-color:rgba(82,168,236,0.8)}.mce-colorbutton{position:relative}.mce-colorbutton .mce-preview{display:block;position:absolute;left:50%;top:50%;margin-left:-8px;margin-top:7px;background:gray;width:16px;height:2px;overflow:hidden}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;width:100px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-combobox input{border-color:1px solid #c5c5c5;border-right-color:rgba(0,0,0,0.15);height:28px}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox *:focus{border-color:#59a5e1;border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#000}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:gray;color:white}.mce-path .mce-divider{display:inline}.mce-fieldset{border:0 solid #9e9e9e;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-iframe{border:0 solid #c5c5c5;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);border:0 solid #c5c5c5;overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label-disabled .mce-text{color:#999}.mce-label.mce-multiline{white-space:pre-wrap}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #ddd}.mce-menubar .mce-menubtn button{color:#000}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubtn:focus{border-color:transparent;background:#ddd;filter:none}.mce-menubtn.mce-disabled span{color:#999}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-listbox span{width:100%;display:block;overflow:hidden}.mce-menu-item{display:block;padding:6px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal}.mce-menu-item.mce-disabled .mce-text{color:#999}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0);zoom:1}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:white}.mce-menu-shortcut{display:inline-block;color:#999}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 20px 0 20px}.mce-menu-item .mce-caret{margin-top:6px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #666}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret{border-left-color:#FFF}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item-sep,.mce-menu-item-sep:hover{padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#e5e5e5;border-bottom:1px solid white;cursor:default;filter:none}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item.mce-active{background-color:#c8def4;outline:1px solid #c5c5c5}.mce-menu-item-checkbox.mce-active{background-color:#FFF;outline:0}.mce-menu{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#FFF;border:1px solid #CCC;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline;*zoom:1}.mce-menu-sub{margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}i.mce-radio{padding:1px;margin:0 3px 0 0;background-color:#fafafa;border:1px solid #cacece;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}i.mce-radio:after{font-family:Arial;font-size:12px;color:#000;content:'\25cf'}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#000}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#c5c5c5;border-right-color:#c5c5c5}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #ccc}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #ccc;border-width:1px 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-textbox{background:#FFF;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);display:inline-block;-webkit-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:normal;color:#000}.mce-textbox:focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-inserttime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-untitled:before{content:"\e029"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e026"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#BBB}.mce-i-tablerowprops:before{content:"\e604"}.mce-i-tablecellprops:before{content:"\e605"}.mce-i-table2:before{content:"\e606"}.mce-i-tablemergecells:before{content:"\e607"}.mce-i-tableinsertcolbefore:before{content:"\e608"}.mce-i-tableinsertcolafter:before{content:"\e609"}.mce-i-tableinsertrowbefore:before{content:"\e60a"}.mce-i-tableinsertrowafter:before{content:"\e60b"}.mce-i-tablesplitcells:before{content:"\e60d"}.mce-i-tabledelete:before{content:"\e60e"}.mce-i-tableleftheader:before{content:"\e62a"}.mce-i-tabletopheader:before{content:"\e62b"}.mce-i-tabledeleterow:before{content:"\e800"}.mce-i-tabledeletecol:before{content:"\e801"}.mce-colorbtn-trans div{text-align: center;vertical-align: middle;font-weight: bold;font-size: 20px;line-height: 16px;color: #707070;}.mce-grid td.mce-grid-cell div{border: 1px solid #d6d6d6;width: 15px;height: 15px;margin: 0;cursor: pointer;} \ No newline at end of file From 71d3c1112efb6d708440834bc2c49094bc2c2219 Mon Sep 17 00:00:00 2001 From: Steve Fawcett Date: Tue, 27 Feb 2018 17:48:53 +0000 Subject: [PATCH 021/342] Fix: TinyMCE overlay was showing in front of login overlay --- src/Umbraco.Web.UI.Client/src/less/pages/login.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/pages/login.less b/src/Umbraco.Web.UI.Client/src/less/pages/login.less index 5443134dd2..c33862d515 100644 --- a/src/Umbraco.Web.UI.Client/src/less/pages/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/pages/login.less @@ -5,7 +5,7 @@ width: 100%; height: 100%; position: absolute; - z-index: 10000; + z-index: 65537; top: 0; left: 0; margin: 0 !important; From be2ed0e8da2ffe1d9280d6c26b6aaf8e8fd614fd Mon Sep 17 00:00:00 2001 From: "skg@novicell.dk" Date: Thu, 1 Mar 2018 20:17:38 +0100 Subject: [PATCH 022/342] Added a generic TableExists-method --- src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs index 944d5a8a74..f03e751427 100644 --- a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs +++ b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs @@ -31,6 +31,13 @@ namespace Umbraco.Core.Persistence return _syntaxProvider.DoesTableExist(_db, tableName); } + public bool TableExist() + { + var poco = Database.PocoData.ForType(typeof(T)); + var tableName = poco.TableInfo.TableName; + return TableExist(tableName); + } + internal void UninstallDatabaseSchema() { var creation = new DatabaseSchemaCreation(_db, _logger, _syntaxProvider); @@ -194,4 +201,4 @@ namespace Umbraco.Core.Persistence _db.Execute(sql); } } -} \ No newline at end of file +} From 20216df8cfce3e656b59c615cf95ddc302f48d63 Mon Sep 17 00:00:00 2001 From: Rune Hem Strand Date: Fri, 2 Mar 2018 17:49:02 +0100 Subject: [PATCH 023/342] Add check to avoid cicular template reference --- .../src/views/templates/edit.controller.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 5d2129668e..8b03b90a67 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -516,9 +516,13 @@ var availableMasterTemplates = []; // filter out the current template and the selected master template - angular.forEach(vm.templates, function(template){ - if(template.alias !== vm.template.alias && template.alias !== vm.template.masterTemplateAlias) { - availableMasterTemplates.push(template); + angular.forEach(vm.templates, function (template) { + if (template.alias !== vm.template.alias && template.alias !== vm.template.masterTemplateAlias) { + var templatePathArray = template.path.split(','); + // filter descendant templates of current template + if (templatePathArray.indexOf(String(vm.template.id)) === -1) { + availableMasterTemplates.push(template); + } } }); From 13f42773d73c66a22674e4c2de15737962c6af74 Mon Sep 17 00:00:00 2001 From: Peter Duncanson Date: Tue, 6 Mar 2018 17:02:16 +0000 Subject: [PATCH 024/342] Added web.config to fonts folder to allow for caching out of the box --- src/Umbraco.Web.UI.Client/src/assets/fonts/web.config | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/assets/fonts/web.config diff --git a/src/Umbraco.Web.UI.Client/src/assets/fonts/web.config b/src/Umbraco.Web.UI.Client/src/assets/fonts/web.config new file mode 100644 index 0000000000..42051b6de2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/fonts/web.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 38e9a3d2eef332ce3fdeec2f51621969be9367d9 Mon Sep 17 00:00:00 2001 From: Nathalie Festin Date: Sun, 11 Mar 2018 20:41:51 +0100 Subject: [PATCH 025/342] Bug in exclude - Dialogoption.filter --- .../treepicker/treepicker.controller.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js index b7512e1e8c..215b363eb6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js @@ -20,16 +20,16 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", selectedSearchResults: [] } - $scope.model.selection = []; - - //Used for toggling an empty-state message + $scope.model.selection = []; + + //Used for toggling an empty-state message //Some trees can have no items (dictionary & forms email templates) $scope.hasItems = true; - $scope.emptyStateMessage = dialogOptions.emptyStateMessage; - + $scope.emptyStateMessage = dialogOptions.emptyStateMessage; - //This is called from ng-init - //it turns out it is called from the angular html : / Have a look at views/common / overlays / contentpicker / contentpicker.html you'll see ng-init. + + //This is called from ng-init + //it turns out it is called from the angular html : / Have a look at views/common / overlays / contentpicker / contentpicker.html you'll see ng-init. //this is probably an anti pattern IMO and shouldn't be used $scope.init = function (contentType) { @@ -109,7 +109,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", else { if (dialogOptions.filter.startsWith("!")) { dialogOptions.filterExclude = true; - dialogOptions.filter = dialogOptions.filter.substring(1); + //dialogOptions.filter = dialogOptions.filter.substring(1); // Can't substring the value on dialogoptions.filter as it then keeps it's modified value next time the dialog opens + dialogOptions.filterTypes = dialogOptions.filter.substring(1); } //used advanced filtering @@ -157,8 +158,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //gets the tree object when it loads function treeLoadedHandler(ev, args) { - //args.tree contains children (args.tree.root.children) - $scope.hasItems = args.tree.root.children.length > 0; + //args.tree contains children (args.tree.root.children) + $scope.hasItems = args.tree.root.children.length > 0; tree = args.tree; } @@ -215,7 +216,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", icon: "icon-folder", id: id, name: text - }; + }; if ($scope.multiPicker) { if (entity) { @@ -315,7 +316,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", } }); } else { - var a = dialogOptions.filter.toLowerCase().replace(/\s/g, '').split(','); + var a = dialogOptions.filterTypes.toLowerCase().replace(/\s/g, '').split(','); angular.forEach(nodes, function (value, key) { var found = a.indexOf(value.metaData.contentType.toLowerCase()) >= 0; From 4512cf0145ac3519771658357eca403c2fc9034f Mon Sep 17 00:00:00 2001 From: Nathalie Festin Date: Sun, 11 Mar 2018 20:49:43 +0100 Subject: [PATCH 026/342] Filtering items 'Allow items of type' in mini list view in MNTP --- .../components/umbminilistview.directive.js | 22 ++++++++++++++++--- .../src/less/listview.less | 4 ++++ .../treepicker/treepicker.controller.js | 16 +++++++++----- .../overlays/treepicker/treepicker.html | 7 +++--- .../views/components/umb-mini-list-view.html | 4 ++-- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js index ce14aaedc5..1cf934b78c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js @@ -8,7 +8,7 @@ scope.search = ""; scope.miniListViews = []; scope.breadcrumb = []; - + var miniListViewsHistory = []; var goingForward = true; var skipAnimation = true; @@ -60,9 +60,13 @@ entityResource.getPagedChildren(miniListView.node.id, scope.entityType, miniListView.pagination) .then(function (data) { + // update children miniListView.children = data.items; _.each(miniListView.children, function(c) { + // child allowed by default + c.allowed = true; + // convert legacy icon for node if(c.icon) { c.icon = iconHelper.convertFromLegacyIcon(c.icon); @@ -74,6 +78,17 @@ c.published = c.metaData.IsPublished; } } + + // filter items if there is a filter and it's not advanced + // ** ignores advanced filter at the moment + if (scope.entityTypeFilter && !scope.entityTypeFilter.filterAdvanced) { + var a = scope.entityTypeFilter.filter.toLowerCase().replace(/\s/g, '').split(','); + var found = a.indexOf(c.metaData.ContentTypeAlias.toLowerCase()) >= 0; + + if (!scope.entityTypeFilter.filterExclude && !found || scope.entityTypeFilter.filterExclude && found) { + c.allowed = false; + } + } }); // update pagination miniListView.pagination.totalItems = data.totalItems; @@ -89,7 +104,7 @@ }; scope.selectNode = function(node) { - if(scope.onSelect) { + if (scope.onSelect && node.allowed) { scope.onSelect({'node': node}); } }; @@ -202,7 +217,8 @@ entityType: "@", startNodeId: "=", onSelect: "&", - onClose: "&" + onClose: "&", + entityTypeFilter: "=" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/less/listview.less b/src/Umbraco.Web.UI.Client/src/less/listview.less index d2393efb11..284de6e844 100644 --- a/src/Umbraco.Web.UI.Client/src/less/listview.less +++ b/src/Umbraco.Web.UI.Client/src/less/listview.less @@ -157,6 +157,10 @@ /* TEMP */ +.umb-minilistview { + .umb-table-row.not-allowed { opacity: 0.6; cursor: not-allowed; } +} + .umb-listview .table-striped tbody td { position: relative } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js index 215b363eb6..e0b5689b72 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js @@ -83,11 +83,11 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //if a alternative startnode is used, we need to check if it is a container if ($scope.enableSearh && dialogOptions.startNodeId && dialogOptions.startNodeId !== -1 && dialogOptions.startNodeId !== "-1") { entityResource.getById(dialogOptions.startNodeId, $scope.entityType).then(function(node) { - if (node.metaData.IsContainer) { - openMiniListView(node); - } - initTree(); - }); + if (node.metaData.IsContainer) { + openMiniListView(node); + } + initTree(); + }); } else { initTree(); @@ -120,6 +120,12 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", dialogOptions.filter = angular.fromJson(dialogOptions.filter); } } + + $scope.filter = { + filterAdvanced: dialogOptions.filterAdvanced, + filterExclude: dialogOptions.filterExclude, + filter: dialogOptions.filterTypes + }; } function initTree() { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html index ab5e61339f..a3a5fd6107 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html @@ -22,7 +22,7 @@ {{ emptyStateMessage }} - +
+ on-close="closeMiniListView()" + entity-type-filter="filter"> -
\ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html index 9959d7b345..90a72d117e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html @@ -1,4 +1,4 @@ -
+
@@ -61,7 +61,7 @@
+ ng-class="{'-selected':child.selected, 'not-allowed':!child.allowed}">
  From 4babcbf96e7d4fb42c5b7eef98159bb265f54c11 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sat, 17 Mar 2018 17:16:14 +0100 Subject: [PATCH 027/342] Hide tooltip of slider property editor in document type editor property preview --- .../src/less/property-editors.less | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 2d317fa4a0..c2eaca05fc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -879,7 +879,21 @@ ul.color-picker li a { // // Nested boolean (e.g. list view bulk action permissions) -// ---------------------=====----------------------------- +// ------------------------------------------------------- .umb-nested-boolean label {margin-bottom: 8px; float: left; width: 320px;} .umb-nested-boolean label span {float: left; width: 80%;} .umb-nested-boolean label input[type='checkbox'] {margin-right: 10px; float: left;} + + +// +// Custom styles of property editors in property preview in document type editor +// ----------------------------------------------------------------------------- +.umb-group-builder__property-preview { + .umb-property-editor { + .slider { + .tooltip { + display: none; + } + } + } +} From 587d9b6d6c09abaeaea2da53dc0c7af13dd8b86a Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sun, 18 Mar 2018 20:05:16 +0100 Subject: [PATCH 028/342] Remove umb-pane from dialog --- .../src/views/datatypes/move.html | 2 +- .../src/views/documenttypes/copy.html | 2 +- .../src/views/documenttypes/move.html | 2 +- .../src/views/media/move.html | 91 +++++++++---------- .../src/views/mediatypes/copy.html | 2 +- .../src/views/mediatypes/move.html | 2 +- 6 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/move.html b/src/Umbraco.Web.UI.Client/src/views/datatypes/move.html index 09390cdb81..f05b8a6c79 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/move.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html index db1a0db640..2976f65917 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/move.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/move.html index c982abc0c0..1ed224b5ba 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/move.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/move.html b/src/Umbraco.Web.UI.Client/src/views/media/move.html index d04d66f706..b37b71b639 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/move.html @@ -1,56 +1,55 @@
-
-
+
+
-
-
-
{{error.errorMsg}}
-
{{error.data.message}}
+
+
+
{{error.errorMsg}}
+
{{error.data.message}}
+
-
-
-
- {{currentNode.name}} was moved underneath {{target.name}} -
- -
+
+
+ {{currentNode.name}} was moved underneath {{target.name}} +
+ +
-

- Choose where to move - {{currentNode.name}} - to in the tree structure below -

+

+ Choose where to move + {{currentNode.name}} + to in the tree structure below +

-
+
-
- - -
+
+ + +
- - + + -
-
-
- - - +
+
+
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html index 319e59c4cc..fcc26b9928 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/move.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/move.html index 59172eb547..18f7aee337 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/move.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/move.html @@ -1,4 +1,4 @@ -
+
From b962d2a9d31263249c08734cc13e80c681ff4741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Vanbrabandt?= Date: Tue, 27 Mar 2018 15:55:28 +0200 Subject: [PATCH 029/342] Update NL.xml with missing translations --- src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index fd49f9764b..2265ba590a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -172,6 +172,13 @@ Doel Dit betekend de volgende tijd op de server: Wat houd dit in?]]> + Ben je er zeker van dat je dit item wilt verwijderen? + Eigenschap %0% gebruikt editor %1% welke niet wordt ondersteund door Nested Content. + Voeg nog een tekstvak toe + Verwijder dit tekstvak + Content root + Deze waarde is verborgen. Indien u toegang nodig heeft om deze waarde te bekijken, contacteer dan uw website administrator. + Deze waarde is verborgen Klik om te uploaden @@ -1146,7 +1153,12 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je die gebruik maken van deze editor zullen geupdate worden met deze nieuwe instellingen Lid kan bewerken + Toestaan dat deze eigenschap kan worden gewijzigd door het lid op zijn profiel pagina. + Omvat gevoelige gegevens + Verberg deze eigenschap voor de content editor dat geen toegang heeft tot het bekijken van gevoelige informatie. Toon in het profiel van leden + Toelaten dat deze eigenschap wordt getoond op de profiel pagina van het lid. + tab heeft geen sorteervolgorde From b0ab408fda136677e93413307ff1e0db0ad88c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Vanbrabandt?= Date: Tue, 27 Mar 2018 17:19:24 +0200 Subject: [PATCH 030/342] Update NL.xml Set die instead of dat --- src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 2265ba590a..1e03f6f76c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -1155,7 +1155,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Lid kan bewerken Toestaan dat deze eigenschap kan worden gewijzigd door het lid op zijn profiel pagina. Omvat gevoelige gegevens - Verberg deze eigenschap voor de content editor dat geen toegang heeft tot het bekijken van gevoelige informatie. + Verberg deze eigenschap voor de content editor die geen toegang heeft tot het bekijken van gevoelige informatie. Toon in het profiel van leden Toelaten dat deze eigenschap wordt getoond op de profiel pagina van het lid. From a623e4034c75106d991e006aaa5b85e0d7c9edbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Gregersen?= Date: Wed, 28 Mar 2018 11:00:35 +0200 Subject: [PATCH 031/342] Updated documentation + default values for getPagedDescendants Changed type in example, from unknown "Content" to known "Document". Also changed the default values to show the first page, with a 100 results, instead the 100th page with 1 result. --- .../src/common/resources/entity.resource.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 5dd353d9e0..72f8ad5539 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -406,7 +406,7 @@ function entityResource($q, $http, umbRequestHelper) { * * ##usage *
-          * entityResource.getPagedDescendants(1234, "Content", {pageSize: 10, pageNumber: 2})
+          * entityResource.getPagedDescendants(1234, "Document", {pageSize: 10, pageNumber: 2})
           *    .then(function(contentArray) {
           *        var children = contentArray; 
           *        alert('they are here!');
@@ -416,8 +416,8 @@ function entityResource($q, $http, umbRequestHelper) {
           * @param {Int} parentid id of content item to return descendants of
           * @param {string} type Object type name
           * @param {Object} options optional options object
-          * @param {Int} options.pageSize if paging data, number of nodes per page, default = 1
-          * @param {Int} options.pageNumber if paging data, current page index, default = 100
+          * @param {Int} options.pageSize if paging data, number of nodes per page, default = 100
+          * @param {Int} options.pageNumber if paging data, current page index, default = 1
           * @param {String} options.filter if provided, query will only return those with names matching the filter
           * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending`
           * @param {String} options.orderBy property to order items by, default: `SortOrder`
@@ -427,8 +427,8 @@ function entityResource($q, $http, umbRequestHelper) {
         getPagedDescendants: function (parentId, type, options) {
 
             var defaults = {
-                pageSize: 1,
-                pageNumber: 100,
+                pageSize: 100,
+                pageNumber: 1,
                 filter: '',
                 orderDirection: "Ascending",
                 orderBy: "SortOrder"

From 73565d9be1476b5de46f37b420e20da8c0371d25 Mon Sep 17 00:00:00 2001
From: Marc Goodson 
Date: Thu, 29 Mar 2018 09:12:57 +0100
Subject: [PATCH 032/342] Consider the 'case' of the Member Group/Role Name
 when comparing, during assignment, whether or not the Member Group/Role
 exists and whether it needs to be created... and whether the Member belongs
 to a particular Group/Role and whether they need to be assigned.

---
 .../Persistence/Repositories/MemberGroupRepository.cs       | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
index 4006ea26c9..404c32640a 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
@@ -242,7 +242,7 @@ namespace Umbraco.Core.Persistence.Repositories
                .Where(dto => dto.NodeObjectType == NodeObjectTypeId)
                .Where("umbracoNode." + SqlSyntax.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames });
             var existingRoles = Database.Fetch(existingSql).Select(x => x.Text);
-            var missingRoles = roleNames.Except(existingRoles);
+            var missingRoles = roleNames.Except(existingRoles, StringComparer.CurrentCultureIgnoreCase);
             var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray();
 
             if (UnitOfWork.Events.DispatchCancelable(SavingMemberGroup, this, new SaveEventArgs(missingGroups)))
@@ -280,8 +280,8 @@ namespace Umbraco.Core.Persistence.Repositories
                 //exist in the roleNames list, then determine which ones are not currently assigned.
                 var mId = memberId;
                 var found = currentlyAssigned.Where(x => x.MemberId == mId).ToArray();
-                var assignedRoles = found.Where(x => roleNames.Contains(x.RoleName)).Select(x => x.RoleName);
-                var nonAssignedRoles = roleNames.Except(assignedRoles);
+                var assignedRoles = found.Where(x => roleNames.Contains(x.RoleName,StringComparer.CurrentCultureIgnoreCase)).Select(x => x.RoleName);
+                var nonAssignedRoles = roleNames.Except(assignedRoles, StringComparer.CurrentCultureIgnoreCase);
                 foreach (var toAssign in nonAssignedRoles)
                 {
                     var groupId = rolesForNames.First(x => x.Text == toAssign).NodeId;

From 23612d7599e9f83b758700d73d9a42dd50f51ec9 Mon Sep 17 00:00:00 2001
From: leekelleher 
Date: Mon, 19 Mar 2018 13:25:32 +0000
Subject: [PATCH 033/342] U4-9668 RelationService - Relates entities by ID and
 a relationType + Alias

Currently you need to supply both parent & child entities to create a new relation.
When the underlying code requires the entity IDs (int).

This patch adds methods to accept the int IDs.
http://issues.umbraco.org/issue/U4-9668

This patch also de-duplicates the logic from `Relate(IUmbracoEntity, IUmbracoEntity, string)`,
as it was the same as the method body of `Relate(IUmbracoEntity, IUmbracoEntity, IRelationType)`.
---
 src/Umbraco.Core/Services/IRelationService.cs | 18 ++++
 src/Umbraco.Core/Services/RelationService.cs  | 86 +++++++++++--------
 2 files changed, 67 insertions(+), 37 deletions(-)

diff --git a/src/Umbraco.Core/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs
index 5fb1f3c8cc..6006a6b7c3 100644
--- a/src/Umbraco.Core/Services/IRelationService.cs
+++ b/src/Umbraco.Core/Services/IRelationService.cs
@@ -189,6 +189,15 @@ namespace Umbraco.Core.Services
             IEnumerable relations,
             bool loadBaseType = false);
 
+        /// 
+        /// Relates two objects by their entity Ids.
+        /// 
+        /// Id of the parent
+        /// Id of the child
+        /// The type of relation to create
+        /// The created 
+        IRelation Relate(int parentId, int childId, IRelationType relationType);
+
         /// 
         /// Relates two objects that are based on the  interface.
         /// 
@@ -198,6 +207,15 @@ namespace Umbraco.Core.Services
         /// The created 
         IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType);
 
+        /// 
+        /// Relates two objects by their entity Ids.
+        /// 
+        /// Id of the parent
+        /// Id of the child
+        /// Alias of the type of relation to create
+        /// The created 
+        IRelation Relate(int parentId, int childId, string relationTypeAlias);
+
         /// 
         /// Relates two objects that are based on the  interface.
         /// 
diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs
index b05e8e2391..ccf86d425d 100644
--- a/src/Umbraco.Core/Services/RelationService.cs
+++ b/src/Umbraco.Core/Services/RelationService.cs
@@ -394,6 +394,39 @@ namespace Umbraco.Core.Services
             }
         }
 
+        /// 
+        /// Relates two objects by their entity Ids.
+        /// 
+        /// Id of the parent
+        /// Id of the child
+        /// The type of relation to create
+        /// The created 
+        public IRelation Relate(int parentId, int childId, IRelationType relationType)
+        {
+            // Ensure that the RelationType has an indentity before using it to relate two entities
+            if (relationType.HasIdentity == false)
+                Save(relationType);
+
+            var relation = new Relation(parentId, childId, relationType);
+
+            using (var uow = UowProvider.GetUnitOfWork())
+            {
+                var saveEventArgs = new SaveEventArgs(relation);
+                if (uow.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
+                {
+                    uow.Commit();
+                    return relation;
+                }
+
+                var repository = RepositoryFactory.CreateRelationRepository(uow);
+                repository.AddOrUpdate(relation);
+                uow.Commit();
+                saveEventArgs.CanCancel = false;
+                uow.Events.Dispatch(SavedRelation, this, saveEventArgs);
+                return relation;
+            }
+        }
+
         /// 
         /// Relates two objects that are based on the  interface.
         /// 
@@ -403,28 +436,23 @@ namespace Umbraco.Core.Services
         /// The created 
         public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType)
         {
-            //Ensure that the RelationType has an indentity before using it to relate two entities
-            if (relationType.HasIdentity == false)
-                Save(relationType);
+            return Relate(parent.Id, child.Id, relationType);
+        }
 
-            var relation = new Relation(parent.Id, child.Id, relationType);
+        /// 
+        /// Relates two objects by their entity Ids.
+        /// 
+        /// Id of the parent
+        /// Id of the child
+        /// Alias of the type of relation to create
+        /// The created 
+        public IRelation Relate(int parentId, int childId, string relationTypeAlias)
+        {
+            var relationType = GetRelationTypeByAlias(relationTypeAlias);
+            if (relationType == null || string.IsNullOrEmpty(relationType.Alias))
+                throw new ArgumentNullException(string.Format("No RelationType with Alias '{0}' exists.", relationTypeAlias));
 
-            using (var uow = UowProvider.GetUnitOfWork())
-            {
-                var repository = RepositoryFactory.CreateRelationRepository(uow);
-                var saveEventArgs = new SaveEventArgs(relation);
-                if (uow.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
-                {
-                    uow.Commit();
-                    return relation;
-                }
-
-                repository.AddOrUpdate(relation);
-                uow.Commit();
-                saveEventArgs.CanCancel = false;
-                uow.Events.Dispatch(SavedRelation, this, saveEventArgs);
-                return relation;
-            }
+            return Relate(parentId, childId, relationType);
         }
 
         /// 
@@ -440,23 +468,7 @@ namespace Umbraco.Core.Services
             if (relationType == null || string.IsNullOrEmpty(relationType.Alias))
                 throw new ArgumentNullException(string.Format("No RelationType with Alias '{0}' exists.", relationTypeAlias));
 
-            var relation = new Relation(parent.Id, child.Id, relationType);
-
-            using (var uow = UowProvider.GetUnitOfWork())
-            {
-                var saveEventArgs = new SaveEventArgs(relation);
-                if (uow.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
-                {
-                    uow.Commit();
-                    return relation;
-                }
-                var repository = RepositoryFactory.CreateRelationRepository(uow);
-                repository.AddOrUpdate(relation);
-                uow.Commit();
-                saveEventArgs.CanCancel = false;
-                uow.Events.Dispatch(SavedRelation, this, saveEventArgs);
-                return relation;
-            }
+            return Relate(parent.Id, child.Id, relationType);
         }
 
         /// 

From 639ef53c88d0bdb2e46f175e264fb379ffd39c18 Mon Sep 17 00:00:00 2001
From: leekelleher 
Date: Thu, 5 Apr 2018 17:56:10 +0100
Subject: [PATCH 034/342] U4-9088 - Macro Container Property Type returned
 incorrect object-type

The `PropertyValueType` attribute marks this converter as returning an `IHtmlString`, but `ConvertDataToSource` returns a `string` type.
This patch corrects the return type to be a `HtmlString`.
---
 .../ValueConverters/MacroContainerValueConverter.cs         | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs
index 436d131bcd..74ad2090ff 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
     /// Ensures macro syntax is parsed for the macro container which will work when getting the field
     /// values in any way (i.e. dynamically, using Field(), or IPublishedContent)
     /// 
-    [PropertyValueType(typeof (IHtmlString))]
+    [PropertyValueType(typeof(IHtmlString))]
     [PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Request)]
     [DefaultPropertyValueConverter]
     public class MacroContainerValueConverter : PropertyValueConverterBase
@@ -64,7 +64,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
             // ensure string is parsed for macros and macros are executed correctly
             sourceString = RenderMacros(sourceString, preview);
 
-            return sourceString;
+            return new HtmlString(sourceString);
         }
     }
-}
\ No newline at end of file
+}

From 6abd778fd9befb90c60f145df928b9115bb36435 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Vanbrabandt?= 
Date: Sat, 7 Apr 2018 13:50:10 +0200
Subject: [PATCH 035/342] Fix wrong closing i tag and wrong title for edit
 button

---
 .../src/views/components/umb-node-preview.html                | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html
index d04de47757..eee7b84360 100644
--- a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html
+++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html
@@ -13,9 +13,9 @@
         
From 78121dd0f2e8d93daba1f68235f1d2e7df37e1dd Mon Sep 17 00:00:00 2001 From: AndyButland Date: Mon, 9 Apr 2018 08:52:54 +0200 Subject: [PATCH 036/342] Re-send of invitation from user profile. Allow delete of users that haven't logged in from user profile. Prevent disable/enable and change password options from user profile for invited users. --- .../src/common/resources/users.resource.js | 33 ++++++++++++- .../src/views/users/user.controller.js | 46 ++++++++++++++++++- .../src/views/users/views/user/details.html | 34 ++++++++++++-- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 7 +++ .../umbraco/config/lang/en_us.xml | 7 +++ src/Umbraco.Web/Editors/UsersController.cs | 32 +++++++++++++ 6 files changed, 154 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js index 72564398c0..0fd308ffd0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js @@ -280,7 +280,7 @@ * }); * * - * @param {Array} id user id. + * @param {Int} userId user id. * @returns {Promise} resourcePromise object containing the user. * */ @@ -406,6 +406,36 @@ "Failed to save user"); } + /** + * @ngdoc method + * @name umbraco.resources.usersResource#deleteNonLoggedInUser + * @methodOf umbraco.resources.usersResource + * + * @description + * Deletes a user that hasn't already logged in (and hence we know has made no content updates that would create related records) + * + * ##usage + *
+          * usersResource.deleteNonLoggedInUser(1)
+          *    .then(function() {
+          *        alert("user was deleted");
+          *    });
+          * 
+ * + * @param {Int} userId user id. + * @returns {Promise} resourcePromise object. + * + */ + function deleteNonLoggedInUser(userId) { + + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "userApiBaseUrl", + "PostDeleteNonLoggedInUser", { id: userId })), + 'Failed to delete the user ' + userId); + } + var resource = { disableUsers: disableUsers, @@ -417,6 +447,7 @@ createUser: createUser, inviteUser: inviteUser, saveUser: saveUser, + deleteNonLoggedInUser: deleteNonLoggedInUser, clearAvatar: clearAvatar }; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index 0f3519c7ba..a1b30e38b7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -31,6 +31,8 @@ vm.disableUser = disableUser; vm.enableUser = enableUser; vm.unlockUser = unlockUser; + vm.resendInvite = resendInvite; + vm.deleteNonLoggedInUser = deleteNonLoggedInUser; vm.changeAvatar = changeAvatar; vm.clearAvatar = clearAvatar; vm.save = save; @@ -49,7 +51,9 @@ "sections_users", "content_contentRoot", "media_mediaRoot", - "user_noStartNodes" + "user_noStartNodes", + "user_defaultInvitationMessage", + "user_deleteUserConfirmation" ]; localizationService.localizeMany(labelKeys).then(function (values) { @@ -61,6 +65,8 @@ vm.labels.contentRoot = values[5]; vm.labels.mediaRoot = values[6]; vm.labels.noStartNodes = values[7]; + vm.labels.defaultInvitationMessage = values[8]; + vm.labels.deleteUserConfirmation = values[9]; }); // get user @@ -350,6 +356,44 @@ }); } + function resendInvite() { + vm.resendInviteButtonState = "busy"; + + if (vm.resendInviteMessage) { + vm.user.message = vm.resendInviteMessage; + } + else { + vm.user.message = vm.labels.defaultInvitationMessage; + } + + usersResource.inviteUser(vm.user).then(function (data) { + vm.resendInviteButtonState = "success"; + vm.resendInviteMessage = ""; + formHelper.showNotifications(data); + }, function (error) { + vm.resendInviteButtonState = "error"; + formHelper.showNotifications(error.data); + }); + } + + function deleteNonLoggedInUser() { + vm.deleteNotLoggedInUserButtonState = "busy"; + + var confirmationMessage = vm.labels.deleteUserConfirmation; + if (!confirm(confirmationMessage)) { + vm.deleteNotLoggedInUserButtonState = "danger"; + return; + } + + usersResource.deleteNonLoggedInUser(vm.user.id).then(function (data) { + formHelper.showNotifications(data); + goToPage(vm.breadcrumbs[0]); + }, function (error) { + vm.deleteNotLoggedInUserButtonState = "error"; + formHelper.showNotifications(error.data); + }); + } + function clearAvatar() { // get user usersResource.clearAvatar(vm.user.id).then(function (data) { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index 67f204aa2f..8b9be7acdc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -239,7 +239,7 @@
-
- + + +
+
+ + + +
+
Last login: @@ -358,4 +386,4 @@
-
\ No newline at end of file +
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 0137e5d0e0..bdc2b0cd6f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -46,6 +46,7 @@ Set permissions Unlock Create Content Template + Resend Invitation Content @@ -1419,6 +1420,9 @@ To manage your website, simply open the Umbraco back office and start adding con An error occurred while unlocking the user Member was exported to file An error occurred while exporting the member + User %0% was deleted + Invite user + Invitation has been re-sent to %0% Uses CSS syntax ex: h1, .redHeader, .blueTex @@ -1996,6 +2000,9 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> Invite + Resending invitation... + Delete User + Are you sure you wish to delete this user account? 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 1ff807b79b..e1db8003f6 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -46,6 +46,7 @@ Set permissions Unlock Create Content Template + Resend Invitation Content @@ -1416,6 +1417,9 @@ To manage your website, simply open the Umbraco back office and start adding con An error occurred while unlocking the user Member was exported to file An error occurred while exporting the member + User %0% was deleted + Invite user + Invitation has been re-sent to %0% Uses CSS syntax ex: h1, .redHeader, .blueTex @@ -1988,6 +1992,9 @@ To manage your website, simply open the Umbraco back office and start adding con ]]> Invite + Resending invitation... + Delete User + Are you sure you wish to delete this user account? Validation diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index f1670df346..f089c44afd 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -412,6 +412,8 @@ namespace Umbraco.Web.Editors await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message); + display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/resendInviteHeader"), Services.TextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name })); + return display; } @@ -699,6 +701,36 @@ namespace Umbraco.Web.Editors Services.TextService.Localize("speechBubbles/setUserGroupOnUsersSuccess")); } + /// + /// Deletes the non-logged in user provided id + /// + /// User Id + /// + /// Limited to users that haven't logged in to avoid issues with related records constrained + /// with a foreign key on the user Id + /// + public async Task PostDeleteNonLoggedInUser(int id) + { + var user = Services.UserService.GetUserById(id); + if (user == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + // Check user hasn't logged in. If they have they may have made content changes which will mean + // the Id is associated with audit trails, versions etc. and can't be removed. + if (user.LastLoginDate != default(DateTime)) + { + throw new HttpResponseException(HttpStatusCode.BadRequest); + } + + var userName = user.Name; + Services.UserService.Delete(user, true); + + return Request.CreateNotificationSuccessResponse( + Services.TextService.Localize("speechBubbles/deleteUserSuccess", new[] { userName })); + } + public class PagedUserResult : PagedResult { public PagedUserResult(long totalItems, long pageNumber, long pageSize) : base(totalItems, pageNumber, pageSize) From cabcb08b98781ab1cdac28ce9d1580423ef18c04 Mon Sep 17 00:00:00 2001 From: Damiaan Date: Mon, 9 Apr 2018 09:16:58 +0200 Subject: [PATCH 037/342] Typo in the docs reference Makes it hard to navigate the documentation :-) --- src/Umbraco.Core/Services/ConsentService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Services/ConsentService.cs b/src/Umbraco.Core/Services/ConsentService.cs index 674d042e1f..7f9b6b36ca 100644 --- a/src/Umbraco.Core/Services/ConsentService.cs +++ b/src/Umbraco.Core/Services/ConsentService.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { /// - /// Implements . + /// Implements . /// internal class ConsentService : ScopeRepositoryService, IConsentService { From a5aa1b4db71179dd0a71d8279bb6260a37df76cd Mon Sep 17 00:00:00 2001 From: Robert Dyson Date: Fri, 13 Apr 2018 11:18:17 +0100 Subject: [PATCH 038/342] Allow capital letters for email address entry in RegesterModel --- src/Umbraco.Web/Models/RegisterModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs index b51f09b631..2764d316b9 100644 --- a/src/Umbraco.Web/Models/RegisterModel.cs +++ b/src/Umbraco.Web/Models/RegisterModel.cs @@ -47,7 +47,7 @@ namespace Umbraco.Web.Models } [Required] - [RegularExpression(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", + [RegularExpression(@"[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?", ErrorMessage = "Please enter a valid e-mail address")] public string Email { get; set; } From e3faa054352ed8393637890cd6b38bd672db9ccf Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sun, 15 Apr 2018 19:03:38 +0200 Subject: [PATCH 039/342] Lookup member type to map this name of this to the MemberListDisplay model --- src/Umbraco.Web/Editors/MemberController.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 8ff7988a9a..7bc30d7b74 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -140,13 +140,16 @@ namespace Umbraco.Web.Editors /// public MemberListDisplay GetListNodeDisplay(string listName) { + var foundType = Services.MemberTypeService.Get(listName); + var name = foundType != null ? foundType.Name : listName; + var display = new MemberListDisplay { ContentTypeAlias = listName, - ContentTypeName = listName, + ContentTypeName = name, Id = listName, IsContainer = true, - Name = listName == Constants.Conventions.MemberTypes.AllMembersListId ? "All Members" : listName, + Name = listName == Constants.Conventions.MemberTypes.AllMembersListId ? "All Members" : name, Path = "-1," + listName, ParentId = -1 }; From 759e1f2e5a2e24aeaf65c7bf3395f3141096dcfc Mon Sep 17 00:00:00 2001 From: Dave Woestenborghs Date: Wed, 18 Apr 2018 08:27:03 +0200 Subject: [PATCH 040/342] U4-11233 make sure when a tour step is marked as intro get's displayed centered, even if element is set --- .../directives/components/application/umbtour.directive.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js index 55641b6ec7..7e981c065d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js @@ -304,6 +304,12 @@ In the following example you see how to run some custom logic before a step goes scope.elementNotFound = false; $timeout(function () { + // clear element when step as marked as intro, so it always displays in the center + if (scope.model.currentStep && scope.model.currentStep.type === "intro") { + scope.model.currentStep.element = null; + scope.model.currentStep.eventElement = null; + scope.model.currentStep.event = null; + } // if an element isn't set - show the popover in the center if(scope.model.currentStep && !scope.model.currentStep.element) { From b4926416333ab5c830836230b6059dcf745685ce Mon Sep 17 00:00:00 2001 From: mvanhelmont Date: Sun, 29 Apr 2018 08:48:49 +0200 Subject: [PATCH 041/342] U4-11275 This is a simple fix for U4-11275 so that packages also can set routePath to something like a overview page. Like the new user section when you don't want to display children. Is a copy of /:section/:tree/:method/:id --- src/Umbraco.Web.UI.Client/src/routes.js | 35 +++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 3fc4d3f78e..314b3b0c53 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -31,11 +31,11 @@ app.config(function ($routeProvider) { userService.getCurrentUser({ broadcastEvent: broadcast }).then(function (user) { //is auth, check if we allow or reject if (isRequired) { - + //This checks the current section and will force a redirect to 'content' as the default if ($route.current.params.section.toLowerCase() === "default" || $route.current.params.section.toLowerCase() === "umbraco" || $route.current.params.section === "") { $route.current.params.section = "content"; - } + } // U4-5430, Benjamin Howarth // We need to change the current route params if the user only has access to a single section @@ -141,13 +141,32 @@ app.config(function ($routeProvider) { resolve: canRoute(true) }) .when('/:section/:tree/:method', { - templateUrl: function (rp) { + //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method. + template: "
", + //This controller will execute for this route, then we replace the template dynamnically based on the current tree. + controller: function ($scope, $route, $routeParams, treeService) { - //if there is no method registered for this then show the dashboard - if (!rp.method) - return "views/common/dashboard.html"; - - return ('views/' + rp.tree + '/' + rp.method + '.html'); + if (!$routeParams.tree || !$routeParams.method) { + $scope.templateUrl = "views/common/dashboard.html"; + } + + // Here we need to figure out if this route is for a package tree and if so then we need + // to change it's convention view path to: + // /App_Plugins/{mypackage}/backoffice/{treetype}/{method}.html + + // otherwise if it is a core tree we use the core paths: + // views/{treetype}/{method}.html + + var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree); + + if (packageTreeFolder) { + $scope.templateUrl = (Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath + + "/" + packageTreeFolder + + "/backoffice/" + $routeParams.tree + "/" + $routeParams.method + ".html"); + } + else { + $scope.templateUrl = ('views/' + $routeParams.tree + '/' + $routeParams.method + '.html'); + } }, resolve: canRoute(true) }) From 9ffca1bcab1674d7b28c1ee0a09bda6749e04583 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 30 Apr 2018 09:13:34 +0200 Subject: [PATCH 042/342] Implements doc type collections --- .../common/resources/contenttype.resource.js | 12 +++- .../views/documenttypes/create.controller.js | 38 ++++++++++++ .../src/views/documenttypes/create.html | 40 ++++++++++++- .../Editors/ContentTypeController.cs | 59 ++++++++++++++++++- .../DocumentTypeCollectionDisplay.cs | 14 +++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 6 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/DocumentTypeCollectionDisplay.cs 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 8bfcdfcc5a..43c13dded4 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 @@ -260,14 +260,22 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to copy content'); }, - createContainer: function(parentId, name) { + createContainer: function (parentId, name) { return umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateContainer", { parentId: parentId, name: name })), + $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateContainer", { parentId: parentId, name: name })), 'Failed to create a folder under parent id ' + parentId); }, + createCollection: function (parentId, collectionName, itemName) { + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateCollection", { parentId: parentId, collectionName: collectionName, itemName: itemName})), + 'Failed to create collection under ' + parentId); + + }, + renameContainer: function(id, name) { return umbRequestHelper.resourcePromise( diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.controller.js index aade96c3cc..165c6e6f2a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.controller.js @@ -12,6 +12,7 @@ function DocumentTypesCreateController($scope, $location, navigationService, con allowCreateFolder: $scope.dialogOptions.currentNode.parentId === null || $scope.dialogOptions.currentNode.nodeType === "container", folderName: "", creatingFolder: false, + creatingDoctypeCollection: false }; var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates; @@ -24,6 +25,10 @@ function DocumentTypesCreateController($scope, $location, navigationService, con $scope.model.creatingFolder = true; }; + $scope.showCreateDocTypeCollection = function () { + $scope.model.creatingDoctypeCollection = true; + }; + $scope.createContainer = function () { if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder })) { @@ -61,6 +66,39 @@ function DocumentTypesCreateController($scope, $location, navigationService, con } }; + $scope.createCollection = function () { + + if (formHelper.submitForm({ scope: $scope, formCtrl: this.createDoctypeCollectionForm, statusMessage: "Creating Doctype Collection..." })) { + + contentTypeResource.createCollection(node.id, $scope.model.collectionName, $scope.model.collectionItemName).then(function (collectionData) { + + navigationService.hideMenu(); + $location.search('create', null); + $location.search('notemplate', null); + + formHelper.resetForm({ + scope: $scope + }); + + var section = appState.getSectionState("currentSection"); + + // redirect to the item id + $location.path("/settings/documenttypes/edit/" + collectionData.ItemId); + + }, function (err) { + + $scope.error = err; + + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + } + }; + // Disabling logic for creating document type with template if disableTemplates is set to true if (!disableTemplates) { $scope.createDocType = function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.html index e5043be785..549ad0452b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/create.html @@ -1,6 +1,6 @@