From d99d5a0b36ebbcbfb53077bcfce9c73a98901bef Mon Sep 17 00:00:00 2001 From: Patrick Scott Date: Thu, 3 Nov 2016 14:30:57 +0000 Subject: [PATCH 01/61] Issue U4-5572 convert file name to friendly media item name --- src/Umbraco.Core/StringExtensions.cs | 30 +++++++++++++++++++--- src/Umbraco.Web/Editors/MediaController.cs | 10 +------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 036b5b979f..02f2193e20 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -105,8 +105,8 @@ namespace Umbraco.Core //if the resolution was success, return it, otherwise just return the path, we've detected // it's a path but maybe it's relative and resolution has failed, etc... in which case we're just // returning what was given to us. - return resolvedUrlResult.Success - ? resolvedUrlResult + return resolvedUrlResult.Success + ? resolvedUrlResult : Attempt.Succeed(input); } } @@ -128,7 +128,7 @@ namespace Umbraco.Core } internal static readonly Regex Whitespace = new Regex(@"\s+", RegexOptions.Compiled); - internal static readonly string[] JsonEmpties = new [] { "[]", "{}" }; + internal static readonly string[] JsonEmpties = new[] { "[]", "{}" }; internal static bool DetectIsEmptyJson(this string input) { return JsonEmpties.Contains(Whitespace.Replace(input, string.Empty)); @@ -1470,5 +1470,29 @@ namespace Umbraco.Core byte[] hash = md5.ComputeHash(myStringBytes); return new Guid(hash); } + + + /// + /// Converts a file name to a friendly name for a content item + /// + /// + /// + public static string friendlyNameFromFilename(this string fileName) + { + // strip the file extension + fileName = StripFileExtension(fileName); + + // underscores and dashes to spaces + fileName = ReplaceMany(fileName, new char[] { '_', '-' }, ' '); + + // any other conversions ? + + // Pascalcase (to be done last) + fileName = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(fileName); + + + return fileName; + } + } } diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 08140f9c66..7f6c9f2580 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -534,15 +534,7 @@ namespace Umbraco.Web.Editors if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext)) mediaType = Constants.Conventions.MediaTypes.Image; - //TODO: make the media item name "nice" since file names could be pretty ugly, we have - // string extensions to do much of this but we'll need: - // * Pascalcase the name (use string extensions) - // * strip the file extension - // * underscores to spaces - // * probably remove 'ugly' characters - let's discuss - // All of this logic should exist in a string extensions method and be unit tested - // http://issues.umbraco.org/issue/U4-5572 - var mediaItemName = fileName; + var mediaItemName = fileName.friendlyNameFromFilename(); var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, Security.CurrentUser.Id); From 3b231f5138e470fda9dfa3f7a48f231129a3164b Mon Sep 17 00:00:00 2001 From: Eyescream Date: Fri, 28 Jul 2017 15:49:07 +0200 Subject: [PATCH 02/61] Update fileupload.controller.js Uploading a file with a comma in it resulted in an error (because server side there was a split on the comma character). The comma later was replaced with a dash, so it's safe to replace it before sending it to the server. --- .../views/propertyeditors/fileupload/fileupload.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js index 3c8170e54b..e2112bdc6a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js @@ -118,7 +118,7 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag for (var i = 0; i < args.files.length; i++) { //save the file object to the scope's files collection $scope.files.push({ alias: $scope.model.alias, file: args.files[i] }); - newVal += args.files[i].name + ","; + newVal += args.files[i].name.replace(',','-') + ","; } //this is required to re-validate From a8ea535bb2ef9d24ccd28edbd44b58c28909c75e Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Mon, 22 Jan 2018 18:52:54 +0100 Subject: [PATCH 03/61] "Select editor" items shouldn't have a max height --- src/Umbraco.Web.UI.Client/src/less/components/card.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/card.less b/src/Umbraco.Web.UI.Client/src/less/components/card.less index bceef1767b..ecbd36e1e4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/card.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/card.less @@ -97,7 +97,6 @@ text-align: center; width: 100px; - height: 105px; box-sizing: border-box; } @@ -115,6 +114,7 @@ width: 100%; height: 100%; border-radius: 3px; + padding-bottom: 5px; } From 477761a81adf317c6f1b94d9d59d0c4ec296e698 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 22 Mar 2018 10:41:14 +0100 Subject: [PATCH 04/61] Added property editor alias to PreValueDisplayResolver warning and updated key lookup --- .../Models/Mapping/DataTypeModelMapper.cs | 2 +- .../Models/Mapping/PreValueDisplayResolver.cs | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs index 60d9eb7d3b..c2f8d8e25d 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs @@ -116,7 +116,7 @@ namespace Umbraco.Web.Models.Mapping var fields = editor.PreValueEditor.Fields.Select(Mapper.Map).ToArray(); if (defaultVals != null) { - PreValueDisplayResolver.MapPreValueValuesToPreValueFields(fields, defaultVals); + PreValueDisplayResolver.MapPreValueValuesToPreValueFields(fields, defaultVals, editor.Alias); } return fields; }); diff --git a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs index f4e7a78504..9d1331e691 100644 --- a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs @@ -18,27 +18,31 @@ namespace Umbraco.Web.Models.Mapping public PreValueDisplayResolver(IDataTypeService dataTypeService) { _dataTypeService = dataTypeService; - } - + } + /// - /// Maps pre-values in the dictionary to the values for the fields + /// Maps pre-values in the dictionary to the values for the fields. /// - /// - /// - internal static void MapPreValueValuesToPreValueFields(PreValueFieldDisplay[] fields, IDictionary preValues) + /// The fields. + /// The pre-values. + /// The editor alias. + internal static void MapPreValueValuesToPreValueFields(PreValueFieldDisplay[] fields, IDictionary preValues, string editorAlias) { - if (fields == null) throw new ArgumentNullException("fields"); - if (preValues == null) throw new ArgumentNullException("preValues"); - //now we need to wire up the pre-values values with the actual fields defined + if (fields == null) throw new ArgumentNullException(nameof(fields)); + if (preValues == null) throw new ArgumentNullException(nameof(preValues)); + + // Now we need to wire up the pre-values values with the actual fields defined foreach (var field in fields) { - var found = preValues.Any(x => x.Key.InvariantEquals(field.Key)); - if (found == false) + // If the dictionary would be constructed with StringComparer.InvariantCultureIgnoreCase, we could just use TryGetValue + var preValue = preValues.SingleOrDefault(x => x.Key.InvariantEquals(field.Key)); + if (preValue.Key == null) { - LogHelper.Warn("Could not find persisted pre-value for field " + field.Key); + LogHelper.Warn("Could not find persisted pre-value for field {0} on property editor {1}", () => field.Key, () => editorAlias); continue; } - field.Value = preValues.Single(x => x.Key.InvariantEquals(field.Key)).Value; + + field.Value = preValue.Value; } } @@ -54,20 +58,20 @@ namespace Umbraco.Web.Models.Mapping } } - //set up the defaults + // Set up the defaults var dataTypeService = _dataTypeService; var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(source.Id); IDictionary dictionaryVals = preVals.FormatAsDictionary().ToDictionary(x => x.Key, x => (object)x.Value); var result = Enumerable.Empty().ToArray(); - //if we have a prop editor, then format the pre-values based on it and create it's fields. + // If we have a prop editor, then format the pre-values based on it and create it's fields if (propEd != null) { result = propEd.PreValueEditor.Fields.Select(Mapper.Map).ToArray(); dictionaryVals = propEd.PreValueEditor.ConvertDbToEditor(propEd.DefaultPreValues, preVals); } - MapPreValueValuesToPreValueFields(result, dictionaryVals); + MapPreValueValuesToPreValueFields(result, dictionaryVals, source.PropertyEditorAlias); return result; } From 8ddda5868e5a29bf13ab2ac20e5bf6750e5d6337 Mon Sep 17 00:00:00 2001 From: Claus Date: Thu, 5 Apr 2018 10:41:08 +0200 Subject: [PATCH 05/61] fixes U4-10816 Umbraco V7.7.1 - Media - File upload taking full path --- src/Umbraco.Web/Editors/MediaController.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index e22c83cbeb..d2f0d060e0 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -466,6 +466,17 @@ namespace Umbraco.Web.Editors [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) { + //Recent versions of IE/Edge may send in the full clientside file path instead of just the file name. + //To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all + //uploaded files to being *only* the actual file name (as it should be). + if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) + { + foreach (var file in contentItem.UploadedFiles) + { + file.FileName = Path.GetFileName(file.FileName); + } + } + //If we've reached here it means: // * Our model has been bound // * and validated From 08bf48b8b7bbf9b0cad1ac590bc9d46fe5fd6e14 Mon Sep 17 00:00:00 2001 From: Claus Date: Thu, 5 Apr 2018 11:04:56 +0200 Subject: [PATCH 06/61] adding the fix for content controller - to support when uploads are done via content. --- src/Umbraco.Web/Editors/ContentController.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index be78638a9e..3ef3b8a40b 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -543,6 +544,17 @@ namespace Umbraco.Web.Editors private ContentItemDisplay PostSaveInternal(ContentItemSave contentItem, Func> saveMethod) { + //Recent versions of IE/Edge may send in the full clientside file path instead of just the file name. + //To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all + //uploaded files to being *only* the actual file name (as it should be). + if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) + { + foreach (var file in contentItem.UploadedFiles) + { + file.FileName = Path.GetFileName(file.FileName); + } + } + //If we've reached here it means: // * Our model has been bound // * and validated From 0cd3bb38ed00ce9ebcfb1b9396f199bb7d7107f0 Mon Sep 17 00:00:00 2001 From: RaduOrleanu Date: Fri, 17 Aug 2018 15:07:57 +0200 Subject: [PATCH 07/61] Found additional usages for (var web = new HttpClient()) and replaced with private static readonly HttpClient HttpClient = new HttpClient(); --- src/Umbraco.Web/Editors/HelpController.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/Editors/HelpController.cs b/src/Umbraco.Web/Editors/HelpController.cs index 50e6547e36..8f981d9c88 100644 --- a/src/Umbraco.Web/Editors/HelpController.cs +++ b/src/Umbraco.Web/Editors/HelpController.cs @@ -7,20 +7,18 @@ using System.Threading.Tasks; namespace Umbraco.Web.Editors { public class HelpController : UmbracoAuthorizedJsonController - { + { + private static readonly HttpClient HttpClient = new HttpClient(); public async Task> GetContextHelpForPage(string section, string tree, string baseUrl = "https://our.umbraco.com") { var url = string.Format(baseUrl + "/Umbraco/Documentation/Lessons/GetContextHelpDocs?sectionAlias={0}&treeAlias={1}", section, tree); - using (var web = new HttpClient()) - { - //fetch dashboard json and parse to JObject - var json = await web.GetStringAsync(url); - var result = JsonConvert.DeserializeObject>(json); - if (result != null) - return result; + //fetch dashboard json and parse to JObject + var json = await HttpClient.GetStringAsync(url); + var result = JsonConvert.DeserializeObject>(json); + if (result != null) + return result; - return new List(); - } + return new List(); } } From 051b595f827d20d4563a4ecbd80da0bbe4e34635 Mon Sep 17 00:00:00 2001 From: RaduOrleanu Date: Fri, 17 Aug 2018 15:18:08 +0200 Subject: [PATCH 08/61] Found additional usages for (var web = new HttpClient()) and replaced with private static readonly HttpClient HttpClient = new HttpClient(); --- src/Umbraco.Core/Services/PackagingService.cs | 7 ++-- .../Editors/DashboardController.cs | 35 ++++++++-------- src/Umbraco.Web/Install/InstallHelper.cs | 6 +-- src/Umbraco.Web/Scheduling/ScheduledTasks.cs | 40 +++++++++---------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index dcef982992..3e1a09fd6c 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -49,7 +49,7 @@ namespace Umbraco.Core.Services private Dictionary _importedContentTypes; private IPackageInstallation _packageInstallation; private readonly IUserService _userService; - + private static readonly HttpClient HttpClient = new HttpClient(); public PackagingService( ILogger logger, @@ -89,7 +89,6 @@ namespace Umbraco.Core.Services /// public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId) { - using (var httpClient = new HttpClient()) using (var uow = _uowProvider.GetUnitOfWork()) { //includeHidden = true because we don't care if it's hidden we want to get the file regardless @@ -97,7 +96,7 @@ namespace Umbraco.Core.Services byte[] bytes; try { - bytes = httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); + bytes = HttpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); } catch (HttpRequestException ex) { @@ -1746,7 +1745,7 @@ namespace Umbraco.Core.Services internal InstallationSummary InstallPackage(string packageFilePath, int userId = 0, bool raiseEvents = false) { - var metaData = GetPackageMetaData(packageFilePath); + var metaData = GetPackageMetaData(packageFilePath); if (raiseEvents) { diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 55286d99b4..19fbb2e59e 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -27,6 +27,8 @@ namespace Umbraco.Web.Editors [WebApi.UmbracoAuthorize] public class DashboardController : UmbracoApiController { + //we have just one instance of HttpClient shared for the entire application + private static readonly HttpClient HttpClient = new HttpClient(); //we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side [ValidateAngularAntiForgeryToken] public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/") @@ -54,13 +56,10 @@ namespace Umbraco.Web.Editors //content is null, go get it try { - using (var web = new HttpClient()) - { - //fetch dashboard json and parse to JObject - var json = await web.GetStringAsync(url); - content = JObject.Parse(json); - result = content; - } + //fetch dashboard json and parse to JObject + var json = await HttpClient.GetStringAsync(url); + content = JObject.Parse(json); + result = content; ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); } @@ -93,17 +92,17 @@ namespace Umbraco.Web.Editors //content is null, go get it try { - using (var web = new HttpClient()) - { - //fetch remote css - content = await web.GetStringAsync(url); + // using (var web = new HttpClient()) + // { + //fetch remote css + content = await HttpClient.GetStringAsync(url); - //can't use content directly, modified closure problem - result = content; + //can't use content directly, modified closure problem + result = content; - //save server content for 30 mins - ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); - } + //save server content for 30 mins + ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); + // } } catch (HttpRequestException ex) { @@ -119,12 +118,12 @@ namespace Umbraco.Web.Editors Content = new StringContent(result, Encoding.UTF8, "text/css") }; } - + [ValidateAngularAntiForgeryToken] public IEnumerable> GetDashboard(string section) { var dashboardHelper = new DashboardHelper(Services.SectionService); - return dashboardHelper.GetDashboard(section, Security.CurrentUser); + return dashboardHelper.GetDashboard(section, Security.CurrentUser); } } } diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 29cfe76ca2..5f3ca61b43 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -23,6 +23,7 @@ namespace Umbraco.Web.Install { internal class InstallHelper { + private static readonly HttpClient HttpClient = new HttpClient(); private readonly UmbracoContext _umbContext; private InstallationType? _installationType; @@ -199,8 +200,7 @@ namespace Umbraco.Web.Install UmbracoVersion.Current); using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) - using (var httpClient = new HttpClient()) - using (var response = httpClient.SendAsync(request).Result) + using (var response = HttpClient.SendAsync(request).Result) { packages = response.Content.ReadAsAsync>().Result.ToList(); } @@ -213,4 +213,4 @@ namespace Umbraco.Web.Install return packages; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index ad7fef5fd9..052142bd81 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -17,11 +17,12 @@ namespace Umbraco.Web.Scheduling internal class ScheduledTasks : RecurringTaskBase { + private static readonly HttpClient HttpClient = new HttpClient(); private readonly ApplicationContext _appContext; private readonly IUmbracoSettingsSection _settings; private static readonly Hashtable ScheduledTaskTimes = new Hashtable(); - public ScheduledTasks(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, + public ScheduledTasks(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, ApplicationContext appContext, IUmbracoSettingsSection settings) : base(runner, delayMilliseconds, periodMilliseconds) { @@ -61,28 +62,25 @@ namespace Umbraco.Web.Scheduling private async Task GetTaskByHttpAync(string url, CancellationToken token) { - using (var wc = new HttpClient()) + if (Uri.TryCreate(_appContext.UmbracoApplicationUrl, UriKind.Absolute, out var baseUri)) { - if (Uri.TryCreate(_appContext.UmbracoApplicationUrl, UriKind.Absolute, out var baseUri)) - { - wc.BaseAddress = baseUri; - } - var request = new HttpRequestMessage(HttpMethod.Get, url); - - //TODO: pass custom the authorization header, currently these aren't really secured! - //request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); - - try - { - var result = await wc.SendAsync(request, token).ConfigureAwait(false); // ConfigureAwait(false) is recommended? http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html - return result.StatusCode == HttpStatusCode.OK; - } - catch (Exception ex) - { - LogHelper.Error("An error occurred calling web task for url: " + url, ex); - } - return false; + HttpClient.BaseAddress = baseUri; } + var request = new HttpRequestMessage(HttpMethod.Get, url); + + //TODO: pass custom the authorization header, currently these aren't really secured! + //request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); + + try + { + var result = await HttpClient.SendAsync(request, token).ConfigureAwait(false); // ConfigureAwait(false) is recommended? http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html + return result.StatusCode == HttpStatusCode.OK; + } + catch (Exception ex) + { + LogHelper.Error("An error occurred calling web task for url: " + url, ex); + } + return false; } public override async Task PerformRunAsync(CancellationToken token) From 03e642fced5a62e6ba3b0fff06bf68e4e1bc2ca8 Mon Sep 17 00:00:00 2001 From: RaduOrleanu Date: Fri, 17 Aug 2018 15:21:33 +0200 Subject: [PATCH 09/61] Found additional usages for (var web = new HttpClient()) and replaced with private static readonly HttpClient HttpClient = new HttpClient(); --- src/Umbraco.Web/Editors/DashboardController.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index 19fbb2e59e..b9fcbf66e7 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -92,8 +92,6 @@ namespace Umbraco.Web.Editors //content is null, go get it try { - // using (var web = new HttpClient()) - // { //fetch remote css content = await HttpClient.GetStringAsync(url); @@ -102,7 +100,6 @@ namespace Umbraco.Web.Editors //save server content for 30 mins ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem(key, () => result, new TimeSpan(0, 30, 0)); - // } } catch (HttpRequestException ex) { From 07e5161988b8b00c71bca2c92bbee98bb8ed61fa Mon Sep 17 00:00:00 2001 From: RaduOrleanu Date: Sat, 18 Aug 2018 20:21:24 +0200 Subject: [PATCH 10/61] U4-11570 - changed WebClient() from created inside of using to check if null and then creating it --- .../Editors/CanvasDesignerController.cs | 15 ++++++++------- .../Install/InstallSteps/NewInstallStep.cs | 3 ++- .../EmbedProviders/AbstractOEmbedProvider.cs | 17 +++++++++-------- .../umbraco.presentation/keepAliveService.cs | 12 ++++++------ .../umbraco/dashboard/FeedProxy.aspx.cs | 16 ++++++++++------ .../businesslogic/Packager/Installer.cs | 3 ++- 6 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web/Editors/CanvasDesignerController.cs b/src/Umbraco.Web/Editors/CanvasDesignerController.cs index 722167d18a..8284bf0b12 100644 --- a/src/Umbraco.Web/Editors/CanvasDesignerController.cs +++ b/src/Umbraco.Web/Editors/CanvasDesignerController.cs @@ -20,9 +20,10 @@ using Umbraco.Core.Services; namespace Umbraco.Web.Editors { - + public class CanvasDesignerController : UmbracoApiController { + private static WebClient _webClient; [HttpGet] public HttpResponseMessage GetGoogleFont() @@ -35,10 +36,10 @@ namespace Umbraco.Web.Editors var googleWebFontAPIURL = string.Format("https://www.googleapis.com/webfonts/v1/webfonts?key={0}", APIKey); var response = "{}"; - using (var client = new System.Net.WebClient()) - { - response = client.DownloadString(new Uri(googleWebFontAPIURL)); - } + if (_webClient == null) + _webClient = new WebClient(); + + response = _webClient.DownloadString(new Uri(googleWebFontAPIURL)); var resp = Request.CreateResponse(); resp.Content = new StringContent(response); @@ -59,7 +60,7 @@ namespace Umbraco.Web.Editors // Prepare string parameter result string[] paramLines = paramBlock.Trim().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - IList parameters = new List(); + IList parameters = new List(); foreach (var line in paramLines) { if (!line.Contains("@import")) @@ -121,4 +122,4 @@ namespace Umbraco.Web.Editors } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index dfcb441cce..0eaf3cc663 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; +using System.Net; using System.Web; using System.Web.Security; using Umbraco.Core; @@ -80,7 +81,7 @@ namespace Umbraco.Web.Install.InstallSteps { try { - var client = new System.Net.WebClient(); + var client = new WebClient(); var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} }; client.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); } diff --git a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs index 2f8ec85075..737b04fc83 100644 --- a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs +++ b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs @@ -12,18 +12,20 @@ namespace Umbraco.Web.Media.EmbedProviders { //TODO: Make all Http calls async - public abstract class AbstractOEmbedProvider: IEmbedProvider + public abstract class AbstractOEmbedProvider : IEmbedProvider { + private static WebClient _webClient; + public virtual bool SupportsDimensions { get { return true; } } [ProviderSetting] - public string APIEndpoint{ get;set; } + public string APIEndpoint { get; set; } [ProviderSetting] - public Dictionary RequestParams{ get;set; } + public Dictionary RequestParams { get; set; } public abstract string GetMarkup(string url, int maxWidth, int maxHeight); @@ -51,10 +53,9 @@ namespace Umbraco.Web.Media.EmbedProviders public virtual string DownloadResponse(string url) { - using (var webClient = new WebClient()) - { - return webClient.DownloadString(url); - } + if (_webClient == null) + _webClient = new WebClient(); + return _webClient.DownloadString(url); } public virtual T GetJsonResponse(string url) where T : class @@ -79,4 +80,4 @@ namespace Umbraco.Web.Media.EmbedProviders } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs b/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs index abc3425f3f..192c35a233 100644 --- a/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs +++ b/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs @@ -10,8 +10,9 @@ namespace umbraco.presentation [Obsolete("This is no longer used and will be removed in future versions")] public class keepAliveService { + private static WebClient _webClient; //NOTE: sender will be the umbraco ApplicationContext - public static void PingUmbraco(object sender) + public static void PingUmbraco(object sender) { if (sender == null || !(sender is ApplicationContext)) return; @@ -21,10 +22,9 @@ namespace umbraco.presentation var url = appContext.UmbracoApplicationUrl + "/ping.aspx"; try { - using (var wc = new WebClient()) - { - wc.DownloadString(url); - } + if (_webClient == null) + _webClient = new WebClient(); + _webClient.DownloadString(url); } catch(Exception ee) { @@ -32,4 +32,4 @@ namespace umbraco.presentation } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs index b304cda0b6..488e4f06c2 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Logging; +using System.Net.Http; +using Umbraco.Core.Logging; using Umbraco.Web; namespace dashboardUtilities @@ -14,6 +15,8 @@ namespace dashboardUtilities public partial class FeedProxy : UmbracoEnsuredPage { + private static WebClient _webClient; + protected void Page_Load(object sender, EventArgs e) { try @@ -31,9 +34,10 @@ namespace dashboardUtilities && feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null && requestUri.Port == 80) { - using (var client = new WebClient()) - { - var response = client.DownloadString(requestUri); + if (_webClient == null) + _webClient = new WebClient(); + + var response = _webClient.DownloadString(requestUri); if (string.IsNullOrEmpty(response) == false) { @@ -41,7 +45,7 @@ namespace dashboardUtilities Response.ContentType = Request.CleanForXss("type") ?? MediaTypeNames.Text.Xml; Response.Write(response); } - } + } else { @@ -57,4 +61,4 @@ namespace dashboardUtilities } } } -} \ No newline at end of file +} diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index a502ef5a32..eb46e82e8b 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -14,6 +14,7 @@ using umbraco.cms.businesslogic.web; using umbraco.BusinessLogic; using System.Diagnostics; using System.IO.Compression; +using System.Net; using umbraco.cms.businesslogic.template; using umbraco.interfaces; using Umbraco.Core.Events; @@ -685,7 +686,7 @@ namespace umbraco.cms.businesslogic.packager if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - var wc = new System.Net.WebClient(); + var wc = new WebClient(); wc.DownloadFile( "http://" + PackageServer + "/fetch?package=" + Package.ToString(), From efbcce526154b32c97c21b5b7eb2a28fa9ba90b0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 21 Aug 2018 10:41:50 +1000 Subject: [PATCH 11/61] Fixes SQL generation for paged results for both UserRepository and AuditRepository --- .../Repositories/AuditRepository.cs | 37 +++----- .../Repositories/UserRepository.cs | 93 +++++++++++-------- .../Repositories/VersionableRepositoryBase.cs | 5 +- .../Repositories/AuditRepositoryTest.cs | 90 +++++++++++++++--- .../Repositories/UserRepositoryTest.cs | 69 +++++++++++++- 5 files changed, 215 insertions(+), 79 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs index f7c8b0d3cd..e602358cf6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -47,43 +48,32 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); if (query == null) query = new Query(); + var queryHasWhereClause = query.GetWhereClauses().Any(); var translatorIds = new SqlTranslator(sql, query); var translatedQuery = translatorIds.Translate(); - var customFilterWheres = customFilter != null ? customFilter.GetWhereClauses().ToArray() : null; + var customFilterWheres = customFilter?.GetWhereClauses().ToArray(); var hasCustomFilter = customFilterWheres != null && customFilterWheres.Length > 0; if (hasCustomFilter) { var filterSql = new Sql(); - var first = true; - foreach (var filterClaus in customFilterWheres) + foreach (var filterClause in customFilterWheres) { - if (first == false) - { - filterSql.Append(" AND "); - } - filterSql.Append(string.Format("({0})", filterClaus.Item1), filterClaus.Item2); - first = false; + filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2); } - translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql); + translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql, queryHasWhereClause); } if (auditTypeFilter.Length > 0) { var filterSql = new Sql(); - var first = true; - foreach (var filterClaus in auditTypeFilter) + foreach (var filterClause in auditTypeFilter) { - if (first == false || hasCustomFilter) - { - filterSql.Append(" AND "); - } - filterSql.Append("(logHeader = @logHeader)", new {logHeader = filterClaus.ToString() }); - first = false; + filterSql.Append("AND (logHeader = @logHeader)", new { logHeader = filterClause.ToString() }); } - translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql); + translatedQuery = GetFilteredSqlForPagedResults(translatedQuery, filterSql, queryHasWhereClause || hasCustomFilter); } if (orderDirection == Direction.Descending) @@ -99,7 +89,7 @@ namespace Umbraco.Core.Persistence.Repositories dto => new AuditItem(dto.Id, dto.Comment, Enum.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId)).ToArray(); //Mapping the DateStamp - for (int i = 0; i < pages.Length; i++) + for (var i = 0; i < pages.Length; i++) { pages[i].CreateDate = pagedResult.Items[i].Datestamp; } @@ -169,14 +159,17 @@ namespace Umbraco.Core.Persistence.Repositories } #endregion - private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql) + private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql, bool hasWhereClause) { Sql filteredSql; // Apply filter if (filterSql != null) { - var sqlFilter = " WHERE " + filterSql.SQL.TrimStart("AND "); + //ensure we don't append a WHERE if there is already one + var sqlFilter = hasWhereClause + ? filterSql.SQL + : " WHERE " + filterSql.SQL.TrimStart("AND "); //NOTE: this is certainly strange - NPoco handles this much better but we need to re-create the sql // instance a couple of times to get the parameter order correct, for some reason the first diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 5701cd1008..82ad1fdcbf 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Text; +using System.Text.RegularExpressions; using System.Web.Security; using Newtonsoft.Json; using Umbraco.Core.Configuration; @@ -707,10 +708,10 @@ ORDER BY colName"; - private IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, - string[] includeUserGroups = null, - string[] excludeUserGroups = null, - UserState[] userState = null, + private IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, + string[] includeUserGroups = null, + string[] excludeUserGroups = null, + UserState[] userState = null, IQuery customFilter = null) { if (string.IsNullOrWhiteSpace(orderBy)) throw new ArgumentException("Value cannot be null or whitespace.", "orderBy"); @@ -718,23 +719,23 @@ ORDER BY colName"; Sql filterSql = null; var customFilterWheres = customFilter != null ? customFilter.GetWhereClauses().ToArray() : null; - var hasCustomFilter = customFilterWheres != null && customFilterWheres.Length > 0; + var hasCustomFilter = customFilterWheres != null && customFilterWheres.Length > 0; if (hasCustomFilter - || (includeUserGroups != null && includeUserGroups.Length > 0) || (excludeUserGroups != null && excludeUserGroups.Length > 0) - || (userState != null && userState.Length > 0 && userState.Contains(UserState.All) == false)) - filterSql = new Sql(); + || (includeUserGroups != null && includeUserGroups.Length > 0) || (excludeUserGroups != null && excludeUserGroups.Length > 0) + || (userState != null && userState.Length > 0 && userState.Contains(UserState.All) == false)) + filterSql = new Sql(); if (hasCustomFilter) { foreach (var filterClause in customFilterWheres) { - filterSql.Append(string.Format("AND ({0})", filterClause.Item1), filterClause.Item2); + filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2); } - } + } if (includeUserGroups != null && includeUserGroups.Length > 0) { - var subQuery = @"AND (umbracoUser.id IN (SELECT DISTINCT umbracoUser.id + const string subQuery = @"AND (umbracoUser.id IN (SELECT DISTINCT umbracoUser.id FROM umbracoUser INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId @@ -744,21 +745,21 @@ ORDER BY colName"; if (excludeUserGroups != null && excludeUserGroups.Length > 0) { - var subQuery = @"AND (umbracoUser.id NOT IN (SELECT DISTINCT umbracoUser.id + const string subQuery = @"AND (umbracoUser.id NOT IN (SELECT DISTINCT umbracoUser.id FROM umbracoUser INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId WHERE umbracoUserGroup.userGroupAlias IN (@userGroups)))"; filterSql.Append(subQuery, new { userGroups = excludeUserGroups }); - } + } if (userState != null && userState.Length > 0) - { + { //the "ALL" state doesn't require any filtering so we ignore that, if it exists in the list we don't do any filtering if (userState.Contains(UserState.All) == false) { var sb = new StringBuilder("("); - var appended = false; + var appended = false; if (userState.Contains(UserState.Active)) { @@ -788,67 +789,81 @@ ORDER BY colName"; filterSql.Append("AND " + sb); } - } - - // Get base query for returning IDs - var sqlBaseIds = GetBaseQuery("id"); - + } + + // Get base query for returning IDs + var sqlBaseIds = GetBaseQuery("id"); + if (query == null) query = new Query(); + var queryHasWhereClause = query.GetWhereClauses().Any(); var translatorIds = new SqlTranslator(sqlBaseIds, query); - var sqlQueryIds = translatorIds.Translate(); - - //get sorted and filtered sql + var sqlQueryIds = translatorIds.Translate(); + var sqlBaseFull = GetBaseQuery("umbracoUser.*, umbracoUserGroup.*, umbracoUserGroup2App.*, umbracoUserStartNode.*"); + var translatorFull = new SqlTranslator(sqlBaseFull, query); + + //get sorted and filtered sql var sqlNodeIdsWithSort = GetSortedSqlForPagedResults( - GetFilteredSqlForPagedResults(sqlQueryIds, filterSql), + GetFilteredSqlForPagedResults(sqlQueryIds, filterSql, queryHasWhereClause), orderDirection, orderBy); // Get page of results and total count var pagedResult = Database.Page(pageIndex + 1, pageSize, sqlNodeIdsWithSort); - totalRecords = Convert.ToInt32(pagedResult.TotalItems); - - //NOTE: We need to check the actual items returned, not the 'totalRecords', that is because if you request a page number - // that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in - // the pageResult. + totalRecords = Convert.ToInt32(pagedResult.TotalItems); + + //NOTE: We need to check the actual items returned, not the 'totalRecords', that is because if you request a page number + // that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in + // the pageResult. if (pagedResult.Items.Any()) { //Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query var args = sqlNodeIdsWithSort.Arguments; string sqlStringCount, sqlStringPage; Database.BuildPageQueries(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage); + + var sqlQueryFull = translatorFull.Translate(); - var sqlQueryFull = GetBaseQuery("umbracoUser.*, umbracoUserGroup.*, umbracoUserGroup2App.*, umbracoUserStartNode.*"); - - var fullQueryWithPagedInnerJoin = sqlQueryFull - .Append("INNER JOIN (") - //join the paged query with the paged query arguments + //We need to make this FULL query an inner join on the paged ID query + var splitQuery = sqlQueryFull.SQL.Split(new[] { "WHERE " }, StringSplitOptions.None); + var fullQueryWithPagedInnerJoin = new Sql(splitQuery[0]) + .Append("INNER JOIN (") + //join the paged query with the paged query arguments .Append(sqlStringPage, args) .Append(") temp ") .Append("ON umbracoUser.id = temp.id"); AddGroupLeftJoin(fullQueryWithPagedInnerJoin); + if (splitQuery.Length > 1) + { + //add the original where clause back with the original arguments + fullQueryWithPagedInnerJoin.Where(splitQuery[1], sqlQueryIds.Arguments); + } + //get sorted and filtered sql var fullQuery = GetSortedSqlForPagedResults( - GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, filterSql), + GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, filterSql, queryHasWhereClause), orderDirection, orderBy); var users = ConvertFromDtos(Database.Fetch(new UserGroupRelator().Map, fullQuery)) .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. return users; - } + } return Enumerable.Empty(); } - private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql) + private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql, bool hasWhereClause) { Sql filteredSql; // Apply filter if (filterSql != null) { - var sqlFilter = " WHERE " + filterSql.SQL.TrimStart("AND "); + //ensure we don't append a WHERE if there is already one + var sqlFilter = hasWhereClause + ? filterSql.SQL + : " WHERE " + filterSql.SQL.TrimStart("AND "); //NOTE: this is certainly strange - NPoco handles this much better but we need to re-create the sql // instance a couple of times to get the parameter order correct, for some reason the first @@ -915,4 +930,4 @@ ORDER BY colName"; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 883b05b2fd..aa7566a4c3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -290,6 +290,9 @@ namespace Umbraco.Core.Persistence.Repositories // Apply filter if (defaultFilter != null) { + //NOTE: It is assumed here that the `sql` already contains a WHERE clause, see UserRepository.GetFilteredSqlForPagedResults + // for an example of when it's not assumed there's already a WHERE clause + var filterResult = defaultFilter(); //NOTE: this is certainly strange - NPoco handles this much better but we need to re-create the sql @@ -993,4 +996,4 @@ ORDER BY contentNodeId, versionId, propertytypeid } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs index 04677c208b..e6f8f44019 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs @@ -66,6 +66,46 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Get_Paged_Items_By_User_Id_With_Query_And_Filter() + { + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax)) + { + for (int i = 0; i < 100; i++) + { + repo.AddOrUpdate(new AuditItem(i, string.Format("Content {0} created", i), AuditType.New, 0)); + repo.AddOrUpdate(new AuditItem(i, string.Format("Content {0} published", i), AuditType.Publish, 0)); + } + unitOfWork.Commit(); + } + + using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax)) + { + var query = Query.Builder.Where(x => x.UserId == 0); + + try + { + DatabaseContext.Database.EnableSqlTrace = true; + DatabaseContext.Database.EnableSqlCount(); + + var page = repo.GetPagedResultsByQuery(query, 0, 10, out var total, Direction.Descending, + new[] { AuditType.Publish }, + Query.Builder.Where(x => x.UserId > -1)); + + Assert.AreEqual(10, page.Count()); + Assert.AreEqual(100, total); + } + finally + { + DatabaseContext.Database.EnableSqlTrace = false; + DatabaseContext.Database.DisableSqlCount(); + } + } + } + + [Test] public void Get_Paged_Items_With_AuditType_Filter() { @@ -83,14 +123,24 @@ namespace Umbraco.Tests.Persistence.Repositories using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax)) { - long total; - var page = repo.GetPagedResultsByQuery(Query.Builder, 0, 9, out total, Direction.Descending, - new[] {AuditType.Publish}, null) - .ToArray(); + try + { + DatabaseContext.Database.EnableSqlTrace = true; + DatabaseContext.Database.EnableSqlCount(); - Assert.AreEqual(9, page.Length); - Assert.IsTrue(page.All(x => x.AuditType == AuditType.Publish)); - Assert.AreEqual(100, total); + var page = repo.GetPagedResultsByQuery(Query.Builder, 0, 9, out var total, Direction.Descending, + new[] { AuditType.Publish }, null) + .ToArray(); + + Assert.AreEqual(9, page.Length); + Assert.IsTrue(page.All(x => x.AuditType == AuditType.Publish)); + Assert.AreEqual(100, total); + } + finally + { + DatabaseContext.Database.EnableSqlTrace = false; + DatabaseContext.Database.DisableSqlCount(); + } } } @@ -111,15 +161,25 @@ namespace Umbraco.Tests.Persistence.Repositories using (var repo = new AuditRepository(unitOfWork, CacheHelper, Logger, SqlSyntax)) { - long total; - var page = repo.GetPagedResultsByQuery(Query.Builder, 0, 8, out total, Direction.Descending, - null, Query.Builder.Where(item => item.Comment == "Content created")) - .ToArray(); + try + { + DatabaseContext.Database.EnableSqlTrace = true; + DatabaseContext.Database.EnableSqlCount(); - Assert.AreEqual(8, page.Length); - Assert.IsTrue(page.All(x => x.Comment == "Content created")); - Assert.AreEqual(100, total); + var page = repo.GetPagedResultsByQuery(Query.Builder, 0, 8, out var total, Direction.Descending, + null, Query.Builder.Where(item => item.Comment == "Content created")) + .ToArray(); + + Assert.AreEqual(8, page.Length); + Assert.IsTrue(page.All(x => x.Comment == "Content created")); + Assert.AreEqual(100, total); + } + finally + { + DatabaseContext.Database.EnableSqlTrace = false; + DatabaseContext.Database.DisableSqlCount(); + } } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 334c4f98c2..3fa490e742 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; @@ -392,7 +393,71 @@ namespace Umbraco.Tests.Persistence.Repositories var result = repository.Count(query); // Assert - Assert.That(result, Is.GreaterThanOrEqualTo(2)); + Assert.AreEqual(2, result); + } + } + + [Test] + public void Can_Get_Paged_Results_By_Query_And_Filter_And_Groups() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + var query = Query.Builder.Where(x => x.Username == "TestUser1" || x.Username == "TestUser2"); + + try + { + DatabaseContext.Database.EnableSqlTrace = true; + DatabaseContext.Database.EnableSqlCount(); + + // Act + var result = repository.GetPagedResultsByQuery(query, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending, + excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias }, + filter: Query.Builder.Where(x => x.Id > -1)); + + // Assert + Assert.AreEqual(2, totalRecs); + } + finally + { + DatabaseContext.Database.EnableSqlTrace = false; + DatabaseContext.Database.DisableSqlCount(); + } + } + } + + [Test] + public void Can_Get_Paged_Results_With_Filter_And_Groups() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var users = CreateAndCommitMultipleUsers(repository, unitOfWork); + + try + { + DatabaseContext.Database.EnableSqlTrace = true; + DatabaseContext.Database.EnableSqlCount(); + + // Act + var result = repository.GetPagedResultsByQuery(null, 0, 10, out var totalRecs, user => user.Id, Direction.Ascending, + includeUserGroups: new[] { Constants.Security.AdminGroupAlias, Constants.Security.SensitiveDataGroupAlias }, + excludeUserGroups: new[] { Constants.Security.TranslatorGroupAlias }, + filter: Query.Builder.Where(x => x.Id == 0)); + + // Assert + Assert.AreEqual(1, totalRecs); + } + finally + { + DatabaseContext.Database.EnableSqlTrace = false; + DatabaseContext.Database.DisableSqlCount(); + } } } @@ -439,4 +504,4 @@ namespace Umbraco.Tests.Persistence.Repositories return new IUser[] { user1, user2, user3 }; } } -} \ No newline at end of file +} From 412069414b0d74bc38125b8022427d1a42a22c12 Mon Sep 17 00:00:00 2001 From: RaduOrleanu Date: Wed, 29 Aug 2018 12:39:06 +0200 Subject: [PATCH 12/61] U4-11570 - performed instantiation locally in classes where WebClient or HttpClient is used only in 1 method and added null check --- src/Umbraco.Core/Services/PackagingService.cs | 8 ++++++-- src/Umbraco.Web/Install/InstallHelper.cs | 9 ++++++--- src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs | 8 ++++++-- src/Umbraco.Web/Scheduling/ScheduledTasks.cs | 10 +++++++--- src/umbraco.cms/businesslogic/Packager/Installer.cs | 8 ++++++-- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 3e1a09fd6c..f8fd604097 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -49,7 +49,7 @@ namespace Umbraco.Core.Services private Dictionary _importedContentTypes; private IPackageInstallation _packageInstallation; private readonly IUserService _userService; - private static readonly HttpClient HttpClient = new HttpClient(); + private static HttpClient _httpClient; public PackagingService( ILogger logger, @@ -96,7 +96,11 @@ namespace Umbraco.Core.Services byte[] bytes; try { - bytes = HttpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); + if (_httpClient == null) + { + _httpClient = new HttpClient(); + } + bytes = _httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); } catch (HttpRequestException ex) { diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 5f3ca61b43..145395e235 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -23,7 +23,7 @@ namespace Umbraco.Web.Install { internal class InstallHelper { - private static readonly HttpClient HttpClient = new HttpClient(); + private static HttpClient _httpClient; private readonly UmbracoContext _umbContext; private InstallationType? _installationType; @@ -192,15 +192,18 @@ namespace Umbraco.Web.Install internal IEnumerable GetStarterKits() { + if (_httpClient == null) + { + _httpClient = new HttpClient(); + } var packages = new List(); - try { var requestUri = string.Format("https://our.umbraco.com/webapi/StarterKit/Get/?umbracoVersion={0}", UmbracoVersion.Current); using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) - using (var response = HttpClient.SendAsync(request).Result) + using (var response = _httpClient.SendAsync(request).Result) { packages = response.Content.ReadAsAsync>().Result.ToList(); } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 0eaf3cc663..4dfa793675 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -26,6 +26,7 @@ namespace Umbraco.Web.Install.InstallSteps { private readonly HttpContextBase _http; private readonly ApplicationContext _applicationContext; + private static WebClient _webClient; public NewInstallStep(HttpContextBase http, ApplicationContext applicationContext) { @@ -81,9 +82,12 @@ namespace Umbraco.Web.Install.InstallSteps { try { - var client = new WebClient(); + if (_webClient == null) + { + _webClient = new WebClient(); + } var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} }; - client.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); + _webClient.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); } catch { /* fail in silence */ } } diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index 052142bd81..a8a25fe1fd 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -17,7 +17,7 @@ namespace Umbraco.Web.Scheduling internal class ScheduledTasks : RecurringTaskBase { - private static readonly HttpClient HttpClient = new HttpClient(); + private static HttpClient _httpClient; private readonly ApplicationContext _appContext; private readonly IUmbracoSettingsSection _settings; private static readonly Hashtable ScheduledTaskTimes = new Hashtable(); @@ -62,9 +62,13 @@ namespace Umbraco.Web.Scheduling private async Task GetTaskByHttpAync(string url, CancellationToken token) { + if (_httpClient == null) + { + _httpClient = new HttpClient(); + } if (Uri.TryCreate(_appContext.UmbracoApplicationUrl, UriKind.Absolute, out var baseUri)) { - HttpClient.BaseAddress = baseUri; + _httpClient.BaseAddress = baseUri; } var request = new HttpRequestMessage(HttpMethod.Get, url); @@ -73,7 +77,7 @@ namespace Umbraco.Web.Scheduling try { - var result = await HttpClient.SendAsync(request, token).ConfigureAwait(false); // ConfigureAwait(false) is recommended? http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html + var result = await _httpClient.SendAsync(request, token).ConfigureAwait(false); // ConfigureAwait(false) is recommended? http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html return result.StatusCode == HttpStatusCode.OK; } catch (Exception ex) diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index eb46e82e8b..f3733d5e5a 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -47,6 +47,7 @@ namespace umbraco.cms.businesslogic.packager private readonly List _binaryFileErrors = new List(); private int _currentUserId = -1; + private static WebClient _webClient; public string Name { get; private set; } @@ -686,9 +687,12 @@ namespace umbraco.cms.businesslogic.packager if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - var wc = new WebClient(); + if (_webClient == null) + { + _webClient = new WebClient(); + } - wc.DownloadFile( + _webClient.DownloadFile( "http://" + PackageServer + "/fetch?package=" + Package.ToString(), IOHelper.MapPath(SystemDirectories.Packages + "/" + Package + ".umb")); From c95cc14703cf519283096416087263a8c09a5ffc Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sat, 1 Sep 2018 11:57:05 +0100 Subject: [PATCH 13/61] Make the file extension for files visible when picking a file using the media picker --- .../src/less/components/umb-media-grid.less | 13 +++++++++++++ .../src/less/property-editors.less | 19 ++++++++++++++++++- .../src/views/components/umb-media-grid.html | 6 ++++-- .../mediapicker/mediapicker.html | 5 ++++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 79e859b69a..3a9d612c4d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -33,6 +33,19 @@ background-color: @gray-10; } +.umb-media-grid__item-file-icon > span { + color: @white; + background: @gray-4; + padding: 1px 3px; + font-size: 10px; + line-height: 130%; + display: block; + margin-top: -30px; + margin-left: -10px; + /*Need to reset the opacity?*/ + opacity: 0.99; +} + .umb-media-grid__item.-selected { box-shadow: 0 2px 8px 0 rgba(0,0,0,.35); } 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 1855a3a6b0..fabe7c2291 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -363,13 +363,30 @@ ul.color-picker li { text-align: center; } -.umb-sortable-thumbnails .umb-icon-holder .icon{ +.umb-sortable-thumbnails .umb-icon-holder .icon { font-size: 40px; line-height: 50px; color: @gray-3; display: block; } +.umb-sortable-thumbnails .umb-icon-holder .file-icon > span { + color: @white; + background: @gray-4; + padding: 1px 3px; + font-size: 10px; + line-height: 130%; + display: block; + margin-top: -30px; + width: 2em; +} + +.umb-sortable-thumbnails .umb-icon-holder .file-icon + small { + display: block; + margin-top: 1em; +} + + .umb-sortable-thumbnails .umb-sortable-thumbnails__wrapper { width: 124px; height: 124px; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index 9df33e7e0e..bad1ccc1ce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -21,8 +21,10 @@ {{item.name}} - - + + + .{{item.extension}} + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index fd28929616..2efed8b3e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -17,7 +17,10 @@ - + + + .{{image.extension}} + {{image.name}} From ef3abea005989db831eee20d6aedb67ea7b6fe61 Mon Sep 17 00:00:00 2001 From: Kim Holzmann Date: Sat, 1 Sep 2018 12:57:32 +0200 Subject: [PATCH 14/61] U4-8824 Selection in media grid doesn't match when filter has value. --- .../listview/layouts/grid/grid.html | 2 +- .../listview/listview.controller.js | 27 ++++++------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html index cf1c3f6411..29dd1b870f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/grid/grid.html @@ -82,7 +82,7 @@ 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..cb42646bd5 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 @@ -213,10 +213,6 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie }, 500); - if (reload === true) { - $scope.reloadView($scope.contentId, true); - } - if (err.data && angular.isArray(err.data.notifications)) { for (var i = 0; i < err.data.notifications.length; i++) { notificationsService.showNotification(err.data.notifications[i]); @@ -250,12 +246,12 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie with simple values */ $scope.getContent = function() { - $scope.reloadView($scope.contentId, true); + $scope.reloadView($scope.contentId); } - $scope.reloadView = function (id, reloadFolders) { - + $scope.reloadView = function (id) { $scope.viewLoaded = false; + $scope.folders = []; listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); @@ -268,20 +264,13 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie if ($scope.listViewResultSet.items) { _.each($scope.listViewResultSet.items, function (e, index) { setPropertyValues(e); + if (e.contentTypeAlias === 'Folder') { + $scope.folders.push(e); + } }); } - if (reloadFolders && $scope.entityType === 'media') { - //The folders aren't loaded - we only need to do this once since we're never changing node ids - mediaResource.getChildFolders($scope.contentId) - .then(function (folders) { - $scope.folders = folders; - $scope.viewLoaded = true; - }); - - } else { - $scope.viewLoaded = true; - } + $scope.viewLoaded = true; //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last @@ -620,7 +609,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie $scope.options.allowBulkMove || $scope.options.allowBulkDelete; - $scope.reloadView($scope.contentId, true); + $scope.reloadView($scope.contentId); } function getLocalizedKey(alias) { From c6a31b30f155da2355e5eb49b6c8d249ad1f5cc5 Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sat, 1 Sep 2018 19:27:55 +0100 Subject: [PATCH 15/61] position relative to stack the item above to avoid opacity issue --- .../src/less/components/umb-media-grid.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 3a9d612c4d..21684c624b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -42,8 +42,7 @@ display: block; margin-top: -30px; margin-left: -10px; - /*Need to reset the opacity?*/ - opacity: 0.99; + position: relative; } .umb-media-grid__item.-selected { From 1437af62b36c5c302bd40863e6015ebd6744e0c5 Mon Sep 17 00:00:00 2001 From: villian Date: Mon, 3 Sep 2018 17:39:17 +0300 Subject: [PATCH 16/61] fix U4-8143 --- .../propertyeditors/contentpicker/contentpicker.controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 76d6fa77aa..c1fe21b1b5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -35,6 +35,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper return $scope.model.config.idType === "udi" ? i.udi : i.id; }); $scope.model.value = trim(currIds.join(), ","); + angularHelper.getCurrentForm($scope).$setDirty(); //Validate! if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) { @@ -185,7 +186,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper if (angular.isArray(model.selection)) { _.each(model.selection, function (item, i) { $scope.add(item); - }); + }); + angularHelper.getCurrentForm($scope).$setDirty(); } $scope.contentPickerOverlay.show = false; From cbdfb6f25f5dd60b7a78858aab1d11b3ca29c96e Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Mon, 3 Sep 2018 17:32:39 +0200 Subject: [PATCH 17/61] Hide "Created Date" field when content hasn't yet been created --- .../src/views/components/content/umb-content-node-info.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 8584b30177..62d6004a95 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -171,7 +171,7 @@ - + {{node.createDateFormatted}} by {{ node.owner.name }} From c8664a886abb2f7ef5126fab701a70d3320758ba Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Tue, 4 Sep 2018 14:27:58 +0200 Subject: [PATCH 18/61] ID property is now hidden as well when creating new content --- .../src/views/components/content/umb-content-node-info.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 62d6004a95..7e80aa2fca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -201,7 +201,7 @@ - +
{{ node.id }}
{{ node.key }}
From 5986899b5edf30023624e6d4c91c939cbc1619ee Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 6 Sep 2018 11:06:12 +0200 Subject: [PATCH 19/61] Fixes #U4-10383 Feature request - Ignore casing when getting member email and username from IPublishedContent --- src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs b/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs index 35b3d6ee62..f914c817ce 100644 --- a/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs @@ -129,11 +129,11 @@ namespace Umbraco.Web.PublishedCache public override IPublishedProperty GetProperty(string alias) { - switch (alias) + switch (alias.ToLowerInvariant()) { - case "Email": + case "email": return new PropertyResult("Email", Email, PropertyResultType.CustomProperty); - case "UserName": + case "username": return new PropertyResult("UserName", UserName, PropertyResultType.CustomProperty); } From 8ff671a7c67a59bc8621e0c19cd822e58f6d8340 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 7 Sep 2018 13:10:55 +1000 Subject: [PATCH 20/61] Changes compilation batch mode to true uses Roslyn compiler for views --- build/NuSpecs/UmbracoCms.nuspec | 1 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 15 + src/Umbraco.Web.UI/packages.config | 2 + src/Umbraco.Web.UI/web.Template.Debug.config | 2 - src/Umbraco.Web.UI/web.Template.config | 815 ++++++++++--------- 5 files changed, 429 insertions(+), 406 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index d3ab992a23..e8bf606dc3 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -20,6 +20,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c07cd295f9..e3ea93b98e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1,5 +1,7 @@  + + 9.0.30729 @@ -49,6 +51,9 @@ true true + + + bin\ @@ -158,6 +163,9 @@ ..\packages\Microsoft.CodeAnalysis.CSharp.1.0.0\lib\net45\Microsoft.CodeAnalysis.CSharp.dll + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.2\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll @@ -1085,4 +1093,11 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 8a4b77ca3a..677d5da297 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -22,7 +22,9 @@ + + diff --git a/src/Umbraco.Web.UI/web.Template.Debug.config b/src/Umbraco.Web.UI/web.Template.Debug.config index 014fe14329..741d4d192d 100644 --- a/src/Umbraco.Web.UI/web.Template.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.Debug.config @@ -318,8 +318,6 @@ - - diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index b4f046b277..10794e6aa2 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -1,456 +1,463 @@ - -
-
-
-
-
+ +
+
+
+
+
- -
-
-
-
-
- + +
+
+
+
+
+ - -
-
-
- - + +
+
+
+ + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - - - - - - - + + + + + + + + + - - - - + + + + - - + + - - + + - - - - - - + + + + + + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - + + - - + + - - + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - + + + + + + + - - - - - + - - - - - - - - - + + - - - - - - - - - - - - - + + + + + + + + + - - - - - + + + + + + + + + + + + + - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - From da88cc2d04fef1a617f364eb9635bf791b0583fd Mon Sep 17 00:00:00 2001 From: "Ali.Taheri" Date: Fri, 7 Sep 2018 11:04:35 +0100 Subject: [PATCH 21/61] temp-2939 - Removed hardcoded number of rows from textarea with the default value of 10. --- .../src/views/propertyeditors/textarea/textarea.html | 4 ++-- src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html index ffb692ced0..51938bd676 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html @@ -1,9 +1,9 @@
- + Required
{{model.count}} characters left
-
\ No newline at end of file + diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs index 659b1b3224..f4273303b4 100644 --- a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs @@ -20,6 +20,9 @@ namespace Umbraco.Web.PropertyEditors { [PreValueField("maxChars", "Maximum allowed characters", "number", Description = "If empty - no character limit")] public bool MaxChars { get; set; } + + [PreValueField("rows", "Number of rows", "number", Description = "If empty - 10 rows would be set as the default value")] + public bool Rows { get; set; } } } } From 010a2e012e32f0814609047c663b3f866d38a691 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Fri, 7 Sep 2018 13:54:20 +0100 Subject: [PATCH 22/61] Clean up multiple textstring(repeatable textstring) view - (#2934) * Clean up multiple textstring(repeatable textstring) view - Introduced some styling rules to make the view look cleaner * Removes unnecessary additional included files from csproj --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-multiple-textbox.less | 7 +++++++ src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index a391e16635..2eaabe7842 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -126,6 +126,7 @@ @import "components/umb-querybuilder.less"; @import "components/umb-pagination.less"; @import "components/umb-mini-list-view.less"; +@import "components/umb-multiple-textbox.less"; @import "components/umb-badge.less"; @import "components/umb-nested-content.less"; @import "components/umb-checkmark.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less new file mode 100644 index 0000000000..f4001d2d91 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less @@ -0,0 +1,7 @@ +.umb-multiple-textbox .textbox-wrapper{ + padding: 5px 0; +} + +.umb-multiple-textbox .textbox-wrapper:last-child { + padding: 5px 0 10px; +} diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c07cd295f9..b5e022fa8e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1085,4 +1085,4 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" - \ No newline at end of file + From e05a55c5329af18071c49c785471e8a5da3c0bdb Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 7 Sep 2018 15:20:29 +0200 Subject: [PATCH 23/61] Fixes incorrectly named method and removes extraneous spaces --- src/Umbraco.Core/StringExtensions.cs | 12 ++++++------ src/Umbraco.Web/Editors/MediaController.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 835b333813..c6b4da96eb 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1574,29 +1574,29 @@ namespace Umbraco.Core guid[left] = guid[right]; guid[right] = temp; } - - + /// /// Converts a file name to a friendly name for a content item /// /// /// - public static string friendlyNameFromFilename(this string fileName) + public static string ToFriendlyName(this string fileName) { // strip the file extension - fileName = StripFileExtension(fileName); + fileName = fileName.StripFileExtension(); // underscores and dashes to spaces - fileName = ReplaceMany(fileName, new char[] { '_', '-' }, ' '); + fileName = fileName.ReplaceMany(new[] { '_', '-' }, ' '); // any other conversions ? // Pascalcase (to be done last) fileName = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(fileName); + // Replace multiple consecutive spaces with a single space + fileName = string.Join(" ", fileName.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); return fileName; } - } } diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index eb276664e6..59693e94a2 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -717,7 +717,7 @@ namespace Umbraco.Web.Editors mediaType = result.FormData["contentTypeAlias"]; } - var mediaItemName = fileName.friendlyNameFromFilename(); + var mediaItemName = fileName.ToFriendlyName(); var f = mediaService.CreateMedia(mediaItemName, parentId, mediaType, Security.CurrentUser.Id); From b2133aace1ee18b5cfe5df669ebf8ed47a2c38dd Mon Sep 17 00:00:00 2001 From: Chris Houston Date: Sat, 8 Sep 2018 23:28:44 -0400 Subject: [PATCH 24/61] Fixing issue #2910 --- src/Umbraco.Web.UI.Client/src/less/components/umb-table.less | 2 +- src/Umbraco.Web.UI.Client/src/views/components/umb-table.html | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 7679fd3c24..2c1fc4a701 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -165,7 +165,7 @@ input.umb-table__input { } -.-content .-unpublished { +.-content :not(.with-unpublished-version).-unpublished { .umb-table__name > * { opacity: .4; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html index 8c5c7fcc4a..b5f4c39a8c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-table.html @@ -32,7 +32,8 @@ ng-class="{ '-selected':item.selected, '-published':item.published, - '-unpublished':!item.published + '-unpublished':!item.published, + 'with-unpublished-version':!item.published && item.hasPublishedVersion }" ng-click="selectItem(item, $index, $event)"> From 91ebc649d47574d05a71880c7cd59a816e30fd01 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Sun, 9 Sep 2018 16:35:48 +0200 Subject: [PATCH 25/61] Re-apply missing changes from #2446 --- .../less/components/umb-multiple-textbox.less | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less index f4001d2d91..21f59a3e2d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-multiple-textbox.less @@ -1,7 +1,34 @@ -.umb-multiple-textbox .textbox-wrapper{ - padding: 5px 0; +.umb-multiple-textbox .textbox-wrapper { + align-items: center; + margin-bottom: 15px; } -.umb-multiple-textbox .textbox-wrapper:last-child { - padding: 5px 0 10px; +.umb-multiple-textbox .textbox-wrapper .umb-editor { + margin-bottom: 0; +} + +.umb-multiple-textbox .textbox-wrapper i { + margin-right: 5px; +} + +.umb-multiple-textbox .textbox-wrapper i.handle { + margin-left: 5px; +} + +.umb-multiple-textbox .textbox-wrapper a.remove { + margin-left: 5px; + text-decoration: none; +} + +.umb-multiple-textbox .add-link { + &:extend(.umb-node-preview-add); +} + +.umb-editor-wrapper .umb-multiple-textbox .add-link { + &:extend(.umb-editor-wrapper .umb-node-preview); +} + +.umb-modal .umb-multiple-textbox .textbox-wrapper .umb-editor { + flex: 1 1 auto; + width: auto; } From 2f74720c38e734f359cdd3e9a7556860fb96801f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 10 Sep 2018 11:12:46 +1000 Subject: [PATCH 26/61] updates wording --- .../Services/ServerRegistrationService.cs | 4 ++-- src/Umbraco.Core/Sync/ServerRole.cs | 4 ++-- src/Umbraco.Tests/ApplicationUrlHelperTests.cs | 6 +++--- src/Umbraco.Web/Mvc/MasterControllerFactory.cs | 12 ++++++------ .../Routing/RedirectTrackingEventHandler.cs | 8 ++++---- src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs | 4 ++-- src/Umbraco.Web/Scheduling/KeepAlive.cs | 2 +- src/Umbraco.Web/Scheduling/LogScrubber.cs | 4 ++-- src/Umbraco.Web/Scheduling/ScheduledPublishing.cs | 4 ++-- src/Umbraco.Web/Scheduling/ScheduledTasks.cs | 2 +- src/Umbraco.Web/Scheduling/Scheduler.cs | 4 ++-- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Core/Services/ServerRegistrationService.cs b/src/Umbraco.Core/Services/ServerRegistrationService.cs index 6f311019af..cdd06d6c3e 100644 --- a/src/Umbraco.Core/Services/ServerRegistrationService.cs +++ b/src/Umbraco.Core/Services/ServerRegistrationService.cs @@ -77,7 +77,7 @@ namespace Umbraco.Core.Services regs = xr.Repository.GetAll().ToArray(); // default role is single server, but if registrations contain more - // than one active server, then role is master or slave + // than one active server, then role is master or replica _currentServerRole = regs.Count(x => x.IsActive) > 1 ? (server.IsMaster ? ServerRole.Master : ServerRole.Slave) : ServerRole.Single; @@ -175,4 +175,4 @@ namespace Umbraco.Core.Services return _currentServerRole; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Sync/ServerRole.cs b/src/Umbraco.Core/Sync/ServerRole.cs index cbc121c4bc..11f8811771 100644 --- a/src/Umbraco.Core/Sync/ServerRole.cs +++ b/src/Umbraco.Core/Sync/ServerRole.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Sync Single = 1, /// - /// In a multi-servers environment, the server is a slave server. + /// In a multi-servers environment, the server is a replica server. /// Slave = 2, @@ -25,4 +25,4 @@ namespace Umbraco.Core.Sync /// Master = 3 } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/ApplicationUrlHelperTests.cs b/src/Umbraco.Tests/ApplicationUrlHelperTests.cs index 578a569104..0eb4f53ac2 100644 --- a/src/Umbraco.Tests/ApplicationUrlHelperTests.cs +++ b/src/Umbraco.Tests/ApplicationUrlHelperTests.cs @@ -129,7 +129,7 @@ namespace Umbraco.Tests public void SetApplicationUrlFromDcSettingsSsl2() { // set from distributed call settings - // other servers are slave servers + // other servers are replica servers var settings = Mock.Of(section => section.DistributedCall == Mock.Of(callSection => callSection.Enabled == true && callSection.Servers == new IServer[] @@ -224,7 +224,7 @@ namespace Umbraco.Tests [Test] public void ServerRoleUnknown2() { - // distributed call enabled, cannot find server, assume it's an undeclared slave + // distributed call enabled, cannot find server, assume it's an undeclared replica var settings = Mock.Of(section => section.DistributedCall == Mock.Of(callSection => callSection.Enabled == true && callSection.Servers == new IServer[] @@ -301,4 +301,4 @@ namespace Umbraco.Tests Assert.AreEqual("httpx://whatever.com/umbraco", appCtx._umbracoApplicationUrl); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs index c60e9d883e..1bfadbee4b 100644 --- a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs @@ -17,11 +17,11 @@ namespace Umbraco.Web.Mvc /// internal class MasterControllerFactory : DefaultControllerFactory { - private readonly FilteredControllerFactoriesResolver _slaveFactories; + private readonly FilteredControllerFactoriesResolver _replicaFactories; public MasterControllerFactory(FilteredControllerFactoriesResolver factoryResolver) { - _slaveFactories = factoryResolver; + _replicaFactories = factoryResolver; } /// @@ -36,7 +36,7 @@ namespace Umbraco.Web.Mvc /// public override IController CreateController(RequestContext requestContext, string controllerName) { - var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); + var factory = _replicaFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); return factory != null ? factory.CreateController(requestContext, controllerName) : base.CreateController(requestContext, controllerName); @@ -53,7 +53,7 @@ namespace Umbraco.Web.Mvc /// The name of the controller. internal Type GetControllerTypeInternal(RequestContext requestContext, string controllerName) { - var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); + var factory = _replicaFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); if (factory != null) { //check to see if the factory is of type UmbracoControllerFactory which exposes the GetControllerType method so we don't have to create @@ -88,7 +88,7 @@ namespace Umbraco.Web.Mvc if (controller is Controller) { var requestContext = ((Controller)controller).ControllerContext.RequestContext; - var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); + var factory = _replicaFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); if (factory != null) { factory.ReleaseController(controller); @@ -98,4 +98,4 @@ namespace Umbraco.Web.Mvc if (!released) base.ReleaseController(controller); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs b/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs index 77577e26e2..75cce71f73 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs @@ -241,8 +241,8 @@ namespace Umbraco.Web.Routing /// private void PageCacheRefresher_CacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs cacheRefresherEventArgs) { - // only on master / single, not on slaves! - if (IsSlaveServer) return; + // only on master / single, not on replicas! + if (IsReplicaServer) return; // simply getting OldRoutes will register it in the request cache, // so whatever we do with it, try/finally it to ensure it's cleared @@ -299,8 +299,8 @@ namespace Umbraco.Web.Routing return route == null; } - // gets a value indicating whether server is 'slave' - private static bool IsSlaveServer + // gets a value indicating whether server is 'replica' + private static bool IsReplicaServer { get { diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs index c10c1c5315..d6e72f936b 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs @@ -31,7 +31,7 @@ namespace Umbraco.Web.Scheduling switch (_appContext.GetCurrentServerRole()) { case ServerRole.Slave: - LogHelper.Debug("Does not run on slave servers."); + LogHelper.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: LogHelper.Debug("Does not run on servers with unknown role."); @@ -80,4 +80,4 @@ namespace Umbraco.Web.Scheduling get { return true; } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index 0e6c2c7fb4..eb41fa4c82 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Scheduling switch (_appContext.GetCurrentServerRole()) { case ServerRole.Slave: - Logger.Debug("Does not run on slave servers."); + Logger.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: Logger.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs index 9000fc72cf..c525f537d0 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs @@ -63,7 +63,7 @@ namespace Umbraco.Web.Scheduling switch (_appContext.GetCurrentServerRole()) { case ServerRole.Slave: - LogHelper.Debug("Does not run on slave servers."); + LogHelper.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: LogHelper.Debug("Does not run on servers with unknown role."); @@ -94,4 +94,4 @@ namespace Umbraco.Web.Scheduling get { return false; } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 52cca003b7..bf82f6cb67 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -41,7 +41,7 @@ namespace Umbraco.Web.Scheduling switch (_appContext.GetCurrentServerRole()) { case ServerRole.Slave: - Logger.Debug("Does not run on slave servers."); + Logger.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: Logger.Debug("Does not run on servers with unknown role."); @@ -106,4 +106,4 @@ namespace Umbraco.Web.Scheduling get { return false; } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index ad7fef5fd9..41daf796d6 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -92,7 +92,7 @@ namespace Umbraco.Web.Scheduling switch (_appContext.GetCurrentServerRole()) { case ServerRole.Slave: - LogHelper.Debug("Does not run on slave servers."); + LogHelper.Debug("Does not run on replica servers."); return true; // DO repeat, server role can change case ServerRole.Unknown: LogHelper.Debug("Does not run on servers with unknown role."); diff --git a/src/Umbraco.Web/Scheduling/Scheduler.cs b/src/Umbraco.Web/Scheduling/Scheduler.cs index 5a8d409a8c..4a74aaa55c 100644 --- a/src/Umbraco.Web/Scheduling/Scheduler.cs +++ b/src/Umbraco.Web/Scheduling/Scheduler.cs @@ -104,13 +104,13 @@ namespace Umbraco.Web.Scheduling _keepAliveRunner.TryAdd(tasks[0]); // scheduled publishing/unpublishing - // install on all, will only run on non-slaves servers + // install on all, will only run on non-replica servers _publishingRunner.TryAdd(tasks[1]); _tasksRunner.TryAdd(tasks[2]); // log scrubbing - // install on all, will only run on non-slaves servers + // install on all, will only run on non-replica servers _scrubberRunner.TryAdd(tasks[3]); if (healthCheckConfig.NotificationSettings.Enabled) From fbbc89b477effbc6487d775e170c6ebc7578b736 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Tue, 11 Sep 2018 16:29:21 +0100 Subject: [PATCH 27/61] Removed the anchor tag around notification --- .../src/views/components/notifications/umb-notifications.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html b/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html index 9962e2dd84..6c74014c7e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html @@ -11,10 +11,8 @@ From 9e3cf00cc0e9e02ea7a43b36a30e25f9a6c07a1b Mon Sep 17 00:00:00 2001 From: Paul Seal Date: Wed, 12 Sep 2018 13:32:10 +0100 Subject: [PATCH 28/61] update the release notes url --- build/NuSpecs/tools/Readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/NuSpecs/tools/Readme.txt b/build/NuSpecs/tools/Readme.txt index aa648caaaa..100e29ab7f 100644 --- a/build/NuSpecs/tools/Readme.txt +++ b/build/NuSpecs/tools/Readme.txt @@ -21,6 +21,6 @@ The following items will now be automatically included when creating a deploy pa system: umbraco, umbraco_client, config\splashes and global.asax. Please read the release notes on our.umbraco.com: -http://our.umbraco.com/contribute/releases +https://our.umbraco.com/download/releases -- Umbraco \ No newline at end of file +- Umbraco From a0ae39a147005d37650d8e9299b223c0c0d68a49 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Thu, 13 Sep 2018 10:27:48 +0100 Subject: [PATCH 29/61] Reworked the notifications banner to use ng-switch to show the anchor tag when there is a notification url. --- .../components/notifications/umb-notifications.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html b/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html index 6c74014c7e..7cd0d1a510 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html @@ -9,10 +9,15 @@
- -
+
+ {{notification.headline}} + +
+ {{notification.headline}} + +
From 1acc61ede835f1331fe87cc8927e9f73c927cb7c Mon Sep 17 00:00:00 2001 From: "Ali.Taheri" Date: Thu, 13 Sep 2018 16:24:50 +0100 Subject: [PATCH 30/61] temp-2939 - corrected the data type from boolean to int for rich text editor. --- src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs index f4273303b4..5c17449df6 100644 --- a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs @@ -19,10 +19,10 @@ namespace Umbraco.Web.PropertyEditors internal class TextAreaPreValueEditor : PreValueEditor { [PreValueField("maxChars", "Maximum allowed characters", "number", Description = "If empty - no character limit")] - public bool MaxChars { get; set; } + public int MaxChars { get; set; } [PreValueField("rows", "Number of rows", "number", Description = "If empty - 10 rows would be set as the default value")] - public bool Rows { get; set; } + public int Rows { get; set; } } } } From 28ae25c1661276efc6dea70970332b7a6a3ec2fd Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Fri, 14 Sep 2018 00:57:19 +0200 Subject: [PATCH 31/61] Move logic to load moment.js locales to public function in userservice --- .../src/common/services/user.service.js | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index 80565c23e3..a598cb905c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -143,7 +143,6 @@ angular.module('umbraco.services') /** Called to update the current user's timeout */ function setUserTimeoutInternal(newTimeout) { - var asNumber = parseFloat(newTimeout); if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) { currentUser.remainingAuthSeconds = newTimeout; @@ -151,6 +150,31 @@ angular.module('umbraco.services') } } + function getMomentLocales(locales, supportedLocales) { + + var localeUrls = []; + var locales = locales.split(','); + for (var i = 0; i < locales.length; i++) { + var locale = locales[i].toString().toLowerCase(); + console.log("locale", locale); + + if (locale !== 'en-us') { + + if (supportedLocales.indexOf(locale + '.js') > -1) { + localeUrls.push('lib/moment/' + locale + '.js'); + } + if (locale.indexOf('-') > -1) { + var majorLocale = locale.split('-')[0] + '.js'; + if (supportedLocales.indexOf(majorLocale) > -1) { + localeUrls.push('lib/moment/' + majorLocale); + } + } + } + } + + return localeUrls; + } + /** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */ function userAuthExpired(isLogout) { //store the last user id and clear the user @@ -178,7 +202,7 @@ angular.module('umbraco.services') } }); - return { + var services = { /** Internal method to display the login dialog */ _showLoginDialog: function () { @@ -279,47 +303,40 @@ angular.module('umbraco.services') /** Loads the Moment.js Locale for the current user. */ loadMomentLocaleForCurrentUser: function () { - function loadLocales(currentUser, supportedLocales) { - var locale = currentUser.locale.toLowerCase(); - if (locale !== 'en-us') { - var localeUrls = []; - if (supportedLocales.indexOf(locale + '.js') > -1) { - localeUrls.push('lib/moment/' + locale + '.js'); - } - if (locale.indexOf('-') > -1) { - var majorLocale = locale.split('-')[0] + '.js'; - if (supportedLocales.indexOf(majorLocale) > -1) { - localeUrls.push('lib/moment/' + majorLocale); - } - } - return assetsService.load(localeUrls, $rootScope); - } - else { - //return a noop promise - var deferred = $q.defer(); - var promise = deferred.promise; - deferred.resolve(true); - return promise; - } - } - var promises = { currentUser: this.getCurrentUser(), supportedLocales: javascriptLibraryService.getSupportedLocalesForMoment() } return $q.all(promises).then(function (values) { - return loadLocales(values.currentUser, values.supportedLocales); + return services.loadLocales(values.currentUser.locale, values.supportedLocales); }); - - }, + /** Loads specific Moment.js Locales. */ + loadLocales: function (locales, supportedLocales) { + + var localeUrls = getMomentLocales(locales, supportedLocales); + console.log("localeUrls", localeUrls); + + if (localeUrls.length >= 1) { + return assetsService.load(localeUrls, $rootScope); + } + else { + //return a noop promise + var deferred = $q.defer(); + var promise = deferred.promise; + deferred.resolve(true); + return promise; + } + }, + /** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */ setUserTimeout: function (newTimeout) { setUserTimeoutInternal(newTimeout); } }; + return services; }); From e9f8fb8d1fd3f88ee921047edcfdf064bfc53dcb Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Fri, 14 Sep 2018 01:10:17 +0200 Subject: [PATCH 32/61] Remove console.log --- src/Umbraco.Web.UI.Client/src/common/services/user.service.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index a598cb905c..922e33ae08 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -156,8 +156,6 @@ angular.module('umbraco.services') var locales = locales.split(','); for (var i = 0; i < locales.length; i++) { var locale = locales[i].toString().toLowerCase(); - console.log("locale", locale); - if (locale !== 'en-us') { if (supportedLocales.indexOf(locale + '.js') > -1) { @@ -318,7 +316,6 @@ angular.module('umbraco.services') loadLocales: function (locales, supportedLocales) { var localeUrls = getMomentLocales(locales, supportedLocales); - console.log("localeUrls", localeUrls); if (localeUrls.length >= 1) { return assetsService.load(localeUrls, $rootScope); From 7f4280c67f4fcce342b6ea9e9e41074c5cd6ffb4 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Fri, 14 Sep 2018 10:06:32 +0100 Subject: [PATCH 33/61] added checks for whitespace --- .../src/views/components/notifications/umb-notifications.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html b/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html index 7cd0d1a510..3b78562c1a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html @@ -10,7 +10,7 @@
- + {{notification.headline}} From 6ee235b640105826ab58101cf1ffd10c2f8d8cf2 Mon Sep 17 00:00:00 2001 From: Damiaan Date: Mon, 17 Sep 2018 20:42:22 +0200 Subject: [PATCH 34/61] old issue tracker reference Since the github issue tracker is used, we should at least mention that. --- .github/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 74f9762040..e8a9889916 100644 --- a/.github/README.md +++ b/.github/README.md @@ -46,4 +46,5 @@ Umbraco is contribution focused and community driven. If you want to contribute Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md). -To view existing issues, please visit [http://issues.umbraco.org](http://issues.umbraco.org). +You can comment and report issues on the [github issue tracker](https://github.com/umbraco/Umbraco-CMS/issues). +Since [September 2018](https://umbraco.com/blog/a-second-take-on-umbraco-issue-tracker-hello-github-issues/) the old issue tracker is in read only mode, but can still be found at [http://issues.umbraco.org](http://issues.umbraco.org). From 8bf8e0a7415a42e00edaab08c4b13994d22eb22d Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 20 Sep 2018 08:48:46 +0200 Subject: [PATCH 35/61] Updates CDF --- build/NuSpecs/UmbracoCms.Core.nuspec | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 14 +++++++------- src/Umbraco.Core/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 7 ++++--- src/Umbraco.Web.UI/packages.config | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 ++-- src/Umbraco.Web/packages.config | 2 +- src/umbraco.cms/packages.config | 2 +- src/umbraco.cms/umbraco.cms.csproj | 4 ++-- src/umbraco.controls/packages.config | 2 +- src/umbraco.controls/umbraco.controls.csproj | 4 ++-- src/umbraco.editorControls/packages.config | 2 +- .../umbraco.editorControls.csproj | 4 ++-- 13 files changed, 26 insertions(+), 25 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 1b0e082aa8..0ae9591c97 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -28,7 +28,7 @@ - + diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4b933bf728..aa3b2db784 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1,4 +1,4 @@ - + Debug @@ -45,8 +45,8 @@ ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll - - ..\packages\ClientDependency.1.9.6\lib\net45\ClientDependency.Core.dll + + ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll ..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll @@ -567,8 +567,8 @@ - - + + @@ -638,7 +638,7 @@ - + @@ -1688,4 +1688,4 @@ --> - + \ No newline at end of file diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index eaeddb1627..4d7c2380b4 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -1,7 +1,7 @@  - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index b5e022fa8e..1efd3052aa 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -49,6 +49,7 @@ true true + bin\ @@ -116,8 +117,8 @@ ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll True - - ..\packages\ClientDependency.1.9.6\lib\net45\ClientDependency.Core.dll + + ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll ..\packages\ClientDependency-Mvc5.1.8.0.0\lib\net45\ClientDependency.Core.Mvc.dll @@ -1085,4 +1086,4 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 8a4b77ca3a..a27a63329e 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -1,7 +1,7 @@  - + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e970c09fca..7e961bf7af 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -107,8 +107,8 @@ ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll True - - ..\packages\ClientDependency.1.9.6\lib\net45\ClientDependency.Core.dll + + ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll ..\packages\dotless.1.5.2\lib\dotless.Core.dll diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 0acf6ef32e..e03c088146 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -1,7 +1,7 @@  - + diff --git a/src/umbraco.cms/packages.config b/src/umbraco.cms/packages.config index 7514fb8672..404427fb5c 100644 --- a/src/umbraco.cms/packages.config +++ b/src/umbraco.cms/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index c38dbb2d2c..31e1faecac 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -106,8 +106,8 @@ false - - ..\packages\ClientDependency.1.9.6\lib\net45\ClientDependency.Core.dll + + ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll ..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll diff --git a/src/umbraco.controls/packages.config b/src/umbraco.controls/packages.config index de6a15b59a..cbccba27be 100644 --- a/src/umbraco.controls/packages.config +++ b/src/umbraco.controls/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/umbraco.controls/umbraco.controls.csproj b/src/umbraco.controls/umbraco.controls.csproj index d8b17c9240..e1b190b701 100644 --- a/src/umbraco.controls/umbraco.controls.csproj +++ b/src/umbraco.controls/umbraco.controls.csproj @@ -68,8 +68,8 @@ false - - ..\packages\ClientDependency.1.9.6\lib\net45\ClientDependency.Core.dll + + ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll diff --git a/src/umbraco.editorControls/packages.config b/src/umbraco.editorControls/packages.config index de6a15b59a..cbccba27be 100644 --- a/src/umbraco.editorControls/packages.config +++ b/src/umbraco.editorControls/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/umbraco.editorControls/umbraco.editorControls.csproj b/src/umbraco.editorControls/umbraco.editorControls.csproj index 5ff7ca7184..1bbd3b8a24 100644 --- a/src/umbraco.editorControls/umbraco.editorControls.csproj +++ b/src/umbraco.editorControls/umbraco.editorControls.csproj @@ -114,8 +114,8 @@ {651E1350-91B6-44B7-BD60-7207006D7003} Umbraco.Web - - ..\packages\ClientDependency.1.9.6\lib\net45\ClientDependency.Core.dll + + ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll System From 1050e9d0a10e6c7619ed12ba4fdb712789277465 Mon Sep 17 00:00:00 2001 From: Paul Daly Date: Thu, 20 Sep 2018 13:15:22 +0100 Subject: [PATCH 36/61] Preview update: smartphone sizes The current most used screen size on a smarthphone is 360x640 according to Globalstats Statcounter. With this in mind I have adjusted the sizes of the preview frames for both portrait and landscape. Minor change but something that has been on my mind for a while. --- src/Umbraco.Web.UI.Client/src/less/canvas-designer.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less index 7410c09580..78c1616bc6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less +++ b/src/Umbraco.Web.UI.Client/src/less/canvas-designer.less @@ -790,13 +790,13 @@ h4.panel-title { } .smartphone-portrait { - width: 320px; - height: 504px; + width: 360px; + height: 640px; } .smartphone-landscape { - width: 480px; - height: 256px; + width: 640px; + height: 360px; } .border { From 3f79067e2678fda2cbd07eb8d08a663f3c3ec7a2 Mon Sep 17 00:00:00 2001 From: Jim Osborn Date: Mon, 24 Sep 2018 12:38:04 +0100 Subject: [PATCH 37/61] Fixes content getting deleted when saving an RTE before it's fully loaded. --- .../src/views/propertyeditors/rte/rte.controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index c5a739bec2..5e37bcce5d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -377,7 +377,9 @@ angular.module("umbraco") var unsubscribe = $scope.$on("formSubmitting", function () { //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer // we do parse it out on the server side but would be nice to do that on the client side before as well. - $scope.model.value = tinyMceEditor ? tinyMceEditor.getContent() : null; + if (tinyMceEditor !== undefined && tinyMceEditor != null) { + $scope.model.value = tinyMceEditor.getContent(); + } }); //when the element is disposed we need to unsubscribe! From 70a851dbf9147736f5d11c4c08964985ee95cff4 Mon Sep 17 00:00:00 2001 From: Pavel Budik Date: Mon, 24 Sep 2018 14:23:00 +0200 Subject: [PATCH 38/61] #3024: Removed unnecessary queries in MultiNodeTreePickerPropertyConverter --- .../MultiNodeTreePickerPropertyConverter.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs index 249b7e0cac..4008c1c61b 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs @@ -164,13 +164,23 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var multiNodeTreePicker = new List(); var objectType = UmbracoObjectTypes.Unknown; + IPublishedContent multiNodeTreePickerItem = null; foreach (var udi in udis) { - var multiNodeTreePickerItem = - GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, umbHelper.TypedContent) - ?? GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, umbHelper.TypedMedia) - ?? GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, umbHelper.TypedMember); + switch (udi.EntityType) + { + case Constants.UdiEntityType.Document: + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, umbHelper.TypedContent); + break; + case Constants.UdiEntityType.Media: + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, umbHelper.TypedMedia); + break; + case Constants.UdiEntityType.Member: + multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, umbHelper.TypedMember); + break; + } + if (multiNodeTreePickerItem != null) { multiNodeTreePicker.Add(multiNodeTreePickerItem); From 3d0d8d74305f8613d24285faa291446ece39b43e Mon Sep 17 00:00:00 2001 From: kedde Date: Mon, 24 Sep 2018 20:00:08 +0200 Subject: [PATCH 39/61] fix mysql casing --- .../TargetVersionSevenTwelveZero/SetDefaultTagsStorageType.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwelveZero/SetDefaultTagsStorageType.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwelveZero/SetDefaultTagsStorageType.cs index 8e13fa1013..6172c60b2c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwelveZero/SetDefaultTagsStorageType.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwelveZero/SetDefaultTagsStorageType.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwelveZ // We need to get all datatypes with an alias of "umbraco.tags" so we can loop over them and set the missing values if needed var datatypes = Context.Database.Fetch("SELECT * FROM cmsDataType"); var tagsDataTypes = datatypes.Where(x => string.Equals(x.PropertyEditorAlias, Constants.PropertyEditors.TagsAlias, StringComparison.InvariantCultureIgnoreCase)); - var dataTypePreValues = Context.Database.Fetch("SELECT * FROM cmsDataTypePrevalues"); + var dataTypePreValues = Context.Database.Fetch("SELECT * FROM cmsDataTypePreValues"); foreach (var datatype in tagsDataTypes) { @@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwelveZ // if the "storageType" has not been set we do so by adding a new row in the table for the nodid and set it if (result == null) { - Insert.IntoTable("CmsDataTypePrevalues").Row(new + Insert.IntoTable("cmsDataTypePreValues").Row(new { datatypeNodeId = datatype.DataTypeId, value = "Csv", From f9973f602ca58cbcc46f6aa73f6a196ac885c29a Mon Sep 17 00:00:00 2001 From: Umbraco HQ Date: Tue, 25 Sep 2018 14:03:21 +0200 Subject: [PATCH 40/61] Fixes the belle build (requires newer node version) --- build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 b/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 index 9b465fd78d..2a1e9e6831 100644 --- a/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 +++ b/build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 @@ -94,14 +94,14 @@ function Get-UmbracoBuildEnv } # ensure we have node - $node = "$path\node-v6.9.1-win-x86" - $source = "http://nodejs.org/dist/v6.9.1/node-v6.9.1-win-x86.7z" + $node = "$path\node-v8.12.0-win-x86" + $source = "http://nodejs.org/dist/v8.12.0/node-v8.12.0-win-x86.7z " if (-not (test-path $node)) { Write-Host "Download Node..." - Invoke-WebRequest $source -OutFile "$path\node-v6.9.1-win-x86.7z" - &$sevenZip x "$path\node-v6.9.1-win-x86.7z" -o"$path" -aos > $nul - Remove-File "$path\node-v6.9.1-win-x86.7z" + Invoke-WebRequest $source -OutFile "$path\node-v8.12.0-win-x86.7z" + &$sevenZip x "$path\node-v8.12.0-win-x86.7z" -o"$path" -aos > $nul + Remove-File "$path\node-v8.12.0-win-x86.7z" } # note: why? node already brings everything we need! From 35ec07f97089ba2aa8f3729282609664efc75674 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 25 Sep 2018 16:15:37 +0200 Subject: [PATCH 41/61] Some tiny code style updates --- src/Umbraco.Web/Editors/HelpController.cs | 8 ++++++-- src/Umbraco.Web/Install/InstallHelper.cs | 3 +-- src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs | 3 +-- src/Umbraco.Web/Scheduling/ScheduledTasks.cs | 6 ++---- src/umbraco.cms/businesslogic/Packager/Installer.cs | 2 -- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/Editors/HelpController.cs b/src/Umbraco.Web/Editors/HelpController.cs index 8f981d9c88..ccbbcaeee8 100644 --- a/src/Umbraco.Web/Editors/HelpController.cs +++ b/src/Umbraco.Web/Editors/HelpController.cs @@ -8,12 +8,16 @@ namespace Umbraco.Web.Editors { public class HelpController : UmbracoAuthorizedJsonController { - private static readonly HttpClient HttpClient = new HttpClient(); + private static HttpClient _httpClient; public async Task> GetContextHelpForPage(string section, string tree, string baseUrl = "https://our.umbraco.com") { var url = string.Format(baseUrl + "/Umbraco/Documentation/Lessons/GetContextHelpDocs?sectionAlias={0}&treeAlias={1}", section, tree); + + if (_httpClient == null) + _httpClient = new HttpClient(); + //fetch dashboard json and parse to JObject - var json = await HttpClient.GetStringAsync(url); + var json = await _httpClient.GetStringAsync(url); var result = JsonConvert.DeserializeObject>(json); if (result != null) return result; diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 145395e235..5b6e8a0c60 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -193,9 +193,8 @@ namespace Umbraco.Web.Install internal IEnumerable GetStarterKits() { if (_httpClient == null) - { _httpClient = new HttpClient(); - } + var packages = new List(); try { diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 4dfa793675..22f5f0ff7e 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -83,9 +83,8 @@ namespace Umbraco.Web.Install.InstallSteps try { if (_webClient == null) - { _webClient = new WebClient(); - } + var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} }; _webClient.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); } diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index a50b4dffbd..4278b82c34 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -63,13 +63,11 @@ namespace Umbraco.Web.Scheduling private async Task GetTaskByHttpAync(string url, CancellationToken token) { if (_httpClient == null) - { _httpClient = new HttpClient(); - } + if (Uri.TryCreate(_appContext.UmbracoApplicationUrl, UriKind.Absolute, out var baseUri)) - { _httpClient.BaseAddress = baseUri; - } + var request = new HttpRequestMessage(HttpMethod.Get, url); //TODO: pass custom the authorization header, currently these aren't really secured! diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index f3733d5e5a..13d412ce98 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -688,9 +688,7 @@ namespace umbraco.cms.businesslogic.packager Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); if (_webClient == null) - { _webClient = new WebClient(); - } _webClient.DownloadFile( "http://" + PackageServer + "/fetch?package=" + Package.ToString(), From 540e4f17d6f2718e0bb8827e792fa2e566bcfa25 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 26 Sep 2018 09:48:09 +0200 Subject: [PATCH 42/61] Fixes #3042 Code comes from v8, commit: 9bfe9e6bbf0d0e81970c07210da8e15c6754b291 --- .../Security/AuthenticationExtensions.cs | 22 ++++++++++++++++++- .../BackOfficeCookieAuthenticationProvider.cs | 21 +++--------------- src/Umbraco.Web/UmbracoModule.cs | 7 ++++++ 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 9df84cc2b1..bd0891cd1a 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -1,8 +1,10 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -458,5 +460,23 @@ namespace Umbraco.Core.Security return ticket; } + + /// + /// Ensures that the thread culture is set based on the back office user's culture + /// + /// + internal static void EnsureCulture(this IIdentity identity) + { + if (identity is UmbracoBackOfficeIdentity umbIdentity && umbIdentity.IsAuthenticated) + { + Thread.CurrentThread.CurrentUICulture = + Thread.CurrentThread.CurrentCulture = + UserCultures.GetOrAdd(umbIdentity.Culture, s => new CultureInfo(s)); + } + } + /// + /// Used so that we aren't creating a new CultureInfo object for every single request + /// + private static readonly ConcurrentDictionary UserCultures = new ConcurrentDictionary(); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs b/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs index 3ff1266c6b..1a3b9f54ee 100644 --- a/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs +++ b/src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs @@ -102,7 +102,8 @@ namespace Umbraco.Core.Security /// public override async Task ValidateIdentity(CookieValidateIdentityContext context) { - EnsureCulture(context); + //ensure the thread culture is set + context?.Identity?.EnsureCulture(); await EnsureValidSessionId(context); @@ -120,21 +121,5 @@ namespace Umbraco.Core.Security if (_appCtx.IsConfigured && _appCtx.IsUpgrading == false) await SessionIdValidator.ValidateSessionAsync(TimeSpan.FromMinutes(1), context); } - - private void EnsureCulture(CookieValidateIdentityContext context) - { - var umbIdentity = context.Identity as UmbracoBackOfficeIdentity; - if (umbIdentity != null && umbIdentity.IsAuthenticated) - { - Thread.CurrentThread.CurrentCulture = - Thread.CurrentThread.CurrentUICulture = - UserCultures.GetOrAdd(umbIdentity.Culture, s => new CultureInfo(s)); - } - } - - /// - /// Used so that we aren't creating a new CultureInfo object for every single request - /// - private static readonly ConcurrentDictionary UserCultures = new ConcurrentDictionary(); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 99043143a7..29d0de968d 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -509,6 +509,13 @@ namespace Umbraco.Web } }; + app.PostAuthenticateRequest += (sender, e) => + { + var httpContext = ((HttpApplication)sender).Context; + //ensure the thread culture is set + httpContext.User?.Identity?.EnsureCulture(); + }; + app.PostResolveRequestCache += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; From a1224da5e16f948962600c975a90ff98f386da67 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 26 Sep 2018 13:27:16 +0200 Subject: [PATCH 43/61] Fixes #3036 Removes WebClient completely except for in an old legacy class that is not used for anything any more (umbraco.cms.businesslogic.packager.Installer.Fetch). --- .../Editors/CanvasDesignerController.cs | 31 +++------- src/Umbraco.Web/Install/InstallHelper.cs | 5 +- .../Install/InstallSteps/NewInstallStep.cs | 40 +++++++------ .../EmbedProviders/AbstractOEmbedProvider.cs | 13 ++-- .../umbraco.presentation/keepAliveService.cs | 16 ++--- .../umbraco/dashboard/FeedProxy.aspx.cs | 60 +++++++++---------- 6 files changed, 79 insertions(+), 86 deletions(-) diff --git a/src/Umbraco.Web/Editors/CanvasDesignerController.cs b/src/Umbraco.Web/Editors/CanvasDesignerController.cs index 8284bf0b12..e0645b3c23 100644 --- a/src/Umbraco.Web/Editors/CanvasDesignerController.cs +++ b/src/Umbraco.Web/Editors/CanvasDesignerController.cs @@ -1,29 +1,17 @@ using System.Collections.Generic; -using System.Net; using System.Net.Http; -using System.Text; using System.Web.Http; -using AutoMapper; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using umbraco; using Umbraco.Web.WebApi; using System; using System.Net.Http.Headers; using System.Web; -using System.IO; -using Umbraco.Core.Models; -using System.Text.RegularExpressions; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Services; namespace Umbraco.Web.Editors { public class CanvasDesignerController : UmbracoApiController { - private static WebClient _webClient; + private static HttpClient _httpClient; [HttpGet] public HttpResponseMessage GetGoogleFont() @@ -35,17 +23,14 @@ namespace Umbraco.Web.Editors // Google Web Font JSON URL var googleWebFontAPIURL = string.Format("https://www.googleapis.com/webfonts/v1/webfonts?key={0}", APIKey); - var response = "{}"; - if (_webClient == null) - _webClient = new WebClient(); - - response = _webClient.DownloadString(new Uri(googleWebFontAPIURL)); - - var resp = Request.CreateResponse(); - resp.Content = new StringContent(response); - resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - return resp; + if (_httpClient == null) + _httpClient = new HttpClient(); + using (var request = new HttpRequestMessage(HttpMethod.Get, googleWebFontAPIURL)) + { + var response = _httpClient.SendAsync(request).Result; + return response; + } } [HttpGet] diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 5b6e8a0c60..50324bcfd2 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -198,12 +198,11 @@ namespace Umbraco.Web.Install var packages = new List(); try { - var requestUri = string.Format("https://our.umbraco.com/webapi/StarterKit/Get/?umbracoVersion={0}", - UmbracoVersion.Current); + var requestUri = $"https://our.umbraco.com/webapi/StarterKit/Get/?umbracoVersion={UmbracoVersion.Current}"; using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) - using (var response = _httpClient.SendAsync(request).Result) { + var response = _httpClient.SendAsync(request).Result; packages = response.Content.ReadAsAsync>().Result.ToList(); } } diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 22f5f0ff7e..2d6fd89c46 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -1,13 +1,14 @@ using System; -using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration; using System.Net; +using System.Net.Http; +using System.Text; using System.Web; using System.Web.Security; +using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Configuration; -using Umbraco.Core.Persistence; using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps @@ -20,13 +21,12 @@ namespace Umbraco.Web.Install.InstallSteps /// error, etc... and the end-user refreshes the installer then we cannot show the user screen because they've already entered that information so instead we'll /// display a simple continue installation view. /// - [InstallSetupStep(InstallationType.NewInstall, - "User", 20, "")] + [InstallSetupStep(InstallationType.NewInstall, "User", 20, "")] internal class NewInstallStep : InstallSetupStep { private readonly HttpContextBase _http; private readonly ApplicationContext _applicationContext; - private static WebClient _webClient; + private static HttpClient _httpClient; public NewInstallStep(HttpContextBase http, ApplicationContext applicationContext) { @@ -76,17 +76,18 @@ namespace Umbraco.Web.Install.InstallSteps admin.Username = user.Email.Trim(); _applicationContext.Services.UserService.Save(admin); - - + if (user.SubscribeToNewsLetter) { + if (_httpClient == null) + _httpClient = new HttpClient(); + + var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email } }; + var content = new StringContent(JsonConvert.SerializeObject(values), Encoding.UTF8, "application/json"); + try { - if (_webClient == null) - _webClient = new WebClient(); - - var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} }; - _webClient.UploadValues("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); + var response = _httpClient.PostAsync("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", content).Result; } catch { /* fail in silence */ } } @@ -113,13 +114,16 @@ namespace Umbraco.Web.Install.InstallSteps public override string View { - get { return RequiresExecution(null) - //the user UI - ? "user" - //the continue install UI - : "continueinstall"; } + get + { + return RequiresExecution(null) + //the user UI + ? "user" + //the continue install UI + : "continueinstall"; + } } - + public override bool RequiresExecution(UserModel model) { //now we have to check if this is really a new install, the db might be configured and might contain data diff --git a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs index 737b04fc83..fa1ef9fb93 100644 --- a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs +++ b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.Media.EmbedProviders public abstract class AbstractOEmbedProvider : IEmbedProvider { - private static WebClient _webClient; + private static HttpClient _httpClient; public virtual bool SupportsDimensions { @@ -53,9 +53,14 @@ namespace Umbraco.Web.Media.EmbedProviders public virtual string DownloadResponse(string url) { - if (_webClient == null) - _webClient = new WebClient(); - return _webClient.DownloadString(url); + if (_httpClient == null) + _httpClient = new HttpClient(); + + using (var request = new HttpRequestMessage(HttpMethod.Get, url)) + { + var response = _httpClient.SendAsync(request).Result; + return response.Content.ReadAsStringAsync().Result; + } } public virtual T GetJsonResponse(string url) where T : class diff --git a/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs b/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs index 192c35a233..21511bb1dc 100644 --- a/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs +++ b/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs @@ -1,7 +1,5 @@ using System; -using System.Diagnostics; -using System.Net; -using System.Web; +using System.Net.Http; using Umbraco.Core; using Umbraco.Core.Logging; @@ -10,7 +8,7 @@ namespace umbraco.presentation [Obsolete("This is no longer used and will be removed in future versions")] public class keepAliveService { - private static WebClient _webClient; + private static HttpClient _httpClient; //NOTE: sender will be the umbraco ApplicationContext public static void PingUmbraco(object sender) { @@ -22,9 +20,13 @@ namespace umbraco.presentation var url = appContext.UmbracoApplicationUrl + "/ping.aspx"; try { - if (_webClient == null) - _webClient = new WebClient(); - _webClient.DownloadString(url); + if (_httpClient == null) + _httpClient = new HttpClient(); + + using (var request = new HttpRequestMessage(HttpMethod.Get, url)) + { + var response = _httpClient.SendAsync(request).Result; + } } catch(Exception ee) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs index 488e4f06c2..49539b0264 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Web; @@ -6,54 +7,51 @@ namespace dashboardUtilities { using System; using System.Linq; - using System.Net; using System.Net.Mime; - using umbraco; using umbraco.BasePages; - using umbraco.BusinessLogic; using Umbraco.Core.IO; public partial class FeedProxy : UmbracoEnsuredPage { - private static WebClient _webClient; + private static HttpClient _httpClient; protected void Page_Load(object sender, EventArgs e) { try { - if (Request.QueryString.AllKeys.Contains("url") && Request.QueryString["url"] != null) + if (Request.QueryString.AllKeys.Contains("url") == false || Request.QueryString["url"] == null) + return; + + var url = Request.QueryString["url"]; + if (string.IsNullOrWhiteSpace(url) || url.StartsWith("/")) + return; + + if (Uri.TryCreate(url, UriKind.Absolute, out var requestUri) == false) + return; + + var feedProxyXml = XmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); + if (feedProxyXml?.SelectSingleNode($"//allow[@host = '{requestUri.Host}']") != null && requestUri.Port == 80) { - var url = Request.QueryString["url"]; - if (!string.IsNullOrWhiteSpace(url) && !url.StartsWith("/")) + if (_httpClient == null) + _httpClient = new HttpClient(); + + using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) { - Uri requestUri; - if (Uri.TryCreate(url, UriKind.Absolute, out requestUri)) - { - var feedProxyXml = xmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - if (feedProxyXml != null - && feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null - && requestUri.Port == 80) - { - if (_webClient == null) - _webClient = new WebClient(); - - var response = _webClient.DownloadString(requestUri); + var response = _httpClient.SendAsync(request).Result; + var result = response.Content.ReadAsStringAsync().Result; - if (string.IsNullOrEmpty(response) == false) - { - Response.Clear(); - Response.ContentType = Request.CleanForXss("type") ?? MediaTypeNames.Text.Xml; - Response.Write(response); - } + if (string.IsNullOrEmpty(result)) + return; - } - else - { - LogHelper.Debug(string.Format("Access to unallowed feedproxy attempted: {0}", requestUri)); - } - } + Response.Clear(); + Response.ContentType = Request.CleanForXss("type") ?? MediaTypeNames.Text.Xml; + Response.Write(result); } } + else + { + LogHelper.Debug($"Access to unallowed feedproxy attempted: {requestUri}"); + } } catch (Exception ex) { From 5832387484dd447f7033e91514c8f2a47b0739ad Mon Sep 17 00:00:00 2001 From: Dennis Adolfi Date: Tue, 25 Jul 2017 11:43:07 +0200 Subject: [PATCH 44/61] Added the DefaultLabelDataTypeIdas a non-deletable datatype. --- src/Umbraco.Core/Constants-System.cs | 1 + .../Trees/DataTypeTreeController.cs | 39 +++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs index d4deda826e..1969cd8506 100644 --- a/src/Umbraco.Core/Constants-System.cs +++ b/src/Umbraco.Core/Constants-System.cs @@ -61,6 +61,7 @@ public const int DefaultContentListViewDataTypeId = -95; public const int DefaultMediaListViewDataTypeId = -96; public const int DefaultMembersListViewDataTypeId = -97; + public const int DefaultLabelDataTypeId = -92; public const string UmbracoConnectionName = "umbracoDbDSN"; public const string UmbracoMigrationName = "Umbraco"; diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 70da27c1bf..e18debdf2c 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -51,8 +51,8 @@ namespace Umbraco.Web.Trees //if the request is for folders only then just return if (queryStrings["foldersonly"].IsNullOrWhiteSpace() == false && queryStrings["foldersonly"] == "1") return nodes; - //Normal nodes - var sysIds = GetSystemIds(); + //System ListView nodes + var systemListViewDataTypeIds = GetNonDeletableSystemListViewDataTypeIds(); nodes.AddRange( Services.EntityService.GetChildren(intId.Result, UmbracoObjectTypes.DataType) @@ -61,7 +61,7 @@ namespace Umbraco.Web.Trees { var node = CreateTreeNode(dt.Id.ToInvariantString(), id, queryStrings, dt.Name, "icon-autofill", false); node.Path = dt.Path; - if (sysIds.Contains(dt.Id)) + if (systemListViewDataTypeIds.Contains(dt.Id)) { node.Icon = "icon-thumbnail-list"; } @@ -70,17 +70,32 @@ namespace Umbraco.Web.Trees return nodes; } - - private IEnumerable GetSystemIds() - { - var systemIds = new[] + + /// + /// Get all integer identifiers for the non-deletable system datatypes. + /// + private static IEnumerable GetNonDeletableSystemDataTypeIds() + { + var systemIds = new[] { - Constants.System.DefaultContentListViewDataTypeId, - Constants.System.DefaultMediaListViewDataTypeId, + Constants.System.DefaultLabelDataTypeId + }; + + return systemIds.Concat(GetNonDeletableSystemListViewDataTypeIds()); + } + + /// + /// Get all integer identifiers for the non-deletable system listviews. + /// + private static IEnumerable GetNonDeletableSystemListViewDataTypeIds() + { + return new[] + { + Constants.System.DefaultContentListViewDataTypeId, + Constants.System.DefaultMediaListViewDataTypeId, Constants.System.DefaultMembersListViewDataTypeId }; - return systemIds; - } + } protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { @@ -120,7 +135,7 @@ namespace Umbraco.Web.Trees } else { - var sysIds = GetSystemIds(); + var sysIds = GetNonDeletableSystemDataTypeIds(); if (sysIds.Contains(int.Parse(id)) == false) { From 3940176f2338d069adb36423de1c55539f94de9a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 26 Sep 2018 18:01:50 +0200 Subject: [PATCH 45/61] Just a bit of code cleanup --- .../Trees/DataTypeTreeController.cs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index e18debdf2c..21da6fc0c0 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -1,19 +1,14 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Net; using System.Net.Http.Formatting; -using System.Web.Http; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using umbraco; using umbraco.BusinessLogic.Actions; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; @@ -107,8 +102,8 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.Instance.Alias; // root actions - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - menu.Items.Add(ui.Text("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionRefresh.Instance.Alias}"), hasSeparator: true); return menu; } @@ -118,31 +113,28 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.Instance.Alias; - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize(String.Format("actions/{0}", "rename"))) + menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { Icon = "icon icon-edit" }); if (container.HasChildren() == false) - { //can delete data type - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); - } - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); + + menu.Items.Add(Services.TextService.Localize($"actions/{ActionRefresh.Instance.Alias}"), hasSeparator: true); } else { - var sysIds = GetNonDeletableSystemDataTypeIds(); + var nonDeletableSystemDataTypeIds = GetNonDeletableSystemDataTypeIds(); - if (sysIds.Contains(int.Parse(id)) == false) - { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); - } + if (nonDeletableSystemDataTypeIds.Contains(int.Parse(id)) == false) + menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), hasSeparator: true); } return menu; @@ -154,4 +146,4 @@ namespace Umbraco.Web.Trees return Mapper.Map>(results); } } -} \ No newline at end of file +} From 471bc7f116d19b21134159c8476ea725f0e02d84 Mon Sep 17 00:00:00 2001 From: Dave Woestenborghs Date: Wed, 26 Sep 2018 20:48:54 +0200 Subject: [PATCH 46/61] U4-11458 - Fix multiple issues for angular logResource (#2719) --- .../Repositories/AuditRepository.cs | 7 +- .../src/common/resources/log.resource.js | 160 ++++++++++++++---- src/Umbraco.Web/Editors/LogController.cs | 33 ++-- 3 files changed, 144 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs index e602358cf6..4fe235346f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs @@ -48,6 +48,7 @@ namespace Umbraco.Core.Persistence.Repositories var sql = GetBaseQuery(false); if (query == null) query = new Query(); + var queryHasWhereClause = query.GetWhereClauses().Any(); var translatorIds = new SqlTranslator(sql, query); var translatedQuery = translatorIds.Translate(); @@ -58,7 +59,7 @@ namespace Umbraco.Core.Persistence.Repositories { var filterSql = new Sql(); foreach (var filterClause in customFilterWheres) - { + { filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2); } @@ -69,7 +70,7 @@ namespace Umbraco.Core.Persistence.Repositories { var filterSql = new Sql(); foreach (var filterClause in auditTypeFilter) - { + { filterSql.Append("AND (logHeader = @logHeader)", new { logHeader = filterClause.ToString() }); } @@ -166,7 +167,7 @@ namespace Umbraco.Core.Persistence.Repositories // Apply filter if (filterSql != null) { - //ensure we don't append a WHERE if there is already one + //ensure we don't append a WHERE if there is already one var sqlFilter = hasWhereClause ? filterSql.SQL : " WHERE " + filterSql.SQL.TrimStart("AND "); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js index cb676511a5..c4c54c7a50 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js @@ -7,10 +7,60 @@ **/ function logResource($q, $http, umbRequestHelper) { + function isValidDate(input) { + if (input) { + if (Object.prototype.toString.call(input) === "[object Date]" && !isNaN(input.getTime())) { + return true; + } + } + + return false; + }; + + function dateToValidIsoString(input) { + if (isValidDate(input)) { + return input.toISOString(); + } + + return ''; + }; + //the factory object returned return { - getPagedEntityLog: function (options) { + /** + * @ngdoc method + * @name umbraco.resources.logResource#getPagedEntityLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets a paginated log history for a entity + * + * ##usage + *
+        * var options = {
+        *      id : 1234
+        *      pageSize : 10,
+        *      pageNumber : 1,
+        *      orderDirection : "Descending",
+        *      sinceDate : new Date(2018,0,1)
+        * };
+        * logResource.getPagedEntityLog(options)
+        *    .then(function(log) {
+        *        alert('its here!');
+        *    });
+        * 
+ * + * @param {Object} options options object + * @param {Int} options.id the id of the entity + * @param {Int} options.pageSize if paging data, number of nodes per page, default = 10, set to 0 to disable paging + * @param {Int} options.pageNumber if paging data, current page index, default = 1 + * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Descending` + * @param {Date} options.sinceDate if provided this will only get log entries going back to this date + * @returns {Promise} resourcePromise object containing the log. + * + */ + getPagedEntityLog: function(options) { var defaults = { pageSize: 10, @@ -24,17 +74,21 @@ function logResource($q, $http, umbRequestHelper) { angular.extend(defaults, options); //now copy back to the options we will use options = defaults; + + if (options.hasOwnProperty('sinceDate')) { + options.sinceDate = dateToValidIsoString(options.sinceDate); + } + //change asc/desct if (options.orderDirection === "asc") { options.orderDirection = "Ascending"; - } - else if (options.orderDirection === "desc") { + } else if (options.orderDirection === "desc") { options.orderDirection = "Descending"; } - + if (options.id === undefined || options.id === null) { throw "options.id is required"; - } + } return umbRequestHelper.resourcePromise( $http.get( @@ -45,7 +99,37 @@ function logResource($q, $http, umbRequestHelper) { 'Failed to retrieve log data for id'); }, - getPagedUserLog: function (options) { + /** + * @ngdoc method + * @name umbraco.resources.logResource#getPagedUserLog + * @methodOf umbraco.resources.logResource + * + * @description + * Gets a paginated log history for the current user + * + * ##usage + *
+         * var options = {
+         *      pageSize : 10,
+         *      pageNumber : 1,
+         *      orderDirection : "Descending",
+         *      sinceDate : new Date(2018,0,1)
+         * };
+         * logResource.getPagedUserLog(options)
+         *    .then(function(log) {
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {Object} options options object + * @param {Int} options.pageSize if paging data, number of nodes per page, default = 10, set to 0 to disable paging + * @param {Int} options.pageNumber if paging data, current page index, default = 1 + * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Descending` + * @param {Date} options.sinceDate if provided this will only get log entries going back to this date + * @returns {Promise} resourcePromise object containing the log. + * + */ + getPagedUserLog: function(options) { var defaults = { pageSize: 10, @@ -59,11 +143,15 @@ function logResource($q, $http, umbRequestHelper) { angular.extend(defaults, options); //now copy back to the options we will use options = defaults; + + if (options.hasOwnProperty('sinceDate')) { + options.sinceDate = dateToValidIsoString(options.sinceDate); + } + //change asc/desct if (options.orderDirection === "asc") { options.orderDirection = "Ascending"; - } - else if (options.orderDirection === "desc") { + } else if (options.orderDirection === "desc") { options.orderDirection = "Descending"; } @@ -71,10 +159,10 @@ function logResource($q, $http, umbRequestHelper) { $http.get( umbRequestHelper.getApiUrl( "logApiBaseUrl", - "GetPagedEntityLog", + "GetPagedCurrentUserLog", options)), 'Failed to retrieve log data for id'); - }, + }, /** * @ngdoc method @@ -82,6 +170,7 @@ function logResource($q, $http, umbRequestHelper) { * @methodOf umbraco.resources.logResource * * @description + * [OBSOLETE] use getPagedEntityLog instead
* Gets the log history for a give entity id * * ##usage @@ -96,23 +185,24 @@ function logResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the log. * */ - getEntityLog: function (id) { + getEntityLog: function(id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "logApiBaseUrl", - "GetEntityLog", - [{ id: id }])), - 'Failed to retrieve user data for id ' + id); + $http.get( + umbRequestHelper.getApiUrl( + "logApiBaseUrl", + "GetEntityLog", + [{ id: id }])), + 'Failed to retrieve user data for id ' + id); }, - + /** * @ngdoc method * @name umbraco.resources.logResource#getUserLog * @methodOf umbraco.resources.logResource * * @description - * Gets the current users' log history for a given type of log entry + * [OBSOLETE] use getPagedUserLog instead
+ * Gets the current user's log history for a given type of log entry * * ##usage *
@@ -127,14 +217,14 @@ function logResource($q, $http, umbRequestHelper) {
          * @returns {Promise} resourcePromise object containing the log.
          *
          */
-        getUserLog: function (type, since) {            
+        getUserLog: function(type, since) {
             return umbRequestHelper.resourcePromise(
-               $http.get(
-                   umbRequestHelper.getApiUrl(
-                       "logApiBaseUrl",
-                       "GetCurrentUserLog",
-                       [{ logtype: type}, {sinceDate: since }])),
-               'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
+                $http.get(
+                    umbRequestHelper.getApiUrl(
+                        "logApiBaseUrl",
+                        "GetCurrentUserLog",
+                        [{ logtype: type }, { sinceDate:  dateToValidIsoString(since) }])),
+                'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
         },
 
         /**
@@ -158,16 +248,16 @@ function logResource($q, $http, umbRequestHelper) {
          * @returns {Promise} resourcePromise object containing the log.
          *
          */
-        getLog: function (type, since) {            
+        getLog: function(type, since) {
             return umbRequestHelper.resourcePromise(
-               $http.get(
-                   umbRequestHelper.getApiUrl(
-                       "logApiBaseUrl",
-                       "GetLog",
-                       [{ logtype: type}, {sinceDate: since }])),
-               'Failed to retrieve log data of type ' + type + ' since ' + since);
-        }
-    };
+                $http.get(
+                    umbRequestHelper.getApiUrl(
+                        "logApiBaseUrl",
+                        "GetLog",
+                        [{ logtype: type }, { sinceDate: dateToValidIsoString(since) }])),
+                'Failed to retrieve log data of type ' + type + ' since ' + since);
+        }      
+};
 }
 
 angular.module('umbraco.resources').factory('logResource', logResource);
diff --git a/src/Umbraco.Web/Editors/LogController.cs b/src/Umbraco.Web/Editors/LogController.cs
index 5122a458f6..04e34e91e0 100644
--- a/src/Umbraco.Web/Editors/LogController.cs
+++ b/src/Umbraco.Web/Editors/LogController.cs
@@ -1,18 +1,13 @@
-using System;
+using AutoMapper;
+using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-using System.Web.Http;
-using AutoMapper;
-using Umbraco.Core;
-using Umbraco.Web.Models.ContentEditing;
 using umbraco.BusinessLogic;
+using Umbraco.Core;
 using Umbraco.Core.Models;
 using Umbraco.Core.Persistence.DatabaseModelDefinitions;
 using Umbraco.Core.Persistence.Querying;
+using Umbraco.Web.Models.ContentEditing;
 using Umbraco.Web.Mvc;
 
 namespace Umbraco.Web.Editors
@@ -52,31 +47,33 @@ namespace Umbraco.Web.Editors
             var dateQuery = sinceDate.HasValue ? Query.Builder.Where(x => x.CreateDate >= sinceDate) : null;
             var result = Services.AuditService.GetPagedItemsByUser(Security.GetUserId(), pageNumber - 1, pageSize, out totalRecords, orderDirection, customFilter:dateQuery);
             var mapped = Mapper.Map>(result);
-            return new PagedResult(totalRecords, pageNumber + 1, pageSize)
+            return new PagedResult(totalRecords, pageNumber, pageSize)
             {
                 Items = MapAvatarsAndNames(mapped)
             };
-        }
-
-        [Obsolete("Use GetPagedLog instead")]
+        }
+
+        [Obsolete("Use GetPagedEntityLog instead")]
         public IEnumerable GetEntityLog(int id)
         {
             long totalRecords;
             var result = Services.AuditService.GetPagedItemsByEntity(id, 1, int.MaxValue, out totalRecords);
             return Mapper.Map>(result);
         }
-
-        //TODO: Move to CurrentUserController?
+       
         [Obsolete("Use GetPagedCurrentUserLog instead")]
         public IEnumerable GetCurrentUserLog(AuditType logType, DateTime? sinceDate)
         {
             long totalRecords;
+
+            if (sinceDate == null)
+                sinceDate = DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0, 0));
+
             var dateQuery = sinceDate.HasValue ? Query.Builder.Where(x => x.CreateDate >= sinceDate) : null;
-            var result = Services.AuditService.GetPagedItemsByUser(Security.GetUserId(), 1, int.MaxValue, out totalRecords, auditTypeFilter: new[] {logType},customFilter: dateQuery);
+            var result = Services.AuditService.GetPagedItemsByUser(Security.GetUserId(), 0, int.MaxValue, out totalRecords, auditTypeFilter: new[] {logType},customFilter: dateQuery);
             return Mapper.Map>(result);
         }
-
-        [Obsolete("Use GetPagedLog instead")]
+        
         public IEnumerable GetLog(AuditType logType, DateTime? sinceDate)
         {
             if (sinceDate == null)

From b0374695b15c4532b3dba1e1f62b2075924ba187 Mon Sep 17 00:00:00 2001
From: Dave Woestenborghs 
Date: Wed, 26 Sep 2018 21:51:20 +0200
Subject: [PATCH 47/61] Fixed udi's rendered in rte content (#2531)

---
 .../Configuration/UmbracoConfig.cs            |  2 +-
 .../UmbracoSettings/ContentElement.cs         | 22 ++++++++---
 .../UmbracoSettings/IContentSection.cs        |  4 +-
 src/Umbraco.Tests/Umbraco.Tests.csproj        |  3 ++
 .../Web/TemplateUtilitiesTests.cs             | 37 +++++++++++++++++--
 src/Umbraco.Tests/packages.config             |  1 +
 .../Templates/TemplateUtilities.cs            | 33 ++++++++++++++++-
 7 files changed, 89 insertions(+), 13 deletions(-)

diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs
index 82d90073e8..5b6d27b9c5 100644
--- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoConfig.cs
@@ -202,4 +202,4 @@ namespace Umbraco.Core.Configuration
 
         //TODO: Add other configurations here !
     }
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
index 17523ab3a1..9fe35f7cb5 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
@@ -139,8 +139,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
         internal CommaDelimitedConfigurationElement DisallowedUploadFiles
         {
             get { return GetOptionalDelimitedElement("disallowedUploadFiles", new[] {"ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd"}); }
-        }
-
+        }
+
         [ConfigurationProperty("allowedUploadFiles")]
         internal CommaDelimitedConfigurationElement AllowedUploadFiles
         {
@@ -195,6 +195,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
             get { return GetOptionalTextElement("loginBackgroundImage", string.Empty); }
         }
 
+        [ConfigurationProperty("StripUdiAttributes")]
+        internal InnerTextConfigurationElement StripUdiAttributes
+        {
+            get { return GetOptionalTextElement("StripUdiAttributes", true); }
+        }
+
         string IContentSection.NotificationEmailAddress
         {
             get { return Notifications.NotificationEmailAddress; }
@@ -313,8 +319,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
         IEnumerable IContentSection.DisallowedUploadFiles
         {
             get { return DisallowedUploadFiles; }
-        }
-
+        }
+
         IEnumerable IContentSection.AllowedUploadFiles
         {
             get { return AllowedUploadFiles; }
@@ -357,7 +363,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
         string IContentSection.LoginBackgroundImage
         {
             get { return LoginBackgroundImage; }
-        }
-        
+        }
+
+        bool IContentSection.StripUdiAttributes
+        {
+            get { return StripUdiAttributes; }
+        }
     }
 }
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
index d6ee260fa9..6c0d8327f1 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
@@ -75,6 +75,8 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
         bool EnablePropertyValueConverters { get; }
 
         string LoginBackgroundImage { get; }
+
+        bool StripUdiAttributes { get; }
         
     }
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 86c36a8d76..abed9b8245 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -65,6 +65,9 @@
     
       ..\packages\Examine.0.1.89\lib\net45\Examine.dll
     
+    
+      ..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll
+    
     
       ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll
     
diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
index f2a04c5f0f..96461519d8 100644
--- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
+++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
@@ -1,6 +1,6 @@
 using System;
-using System.Linq;
 using System.Web;
+using HtmlAgilityPack;
 using Moq;
 using NUnit.Framework;
 using Umbraco.Core;
@@ -10,7 +10,6 @@ using Umbraco.Core.Models;
 using Umbraco.Core.Persistence.SqlSyntax;
 using Umbraco.Core.Profiling;
 using Umbraco.Core.Scoping;
-using Umbraco.Core.Services;
 using Umbraco.Tests.TestHelpers;
 using Umbraco.Web;
 using Umbraco.Web.Routing;
@@ -34,6 +33,14 @@ namespace Umbraco.Tests.Web
         [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
         //this one has an invalid char so won't match
         [TestCase("hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
+        // with a-tag with data-udi attribute, that needs to be stripped
+        [TestCase("hello  world ", "hello  world ")]
+        // with a-tag with data-udi attribute spelled wrong, so don't need stripping
+        [TestCase("hello  world ", "hello  world ")]
+        // with a img-tag with data-udi id, that needs to be strippde
+        [TestCase("hello  world ", "hello  world ")]
+        // with a img-tag with data-udi id spelled wrong, so don't need stripping
+        [TestCase("hello  world ", "hello  world ")]
         public void ParseLocalLinks(string input, string result)
         {
             var serviceCtxMock = MockHelper.GetMockedServiceContext();
@@ -63,7 +70,7 @@ namespace Umbraco.Tests.Web
                 //setup a quick mock of the WebRouting section
                 Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")),
                 //pass in the custom url provider
-                new[]{ testUrlProvider.Object },
+                new[] { testUrlProvider.Object },
                 true))
             {
                 var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider);
@@ -71,5 +78,27 @@ namespace Umbraco.Tests.Web
                 Assert.AreEqual(result, output);
             }
         }
+        
+        [Test]
+        public void StripDataUdiAttributesUsingSrtringOnLinks()
+        {
+            var input = "hello  world ";
+            var expected = "hello  world ";
+           
+            var result = TemplateUtilities.StripUdiDataAttributes(input);
+
+            Assert.AreEqual(expected, result);
+        }
+
+        [Test]
+        public void StripDataUdiAttributesUsingStringOnImages()
+        {
+            var input = "hello  world ";
+            var expected = "hello  world ";
+
+            var result = TemplateUtilities.StripUdiDataAttributes(input);
+
+            Assert.AreEqual(expected, result);
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config
index 45afd3279c..e458795917 100644
--- a/src/Umbraco.Tests/packages.config
+++ b/src/Umbraco.Tests/packages.config
@@ -3,6 +3,7 @@
   
   
   
+  
   
   
   
diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs
index a7e6738374..39ac69989a 100644
--- a/src/Umbraco.Web/Templates/TemplateUtilities.cs
+++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs
@@ -1,4 +1,6 @@
-using System;
+using HtmlAgilityPack;
+using System;
+using System.Runtime.CompilerServices;
 using System.Text.RegularExpressions;
 using Umbraco.Core;
 using Umbraco.Core.Configuration;
@@ -46,6 +48,11 @@ namespace Umbraco.Web.Templates
         {
             if (urlProvider == null) throw new ArgumentNullException("urlProvider");
 
+            if(string.IsNullOrEmpty(text))
+            {
+                return text;
+            }
+
             // Parse internal links
             var tags = LocalLinkPattern.Matches(text);
             foreach (Match tag in tags)
@@ -74,6 +81,11 @@ namespace Umbraco.Web.Templates
                 }
             }
 
+            if (UmbracoConfig.For.UmbracoSettings().Content.StripUdiAttributes)
+            {
+                text = StripUdiDataAttributes(text);
+            }
+            
             return text;
         }
 
@@ -102,6 +114,9 @@ namespace Umbraco.Web.Templates
         private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?",
             RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
 
+        private static readonly Regex UdiDataAttributePattern = new Regex("data-udi=\"[^\\\"]*\"",
+            RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
         /// 
         /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path.
         /// 
@@ -145,5 +160,21 @@ namespace Umbraco.Web.Templates
         {
             return text.CleanForXss(ignoreFromClean);
         }
+        
+        /// 
+        /// Strips data-udi attributes from rich text
+        /// 
+        /// A html string
+        /// A string stripped from the data-uid attributes
+        public static string StripUdiDataAttributes(string input)
+        {
+            if (string.IsNullOrEmpty(input))
+            {
+                return string.Empty;
+            }
+
+
+            return UdiDataAttributePattern.Replace(input, string.Empty);
+        }
     }
 }

From 0e6376f4e543ed816a3b6b79d65e5defa97eac4e Mon Sep 17 00:00:00 2001
From: Chris Houston 
Date: Fri, 21 Sep 2018 08:36:01 -0400
Subject: [PATCH 48/61] Updated the URL for the issue tracker.

Also updated the version number to reflect the current version.
---
 src/Umbraco.Web.UI.Client/package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 017eb5dce2..b55767c797 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -2,14 +2,14 @@
   "author": "Umbraco HQ",
   "name": "umbraco",
   "homepage": "https://github.com/umbraco/umbraco-cms/",
-  "version": "7.1.2",
+  "version": "7.12.0",
   "license": "MIT",
   "repository": {
     "type": "git",
     "url": "https://github.com/umbraco/Umbraco-CMS.git"
   },
   "bugs": {
-    "url": "https://issues.umbraco.org"
+    "url": "https://github.com/umbraco/Umbraco-CMS/issues"
   },
   "engines": {
     "node": ">= 0.8.4"

From 258baad127b5454b3248ef273abafbef39758767 Mon Sep 17 00:00:00 2001
From: Sebastiaan Janssen 
Date: Wed, 26 Sep 2018 22:01:09 +0200
Subject: [PATCH 49/61] Removed uneccessary properties

---
 src/Umbraco.Web.UI.Client/package.json | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index b55767c797..850d344607 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -1,19 +1,4 @@
 {
-  "author": "Umbraco HQ",
-  "name": "umbraco",
-  "homepage": "https://github.com/umbraco/umbraco-cms/",
-  "version": "7.12.0",
-  "license": "MIT",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/umbraco/Umbraco-CMS.git"
-  },
-  "bugs": {
-    "url": "https://github.com/umbraco/Umbraco-CMS/issues"
-  },
-  "engines": {
-    "node": ">= 0.8.4"
-  },
   "scripts": {
     "install": "bower-installer",
     "test": "karma start test/config/karma.conf.js --singlerun",

From c021cc50ce8532acdd22720753d979b4d5f8e05c Mon Sep 17 00:00:00 2001
From: Marc Goodson 
Date: Wed, 26 Sep 2018 22:16:54 +0100
Subject: [PATCH 50/61] U4-10669 7.8. Show 301 url redirects on info tab too
 (#2452)

---
 .../content/umbcontentnodeinfo.directive.js   | 48 ++++++++++++++-----
 .../common/resources/redirecturls.resource.js | 31 +++++++++++-
 .../content/umb-content-node-info.html        | 18 +++++++
 src/Umbraco.Web.UI/umbraco/config/lang/en.xml |  6 ++-
 .../umbraco/config/lang/en_us.xml             |  6 ++-
 .../RedirectUrlManagementController.cs        | 30 ++++++++++--
 6 files changed, 117 insertions(+), 22 deletions(-)

diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js
index 7f9d05aebc..8f613469a3 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js
@@ -1,7 +1,7 @@
 (function () {
     'use strict';
 
-    function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper) {
+    function ContentNodeInfoDirective($timeout, $location, logResource, eventsService, userService, localizationService, dateHelper, redirectUrlsResource) {
 
         function link(scope, element, attrs, ctrl) {
 
@@ -46,6 +46,9 @@
                 // Make sure to set the node status
                 setNodePublishStatus(scope.node);
 
+                //default setting for redirect url management
+                scope.urlTrackerDisabled = false;
+
                 // Declare a fallback URL for the  directive
                 if (scope.documentType !== null) {
                     scope.previewOpenUrl = '#/settings/documenttypes/edit/' + scope.documentType.id;
@@ -99,7 +102,7 @@
 
                         // get current backoffice user and format dates
                         userService.getCurrentUser().then(function (currentUser) {
-                            angular.forEach(data.items, function(item) {
+                            angular.forEach(data.items, function (item) {
                                 item.timestampFormatted = dateHelper.getLocalDate(item.timestamp, currentUser.locale, 'LLL');
                             });
                         });
@@ -116,6 +119,25 @@
                     });
 
             }
+            function loadRedirectUrls() {
+                scope.loadingRedirectUrls = true;
+                //check if Redirect Url Management is enabled
+                redirectUrlsResource.getEnableState().then(function (response) {
+                    scope.urlTrackerDisabled = response.enabled !== true;
+                    if (scope.urlTrackerDisabled === false) {
+
+                        redirectUrlsResource.getRedirectsForContentItem(scope.node.udi)
+                            .then(function (data) {
+                                scope.redirectUrls = data.searchResults;
+                                scope.hasRedirects = (typeof data.searchResults !== 'undefined' && data.searchResults.length > 0);
+                                scope.loadingRedirectUrls = false;
+                            });
+                    }
+                    else {
+                        scope.loadingRedirectUrls = false;
+                    }
+                });
+            }
 
             function setAuditTrailLogTypeColor(auditTrail) {
                 angular.forEach(auditTrail, function (item) {
@@ -136,27 +158,27 @@
 
             function setNodePublishStatus(node) {
                 // deleted node
-                if(node.trashed === true) {
+                if (node.trashed === true) {
                     scope.publishStatus.label = localizationService.localize("general_deleted");
                     scope.publishStatus.color = "danger";
                 }
 
                 // unpublished node
-                if(node.published === false && node.trashed === false) {
+                if (node.published === false && node.trashed === false) {
                     scope.publishStatus.label = localizationService.localize("content_unpublished");
                     scope.publishStatus.color = "gray";
                 }
 
                 // published node
-                if(node.hasPublishedVersion === true && node.publishDate && node.published === true) {
+                if (node.hasPublishedVersion === true && node.publishDate && node.published === true) {
                     scope.publishStatus.label = localizationService.localize("content_published");
                     scope.publishStatus.color = "success";
                 }
 
                 // published node with pending changes
-                if(node.hasPublishedVersion === true && node.publishDate && node.published === false) {
+                if (node.hasPublishedVersion === true && node.publishDate && node.published === false) {
                     scope.publishStatus.label = localizationService.localize("content_publishedPendingChanges");
-                    scope.publishStatus.color = "success"
+                    scope.publishStatus.color = "success";
                 }
 
             }
@@ -252,12 +274,13 @@
                 });
             }
 
-            // load audit trail when on the info tab
+            // load audit trail and redirects when on the info tab
             evts.push(eventsService.on("app.tabChange", function (event, args) {
-                $timeout(function(){
+                $timeout(function () {
                     if (args.id === -1) {
                         isInfoTab = true;
                         loadAuditTrail();
+                        loadRedirectUrls();
                     } else {
                         isInfoTab = false;
                     }
@@ -265,13 +288,14 @@
             }));
 
             // watch for content updates - reload content when node is saved, published etc.
-            scope.$watch('node.updateDate', function(newValue, oldValue){
+            scope.$watch('node.updateDate', function (newValue, oldValue) {
 
-                if(!newValue) { return; }
-                if(newValue === oldValue) { return; }
+                if (!newValue) { return; }
+                if (newValue === oldValue) { return; }
 
                 if(isInfoTab) {
                     loadAuditTrail();
+                    loadRedirectUrls();
                     formatDatesToLocal();
                     setNodePublishStatus(scope.node);
                 }
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js
index ea40c066f0..fb145418ed 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/redirecturls.resource.js
@@ -40,6 +40,32 @@
                         { searchTerm: searchTerm, page: pageIndex, pageSize: pageSize })),
                 'Failed to retrieve data for searching redirect urls');
         }
+        /**
+   * @ngdoc function
+   * @name umbraco.resources.redirectUrlResource#getRedirectsForContentItem
+   * @methodOf umbraco.resources.redirectUrlResource
+   * @function
+   *
+   * @description
+   * Used to retrieve RedirectUrls for a specific item of content for Information tab
+   * ##usage
+   * 
+   * redirectUrlsResource.getRedirectsForContentItem("udi:123456")
+   *    .then(function(response) {
+   *
+   *    });
+   * 
+ * @param {String} contentUdi identifier for the content item to retrieve redirects for + */ + function getRedirectsForContentItem(contentUdi) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "redirectUrlManagementApiBaseUrl", + "RedirectUrlsForContentItem", + { contentUdi: contentUdi })), + 'Failed to retrieve redirects for content: ' + contentUdi); + } function getEnableState() { @@ -50,7 +76,7 @@ "GetEnableState")), 'Failed to retrieve data to check if the 301 redirect is enabled'); } - + /** * @ngdoc function * @name umbraco.resources.redirectUrlResource#deleteRedirectUrl @@ -107,7 +133,8 @@ searchRedirectUrls: searchRedirectUrls, deleteRedirectUrl: deleteRedirectUrl, toggleUrlTracker: toggleUrlTracker, - getEnableState: getEnableState + getEnableState: getEnableState, + getRedirectsForContentItem: getRedirectsForContentItem }; return resource; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 7e80aa2fca..0706e9596c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -19,6 +19,23 @@ + + + +
+
+ +
+

The following URLs redirect to this content item:

+ +
+
+
+
@@ -61,6 +78,7 @@
+ {{ item.logType }} diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index f7a5ac5ef4..7ec238408b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1688,8 +1688,8 @@ To manage your website, simply open the Umbraco back office and start adding con tab has no sort order - Where is this composition used? - This composition is currently used in the composition of the following content types: + Where is this composition used? + This composition is currently used in the composition of the following content types: @@ -2186,6 +2186,8 @@ To manage your website, simply open the Umbraco back office and start adding con Enable URL tracker Original URL Redirected To + Redirect Url Management + The following URLs redirect to this content item: No redirects have been made When a published page gets renamed or moved a redirect will automatically be made to the new page. Remove 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 655c619095..a7fad7132b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1681,8 +1681,8 @@ To manage your website, simply open the Umbraco back office and start adding con tab has no sort order - Where is this composition used? - This composition is currently used in the composition of the following content types: + Where is this composition used? + This composition is currently used in the composition of the following content types: @@ -2178,6 +2178,8 @@ To manage your website, simply open the Umbraco back office and start adding con Enable URL tracker Original URL Redirected To + Redirect Url Management + The following URLs redirect to this content item: No redirects have been made When a published page gets renamed or moved a redirect will automatically be made to the new page. Remove diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index 9fb7a63c9f..f9a2807003 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -12,6 +12,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using File = System.IO.File; +using Umbraco.Core; namespace Umbraco.Web.Editors { @@ -39,7 +40,7 @@ namespace Umbraco.Web.Editors var redirectUrlService = Services.RedirectUrlService; long resultCount; - var redirects = string.IsNullOrWhiteSpace(searchTerm) + var redirects = string.IsNullOrWhiteSpace(searchTerm) ? redirectUrlService.GetAllRedirectUrls(page, pageSize, out resultCount) : redirectUrlService.SearchRedirectUrls(searchTerm, page, pageSize, out resultCount); @@ -53,11 +54,32 @@ namespace Umbraco.Web.Editors searchResult.TotalCount = resultCount; searchResult.CurrentPage = page; searchResult.PageCount = ((int)resultCount + pageSize - 1) / pageSize; - + return searchResult; } - + /// + /// This lists the RedirectUrls for a particular content item + /// Do we need to consider paging here? + /// + /// Udi of content item to retrieve RedirectUrls for + /// + [HttpGet] + public RedirectUrlSearchResult RedirectUrlsForContentItem(string contentUdi) + { + var redirectsResult = new RedirectUrlSearchResult(); + if (GuidUdi.TryParse(contentUdi, out var guidIdi)) + { + var redirectUrlService = Services.RedirectUrlService; + var redirects = redirectUrlService.GetContentRedirectUrls(guidIdi.Guid); + redirectsResult.SearchResults = Mapper.Map>(redirects).ToArray(); + //not doing paging 'yet' + redirectsResult.TotalCount = redirects.Count(); + redirectsResult.CurrentPage = 1; + redirectsResult.PageCount = 1; + } + return redirectsResult; + } [HttpPost] public IHttpActionResult DeleteRedirectUrl(Guid id) { @@ -100,4 +122,4 @@ namespace Umbraco.Web.Editors return Ok(string.Format("URL tracker is now {0}d", action)); } } -} \ No newline at end of file +} From fce0dd98f960bf1b8c8b8313432b6db196b4ae1e Mon Sep 17 00:00:00 2001 From: Mark Drake Date: Thu, 27 Sep 2018 13:34:20 -0400 Subject: [PATCH 51/61] minimal changes to class nav-tabs styling --- src/Umbraco.Web.UI.Client/src/less/navs.less | 11 ++--------- src/Umbraco.Web.UI.Client/src/less/panel.less | 4 +++- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/navs.less b/src/Umbraco.Web.UI.Client/src/less/navs.less index 0a8c6913e8..329281c0b0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/navs.less +++ b/src/Umbraco.Web.UI.Client/src/less/navs.less @@ -119,13 +119,8 @@ // TABS // ---- -// Give the tabs something to sit on -.nav-tabs { - border-bottom: 1px solid @purple-l3; -} // Make the list-items overlay the bottom border .nav-tabs > li { - margin-bottom: -1px; &.dropdown { margin-bottom: -3px; @@ -137,11 +132,13 @@ } // Actual tabs (as links) .nav-tabs > li > a { + position: relative; color: @gray-3; padding-top: 5px; padding-bottom: 4px; line-height: @baseLineHeight; border: 1px solid transparent; + transform: translateY(1px); &:hover { color: @black; @@ -183,10 +180,6 @@ .show-validation .nav-tabs > li.active.error > a:focus { } -.umb-nav-tabs { - margin: -8px 0 0 0; -} - // PILLS // ----- diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 8005d37ba8..48e28e7ed3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -358,6 +358,7 @@ flex-direction: column; height: 99px; padding: 0 20px; + overflow-y: hidden; } .umb-panel-header-content { @@ -436,7 +437,8 @@ } .umb-panel-header .umb-nav-tabs { - bottom: -1px; + bottom: 0; + margin: 0; } input.umb-panel-header-name-input { From 12a9466cd7210ec079c58d66f3cf32b7b0641e6d Mon Sep 17 00:00:00 2001 From: Mark Drake Date: Thu, 27 Sep 2018 17:03:24 -0400 Subject: [PATCH 52/61] if tinymce has not loaded yet, return the initialized value only --- .../src/views/propertyeditors/rte/rte.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 5e37bcce5d..212db4fc31 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -377,7 +377,7 @@ angular.module("umbraco") var unsubscribe = $scope.$on("formSubmitting", function () { //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer // we do parse it out on the server side but would be nice to do that on the client side before as well. - if (tinyMceEditor !== undefined && tinyMceEditor != null) { + if (tinyMceEditor !== undefined && tinyMceEditor != null && !$scope.isLoading) { $scope.model.value = tinyMceEditor.getContent(); } }); From 7f107a8559f17f23453d1f3076b696931d249fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn?= Date: Sat, 29 Sep 2018 17:42:45 +0200 Subject: [PATCH 53/61] Make TagSupport and related types public #2988 --- src/Umbraco.Core/Models/Property.cs | 4 ++-- src/Umbraco.Core/Models/PropertyTagBehavior.cs | 4 ++-- src/Umbraco.Core/Models/PropertyTags.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 5df2eaae90..25ad161a1a 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -83,7 +83,7 @@ namespace Umbraco.Core.Models /// /// Returns the instance of the tag support, by default tags are not enabled /// - internal PropertyTags TagSupport + public PropertyTags TagSupport { get { return _tagSupport; } } @@ -242,4 +242,4 @@ namespace Umbraco.Core.Models return clone; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/PropertyTagBehavior.cs b/src/Umbraco.Core/Models/PropertyTagBehavior.cs index 0a435ebf95..ad20d598a4 100644 --- a/src/Umbraco.Core/Models/PropertyTagBehavior.cs +++ b/src/Umbraco.Core/Models/PropertyTagBehavior.cs @@ -1,9 +1,9 @@ namespace Umbraco.Core.Models { - internal enum PropertyTagBehavior + public enum PropertyTagBehavior { Replace, Remove, Merge } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/PropertyTags.cs b/src/Umbraco.Core/Models/PropertyTags.cs index 6075502131..22bb4b5caa 100644 --- a/src/Umbraco.Core/Models/PropertyTags.cs +++ b/src/Umbraco.Core/Models/PropertyTags.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Models /// /// A property extension class that allows us to enable tags for any given property /// - internal class PropertyTags + public class PropertyTags { public PropertyTags() { @@ -33,4 +33,4 @@ namespace Umbraco.Core.Models public IEnumerable> Tags { get; set; } } -} \ No newline at end of file +} From 865cdc09c6bd0e0fef342f09139a82b5120bf3fd Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sun, 30 Sep 2018 10:50:05 +0200 Subject: [PATCH 54/61] U4-11588 - Adjustments of color picker (#2864) --- .../forms/hexbackgroundcolor.directive.js | 54 +++++++++-------- .../components/umbcolorswatches.directive.js | 15 +++-- .../components/prevalues/multivalues.less | 1 + .../less/components/umb-color-swatches.less | 59 ++++++++++++++++++- .../src/less/property-editors.less | 29 +-------- .../overlays/iconpicker/iconpicker.html | 8 ++- .../views/components/umb-color-swatches.html | 14 +++-- .../colorpicker/colorpicker.html | 23 +++----- 8 files changed, 123 insertions(+), 80 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/hexbackgroundcolor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/hexbackgroundcolor.directive.js index 5a425b7c90..eb64439e0b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/hexbackgroundcolor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/hexbackgroundcolor.directive.js @@ -11,32 +11,36 @@ function hexBgColor() { restrict: "A", link: function (scope, element, attr, formCtrl) { - var origColor = null; - if (attr.hexBgOrig) { - //set the orig based on the attribute if there is one - origColor = attr.hexBgOrig; - } - - attr.$observe("hexBgColor", function (newVal) { - if (newVal) { - if (!origColor) { - //get the orig color before changing it - origColor = element.css("border-color"); - } - //validate it - test with and without the leading hash. - if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { - element.css("background-color", "#" + newVal); - return; - } - if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { - element.css("background-color", newVal); - return; - } - } - element.css("background-color", origColor); - }); + // Only add inline hex background color if defined and not "true". + if (attr.hexBgInline === undefined || (attr.hexBgInline !== undefined && attr.hexBgInline === "true")) { + var origColor = null; + if (attr.hexBgOrig) { + // Set the orig based on the attribute if there is one. + origColor = attr.hexBgOrig; + } + + attr.$observe("hexBgColor", function (newVal) { + if (newVal) { + if (!origColor) { + // Get the orig color before changing it. + origColor = element.css("border-color"); + } + // Validate it - test with and without the leading hash. + if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { + element.css("background-color", "#" + newVal); + return; + } + if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { + element.css("background-color", newVal); + return; + } + } + + element.css("background-color", origColor); + }); + } } }; } -angular.module('umbraco.directives').directive("hexBgColor", hexBgColor); \ No newline at end of file +angular.module('umbraco.directives').directive("hexBgColor", hexBgColor); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js index 6ed65a9431..dc67cb464b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbcolorswatches.directive.js @@ -1,5 +1,4 @@ - -/** +/** @ngdoc directive @name umbraco.directives.directive:umbColorSwatches @restrict E @@ -15,9 +14,10 @@ Use this directive to generate color swatches to pick from.
@param {array} colors (attribute): The array of colors. -@param {string} colors (attribute): The array of colors. @param {string} selectedColor (attribute): The selected color. @param {string} size (attribute): The size (s, m). +@param {string} useLabel (attribute): Specify if labels should be used. +@param {string} useColorClass (attribute): Specify if color values are css classes. @param {function} onSelect (expression): Callback function when the item is selected. **/ @@ -28,6 +28,11 @@ Use this directive to generate color swatches to pick from. function link(scope, el, attr, ctrl) { + // Set default to true if not defined + if (angular.isUndefined(scope.useColorClass)) { + scope.useColorClass = false; + } + scope.setColor = function (color) { //scope.selectedColor({color: color }); scope.selectedColor = color; @@ -47,7 +52,9 @@ Use this directive to generate color swatches to pick from. colors: '=?', size: '@', selectedColor: '=', - onSelect: '&' + onSelect: '&', + useLabel: '=', + useColorClass: '=?' }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less index 5363a8db9b..43f6697eb1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/prevalues/multivalues.less @@ -10,6 +10,7 @@ .umb-prevalues-multivalues__left { display: flex; flex: 1 1 auto; + overflow: hidden; } .umb-prevalues-multivalues__right { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less index e27bd0d654..df80aef2ce 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-color-swatches.less @@ -1,18 +1,22 @@ .umb-color-swatches { + display: flex; + flex-flow: row wrap; .umb-color-box { border: none; color: white; cursor: pointer; - padding: 5px; + padding: 1px; text-align: center; text-decoration: none; - display: inline-block; margin: 5px; border-radius: 3px; width: 30px; height: 30px; transition: box-shadow .3s; + display: flex; + align-items: center; + justify-content: center; &:hover, &:focus { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); @@ -34,4 +38,55 @@ } } } + + &.with-labels { + + .umb-color-box { + width: 120px; + height: 100%; + display: flex; + flex-flow: row wrap; + + .umb-color-box-inner { + display: flex; + flex-flow: column wrap; + flex: 0 0 100%; + max-width: 100%; + min-height: 80px; + padding-top: 10px; + + .umb-color-box__label { + background: #fff; + font-size: 14px; + display: flex; + flex-flow: column wrap; + flex: 0 0 100%; + padding: 1px 5px; + max-width: 100%; + margin-top: auto; + margin-bottom: -3px; + margin-left: -1px; + margin-right: -1px; + text-indent: 0; + text-align: left; + border: 1px solid @gray-8; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + overflow: hidden; + + .umb-color-box__name { + color: @black; + font-weight: bold; + margin-top: 3px; + } + + .umb-color-box__description { + font-size: 12px; + line-height: 1.5em; + color: @gray-3; + } + } + } + } + } } 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 fabe7c2291..88e5842b91 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -133,33 +133,6 @@ div.umb-codeeditor .umb-btn-toolbar { // // Color picker // -------------------------------------------------- -ul.color-picker li { - padding: 2px; - margin: 3px; - border: 2px solid transparent; - width: 60px; - - &.active { - .check_circle { - opacity: 1; - } - } - - .thumbnail{ - min-width: auto; - width: inherit; - padding: 0; - } - - a { - height: 50px; - display:flex; - align-items: center; - justify-content: center; - cursor:pointer; - margin: 0 0 5px; - } -} /* pre-value editor */ .control-group.color-picker-preval { @@ -180,7 +153,7 @@ ul.color-picker li { div.color-picker-prediv { display: inline-flex; align-items: center; - max-width: 80%; + max-width: 85%; pre { display: inline-flex; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html index 0098463cb4..de99c2a143 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html @@ -1,5 +1,3 @@ - -
@@ -17,7 +15,11 @@
- + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html index eae299e579..a89e51ab32 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html @@ -1,8 +1,14 @@ -
+
- diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html index 8e4a8d2a63..ccea7519ac 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html @@ -1,20 +1,15 @@ 
-
- You haven't defined any colors -
+
+ You haven't defined any colors +
- + + - +
From 3c73f6a764ab49accd275c35d19f76a47f0dedca Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Sun, 30 Sep 2018 10:59:20 +0200 Subject: [PATCH 55/61] Rename name property so title gets set for the doctype icon picker - relates to PR #2864 --- .../iconpicker/iconpicker.controller.js | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js index 3c36505fcb..d9f1c7b6e4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js @@ -12,25 +12,25 @@ function IconPickerOverlay($scope, iconHelper, localizationService) { $scope.model.hideSubmitButton = false; $scope.colors = [ - { name: "Black", value: "color-black" }, - { name: "Blue Grey", value: "color-blue-grey" }, - { name: "Grey", value: "color-grey" }, - { name: "Brown", value: "color-brown" }, - { name: "Blue", value: "color-blue" }, - { name: "Light Blue", value: "color-light-blue" }, - { name: "Indigo", value: "color-indigo" }, - { name: "Purple", value: "color-purple" }, - { name: "Deep Purple", value: "color-deep-purple" }, - { name: "Cyan", value: "color-cyan" }, - { name: "Green", value: "color-green" }, - { name: "Light Green", value: "color-light-green" }, - { name: "Lime", value: "color-lime" }, - { name: "Yellow", value: "color-yellow" }, - { name: "Amber", value: "color-amber" }, - { name: "Orange", value: "color-orange" }, - { name: "Deep Orange", value: "color-deep-orange" }, - { name: "Red", value: "color-red" }, - { name: "Pink", value: "color-pink" } + { label: "Black", value: "color-black" }, + { label: "Blue Grey", value: "color-blue-grey" }, + { label: "Grey", value: "color-grey" }, + { label: "Brown", value: "color-brown" }, + { label: "Blue", value: "color-blue" }, + { label: "Light Blue", value: "color-light-blue" }, + { label: "Indigo", value: "color-indigo" }, + { label: "Purple", value: "color-purple" }, + { label: "Deep Purple", value: "color-deep-purple" }, + { label: "Cyan", value: "color-cyan" }, + { label: "Green", value: "color-green" }, + { label: "Light Green", value: "color-light-green" }, + { label: "Lime", value: "color-lime" }, + { label: "Yellow", value: "color-yellow" }, + { label: "Amber", value: "color-amber" }, + { label: "Orange", value: "color-orange" }, + { label: "Deep Orange", value: "color-deep-orange" }, + { label: "Red", value: "color-red" }, + { label: "Pink", value: "color-pink" } ]; if (!$scope.color) { From 5f1fb144e08093b685cf35a4266da59c71cc3b7f Mon Sep 17 00:00:00 2001 From: imranhaidercogworks Date: Sun, 30 Sep 2018 12:09:44 +0100 Subject: [PATCH 56/61] Uploading SVG's Causes Error (Depending on imageFileTypes Setting) (#3072) --- .../UmbracoSettings/IContentSection.cs | 2 +- src/Umbraco.Core/Constants-Conventions.cs | 7 ++- src/Umbraco.Core/IO/MediaFileSystem.cs | 57 ++++++++++--------- src/Umbraco.Core/Media/Exif/ImageFile.cs | 36 ++++++------ .../Media/Exif/ImageFileFormat.cs | 4 ++ src/Umbraco.Core/Media/Exif/SVGFile.cs | 37 ++++++++++++ .../Media/TypeDetector/JpegDetector.cs | 14 +++++ .../TypeDetector/RasterizedTypeDetector.cs | 16 ++++++ .../Media/TypeDetector/SVGDetector.cs | 24 ++++++++ .../Media/TypeDetector/TIFFDetector.cs | 24 ++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 5 ++ .../imaging/umbimagegravity.directive.js | 36 +++++++----- .../imagecropper/imagecropper.html | 4 +- .../config/umbracoSettings.config | 4 +- src/Umbraco.Web/Editors/MediaController.cs | 2 +- .../FileUploadPropertyValueEditor.cs | 4 +- .../ImageCropperPropertyValueEditor.cs | 2 +- src/umbraco.businesslogic/IO/IOHelper.cs | 32 +++++------ 18 files changed, 225 insertions(+), 85 deletions(-) create mode 100644 src/Umbraco.Core/Media/Exif/SVGFile.cs create mode 100644 src/Umbraco.Core/Media/TypeDetector/JpegDetector.cs create mode 100644 src/Umbraco.Core/Media/TypeDetector/RasterizedTypeDetector.cs create mode 100644 src/Umbraco.Core/Media/TypeDetector/SVGDetector.cs create mode 100644 src/Umbraco.Core/Media/TypeDetector/TIFFDetector.cs diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 6c0d8327f1..bfcbabaccd 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IEnumerable ImageTagAllowedAttributes { get; } IEnumerable ImageAutoFillProperties { get; } - + string ScriptFolderPath { get; } IEnumerable ScriptFileTypes { get; } diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index a19e59f4ef..a56b40005e 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -110,6 +110,11 @@ namespace Umbraco.Core /// Property alias for the Media's file extension. ///
public const string Extension = "umbracoExtension"; + + /// + /// The default height/width of an image file if the size can't be determined from the metadata + /// + public const int DefaultSize = 200; } /// @@ -354,4 +359,4 @@ namespace Umbraco.Core } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index fc6490e8cd..37fcdeba70 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -17,15 +17,15 @@ using Umbraco.Core.Models; namespace Umbraco.Core.IO { - /// - /// A custom file system provider for media - /// - [FileSystemProvider("media")] - public class MediaFileSystem : FileSystemWrapper - { - private readonly IContentSection _contentConfig; + /// + /// A custom file system provider for media + /// + [FileSystemProvider("media")] + public class MediaFileSystem : FileSystemWrapper + { + private readonly IContentSection _contentConfig; private readonly UploadAutoFillProperties _uploadAutoFillProperties; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly object _folderCounterLock = new object(); private long _folderCounter; @@ -39,8 +39,8 @@ namespace Umbraco.Core.IO }; public MediaFileSystem(IFileSystem wrapped) - : this(wrapped, UmbracoConfig.For.UmbracoSettings().Content, ApplicationContext.Current.ProfilingLogger.Logger) - { } + : this(wrapped, UmbracoConfig.For.UmbracoSettings().Content, ApplicationContext.Current.ProfilingLogger.Logger) + { } public MediaFileSystem(IFileSystem wrapped, IContentSection contentConfig, ILogger logger) : base(wrapped) @@ -60,13 +60,13 @@ namespace Umbraco.Core.IO [Obsolete("This low-level method should NOT exist.")] public string GetRelativePath(int propertyId, string fileName) - { + { var sep = _contentConfig.UploadAllowDirectories - ? Path.DirectorySeparatorChar - : '-'; + ? Path.DirectorySeparatorChar + : '-'; - return propertyId.ToString(CultureInfo.InvariantCulture) + sep + fileName; - } + return propertyId.ToString(CultureInfo.InvariantCulture) + sep + fileName; + } [Obsolete("This low-level method should NOT exist.", false)] public string GetRelativePath(string subfolder, string fileName) @@ -264,7 +264,7 @@ namespace Umbraco.Core.IO var filename = Path.GetFileName(sourcepath); var filepath = GetMediaPath(filename, content.Key, propertyType.Key); this.CopyFile(sourcepath, filepath); - + return filepath; } @@ -321,7 +321,7 @@ namespace Umbraco.Core.IO /// /// private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream) - { + { // will use filepath for extension, and filestream for length _uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream); } @@ -368,19 +368,20 @@ namespace Umbraco.Core.IO return new Size(width, height); } } + + //we have no choice but to try to read in via GDI + using (var image = Image.FromStream(stream)) + { + + var fileWidth = image.Width; + var fileHeight = image.Height; + return new Size(fileWidth, fileHeight); + } } catch (Exception) { //We will just swallow, just means we can't read exif data, we don't want to log an error either - } - - //we have no choice but to try to read in via GDI - using (var image = Image.FromStream(stream)) - { - - var fileWidth = image.Width; - var fileHeight = image.Height; - return new Size(fileWidth, fileHeight); + return new Size(Constants.Conventions.Media.DefaultSize, Constants.Conventions.Media.DefaultSize); } } @@ -430,8 +431,8 @@ namespace Umbraco.Core.IO } } - public void DeleteMediaFiles(IEnumerable files) - { + public void DeleteMediaFiles(IEnumerable files) + { files = files.Distinct(); Parallel.ForEach(files, file => diff --git a/src/Umbraco.Core/Media/Exif/ImageFile.cs b/src/Umbraco.Core/Media/Exif/ImageFile.cs index 66350338e9..05a2a955c4 100644 --- a/src/Umbraco.Core/Media/Exif/ImageFile.cs +++ b/src/Umbraco.Core/Media/Exif/ImageFile.cs @@ -2,6 +2,7 @@ using System.Drawing; using System.IO; using System.Text; +using Umbraco.Core.Media.TypeDetector; namespace Umbraco.Core.Media.Exif { @@ -118,22 +119,25 @@ namespace Umbraco.Core.Media.Exif /// The created from the file. public static ImageFile FromStream(Stream stream, Encoding encoding) { - stream.Seek (0, SeekOrigin.Begin); - byte[] header = new byte[8]; - stream.Seek (0, SeekOrigin.Begin); - if (stream.Read (header, 0, header.Length) != header.Length) - throw new NotValidImageFileException (); - - // JPEG - if (header[0] == 0xFF && header[1] == 0xD8) - return new JPEGFile (stream, encoding); - - // TIFF - string tiffHeader = Encoding.ASCII.GetString (header, 0, 4); - if (tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00") - return new TIFFFile (stream, encoding); - - throw new NotValidImageFileException (); + // JPEG + if(JPEGDetector.IsOfType(stream)) + { + return new JPEGFile(stream, encoding); + } + + // TIFF + if (TIFFDetector.IsOfType(stream)) + { + return new TIFFFile(stream, encoding); + } + + // SVG + if (SVGDetector.IsOfType(stream)) + { + return new SVGFile(stream); + } + + throw new NotValidImageFileException (); } #endregion } diff --git a/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs b/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs index 8e1433c947..964b33e6ea 100644 --- a/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs +++ b/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs @@ -17,5 +17,9 @@ /// The file is a TIFF File. /// TIFF, + /// + /// The file is a SVG File. + /// + SVG, } } diff --git a/src/Umbraco.Core/Media/Exif/SVGFile.cs b/src/Umbraco.Core/Media/Exif/SVGFile.cs new file mode 100644 index 0000000000..bc4d94749c --- /dev/null +++ b/src/Umbraco.Core/Media/Exif/SVGFile.cs @@ -0,0 +1,37 @@ +using System; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace Umbraco.Core.Media.Exif +{ + internal class SVGFile : ImageFile + { + public SVGFile(Stream fileStream) + { + fileStream.Position = 0; + + var document = XDocument.Load(fileStream); //if it throws an exception the ugly try catch in MediaFileSystem will catch it + + var width = document.Root?.Attributes().Where(x => x.Name == "width").Select(x => x.Value).FirstOrDefault(); + var height = document.Root?.Attributes().Where(x => x.Name == "height").Select(x => x.Value).FirstOrDefault(); + + Properties.Add(new ExifSInt(ExifTag.PixelYDimension, + height == null ? Constants.Conventions.Media.DefaultSize : int.Parse(height))); + Properties.Add(new ExifSInt(ExifTag.PixelXDimension, + width == null ? Constants.Conventions.Media.DefaultSize : int.Parse(width))); + + Format = ImageFileFormat.SVG; + } + + public override void Save(Stream stream) + { + } + + public override Image ToImage() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Core/Media/TypeDetector/JpegDetector.cs b/src/Umbraco.Core/Media/TypeDetector/JpegDetector.cs new file mode 100644 index 0000000000..5b68f6ad88 --- /dev/null +++ b/src/Umbraco.Core/Media/TypeDetector/JpegDetector.cs @@ -0,0 +1,14 @@ +using System.IO; + +namespace Umbraco.Core.Media.TypeDetector +{ + public class JPEGDetector : RasterizedTypeDetector + { + public static bool IsOfType(Stream fileStream) + { + var header = GetFileHeader(fileStream); + + return header[0] == 0xff && header[1] == 0xD8; + } + } +} diff --git a/src/Umbraco.Core/Media/TypeDetector/RasterizedTypeDetector.cs b/src/Umbraco.Core/Media/TypeDetector/RasterizedTypeDetector.cs new file mode 100644 index 0000000000..9777d55c20 --- /dev/null +++ b/src/Umbraco.Core/Media/TypeDetector/RasterizedTypeDetector.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Umbraco.Core.Media.TypeDetector +{ + public abstract class RasterizedTypeDetector + { + public static byte[] GetFileHeader(Stream fileStream) + { + fileStream.Seek(0, SeekOrigin.Begin); + byte[] header = new byte[8]; + fileStream.Seek(0, SeekOrigin.Begin); + + return header; + } + } +} diff --git a/src/Umbraco.Core/Media/TypeDetector/SVGDetector.cs b/src/Umbraco.Core/Media/TypeDetector/SVGDetector.cs new file mode 100644 index 0000000000..ab9659ca1e --- /dev/null +++ b/src/Umbraco.Core/Media/TypeDetector/SVGDetector.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Xml.Linq; + +namespace Umbraco.Core.Media.TypeDetector +{ + public class SVGDetector + { + public static bool IsOfType(Stream fileStream) + { + var document = new XDocument(); + + try + { + document = XDocument.Load(fileStream); + } + catch (System.Exception ex) + { + return false; + } + + return document.Root?.Name.LocalName == "svg"; + } + } +} diff --git a/src/Umbraco.Core/Media/TypeDetector/TIFFDetector.cs b/src/Umbraco.Core/Media/TypeDetector/TIFFDetector.cs new file mode 100644 index 0000000000..2a6e42d0e0 --- /dev/null +++ b/src/Umbraco.Core/Media/TypeDetector/TIFFDetector.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Text; + +namespace Umbraco.Core.Media.TypeDetector +{ + public class TIFFDetector + { + public static bool IsOfType(Stream fileStream) + { + string tiffHeader = GetFileHeader(fileStream); + + return tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00"; + } + + public static string GetFileHeader(Stream fileStream) + { + var header = RasterizedTypeDetector.GetFileHeader(fileStream); + + string tiffHeader = Encoding.ASCII.GetString(header, 0, 4); + + return tiffHeader; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index aa3b2db784..c7393e524f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -365,6 +365,11 @@ + + + + + diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js index e47032fed3..a7f053fcae 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js @@ -31,10 +31,10 @@ angular.module("umbraco.directives") //elements var $viewport = element.find(".viewport"); var $image = element.find("img"); - var $overlay = element.find(".overlay"); + var $overlay = element.find(".overlay"); - scope.style = function () { - if(scope.dimensions.width <= 0){ + scope.style = function () { + if (scope.dimensions.width <= 0) { setDimensions(); } @@ -45,23 +45,29 @@ angular.module("umbraco.directives") }; scope.setFocalPoint = function(event) { + scope.$emit("imageFocalPointStart"); - scope.$emit("imageFocalPointStart"); + var offsetX = event.offsetX - 10; + var offsetY = event.offsetY - 10; - var offsetX = event.offsetX - 10; - var offsetY = event.offsetY - 10; - - calculateGravity(offsetX, offsetY); - - lazyEndEvent(); + calculateGravity(offsetX, offsetY); + lazyEndEvent(); }; - var setDimensions = function(){ - scope.dimensions.width = $image.width(); - scope.dimensions.height = $image.height(); - - if(scope.center){ + var setDimensions = function () { + if (scope.src.endsWith(".svg")) { + // svg files don't automatically get a size by + // loading them set a default size for now + $image.attr("width", "200"); + $image.attr("height", "200"); + // can't crop an svg file, don't show the focal point + $overlay.remove(); + } + scope.dimensions.width = $image.width(); + scope.dimensions.height = $image.height(); + + if(scope.center){ scope.dimensions.left = scope.center.left * scope.dimensions.width -10; scope.dimensions.top = scope.center.top * scope.dimensions.height -10; }else{ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index e1c0118497..5c6f156aab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -46,8 +46,8 @@ Remove file - -
    + +
    • - jpeg,jpg,gif,bmp,png,tiff,tif + jpeg,jpg,gif,bmp,png,tiff,tif,svg src,alt,border,class,style,align,id,name,onclick,usemap @@ -105,7 +105,7 @@ throw - ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,svg,php,htaccess + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,php,htaccess diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 86e35ccc73..a7bc3c2707 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -942,4 +942,4 @@ namespace Umbraco.Web.Editors return hasPathAccess; } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs index 777a14b768..72ca5c5c59 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -110,7 +110,7 @@ namespace Umbraco.Web.PropertyEditors _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! var ext = _mediaFileSystem.GetExtension(filepath); - if (_mediaFileSystem.IsImageFile(ext)) + if (_mediaFileSystem.IsImageFile(ext) && ext != ".svg") { var preValues = editorValue.PreValues.FormatAsDictionary(); var sizes = preValues.Any() ? preValues.First().Value.Value : string.Empty; @@ -137,4 +137,4 @@ namespace Umbraco.Web.PropertyEditors return string.Join(",", newPaths.Select(x => _mediaFileSystem.GetUrl(x))); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs index 0e6500f3d8..ccee138486 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -154,7 +154,7 @@ namespace Umbraco.Web.PropertyEditors _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! var ext = _mediaFileSystem.GetExtension(filepath); - if (_mediaFileSystem.IsImageFile(ext)) + if (_mediaFileSystem.IsImageFile(ext) && ext != ".svg") { var preValues = editorValue.PreValues.FormatAsDictionary(); var sizes = preValues.Any() ? preValues.First().Value.Value : string.Empty; diff --git a/src/umbraco.businesslogic/IO/IOHelper.cs b/src/umbraco.businesslogic/IO/IOHelper.cs index 8a7e10d9eb..295e0df82d 100644 --- a/src/umbraco.businesslogic/IO/IOHelper.cs +++ b/src/umbraco.businesslogic/IO/IOHelper.cs @@ -13,9 +13,9 @@ using umbraco.businesslogic.Exceptions; namespace umbraco.IO { - [Obsolete("Use Umbraco.Core.IO.IOHelper instead")] + [Obsolete("Use Umbraco.Core.IO.IOHelper instead")] public static class IOHelper - { + { public static char DirSepChar { get @@ -27,42 +27,42 @@ namespace umbraco.IO //helper to try and match the old path to a new virtual one public static string FindFile(string virtualPath) { - return Umbraco.Core.IO.IOHelper.FindFile(virtualPath); + return Umbraco.Core.IO.IOHelper.FindFile(virtualPath); } //Replaces tildes with the root dir public static string ResolveUrl(string virtualPath) { - return Umbraco.Core.IO.IOHelper.ResolveUrl(virtualPath); + return Umbraco.Core.IO.IOHelper.ResolveUrl(virtualPath); } - [Obsolete("Use Umbraco.Web.Templates.TemplateUtilities.ResolveUrlsFromTextString instead, this method on this class will be removed in future versions")] + [Obsolete("Use Umbraco.Web.Templates.TemplateUtilities.ResolveUrlsFromTextString instead, this method on this class will be removed in future versions")] public static string ResolveUrlsFromTextString(string text) { - return Umbraco.Core.IO.IOHelper.ResolveUrlsFromTextString(text); + return Umbraco.Core.IO.IOHelper.ResolveUrlsFromTextString(text); } public static string MapPath(string path, bool useHttpContext) { - return Umbraco.Core.IO.IOHelper.MapPath(path, useHttpContext); + return Umbraco.Core.IO.IOHelper.MapPath(path, useHttpContext); } public static string MapPath(string path) { - return Umbraco.Core.IO.IOHelper.MapPath(path); + return Umbraco.Core.IO.IOHelper.MapPath(path); } //use a tilde character instead of the complete path - [Obsolete("This method is no longer in use and will be removed in future versions")] + [Obsolete("This method is no longer in use and will be removed in future versions")] public static string returnPath(string settingsKey, string standardPath, bool useTilde) { - return Umbraco.Core.IO.IOHelper.ReturnPath(settingsKey, standardPath, useTilde); + return Umbraco.Core.IO.IOHelper.ReturnPath(settingsKey, standardPath, useTilde); } - [Obsolete("This method is no longer in use and will be removed in future versions")] + [Obsolete("This method is no longer in use and will be removed in future versions")] public static string returnPath(string settingsKey, string standardPath) { - return Umbraco.Core.IO.IOHelper.ReturnPath(settingsKey, standardPath); + return Umbraco.Core.IO.IOHelper.ReturnPath(settingsKey, standardPath); } @@ -75,12 +75,12 @@ namespace umbraco.IO /// true if valid, throws a FileSecurityException if not public static bool ValidateEditPath(string filePath, string validDir) { - return Umbraco.Core.IO.IOHelper.ValidateEditPath(filePath, validDir); + return Umbraco.Core.IO.IOHelper.ValidateEditPath(filePath, validDir); } - public static bool ValidateFileExtension(string filePath, List validFileExtensions) + public static bool ValidateFileExtension(string filePath, List validFileExtensions) { - return Umbraco.Core.IO.IOHelper.ValidateFileExtension(filePath, validFileExtensions); + return Umbraco.Core.IO.IOHelper.ValidateFileExtension(filePath, validFileExtensions); } @@ -92,7 +92,7 @@ namespace umbraco.IO /// private static string getRootDirectorySafe() { - return Umbraco.Core.IO.IOHelper.GetRootDirectorySafe(); + return Umbraco.Core.IO.IOHelper.GetRootDirectorySafe(); } } From 093182c56da32a257c35c7478621f36fac99ca70 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Sun, 30 Sep 2018 13:12:11 +0200 Subject: [PATCH 57/61] Allow svg uploads --- src/Umbraco.Web.UI/config/umbracoSettings.Release.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 88d350225b..af5b302ff0 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -51,7 +51,7 @@ throw - ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,svg,php,htaccess + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,php,htaccess Textstring From c5de33dcae07c3fe5fbbdfa5e6512904a5aadf49 Mon Sep 17 00:00:00 2001 From: Mark Drake Date: Sun, 30 Sep 2018 07:38:19 -0400 Subject: [PATCH 58/61] Added an `active` css class for Umbraco Tree items (#3056) --- .../components/tree/umbtreeitem.directive.js | 12 ++++++++++-- .../src/common/services/navigation.service.js | 4 +++- src/Umbraco.Web.UI.Client/src/less/tree.less | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js index 0bb888a59f..93ea0d9ced 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js @@ -18,7 +18,7 @@ */ angular.module("umbraco.directives") -.directive('umbTreeItem', function ($compile, $http, $templateCache, $interpolate, $log, $location, $rootScope, $window, treeService, $timeout, localizationService) { +.directive('umbTreeItem', function ($compile, $http, $templateCache, $interpolate, $log, $location, $rootScope, $window, treeService, $timeout, localizationService, appState) { return { restrict: 'E', replace: true, @@ -132,7 +132,15 @@ angular.module("umbraco.directives") } if (node.selected) { css.push("umb-tree-node-checked"); - } + } + + //is this the current action node (this is not the same as the current selected node!) + var actionNode = appState.getMenuState("currentNode"); + if(actionNode) { + if(actionNode.id === node.id) { + css.push("active"); + } + } return css.join(" "); }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 840066adef..da2fd243e6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -73,6 +73,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo appState.setSectionState("showSearchResults", false); appState.setGlobalState("stickyNavigation", false); appState.setGlobalState("showTray", false); + appState.setMenuState("currentNode", null); if (appState.getGlobalState("isTablet") === true) { appState.setGlobalState("showNavigation", false); @@ -347,7 +348,8 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) { //reset it to whatever is in the url - appState.setSectionState("currentSection", $routeParams.section); + appState.setSectionState("currentSection", $routeParams.section); + setMode("default-hidesectiontree"); } diff --git a/src/Umbraco.Web.UI.Client/src/less/tree.less b/src/Umbraco.Web.UI.Client/src/less/tree.less index acba5f1ac8..fae99ced3e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/tree.less @@ -185,7 +185,8 @@ line-height: 16px; } -.umb-tree div:hover { +.umb-tree div:hover, +.umb-tree div.active { background: @gray-10; } From 8fad7183470ddac9424eb2ce6ed195ea93baf373 Mon Sep 17 00:00:00 2001 From: KimHolzmann Date: Sun, 30 Sep 2018 14:08:01 +0200 Subject: [PATCH 59/61] #2916 Media picker searches entire media archive (#3061) --- .../directives/components/umbmediagrid.directive.js | 12 +++++++++++- .../overlays/mediaPicker/mediapicker.controller.js | 2 +- .../common/overlays/mediaPicker/mediapicker.html | 11 +++++++++-- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 1 + src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 1 + 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js index 12179076cd..9aa4965022 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbmediagrid.directive.js @@ -125,6 +125,14 @@ Use this directive to generate a thumbnail grid of media items. i--; } + if (scope.includeSubFolders !== 'true') { + if (item.parentId !== parseInt(scope.currentFolderId)) { + scope.items.splice(i, 1); + i--; + } + } + + } if (scope.items.length > 0) { @@ -307,7 +315,9 @@ Use this directive to generate a thumbnail grid of media items. itemMaxHeight: "@", itemMinWidth: "@", itemMinHeight: "@", - onlyImages: "@" + onlyImages: "@", + includeSubFolders: "@", + currentFolderId: "@" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index 1b4c533123..5e582eb9c4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -314,7 +314,7 @@ angular.module("umbraco") function searchMedia() { $scope.loading = true; - entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions) + entityResource.getPagedDescendants($scope.currentFolder.id, "Media", $scope.searchOptions) .then(function(data) { // update image data to work with image grid angular.forEach(data.items, diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html index dce749be55..b93a2ccf3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html @@ -11,7 +11,6 @@ -
      @@ -83,7 +88,9 @@ item-max-height="150" item-min-width="100" item-min-height="100" - only-images={{onlyImages}}> + only-images={{onlyImages}} + include-sub-folders={{showChilds}} + current-Folder-id="{{currentFolder.id}}">
      diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 7ec238408b..92263b4955 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -684,6 +684,7 @@ Embed Retrieve selected + Include subfolders in search 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 a7fad7132b..31838eb346 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -684,6 +684,7 @@ Embed Retrieve selected + Include subfolders in search Black From 63a2a155d116ed2620705eec696a94ef5021f797 Mon Sep 17 00:00:00 2001 From: agrath Date: Mon, 1 Oct 2018 02:03:52 +1300 Subject: [PATCH 60/61] User invite flow review (#3000) --- .../Security/BackOfficeUserManager.cs | 6 +++- .../Security/BackOfficeUserStore.cs | 4 ++- .../views/common/dialogs/login.controller.js | 3 ++ .../src/views/common/dialogs/login.html | 12 +++++-- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 3 +- .../umbraco/config/lang/en_us.xml | 3 +- .../Editors/AuthenticationController.cs | 32 +++++++++++++++++-- .../Editors/BackOfficeController.cs | 21 ++++++++---- 8 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Security/BackOfficeUserManager.cs b/src/Umbraco.Core/Security/BackOfficeUserManager.cs index 83db1a8904..fde188ff27 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserManager.cs @@ -236,7 +236,10 @@ namespace Umbraco.Core.Security if (dataProtectionProvider != null) { - manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")); + manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create("ASP.NET Identity")) + { + TokenLifespan = TimeSpan.FromDays(3) + }; } manager.UserLockoutEnabledByDefault = true; @@ -748,6 +751,7 @@ namespace Umbraco.Core.Security var httpContext = HttpContext.Current == null ? (HttpContextBase)null : new HttpContextWrapper(HttpContext.Current); return httpContext.GetCurrentRequestIpAddress(); } + } } diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index e0b91ce175..c6e3399b73 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -630,7 +630,9 @@ namespace Umbraco.Core.Security || identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value) { anythingChanged = true; - user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime(); + //if the LastLoginDate is being set to MinValue, don't convert it ToLocalTime + var dt = identityUser.LastLoginDateUtc == DateTime.MinValue ? DateTime.MinValue : identityUser.LastLoginDateUtc.Value.ToLocalTime(); + user.LastLoginDate = dt; } if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc") || (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js index 3f50926b47..9b703a0987 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js @@ -29,6 +29,7 @@ function init() { // Check if it is a new user var inviteVal = $location.search().invite; + //1 = enter password, 2 = password set, 3 = invalid token if (inviteVal && (inviteVal === "1" || inviteVal === "2")) { $q.all([ @@ -58,6 +59,8 @@ $scope.inviteStep = Number(inviteVal); }); + } else if (inviteVal && inviteVal === "3") { + $scope.inviteStep = Number(inviteVal); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index b4e330c13e..3b48bcc9f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -99,10 +99,18 @@
      - + +