diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index c6842513b2..a89a046252 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -333,7 +333,18 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca notificationsService.error(value); }); }); - } + }, + + import: function (file) { + if (!file) { + throw "file cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "Import", { file: file })), + "Failed to import document type " + file + ); + }, }; } angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.controller.js new file mode 100644 index 0000000000..6356d162dd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.controller.js @@ -0,0 +1,75 @@ +angular.module("umbraco") + .controller("Umbraco.Editors.DocumentTypes.ImportController", + function ($scope, contentTypeResource, navigationService, Upload, umbRequestHelper) { + var vm = this; + vm.serverErrorMessage = ""; + vm.state = "upload"; + vm.model = {}; + vm.uploadStatus = ""; + + $scope.handleFiles = function (files, event) { + if (files && files.length > 0) { + $scope.upload(files[0]); + } + }; + + $scope.upload = function (file) { + Upload.upload({ + url: umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "Upload"), + fields: {}, + file: file + }).success(function (data, status, headers, config) { + if (data.notifications && data.notifications.length > 0) { + + // set error status on file + vm.uploadStatus = "error"; + + // Throw message back to user with the cause of the error + vm.serverErrorMessage = data.notifications[0].message; + } else { + + // set done status on file + vm.uploadStatus = "done"; + vm.model = data; + vm.state = "confirm"; + } + }).error(function (evt, status, headers, config) { + + // set status done + $scope.uploadStatus = "error"; + + // If file not found, server will return a 404 and display this message + if (status === 404) { + $scope.serverErrorMessage = "File not found"; + } + else if (status == 400) { + //it's a validation error + $scope.serverErrorMessage = evt.message; + } + else { + //it's an unhandled error + //if the service returns a detailed error + if (evt.InnerException) { + $scope.serverErrorMessage = evt.InnerException.ExceptionMessage; + + //Check if its the common "too large file" exception + if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) { + $scope.serverErrorMessage = "File too large to upload"; + } + + } else if (evt.Message) { + $scope.serverErrorMessage = evt.Message; + } + } + }); + }; + + $scope.cancel = function () { + navigationService.hideDialog(); + }; + + $scope.import = function () { + contentTypeResource.import(vm.model.tempFileName); + vm.state = "done"; + } + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.html new file mode 100644 index 0000000000..3ce790513a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/import.html @@ -0,0 +1,72 @@ +
+ + + + +
+
+

+ + + To import a document type, find the '.udt' file on your computer by clicking the 'Browse' button and click 'Import' (you'll be asked for confirmation on the next screen) + + +

+ +
+
+ +
+
+ + + Drop to upload + +
+ - or click here to choose files +
+
+
+
+
+ > + cancel + +
+
+ + Name: + + {{vm.model.name}} +
+ + Alias: + + {{vm.model.alias}} +
+
+ +
+
+ {{vm.model.name}} has been imported! +
+
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c90722c08c..ba721a70c4 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -361,7 +361,6 @@ - diff --git a/src/Umbraco.Web.UI/Umbraco/dialogs/importDocumenttype.aspx b/src/Umbraco.Web.UI/Umbraco/dialogs/importDocumenttype.aspx deleted file mode 100644 index 44280f6710..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/dialogs/importDocumenttype.aspx +++ /dev/null @@ -1,49 +0,0 @@ -<%@ Page MasterPageFile="../masterpages/umbracoDialog.Master" Language="c#" Codebehind="importDocumenttype.aspx.cs" AutoEventWireup="false" - Inherits="umbraco.presentation.umbraco.dialogs.importDocumentType" %> - - - - - - - - - - -
- -

- - <%=Services.TextService.Localize("importDocumentTypeHelp")%> - -

- -

- -

- - - <%= Services.TextService.Localize("or") %> <%= Services.TextService.Localize("cancel") %> -
- - - - - <%=Services.TextService.Localize("name")%> - : - -
- - <%=Services.TextService.Localize("alias")%> - : - -
-
- -
- - - has been imported! - -
-
diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index c864ed8f16..79079265af 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -1,21 +1,29 @@ using AutoMapper; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Threading.Tasks; using System.Web.Http; +using System.Xml; +using System.Xml.Linq; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Composing; +using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.UI; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; +using Notification = Umbraco.Web.Models.ContentEditing.Notification; namespace Umbraco.Web.Editors { @@ -454,5 +462,93 @@ namespace Umbraco.Web.Editors return response; } + + [HttpPost] + public HttpResponseMessage Import(string file) + { + var filePath = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), file); + if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(filePath)) + { + return Request.CreateResponse(HttpStatusCode.NotFound); + } + + var xd = new XmlDocument(); + xd.XmlResolver = null; + xd.Load(filePath); + + var userId = Security.GetUserId(); + var element = XElement.Parse(xd.InnerXml); + Current.Services.PackagingService.ImportContentTypes(element, userId); + + // Try to clean up the temporary file. + try + { + System.IO.File.Delete(filePath); + } + catch (Exception ex) + { + Current.Logger.Error(typeof(ContentTypeController), "Error cleaning up temporary udt file in App_Data: " + ex.Message, ex); + } + + return Request.CreateResponse(HttpStatusCode.OK); + } + + [HttpPost] + [FileUploadCleanupFilter(false)] + public async Task Upload() + { + if (Request.Content.IsMimeMultipartContent() == false) + { + throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); + } + + var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + //ensure it exists + Directory.CreateDirectory(root); + var provider = new MultipartFormDataStreamProvider(root); + var result = await Request.Content.ReadAsMultipartAsync(provider); + + //must have a file + if (result.FileData.Count == 0) + { + throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + } + + var model = new ContentTypeImportModel(); + var file = result.FileData[0]; + var fileName = file.Headers.ContentDisposition.FileName.Trim('\"'); + var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); + if (ext.InvariantEquals("udt")) + { + //TODO: Currently it has to be here, it's not ideal but that's the way it is right now + var tempDir = IOHelper.MapPath(SystemDirectories.Data); + + //ensure it's there + Directory.CreateDirectory(tempDir); + + model.TempFileName = "justDelete_" + Guid.NewGuid() + ".udt"; + var tempFileLocation = Path.Combine(tempDir, model.TempFileName); + System.IO.File.Copy(file.LocalFileName, tempFileLocation, true); + + var xd = new XmlDocument + { + XmlResolver = null + }; + xd.Load(tempFileLocation); + + model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; + model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; + } + else + { + model.Notifications.Add(new Notification( + Services.TextService.Localize("speechBubbles/operationFailedHeader"), + Services.TextService.Localize("media/disallowedFileType"), + SpeechBubbleIcon.Warning)); + } + + return model; + + } } } diff --git a/src/Umbraco.Web/Models/ContentTypeImportModel.cs b/src/Umbraco.Web/Models/ContentTypeImportModel.cs new file mode 100644 index 0000000000..961476e5f0 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentTypeImportModel.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models +{ + [DataContract(Name = "contentTypeImportModel")] + public class ContentTypeImportModel : INotificationModel + { + public ContentTypeImportModel() + { + Notifications = new List(); + } + + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "notifications")] + public List Notifications { get; } + + [DataMember(Name = "tempFileName")] + public string TempFileName { get; set; } + } +} diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index 1295f32dd6..da1eae7b2c 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -6,7 +6,6 @@ using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; using Umbraco.Web._Legacy.Actions; using Umbraco.Web.Models.ContentEditing; @@ -74,13 +73,7 @@ namespace Umbraco.Web.Trees // root actions menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionImport.Instance.Alias)), true).ConvertLegacyMenuItem(new EntitySlim - { - Id = int.Parse(id), - Level = 1, - ParentId = Constants.System.Root, - Name = "" - }, "documenttypes", "settings"); + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionImport.Instance.Alias)), true); menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); return menu; } diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index 9aec48825d..a0259ab247 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -257,12 +257,6 @@ namespace Umbraco.Web.Trees "dialogs/sendToTranslation.aspx?id=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, Current.Services.TextService.Localize("actions/sendToTranslate"))); - case "UmbClientMgr.appActions().actionImport()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/importDocumentType.aspx", - Current.Services.TextService.Localize("actions/importDocumentType"))); - } return Attempt.Fail(); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cedb362aa1..2d0dd5fde8 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -238,6 +238,7 @@ + @@ -1284,10 +1285,6 @@ editPackage.aspx - - importDocumenttype.aspx - ASPXCodeBehind - rollBack.aspx @@ -1438,7 +1435,6 @@ ASPXCodeBehind - ASPXCodeBehind diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs b/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs index 729daa367a..d284da7be4 100644 --- a/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs +++ b/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs @@ -1,7 +1,4 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions +namespace Umbraco.Web._Legacy.Actions { /// /// This action is invoked when importing a document type @@ -32,7 +29,7 @@ namespace Umbraco.Web._Legacy.Actions { get { - return string.Format("{0}.actionImport()", ClientTools.Scripts.GetAppActions); + return ""; } } @@ -48,7 +45,7 @@ namespace Umbraco.Web._Legacy.Actions { get { - return "importDocumentType"; + return "import"; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx deleted file mode 100644 index 44280f6710..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx +++ /dev/null @@ -1,49 +0,0 @@ -<%@ Page MasterPageFile="../masterpages/umbracoDialog.Master" Language="c#" Codebehind="importDocumenttype.aspx.cs" AutoEventWireup="false" - Inherits="umbraco.presentation.umbraco.dialogs.importDocumentType" %> - - - - - - - - - - -
- -

- - <%=Services.TextService.Localize("importDocumentTypeHelp")%> - -

- -

- -

- - - <%= Services.TextService.Localize("or") %> <%= Services.TextService.Localize("cancel") %> -
- - - - - <%=Services.TextService.Localize("name")%> - : - -
- - <%=Services.TextService.Localize("alias")%> - : - -
-
- -
- - - has been imported! - -
-
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs deleted file mode 100644 index 38f061e079..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Linq; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using System.Xml; -using System.Xml.Linq; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.presentation.umbraco.dialogs -{ - /// - /// Summary description for importDocumentType. - /// - public class importDocumentType : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - public importDocumentType() - { - - CurrentApp = Constants.Applications.Settings.ToString(); - - } - protected Literal FeedBackMessage; - protected Literal jsShowWindow; - protected Panel Wizard; - protected HtmlTable Table1; - protected HtmlInputHidden tempFile; - protected HtmlInputFile documentTypeFile; - protected Button submit; - protected Panel Confirm; - protected Literal dtName; - protected Literal dtAlias; - protected Button import; - protected Literal dtNameConfirm; - protected Panel done; - private string tempFileName = ""; - - private void Page_Load(object sender, EventArgs e) - { - if (!IsPostBack) - { - submit.Text = Services.TextService.Localize("import"); - import.Text = Services.TextService.Localize("import"); - } - } - - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.submit.Click += new System.EventHandler(this.submit_Click); - this.import.Click += new System.EventHandler(this.import_Click); - this.Load += new System.EventHandler(this.Page_Load); - - } - #endregion - - private void import_Click(object sender, EventArgs e) - { - var xd = new XmlDocument(); - xd.XmlResolver = null; - xd.Load(tempFile.Value); - - var userId = Security.GetUserId(); - - var element = XElement.Parse(xd.InnerXml); - var importContentTypes = Current.Services.PackagingService.ImportContentTypes(element, userId); - var contentType = importContentTypes.FirstOrDefault(); - if (contentType != null) - dtNameConfirm.Text = contentType.Name; - - // Try to clean up the temporary file. - try - { - System.IO.File.Delete(tempFile.Value); - } - catch(Exception ex) - { - Current.Logger.Error(typeof(importDocumentType), "Error cleaning up temporary udt file in App_Data: " + ex.Message, ex); - } - - Wizard.Visible = false; - Confirm.Visible = false; - done.Visible = true; - } - - private void submit_Click(object sender, EventArgs e) - { - tempFileName = "justDelete_" + Guid.NewGuid().ToString() + ".udt"; - var fileName = IOHelper.MapPath(SystemDirectories.Data + "/" + tempFileName); - tempFile.Value = fileName; - - documentTypeFile.PostedFile.SaveAs(fileName); - - var xd = new XmlDocument(); - xd.XmlResolver = null; - xd.Load(fileName); - dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value; - dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value; - - Wizard.Visible = false; - done.Visible = false; - Confirm.Visible = true; - } - } -}