diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs index c549679251..7a51fcbb96 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeAssetsController.cs @@ -8,7 +8,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Web.Common.Attributes; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { [PluginController("UmbracoApi")] public class BackOfficeAssetsController : UmbracoAuthorizedJsonController diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs index d84c3f9253..a4c115f66f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeNotificationsController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Web.WebApi.Filters; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// An abstract controller that automatically checks if any request is a non-GET and if the diff --git a/src/Umbraco.Web/Editors/DictionaryController.cs b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs similarity index 52% rename from src/Umbraco.Web/Editors/DictionaryController.cs rename to src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs index 56701d35ed..a997c3dacf 100644 --- a/src/Umbraco.Web/Editors/DictionaryController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs @@ -1,26 +1,22 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; +using System.Linq; using System.Net.Http; -using System.Web.Http; -using Umbraco.Core; -using Umbraco.Core.Cache; +using Microsoft.AspNetCore.Mvc; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; -using Umbraco.Core.Persistence; using Umbraco.Core.Services; -using Umbraco.Core.Strings; +using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; -using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; +using Umbraco.Web.Security; using Constants = Umbraco.Core.Constants; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// @@ -32,22 +28,30 @@ namespace Umbraco.Web.Editors /// [PluginController("UmbracoApi")] [UmbracoTreeAuthorize(Constants.Trees.Dictionary)] - [EnableOverrideAuthorization] public class DictionaryController : BackOfficeNotificationsController { + private readonly ILogger _logger; + private readonly ILocalizationService _localizationService; + private readonly IWebSecurity _webSecurity; + private readonly IGlobalSettings _globalSettings; + private readonly ILocalizedTextService _localizedTextService; + private readonly UmbracoMapper _umbracoMapper; + public DictionaryController( + ILogger logger, + ILocalizationService localizationService, + IWebSecurity webSecurity, IGlobalSettings globalSettings, - IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger logger, - IRuntimeState runtimeState, - IShortStringHelper shortStringHelper, - UmbracoMapper umbracoMapper, - IPublishedUrlProvider publishedUrlProvider) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) + ILocalizedTextService localizedTextService, + UmbracoMapper umbracoMapper + ) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); + _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); + _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); + _localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); + _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); } /// @@ -57,23 +61,23 @@ namespace Umbraco.Web.Editors /// [HttpDelete] [HttpPost] - public HttpResponseMessage DeleteById(int id) + public IActionResult DeleteById(int id) { - var foundDictionary = Services.LocalizationService.GetDictionaryItemById(id); + var foundDictionary = _localizationService.GetDictionaryItemById(id); if (foundDictionary == null) - throw new HttpResponseException(HttpStatusCode.NotFound); + return NotFound(); - var foundDictionaryDescendants = Services.LocalizationService.GetDictionaryItemDescendants(foundDictionary.Key); + var foundDictionaryDescendants = _localizationService.GetDictionaryItemDescendants(foundDictionary.Key); foreach (var dictionaryItem in foundDictionaryDescendants) { - Services.LocalizationService.Delete(dictionaryItem, Security.CurrentUser.Id); + _localizationService.Delete(dictionaryItem, _webSecurity.CurrentUser.Id); } - Services.LocalizationService.Delete(foundDictionary, Security.CurrentUser.Id); + _localizationService.Delete(foundDictionary, _webSecurity.CurrentUser.Id); - return Request.CreateResponse(HttpStatusCode.OK); + return Ok(); } /// @@ -89,19 +93,18 @@ namespace Umbraco.Web.Editors /// The . /// [HttpPost] - public HttpResponseMessage Create(int parentId, string key) + public ActionResult Create(int parentId, string key) { if (string.IsNullOrEmpty(key)) - return Request - .CreateNotificationValidationErrorResponse("Key can not be empty."); // TODO: translate + throw HttpResponseException.CreateNotificationValidationErrorResponse("Key can not be empty."); // TODO: translate - if (Services.LocalizationService.DictionaryItemExists(key)) + if (_localizationService.DictionaryItemExists(key)) { - var message = Services.TextService.Localize( + var message = _localizedTextService.Localize( "dictionaryItem/changeKeyError", - Security.CurrentUser.GetUserCulture(Services.TextService, GlobalSettings), + _webSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings), new Dictionary { { "0", key } }); - return Request.CreateNotificationValidationErrorResponse(message); + throw HttpResponseException.CreateNotificationValidationErrorResponse(message); } try @@ -109,20 +112,20 @@ namespace Umbraco.Web.Editors Guid? parentGuid = null; if (parentId > 0) - parentGuid = Services.LocalizationService.GetDictionaryItemById(parentId).Key; + parentGuid = _localizationService.GetDictionaryItemById(parentId).Key; - var item = Services.LocalizationService.CreateDictionaryItemWithIdentity( + var item = _localizationService.CreateDictionaryItemWithIdentity( key, parentGuid, string.Empty); - return Request.CreateResponse(HttpStatusCode.OK, item.Id); + return item.Id; } catch (Exception ex) { - Logger.Error(GetType(), ex, "Error creating dictionary with {Name} under {ParentId}", key, parentId); - return Request.CreateNotificationValidationErrorResponse("Error creating dictionary item"); + _logger.Error(GetType(), ex, "Error creating dictionary with {Name} under {ParentId}", key, parentId); + throw HttpResponseException.CreateNotificationValidationErrorResponse("Error creating dictionary item"); } } @@ -138,14 +141,14 @@ namespace Umbraco.Web.Editors /// /// Returns a not found response when dictionary item does not exist /// - public DictionaryDisplay GetById(int id) + public ActionResult GetById(int id) { - var dictionary = Services.LocalizationService.GetDictionaryItemById(id); + var dictionary = _localizationService.GetDictionaryItemById(id); if (dictionary == null) - throw new HttpResponseException(HttpStatusCode.NotFound); + return NotFound(); - return Mapper.Map(dictionary); + return _umbracoMapper.Map(dictionary); } /// @@ -160,27 +163,27 @@ namespace Umbraco.Web.Editors public DictionaryDisplay PostSave(DictionarySave dictionary) { var dictionaryItem = - Services.LocalizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString())); + _localizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString())); if (dictionaryItem == null) - throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Dictionary item does not exist")); + throw HttpResponseException.CreateNotificationValidationErrorResponse("Dictionary item does not exist"); - var userCulture = Security.CurrentUser.GetUserCulture(Services.TextService, GlobalSettings); + var userCulture = _webSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings); if (dictionary.NameIsDirty) { // if the name (key) has changed, we need to check if the new key does not exist - var dictionaryByKey = Services.LocalizationService.GetDictionaryItemByKey(dictionary.Name); + var dictionaryByKey = _localizationService.GetDictionaryItemByKey(dictionary.Name); if (dictionaryByKey != null && dictionaryItem.Id != dictionaryByKey.Id) { - var message = Services.TextService.Localize( + var message = _localizedTextService.Localize( "dictionaryItem/changeKeyError", userCulture, new Dictionary { { "0", dictionary.Name } }); ModelState.AddModelError("Name", message); - throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + throw HttpResponseException.CreateValidationErrorResponse(ModelState); } dictionaryItem.ItemKey = dictionary.Name; @@ -188,26 +191,26 @@ namespace Umbraco.Web.Editors foreach (var translation in dictionary.Translations) { - Services.LocalizationService.AddOrUpdateDictionaryValue(dictionaryItem, - Services.LocalizationService.GetLanguageById(translation.LanguageId), translation.Translation); + _localizationService.AddOrUpdateDictionaryValue(dictionaryItem, + _localizationService.GetLanguageById(translation.LanguageId), translation.Translation); } try { - Services.LocalizationService.Save(dictionaryItem); + _localizationService.Save(dictionaryItem); - var model = Mapper.Map(dictionaryItem); + var model = _umbracoMapper.Map(dictionaryItem); model.Notifications.Add(new BackOfficeNotification( - Services.TextService.Localize("speechBubbles/dictionaryItemSaved", userCulture), string.Empty, + _localizedTextService.Localize("speechBubbles/dictionaryItemSaved", userCulture), string.Empty, NotificationStyle.Success)); return model; } catch (Exception ex) { - Logger.Error(GetType(), ex, "Error saving dictionary with {Name} under {ParentId}", dictionary.Name, dictionary.ParentId); - throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Something went wrong saving dictionary")); + _logger.Error(GetType(), ex, "Error saving dictionary with {Name} under {ParentId}", dictionary.Name, dictionary.ParentId); + throw HttpResponseException.CreateNotificationValidationErrorResponse("Something went wrong saving dictionary"); } } @@ -223,9 +226,9 @@ namespace Umbraco.Web.Editors const int level = 0; - foreach (var dictionaryItem in Services.LocalizationService.GetRootDictionaryItems().OrderBy(ItemSort())) + foreach (var dictionaryItem in _localizationService.GetRootDictionaryItems().OrderBy(ItemSort())) { - var item = Mapper.Map(dictionaryItem); + var item = _umbracoMapper.Map(dictionaryItem); item.Level = 0; list.Add(item); @@ -249,9 +252,9 @@ namespace Umbraco.Web.Editors /// private void GetChildItemsForList(IDictionaryItem dictionaryItem, int level, ICollection list) { - foreach (var childItem in Services.LocalizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort())) + foreach (var childItem in _localizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort())) { - var item = Mapper.Map(childItem); + var item = _umbracoMapper.Map(childItem); item.Level = level; list.Add(item); diff --git a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs index f61b463346..5f5439f046 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; -using System.Net.Http; using Examine; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.IO; @@ -19,7 +16,7 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using SearchResult = Umbraco.Web.Models.ContentEditing.SearchResult; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { [PluginController("UmbracoApi")] public class ExamineManagementController : UmbracoAuthorizedJsonController diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs index bf9b14e86a..47a5efdcbe 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs @@ -9,7 +9,7 @@ using Umbraco.Web.Common.Attributes; using Umbraco.Web.Models; using Umbraco.Web.Mvc; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// The API controller used for getting URLs for images with parameters diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 90b52b845f..e79a481701 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -1,19 +1,14 @@ using System; using System.IO; -using System.Net; -using System.Net.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Media; using Umbraco.Core.Models; -using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Models; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// A controller used to return images for media diff --git a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs index 6eae71a27e..7a2d4de4b0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs @@ -14,7 +14,7 @@ using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { [PluginController("UmbracoApi")] [UmbracoTreeAuthorizeAttribute(Constants.Trees.Templates)] diff --git a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs new file mode 100644 index 0000000000..dd8620d8ee --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Core.Strings; +using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.Common.ActionsResults; +using Umbraco.Web.Common.Attributes; + +using Constants = Umbraco.Core.Constants; + +namespace Umbraco.Web.BackOffice.Controllers +{ + [PluginController("UmbracoApi")] + [UmbracoApplicationAuthorize( + Constants.Applications.Content, + Constants.Applications.Media, + Constants.Applications.Members)] + public class TinyMceController : UmbracoAuthorizedApiController + { + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IShortStringHelper _shortStringHelper; + private readonly IContentSettings _contentSettings; + private readonly IIOHelper _ioHelper; + + public TinyMceController( + IHostingEnvironment hostingEnvironment, + IShortStringHelper shortStringHelper, + IContentSettings contentSettings, + IIOHelper ioHelper + ) + { + _hostingEnvironment = hostingEnvironment; + _shortStringHelper = shortStringHelper; + _contentSettings = contentSettings; + _ioHelper = ioHelper; + } + + [HttpPost] + public async Task UploadImage(List file) + { + // Create an unique folder path to help with concurrent users to avoid filename clash + var imageTempPath = _hostingEnvironment.MapPathWebRoot(Constants.SystemDirectories.TempImageUploads + "/" + Guid.NewGuid().ToString()); + + // Ensure image temp path exists + if(Directory.Exists(imageTempPath) == false) + { + Directory.CreateDirectory(imageTempPath); + } + + // Must have a file + if (file.Count == 0) + { + return NotFound(); + } + + // Should only have one file + if (file.Count > 1) + { + return new UmbracoProblemResult("Only one file can be uploaded at a time", HttpStatusCode.BadRequest); + } + + var formFile = file.First(); + + // Really we should only have one file per request to this endpoint + // var file = result.FileData[0]; + var fileName = formFile.FileName.Trim(new[] { '\"' }).TrimEnd(); + var safeFileName = fileName.ToSafeFileName(_shortStringHelper); + var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); + + if (_contentSettings.IsFileAllowedForUpload(ext) == false || _contentSettings.ImageFileTypes.Contains(ext) == false) + { + // Throw some error - to say can't upload this IMG type + return new UmbracoProblemResult("This is not an image filetype extension that is approved", HttpStatusCode.BadRequest); + } + + var newFilePath = imageTempPath + Path.DirectorySeparatorChar + safeFileName; + var relativeNewFilePath = _ioHelper.GetRelativePath(newFilePath); + + await using (var stream = System.IO.File.Create(newFilePath)) + { + await formFile.CopyToAsync(stream); + } + + return Ok(new { tmpLocation = relativeNewFilePath }); + + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs index 528a67fdff..0288804d7d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs @@ -5,13 +5,12 @@ using System.Linq; using Newtonsoft.Json; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Services; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Models; using Umbraco.Web.Tour; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { [PluginController("UmbracoApi")] public class TourController : UmbracoAuthorizedJsonController diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedJsonController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedJsonController.cs index 70d3155f23..6a68651a50 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedJsonController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedJsonController.cs @@ -3,7 +3,7 @@ using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Filters; -namespace Umbraco.Web.Editors +namespace Umbraco.Web.BackOffice.Controllers { /// /// An abstract API controller that only supports JSON and all requests must contain the correct csrf header diff --git a/src/Umbraco.Web.BackOffice/Controllers/UpdateCheckController.cs b/src/Umbraco.Web.BackOffice/Controllers/UpdateCheckController.cs new file mode 100644 index 0000000000..6ae0d1f612 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/UpdateCheckController.cs @@ -0,0 +1,113 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Semver; +using Umbraco.Composing; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Common.Attributes; +using Umbraco.Web.Models; +using Umbraco.Web.Security; + +namespace Umbraco.Web.BackOffice.Controllers +{ + [PluginController("UmbracoApi")] + public class UpdateCheckController : UmbracoAuthorizedJsonController + { + private readonly IUpgradeService _upgradeService; + private readonly IUmbracoVersion _umbracoVersion; + private readonly ICookieManager _cookieManager; + private readonly IWebSecurity _webSecurity; + private readonly IGlobalSettings _globalSettings; + + public UpdateCheckController( + IUpgradeService upgradeService, + IUmbracoVersion umbracoVersion, + ICookieManager cookieManager, + IWebSecurity webSecurity, + IGlobalSettings globalSettings) + { + _upgradeService = upgradeService ?? throw new ArgumentNullException(nameof(upgradeService)); + _umbracoVersion = umbracoVersion ?? throw new ArgumentNullException(nameof(umbracoVersion)); + _cookieManager = cookieManager ?? throw new ArgumentNullException(nameof(cookieManager)); + _webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity)); + _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); + } + + [UpdateCheckResponseFilter] + public async Task GetCheck() + { + var updChkCookie = _cookieManager.GetCookieValue("UMB_UPDCHK"); + var updateCheckCookie = updChkCookie ?? string.Empty; + if (_globalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && _webSecurity.CurrentUser.IsAdmin()) + { + try + { + var version = new SemVersion(_umbracoVersion.Current.Major, _umbracoVersion.Current.Minor, + _umbracoVersion.Current.Build, _umbracoVersion.Comment); + var result = await _upgradeService.CheckUpgrade(version); + + return new UpgradeCheckResponse(result.UpgradeType, result.Comment, result.UpgradeUrl, _umbracoVersion); + } + catch + { + //We don't want to crash due to this + return null; + } + } + return null; + } + + /// + /// Adds the cookie response if it was successful + /// + /// + /// A filter is required because we are returning an object from the get method and not an HttpResponseMessage + /// + /// + internal class UpdateCheckResponseFilterAttribute : TypeFilterAttribute + { + public UpdateCheckResponseFilterAttribute() : base(typeof(UpdateCheckResponseFilter)) + { + } + + private class UpdateCheckResponseFilter : IActionFilter + { + private readonly IGlobalSettings _globalSettings; + + public UpdateCheckResponseFilter(IGlobalSettings globalSettings) + { + _globalSettings = globalSettings; + } + + public void OnActionExecuted(ActionExecutedContext context) + { + if (context.HttpContext.Response == null) return; + + if (context.Result is ObjectResult objectContent) + { + if (objectContent.Value == null) return; + + context.HttpContext.Response.Cookies.Append("UMB_UPDCHK", "1", new CookieOptions() + { + Path = "/", + Expires = DateTimeOffset.Now.AddDays(_globalSettings.VersionCheckPeriod), + HttpOnly = true, + Secure = _globalSettings.UseHttps + }); + } + } + + public void OnActionExecuting(ActionExecutingContext context) + { + + } + } + } + + } +} diff --git a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs index 131a4ac62c..92d8f1f910 100644 --- a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs +++ b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs @@ -4,11 +4,11 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Umbraco.Core; using Umbraco.Core.Logging; -using Umbraco.Web.Editors; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Web.BackOffice.Filters; +using Umbraco.Web.HealthCheck; -namespace Umbraco.Web.HealthCheck +namespace Umbraco.Web.BackOffice.Controllers { /// /// The API controller used to display the health check info and execute any actions @@ -81,7 +81,7 @@ namespace Umbraco.Web.HealthCheck return check.ExecuteAction(action); } - private HealthCheck GetCheckById(Guid id) + private HealthCheck.HealthCheck GetCheckById(Guid id) { var check = _checks .Where(x => _disabledCheckIds.Contains(x.Id) == false) diff --git a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs index b6cdf75f6f..b5824f73a4 100644 --- a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs +++ b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs @@ -1,8 +1,7 @@ -using Microsoft.AspNetCore.Mvc; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.Hosting; using Umbraco.Web.BackOffice.Filters; -using Umbraco.Web.Editors; +using Umbraco.Web.BackOffice.Controllers; namespace Umbraco.Web.BackOffice.Profiling diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs index 17184dad5e..fc922ca835 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs @@ -3,7 +3,7 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Umbraco.Core.Services; using Umbraco.Web.Common.Attributes; -using Umbraco.Web.Editors; +using Umbraco.Web.BackOffice.Controllers; namespace Umbraco.Web.BackOffice.PropertyEditors { diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs index fb5f9c0a42..2250f85f9b 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs @@ -3,7 +3,7 @@ using System.Xml; using Umbraco.Core; using Umbraco.Core.Hosting; using Umbraco.Core.IO; -using Umbraco.Web.Editors; +using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Common.Attributes; diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs index 242cdc895e..8d9bc06acd 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/RteEmbedController.cs @@ -1,8 +1,7 @@ using System; using System.Text.RegularExpressions; using Umbraco.Core.Logging; -using Umbraco.Web.Editors; -using Umbraco.Web.Mvc; +using Umbraco.Web.BackOffice.Controllers; using Umbraco.Core.Media; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Media.EmbedProviders; diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs index c7dd56f9ae..0886f0e123 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreCookieManager.cs @@ -33,7 +33,10 @@ namespace Umbraco.Web.Common.AspNetCore public void SetCookieValue(string cookieName, string value) { - _httpContextAccessor.HttpContext?.Response.Cookies.Append(cookieName, value); + _httpContextAccessor.HttpContext?.Response.Cookies.Append(cookieName, value, new CookieOptions() + { + + }); } public bool HasCookie(string cookieName) diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index 5ea29cef1d..62b3b7f34c 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -22,6 +22,7 @@ using System; using Umbraco.Web.Common.Middleware; using Umbraco.Web.Common.ModelBinding; using Umbraco.Web.Search; +using Umbraco.Web.Security; using Umbraco.Web.Trees; namespace Umbraco.Web.Common.Runtime @@ -71,6 +72,7 @@ namespace Umbraco.Web.Common.Runtime // register the umbraco context factory composition.RegisterUnique(); + composition.RegisterUnique(factory => factory.GetInstance().GetRequiredUmbracoContext().Security); //register the install components //NOTE: i tried to not have these registered if we weren't installing or upgrading but post install when the site restarts diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 15d3682bd6..38458c932b 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -250,10 +250,10 @@ namespace Umbraco.Web.Editors "memberGroupApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllGroups()) }, - { - "updateCheckApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.GetCheck()) - }, + // { + // "updateCheckApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // controller => controller.GetCheck()) + // }, // { // "templateApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetById(0)) @@ -296,10 +296,10 @@ namespace Umbraco.Web.Editors // "publishedStatusBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetPublishedStatusUrl()) // }, - { - "dictionaryApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.DeleteById(int.MaxValue)) - }, + // { + // "dictionaryApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // controller => controller.DeleteById(int.MaxValue)) + // }, // { // "publishedSnapshotCacheStatusBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetStatus()) @@ -330,10 +330,10 @@ namespace Umbraco.Web.Editors // "webProfilingBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( // controller => controller.GetStatus()) // }, - { - "tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( - controller => controller.UploadImage()) - }, + // { + // "tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + // controller => controller.UploadImage()) + // }, //TODO Reintroduce // { // "imageUrlGeneratorApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web/Editors/TinyMceController.cs b/src/Umbraco.Web/Editors/TinyMceController.cs deleted file mode 100644 index fd69a94096..0000000000 --- a/src/Umbraco.Web/Editors/TinyMceController.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using System.Web.Http; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Mapping; -using Umbraco.Core.Persistence; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Web.Mvc; -using Umbraco.Web.Routing; -using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; -using Constants = Umbraco.Core.Constants; - -namespace Umbraco.Web.Editors -{ - [PluginController("UmbracoApi")] - [UmbracoApplicationAuthorize( - Constants.Applications.Content, - Constants.Applications.Media, - Constants.Applications.Members)] - public class TinyMceController : UmbracoAuthorizedApiController - { - private readonly IIOHelper _ioHelper; - private readonly IShortStringHelper _shortStringHelper; - private readonly IContentSettings _contentSettings; - - public TinyMceController( - IGlobalSettings globalSettings, - IUmbracoContextAccessor umbracoContextAccessor, - ISqlContext sqlContext, - ServiceContext services, - AppCaches appCaches, - IProfilingLogger logger, - IRuntimeState runtimeState, - UmbracoMapper umbracoMapper, - IShortStringHelper shortStringHelper, - IContentSettings contentSettings, - IIOHelper ioHelper, - IPublishedUrlProvider publishedUrlProvider) - : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) - { - _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); - _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); - } - - - [HttpPost] - public async Task UploadImage() - { - if (Request.Content.IsMimeMultipartContent() == false) - { - throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } - - // Create an unique folder path to help with concurrent users to avoid filename clash - var imageTempPath = _ioHelper.MapPath(Constants.SystemDirectories.TempImageUploads + "/" + Guid.NewGuid().ToString()); - - // Temp folderpath (Files come in as bodypart & will need to move/saved into imgTempPath - var folderPath = _ioHelper.MapPath(Constants.SystemDirectories.TempFileUploads); - - // Ensure image temp path exists - if(Directory.Exists(imageTempPath) == false) - { - Directory.CreateDirectory(imageTempPath); - } - - // File uploaded will be saved as bodypart into TEMP folder - var provider = new MultipartFormDataStreamProvider(folderPath); - var result = await Request.Content.ReadAsMultipartAsync(provider); - - // Must have a file - if (result.FileData.Count == 0) - { - return Request.CreateResponse(HttpStatusCode.NotFound); - } - - // Should only have one file - if (result.FileData.Count > 1) - { - return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Only one file can be uploaded at a time"); - } - - // Really we should only have one file per request to this endpoint - var file = result.FileData[0]; - var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }).TrimEnd(); - var safeFileName = fileName.ToSafeFileName(_shortStringHelper); - var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - - if (_contentSettings.IsFileAllowedForUpload(ext) == false || _contentSettings.ImageFileTypes.Contains(ext) == false) - { - // Throw some error - to say can't upload this IMG type - return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "This is not an image filetype extension that is approved"); - } - - //var mediaItemName = fileName.ToFriendlyName(); - var currentFile = file.LocalFileName; - var newFilePath = imageTempPath + Path.DirectorySeparatorChar + safeFileName; - var relativeNewFilePath = _ioHelper.GetRelativePath(newFilePath); - - try - { - // Move the file from bodypart to a real filename - // This is what we return from this API so RTE updates img src path - // Until we fully persist & save the media item when persisting - // If we find data attribute - File.Move(currentFile, newFilePath); - } - catch (Exception ex) - { - // IOException, PathTooLong, DirectoryNotFound, UnathorizedAccess - Logger.Error(ex, "Error when trying to move {CurrentFilePath} to {NewFilePath}", currentFile, newFilePath); - return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, $"Error when trying to move {currentFile} to {newFilePath}", ex); - } - - return Request.CreateResponse(HttpStatusCode.OK, new { tmpLocation = relativeNewFilePath }); - } - } -} diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs deleted file mode 100644 index 8518938a32..0000000000 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using System.Web.Http.Filters; -using Semver; -using Umbraco.Core; -using Umbraco.Web.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Web.Models; -using Umbraco.Web.Mvc; - -namespace Umbraco.Web.Editors -{ - [PluginController("UmbracoApi")] - public class UpdateCheckController : UmbracoAuthorizedJsonController - { - private readonly IUpgradeService _upgradeService; - private readonly IUmbracoVersion _umbracoVersion; - - public UpdateCheckController() { } - - public UpdateCheckController(IUpgradeService upgradeService, IUmbracoVersion umbracoVersion) - { - _upgradeService = upgradeService; - _umbracoVersion = umbracoVersion; - } - - [UpdateCheckResponseFilter] - public async Task GetCheck() - { - var updChkCookie = Request.Headers.GetCookies("UMB_UPDCHK").FirstOrDefault(); - var updateCheckCookie = updChkCookie != null ? updChkCookie["UMB_UPDCHK"].Value : ""; - if (GlobalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && Security.CurrentUser.IsAdmin()) - { - try - { - var version = new SemVersion(_umbracoVersion.Current.Major, _umbracoVersion.Current.Minor, - _umbracoVersion.Current.Build, _umbracoVersion.Comment); - var result = await _upgradeService.CheckUpgrade(version); - - return new UpgradeCheckResponse(result.UpgradeType, result.Comment, result.UpgradeUrl, _umbracoVersion); - } - catch - { - //We don't want to crash due to this - return null; - } - } - return null; - } - - /// - /// Adds the cookie response if it was successful - /// - /// - /// A filter is required because we are returning an object from the get method and not an HttpResponseMessage - /// - internal class UpdateCheckResponseFilter : ActionFilterAttribute - { - public override void OnActionExecuted(HttpActionExecutedContext context) - { - if (context.Response == null) return; - - var objectContent = context.Response.Content as ObjectContent; - if (objectContent == null || objectContent.Value == null) return; - - //there is a result, set the outgoing cookie - var cookie = new CookieHeaderValue("UMB_UPDCHK", "1") - { - Path = "/", - Expires = DateTimeOffset.Now.AddDays(Current.Configs.Global().VersionCheckPeriod), - HttpOnly = true, - Secure = Current.Configs.Global().UseHttps - }; - context.Response.Headers.AddCookies(new[] { cookie }); - } - } - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c577d0b4e1..ecee9c0bb8 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -158,7 +158,6 @@ - @@ -232,7 +231,6 @@ - @@ -331,7 +329,6 @@ -