diff --git a/src/Umbraco.Core/Constants-Packaging.cs b/src/Umbraco.Core/Constants-Packaging.cs index a73adf10de..43c0e6f69a 100644 --- a/src/Umbraco.Core/Constants-Packaging.cs +++ b/src/Umbraco.Core/Constants-Packaging.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core +using System.Xml.Linq; + +namespace Umbraco.Core { public static partial class Constants { @@ -13,39 +15,41 @@ public const string UmbracoPackageExtention = ".umb"; public const string DataTypeNodeName = "DataType"; public const string LanguagesNodeName = "Languages"; - public const string FilesNodeName = "Files"; + public const string FilesNodeName = "files"; public const string StylesheetsNodeName = "Stylesheets"; public const string TemplatesNodeName = "Templates"; - public const string OrgnameNodeName = "orgName"; public const string NameNodeName = "Name"; public const string TemplateNodeName = "Template"; public const string AliasNodeName = "Alias"; - public const string DictionaryitemsNodeName = "DictionaryItems"; - public const string MacrosNodeName = "macros"; + public const string DictionaryItemsNodeName = "DictionaryItems"; + public const string DictionaryItemNodeName = "DictionaryItem"; + public const string MacrosNodeName = "Macros"; public const string DocumentSetNodeName = "DocumentSet"; public const string DocumentTypesNodeName = "DocumentTypes"; public const string DocumentTypeNodeName = "DocumentType"; - public const string FileNodeName = "file"; + public const string FileNodeName = "file"; + public const string OrgNameNodeName = "orgName"; public const string OrgPathNodeName = "orgPath"; public const string GuidNodeName = "guid"; public const string StylesheetNodeName = "styleSheet"; public const string MacroNodeName = "macro"; public const string InfoNodeName = "info"; - public const string PackageRequirementsMajorXpath = "/package/requirements/major"; - public const string PackageRequirementsMinorXpath = "/package/requirements/minor"; - public const string PackageRequirementsPatchXpath = "/package/requirements/patch"; - public const string PackageNameXpath = "/package/name"; - public const string PackageVersionXpath = "/package/version"; - public const string PackageUrlXpath = "/package/url"; - public const string PackageLicenseXpath = "/package/license"; - public const string AuthorNameXpath = "/author/name"; - public const string AuthorWebsiteXpath = "/author/website"; - public const string ReadmeXpath = "/readme"; + public const string PackageRequirementsMajorXpath = "./package/requirements/major"; + public const string PackageRequirementsMinorXpath = "./package/requirements/minor"; + public const string PackageRequirementsPatchXpath = "./package/requirements/patch"; + public const string PackageNameXpath = "./package/name"; + public const string PackageVersionXpath = "./package/version"; + public const string PackageUrlXpath = "./package/url"; + public const string PackageLicenseXpath = "./package/license"; + public const string AuthorNameXpath = "./author/name"; + public const string AuthorWebsiteXpath = "./author/website"; + public const string ReadmeXpath = "./readme"; public const string ControlNodeName = "control"; public const string ActionNodeName = "Action"; public const string ActionsNodeName = "Actions"; public const string UndoNodeAttribute = "undo"; - public const string RunatNodeAttribute = "runat"; + public const string RunatNodeAttribute = "runat"; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/IStyleSheet.cs b/src/Umbraco.Core/Models/IStyleSheet.cs new file mode 100644 index 0000000000..08e10aedea --- /dev/null +++ b/src/Umbraco.Core/Models/IStyleSheet.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Models +{ + public interface IStylesheet : IFile + { + } +} diff --git a/src/Umbraco.Core/Models/Stylesheet.cs b/src/Umbraco.Core/Models/Stylesheet.cs index 2bed225361..7c92b1b9a9 100644 --- a/src/Umbraco.Core/Models/Stylesheet.cs +++ b/src/Umbraco.Core/Models/Stylesheet.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class Stylesheet : File + public class Stylesheet : File, IStylesheet { public Stylesheet(string path) : base(path) { diff --git a/src/Umbraco.Core/Packaging/IUnpackHelper.cs b/src/Umbraco.Core/Packaging/IUnpackHelper.cs index 29d5b62853..c8d1b9234a 100644 --- a/src/Umbraco.Core/Packaging/IUnpackHelper.cs +++ b/src/Umbraco.Core/Packaging/IUnpackHelper.cs @@ -1,9 +1,11 @@ -namespace Umbraco.Core.Packaging +using System.Collections.Generic; + +namespace Umbraco.Core.Packaging { public interface IUnpackHelper { - void UnPack(string sourcefilePath, string destinationDirectory); - string UnPackToTempDirectory(string sourcefilePath); - string ReadTextFileFromArchive(string sourcefilePath, string fileToRead); + string ReadTextFileFromArchive(string packageFilePath, string fileToRead); + bool CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath); + IEnumerable FindMissingFiles(string packageFilePath, IEnumerable expectedFiles); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/PackageImportIssues.cs b/src/Umbraco.Core/Packaging/PackageImportIssues.cs index 80b8a36f3f..284ca9c3f7 100644 --- a/src/Umbraco.Core/Packaging/PackageImportIssues.cs +++ b/src/Umbraco.Core/Packaging/PackageImportIssues.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Services; namespace Umbraco.Core.Packaging { public class PackageImportIssues { public bool ContainsUnsecureFiles { get { return UnsecureFiles != null && UnsecureFiles.Any(); } } - public IEnumerable UnsecureFiles { get; set; } - public IEnumerable> ConflictingMacroAliases { get; set; } - public IEnumerable> ConflictingTemplateAliases { get; set; } - public IEnumerable> ConflictingStylesheetNames { get; set; } - + public IFileInPackageInfo[] UnsecureFiles { get; set; } + public IMacro[] ConflictingMacroAliases { get; set; } + public ITemplate[] ConflictingTemplateAliases { get; set; } + public IStylesheet[] ConflictingStylesheetNames { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/PackageInstallationSummary.cs b/src/Umbraco.Core/Packaging/PackageInstallationSummary.cs index 2902e09fd1..8ad6d36243 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallationSummary.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallationSummary.cs @@ -1,21 +1,21 @@ using System.Collections.Generic; -using System.Xml; using System.Xml.Linq; +using Umbraco.Core.Models; namespace Umbraco.Core.Packaging { public class PackageInstallationSummary { public PackageMetaData MetaData { get; set; } - public IEnumerable DataTypesInstalled { get; set; } - public IEnumerable LanguagesInstalled { get; set; } - public IEnumerable DictionaryItemsInstalled { get; set; } - public IEnumerable MacrosInstalled { get; set; } + public IDataTypeDefinition[] DataTypesInstalled { get; set; } + public ILanguage[] LanguagesInstalled { get; set; } + public IDictionaryItem[] DictionaryItemsInstalled { get; set; } + public IMacro[] MacrosInstalled { get; set; } public IEnumerable> FilesInstalled { get;set;} - public IEnumerable TemplatesInstalled { get; set; } - public IEnumerable DocumentTypesInstalled { get; set; } - public IEnumerable StylesheetsInstalled { get; set; } - public IEnumerable DocumentsInstalled { get; set; } + public ITemplate[] TemplatesInstalled { get; set; } + public IContentType[] DocumentTypesInstalled { get; set; } + public IStylesheet[] StylesheetsInstalled { get; set; } + public IContent[] DocumentsInstalled { get; set; } public IEnumerable> PackageInstallActions { get; set; } public string PackageUninstallActions { get; set; } } diff --git a/src/Umbraco.Core/Packaging/UnpackHelper.cs b/src/Umbraco.Core/Packaging/UnpackHelper.cs index aaa169f453..b8a7ad3fb5 100644 --- a/src/Umbraco.Core/Packaging/UnpackHelper.cs +++ b/src/Umbraco.Core/Packaging/UnpackHelper.cs @@ -1,23 +1,18 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using ICSharpCode.SharpZipLib.Zip; -using Umbraco.Core.IO; namespace Umbraco.Core.Packaging { public class UnpackHelper : IUnpackHelper { - public string UnPackToTempDirectory(string filePath) + public string ReadTextFileFromArchive(string packageFilePath, string fileToRead) { - string tempDir = IOHelper.MapPath(SystemDirectories.Data) + Path.DirectorySeparatorChar + Guid.NewGuid(); - Directory.CreateDirectory(tempDir); - UnPack(filePath, tempDir); - return tempDir; - } + CheckPackageExists(packageFilePath); - public string ReadTextFileFromArchive(string sourcefilePath, string fileToRead) - { - using (var fs = File.OpenRead(sourcefilePath)) + using (var fs = File.OpenRead(packageFilePath)) { using (var zipStream = new ZipInputStream(fs)) { @@ -38,32 +33,50 @@ namespace Umbraco.Core.Packaging fs.Close(); } - throw new FileNotFoundException(string.Format("Could not find file in package file {0}", sourcefilePath), fileToRead); + throw new FileNotFoundException(string.Format("Could not find file in package file {0}", packageFilePath), fileToRead); } - public void UnPack(string sourcefilePath, string destinationDirectory) + private static void CheckPackageExists(string packageFilePath) { - // Unzip - using (var fs = File.OpenRead(sourcefilePath)) + if (File.Exists(packageFilePath) == false) + throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath), + "packageFilePath"); + } + + + public bool CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath) + { + CheckPackageExists(packageFilePath); + + bool fileFoundInArchive = false; + bool fileOverwritten = false; + + using (var fs = File.OpenRead(packageFilePath)) { using (var zipInputStream = new ZipInputStream(fs)) { ZipEntry zipEntry; while ((zipEntry = zipInputStream.GetNextEntry()) != null) { - string fileName = Path.GetFileName(zipEntry.Name); - if (string.IsNullOrEmpty(fileName)) continue; - - using ( var streamWriter = File.Create(Path.Combine(destinationDirectory, fileName))) + if(zipEntry.Name.Equals(fileInPackageName)) { - var data = new byte[2048]; - int size; - while ((size = zipInputStream.Read(data, 0, data.Length)) > 0) + fileFoundInArchive = true; + + fileOverwritten = File.Exists(destinationfilePath); + + using (var streamWriter = File.Open(destinationfilePath, FileMode.Create)) { - streamWriter.Write(data, 0, size); + var data = new byte[2048]; + int size; + while ((size = zipInputStream.Read(data, 0, data.Length)) > 0) + { + streamWriter.Write(data, 0, size); + } + + streamWriter.Close(); } - streamWriter.Close(); + break; } } @@ -71,6 +84,39 @@ namespace Umbraco.Core.Packaging } fs.Close(); } + + if (fileFoundInArchive == false) throw new ArgumentException(string.Format("Could not find file: {0} in package file: {1}", fileInPackageName, packageFilePath), "fileInPackageName"); + + return fileOverwritten; + } + + public IEnumerable FindMissingFiles(string packageFilePath, IEnumerable expectedFiles) + { + CheckPackageExists(packageFilePath); + + var exp = expectedFiles.ToDictionary(k => k, v => true); + + using (var fs = File.OpenRead(packageFilePath)) + { + using (var zipInputStream = new ZipInputStream(fs)) + { + ZipEntry zipEntry; + while ((zipEntry = zipInputStream.GetNextEntry()) != null) + { + if (exp.ContainsKey(zipEntry.Name)) + { + exp[zipEntry.Name] = false; + } + } + + zipInputStream.Close(); + } + fs.Close(); + } + + + return exp.Where(kv => kv.Value).Select(kv => kv.Key); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMacroProperty.cs b/src/Umbraco.Core/Services/IMacroProperty.cs index b6ac6eb3f2..3ff879509e 100644 --- a/src/Umbraco.Core/Services/IMacroProperty.cs +++ b/src/Umbraco.Core/Services/IMacroProperty.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Services { - public interface IMacroProperty + internal interface IMacroProperty { /// /// The sortorder diff --git a/src/Umbraco.Core/Services/IPackageValidationHelper.cs b/src/Umbraco.Core/Services/IPackageValidationHelper.cs index 08c493b537..dbbbeafd61 100644 --- a/src/Umbraco.Core/Services/IPackageValidationHelper.cs +++ b/src/Umbraco.Core/Services/IPackageValidationHelper.cs @@ -4,7 +4,7 @@ namespace Umbraco.Core.Services { public interface IPackageValidationHelper { - bool StylesheetExists(string styleSheetName, out Stylesheet existingStyleSheet); + bool StylesheetExists(string styleSheetName, out IStylesheet existingStylesheet); bool TemplateExists(string templateAlias, out ITemplate existingTemplate); bool MacroExists(string macroAlias, out IMacro existingMacro); } diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index c7e68e6eca..e49f6e24ba 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Xml.Linq; -using Umbraco.Core.Models; - +using Umbraco.Core.Models; + namespace Umbraco.Core.Services { public interface IPackagingService : IService @@ -47,10 +47,10 @@ namespace Umbraco.Core.Services /// /// Imports and saves the 'DictionaryItems' part of the package xml as a list of /// - /// Xml to import + /// Xml to import /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true); + /// An enumerable list of dictionary items + IEnumerable ImportDictionaryItems(XElement element, bool raiseEvents = true); /// /// Imports and saves the 'Languages' part of a package xml as a list of @@ -71,13 +71,22 @@ namespace Umbraco.Core.Services IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true); /// - /// Imports and saves package xml as + /// Imports and saves package xml as /// /// Xml to import /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated Templates - IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); + IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin) + /// Optional parameter indicating whether or not to raise events + /// An enumerable list of generated stylesheets + IEnumerable ImportStylesheets(XElement element, int userId = 0, bool raiseEvents = true); /// /// Exports an to xml as an @@ -185,6 +194,6 @@ namespace Umbraco.Core.Services /// Macro to export /// Optional parameter indicating whether or not to raise events /// containing the xml representation of the IMacro object - XElement Export(IMacro macro, bool raiseEvents = true); + XElement Export(IMacro macro, bool raiseEvents = true); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/PackageInstallerService.cs b/src/Umbraco.Core/Services/PackageInstallerService.cs index bf5cf26e00..b172b617ca 100644 --- a/src/Umbraco.Core/Services/PackageInstallerService.cs +++ b/src/Umbraco.Core/Services/PackageInstallerService.cs @@ -1,94 +1,168 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using System.Data; using System.IO; using System.Linq; -using System.Web.Hosting; using System.Xml.Linq; using System.Xml.XPath; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Packaging; -using File = System.IO.File; namespace Umbraco.Core.Services { public class PackageInstallerService : IPackageInstallerService { - private readonly IPackageValidationHelper _packageValidationHelper; + private readonly IFileService _fileService; + private readonly IMacroService _macroService; private readonly IPackagingService _packagingService; - private readonly IUnpackHelper _unpackHelper; + private IPackageValidationHelper _packageValidationHelper; + private IUnpackHelper _unpackHelper; - - public PackageInstallerService(IPackagingService packagingService, IUnpackHelper unpackHelper, IPackageValidationHelper packageValidationHelper) + public PackageInstallerService(IPackagingService packagingService, IMacroService macroService, + IFileService fileService) { - if (packageValidationHelper != null) _packageValidationHelper = packageValidationHelper; else throw new ArgumentNullException("packageValidationHelper"); - if (packagingService != null) _packagingService = packagingService; else throw new ArgumentNullException("packagingService"); - if (unpackHelper != null) _unpackHelper = unpackHelper; else throw new ArgumentNullException("unpackHelper"); + if (macroService != null) _macroService = macroService; + else throw new ArgumentNullException("macroService"); + if (fileService != null) _fileService = fileService; + else throw new ArgumentNullException("fileService"); + if (packagingService != null) _packagingService = packagingService; + else throw new ArgumentNullException("packagingService"); } - public PackageInstallationSummary InstallPackageFile(string packageFilePath, int userId) + public IPackageValidationHelper PackageValidationHelper { - FileInfo fi = GetPackageFileInfo(packageFilePath); - string tempDir = null; - try + private get { - tempDir = _unpackHelper.UnPackToTempDirectory(fi.FullName); - return InstallFromDirectory(tempDir, userId); + return _packageValidationHelper ?? + (_packageValidationHelper = new PackageValidationHelper(_macroService, _fileService)); } - finally + set { - if (string.IsNullOrEmpty(tempDir) == false && Directory.Exists(tempDir)) + if (_packageValidationHelper != null) { - Directory.Delete(tempDir, true); + throw new PropertyConstraintException("This property allraedy have a value"); } + _packageValidationHelper = value; } } + + public IUnpackHelper UnpackHelper + { + private get { return _unpackHelper ?? (_unpackHelper = new UnpackHelper()); } + set + { + if (_unpackHelper != null) + { + throw new PropertyConstraintException("This property allraedy have a value"); + } + _unpackHelper = value; + } + } + + private string _fullpathToRoot; + public string FullpathToRoot + { + private get { return _fullpathToRoot ?? (_fullpathToRoot = GlobalSettings.FullpathToRoot); } + set + { + + if (_fullpathToRoot != null) + { + throw new PropertyConstraintException("This property allraedy have a value"); + } + + _fullpathToRoot = value; + } + } + + public PackageMetaData GetMetaData(string packageFilePath) { - var rootElement = GetConfigXmlRootElementFromPackageFile(packageFilePath); + XElement rootElement = GetConfigXmlRootElementFromPackageFile(packageFilePath); return GetMetaData(rootElement); } public PackageImportIssues FindPackageImportIssues(string packageFilePath) { - var rootElement = GetConfigXmlRootElementFromPackageFile(packageFilePath); + XElement rootElement = GetConfigXmlRootElementFromPackageFile(packageFilePath); return FindImportIssues(rootElement); } + public PackageInstallationSummary InstallPackageFile(string packageFile, int userId) + { + ValidateFilesExistsInPackage(packageFile); - private FileInfo GetPackageFileInfo(string packageFilePath) + + XElement rootElement = GetConfigXmlRootElementFromPackageFile(packageFile); + + XElement dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); + XElement languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); + XElement dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName); + XElement macroes = rootElement.Element(Constants.Packaging.MacrosNodeName); + XElement files = rootElement.Element(Constants.Packaging.FilesNodeName); + XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); + XElement documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName); + XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); + XElement documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName); + XElement actions = rootElement.Element(Constants.Packaging.ActionsNodeName); + + return new PackageInstallationSummary + { + MetaData = GetMetaData(rootElement), + DataTypesInstalled = + dataTypes == null ? new IDataTypeDefinition[0] : InstallDataTypes(dataTypes, userId), + LanguagesInstalled = languages == null ? new ILanguage[0] : InstallLanguages(languages, userId), + DictionaryItemsInstalled = + dictionaryItems == null ? new IDictionaryItem[0] : InstallDictionaryItems(dictionaryItems), + MacrosInstalled = macroes == null ? new IMacro[0] : InstallMacros(macroes, userId), + FilesInstalled = + packageFile == null + ? Enumerable.Empty>() + : InstallFiles(packageFile, files), + TemplatesInstalled = templates == null ? new ITemplate[0] : InstallTemplats(templates, userId), + DocumentTypesInstalled = + documentTypes == null ? new IContentType[0] : InstallDocumentTypes(documentTypes, userId), + StylesheetsInstalled = + styleSheets == null ? new IStylesheet[0] : InstallStylesheets(styleSheets, userId), + DocumentsInstalled = documentSet == null ? new IContent[0] : InstallDocuments(documentSet, userId), + PackageInstallActions = + actions == null ? Enumerable.Empty>() : GetInstallActions(actions), + PackageUninstallActions = actions == null ? string.Empty : GetUninstallActions(actions) + }; + } + + + private void ValidatePackageFileExists(string packageFilePath) { if (string.IsNullOrEmpty(packageFilePath)) { throw new ArgumentNullException("packageFilePath"); } - var fi = new FileInfo(packageFilePath); - - if (fi.Exists == false) + if (System.IO.File.Exists(packageFilePath) == false) { throw new Exception("Error - file not found. Could find file named '" + packageFilePath + "'"); } - // Check if the file is a valid package - if (fi.Extension.Equals(Constants.Packaging.UmbracoPackageExtention, StringComparison.InvariantCultureIgnoreCase) == false) + if (Path.GetExtension(packageFilePath).Equals(".umb", StringComparison.InvariantCultureIgnoreCase) == false) { - throw new Exception("Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); + throw new Exception( + "Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); } - - return fi; } private XDocument GetConfigXmlDocFromPackageFile(string packageFilePath) { - FileInfo packageFileInfo = GetPackageFileInfo(packageFilePath); + ValidatePackageFileExists(packageFilePath); - string configXmlContent = _unpackHelper.ReadTextFileFromArchive(packageFileInfo.FullName, Constants.Packaging.PackageXmlFileName); + string configXmlContent = UnpackHelper.ReadTextFileFromArchive(packageFilePath, + Constants.Packaging.PackageXmlFileName); return XDocument.Parse(configXmlContent); } @@ -96,293 +170,356 @@ namespace Umbraco.Core.Services private XElement GetConfigXmlRootElementFromPackageFile(string packageFilePath) { - var document = GetConfigXmlDocFromPackageFile(packageFilePath); - if (document.Root == null || document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false) { throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath); } + XDocument document = GetConfigXmlDocFromPackageFile(packageFilePath); + if (document.Root == null || + document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false) + { + throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath); + } return document.Root; } - - private PackageInstallationSummary InstallFromDirectory(string packageDir, int userId) + private void ValidateFilesExistsInPackage(string packageFilePath) { - var configXml = GetConfigXmlDocFromPackageDirectory(packageDir); - var rootElement = configXml.XPathSelectElement(Constants.Packaging.UmbPackageNodeName); - if (rootElement == null) { throw new ArgumentException("File does not have a root node called \"" + Constants.Packaging.UmbPackageNodeName + "\"", packageDir); } - - var dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); - var languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); - var dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryitemsNodeName); - var macroes = rootElement.Element(Constants.Packaging.MacrosNodeName); - var files = rootElement.Element(Constants.Packaging.FilesNodeName); - var templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); - var documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName); - var styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - var documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName); - var actions = rootElement.Element(Constants.Packaging.ActionsNodeName); - - return new PackageInstallationSummary + XElement rootElement = GetConfigXmlRootElementFromPackageFile(packageFilePath); + XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName); + if (filesElement != null) { - MetaData = GetMetaData(rootElement), - DataTypesInstalled = dataTypes == null ? Enumerable.Empty() : InstallDataTypes(dataTypes, userId), - LanguagesInstalled = languages == null ? Enumerable.Empty() : InstallLanguages(languages, userId), - DictionaryItemsInstalled = dictionaryItems == null ? Enumerable.Empty() : InstallDictionaryItems(dictionaryItems, userId), - MacrosInstalled = macroes == null ? Enumerable.Empty() : InstallMacros(macroes, userId), - FilesInstalled = packageDir == null ? Enumerable.Empty>() : InstallFiles(packageDir, files), - TemplatesInstalled = templates == null ? Enumerable.Empty() : InstallTemplats(templates, userId), - DocumentTypesInstalled = documentTypes == null ? Enumerable.Empty() : InstallDocumentTypes(documentTypes, userId), - StylesheetsInstalled = styleSheets == null ? Enumerable.Empty() : InstallStylesheets(styleSheets, userId), - DocumentsInstalled = documentSet == null ? Enumerable.Empty() : InstallDocuments(documentSet, userId), - PackageInstallActions = actions == null ? Enumerable.Empty>() : GetInstallActions(actions), - PackageUninstallActions = actions == null ? string.Empty : GetUninstallActions(actions) - }; + IEnumerable extractFileInPackageInfos = + ExtractFileInPackageInfos(filesElement).ToArray(); + + IEnumerable missingFiles = + _unpackHelper.FindMissingFiles(packageFilePath, + extractFileInPackageInfos.Select(i => i.FileNameInPackage)).ToArray(); + + if (missingFiles.Any()) + { + throw new Exception("The following file(s) are missing in the package: " + + string.Join(", ", missingFiles.Select( + mf => + { + FileInPackageInfo fileInPackageInfo = + extractFileInPackageInfos.Single(fi => fi.FileNameInPackage == mf); + return string.Format("Guid: \"{0}\" Original File: \"{1}\"", + fileInPackageInfo.FileNameInPackage, fileInPackageInfo.RelativePath); + }))); + } + } } + private static string GetUninstallActions(XElement actionsElement) { //saving the uninstall actions untill the package is uninstalled. return actionsElement.Elements(Constants.Packaging.ActionNodeName) - .Where(e => e.HasAttributes && e.Attribute(Constants.Packaging.UndoNodeAttribute) != null && e.Attribute(Constants.Packaging.UndoNodeAttribute) - .Value.Equals("false()", StringComparison.InvariantCultureIgnoreCase) == false) // SelectNodes("Actions/Action [@undo != false()]"); - .Select(m => m.Value).Aggregate((workingSentence, next) => next + workingSentence); + .Where( + e => + e.HasAttributes && e.Attribute(Constants.Packaging.UndoNodeAttribute) != null && + e.Attribute(Constants.Packaging.UndoNodeAttribute) + .Value.Equals("false()", StringComparison.InvariantCultureIgnoreCase) == false) + // SelectNodes("Actions/Action [@undo != false()]"); + .Select(m => m.Value).Aggregate((workingSentence, next) => next + workingSentence); } private static IEnumerable> GetInstallActions(XElement actionsElement) { - if (actionsElement == null) { return Enumerable.Empty>(); } + if (actionsElement == null) + { + return Enumerable.Empty>(); + } - if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root", "actionsElement"); } + if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false) + { + throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root", + "actionsElement"); + } return actionsElement.Elements(Constants.Packaging.ActionNodeName) .Where( e => e.HasAttributes && (e.Attribute(Constants.Packaging.RunatNodeAttribute) == null || - e.Attribute(Constants.Packaging.RunatNodeAttribute).Value.Equals("uninstall", StringComparison.InvariantCultureIgnoreCase) == + e.Attribute(Constants.Packaging.RunatNodeAttribute) + .Value.Equals("uninstall", StringComparison.InvariantCultureIgnoreCase) == false)) // .SelectNodes("Actions/Action [@runat != 'uninstall']") .Select(elemet => { - var aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeName); + XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeName); if (aliasAttr == null) - throw new ArgumentException("missing \"" + Constants.Packaging.AliasNodeName + "\" atribute in alias element", "actionsElement"); + throw new ArgumentException( + "missing \"" + Constants.Packaging.AliasNodeName + "\" atribute in alias element", + "actionsElement"); return new {elemet, alias = aliasAttr.Value}; }).ToDictionary(x => x.alias, x => x.elemet); } - private IEnumerable InstallDocuments(XElement documentsElement, int userId = 0) + private IContent[] InstallDocuments(XElement documentsElement, int userId = 0) { - if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentSetNodeName + "\" as root", "documentsElement"); } - return _packagingService.ImportContent(documentsElement, -1, userId).Select(c => c.Id); - } - - private IEnumerable InstallStylesheets(XElement styleSheetsElement, int userId = 0) - { - if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root", "styleSheetsElement"); } - return _packagingService.ImportStylesheets(styleSheetsElement, userId).Select(f => f.Id); - } - - private IEnumerable InstallDocumentTypes(XElement documentTypes, int userId = 0) - { - if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) + if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false) { - if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) - throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes"); + throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentSetNodeName + "\" as root", + "documentsElement"); + } + return _packagingService.ImportContent(documentsElement, -1, userId).ToArray(); + } + + private IStylesheet[] InstallStylesheets(XElement styleSheetsElement, int userId = 0) + { + if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false) + { + throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root", + "styleSheetsElement"); + } + return _packagingService.ImportStylesheets(styleSheetsElement, userId).ToArray(); + } + + private IContentType[] InstallDocumentTypes(XElement documentTypes, int userId = 0) + { + if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false) + { + if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false) + throw new ArgumentException( + "Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes"); documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes); } - return _packagingService.ImportContentTypes(documentTypes, userId).Select(ct => ct.Id); + return _packagingService.ImportContentTypes(documentTypes, userId).ToArray(); } - private IEnumerable InstallTemplats(XElement templateElement, int userId = 0) + private ITemplate[] InstallTemplats(XElement templateElement, int userId = 0) { - if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root", "templateElement"); } - return _packagingService.ImportTemplates(templateElement, userId).Select(t => t.Id); - } - - - private static IEnumerable> InstallFiles(string packageDir, XElement filesElement) - { - if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("root element must be \"" + Constants.Packaging.FilesNodeName + "\"", "filesElement"); } - - string basePath = HostingEnvironment.ApplicationPhysicalPath; - - var xmlNodeList = filesElement.Elements(Constants.Packaging.FileNodeName); - - return xmlNodeList.Select(e => + if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false) { - var orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName); - if (orgPathElement == null) { throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"", "filesElement"); } - - var guidElement = e.Element(Constants.Packaging.GuidNodeName); - if (guidElement == null) { throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"", "filesElement"); } - - var orgNameElement = e.Element(Constants.Packaging.OrgnameNodeName); - if (orgNameElement == null) { throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgnameNodeName + "\"", "filesElement"); } - - - var destPath = GetFileName(basePath, orgPathElement.Value); - var sourceFile = GetFileName(packageDir, guidElement.Value); - var destFile = GetFileName(destPath, orgNameElement.Value); - - if (Directory.Exists(destPath) == false) Directory.CreateDirectory(destPath); - - var existingOverrided = File.Exists(destFile); - - File.Copy(sourceFile, destFile, true); - - return new KeyValuePair(orgPathElement.Value + "/" + orgNameElement.Value, existingOverrided); - }); + throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root", + "templateElement"); + } + return _packagingService.ImportTemplates(templateElement, userId).ToArray(); } - private IEnumerable InstallMacros(XElement macroElements, int userId = 0) - { - if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root", "macroElements"); } - return _packagingService.ImportMacros(macroElements, userId).Select(m => m.Id); - } - private IEnumerable InstallDictionaryItems(XElement dictionaryItemsElement, int userId = 0) + private IEnumerable> InstallFiles(string packageFilePath, XElement filesElement) { - if (string.Equals(Constants.Packaging.DictionaryitemsNodeName, dictionaryItemsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryitemsNodeName + "\" as root", "dictionaryItemsElement"); } - return _packagingService.ImportDictionaryItems(dictionaryItemsElement, userId).Select(di => di.Id); - } - - private IEnumerable InstallLanguages(XElement languageElement, int userId = 0) - { - if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Must be \"Templates\" as root", "languageElement"); } - return _packagingService.ImportLanguage(languageElement, userId).Select(l => l.Id); - } - - private IEnumerable InstallDataTypes(XElement dataTypeElements, int userId = 0) - { - if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) + return ExtractFileInPackageInfos(filesElement).Select(fpi => { + bool existingOverrided = _unpackHelper.CopyFileFromArchive(packageFilePath, fpi.FileNameInPackage, + fpi.FullPath); - if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) + return new KeyValuePair(fpi.FullPath, existingOverrided); + }).ToArray(); + } + + private IMacro[] InstallMacros(XElement macroElements, int userId = 0) + { + if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false) + { + throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root", + "macroElements"); + } + return _packagingService.ImportMacros(macroElements, userId).ToArray(); + } + + private IDictionaryItem[] InstallDictionaryItems(XElement dictionaryItemsElement) + { + if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) == + false) + { + throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root", + "dictionaryItemsElement"); + } + return _packagingService.ImportDictionaryItems(dictionaryItemsElement).ToArray(); + } + + private ILanguage[] InstallLanguages(XElement languageElement, int userId = 0) + { + if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false) + { + throw new ArgumentException("Must be \"Templates\" as root", "languageElement"); + } + return _packagingService.ImportLanguages(languageElement, userId).ToArray(); + } + + private IDataTypeDefinition[] InstallDataTypes(XElement dataTypeElements, int userId = 0) + { + if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false) + { + if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false) { throw new ArgumentException("Must be \"Templates\" as root", "dataTypeElements"); } } - return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId).Select(e => e.Id); + return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId).ToArray(); } - private static XDocument GetConfigXmlDocFromPackageDirectory(string packageDir) - { - string packageXmlPath = Path.Combine(packageDir, Constants.Packaging.PackageXmlFileName); - if (File.Exists(packageXmlPath) == false) { throw new FileNotFoundException("Could not find " + Constants.Packaging.PackageXmlFileName + " in package"); } - return XDocument.Load(packageXmlPath); - } - - private PackageImportIssues FindImportIssues(XElement rootElement) { - var files = rootElement.Element(Constants.Packaging.FilesNodeName); - var styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - var templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); - var alias = rootElement.Element(Constants.Packaging.MacrosNodeName); + XElement files = rootElement.Element(Constants.Packaging.FilesNodeName); + XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); + XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); + XElement alias = rootElement.Element(Constants.Packaging.MacrosNodeName); var packageImportIssues = new PackageImportIssues { - UnsecureFiles = files == null ? Enumerable.Empty() : FindUnsecureFiles(files), - ConflictingMacroAliases = alias == null ? Enumerable.Empty>() : FindConflictingMacroAliases(alias), - ConflictingTemplateAliases = templates == null ? Enumerable.Empty>() : FindConflictingTemplateAliases(templates), - ConflictingStylesheetNames = styleSheets == null ? Enumerable.Empty>() : FindConflictingStylesheetNames(styleSheets) + UnsecureFiles = files == null ? new IFileInPackageInfo[0] : FindUnsecureFiles(files), + ConflictingMacroAliases = alias == null ? new IMacro[0] : FindConflictingMacroAliases(alias), + ConflictingTemplateAliases = + templates == null ? new ITemplate[0] : FindConflictingTemplateAliases(templates), + ConflictingStylesheetNames = + styleSheets == null ? new IStylesheet[0] : FindConflictingStylesheetNames(styleSheets) }; return packageImportIssues; } - private IEnumerable FindUnsecureFiles(XElement fileElement) + private IFileInPackageInfo[] FindUnsecureFiles(XElement fileElement) { - if (string.Equals(Constants.Packaging.FilesNodeName, fileElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("the root element must be \"Files\"", "fileElement"); } - - return fileElement.Elements(Constants.Packaging.FileNodeName) - .Where(FileNodeIsUnsecure) - .Select(n => - { - var xElement = n.Element(Constants.Packaging.OrgnameNodeName); - if (xElement == null) { throw new ArgumentException("missing a element: " + Constants.Packaging.OrgnameNodeName, "n"); } - return xElement.Value; - }); + return ExtractFileInPackageInfos(fileElement) + .Where(IsFileNodeUnsecure).Cast().ToArray(); } - private IEnumerable> FindConflictingStylesheetNames(XElement stylesheetNotes) + private IStylesheet[] FindConflictingStylesheetNames(XElement stylesheetNotes) { - if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("the root element must be \"Stylesheets\"", "stylesheetNotes"); } + if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName) == false) + { + throw new ArgumentException("the root element must be \"Stylesheets\"", "stylesheetNotes"); + } return stylesheetNotes.Elements(Constants.Packaging.StylesheetNodeName) - .Select(n => + .Select(n => + { + XElement xElement = n.Element(Constants.Packaging.NameNodeName); + if (xElement == null) { - var xElement = n.Element(Constants.Packaging.NameNodeName); - if (xElement == null) { throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element", "stylesheetNotes"); } + throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element", + "stylesheetNotes"); + } - string name = xElement.Value; + string name = xElement.Value; - Stylesheet existingStyleSheet; - if (_packageValidationHelper.StylesheetExists(name, out existingStyleSheet)) - { - // Don't know what to put in here... existing path was the best i could come up with - return new KeyValuePair(name, existingStyleSheet.Path); - } - return new KeyValuePair(name, null); - }) - .Where(kv => kv.Value != null); + IStylesheet existingStyleSheet; + if (PackageValidationHelper.StylesheetExists(name, out existingStyleSheet)) + { + // Don't know what to put in here... existing path was the best i could come up with + return existingStyleSheet; + } + return null; + }) + .Where(v => v != null).ToArray(); } - private IEnumerable> FindConflictingTemplateAliases(XElement templateNotes) + private ITemplate[] FindConflictingTemplateAliases(XElement templateNotes) { - if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName, StringComparison.InvariantCultureIgnoreCase) == false) { throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node", "templateNotes"); } + if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName) == false) + { + throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node", + "templateNotes"); + } return templateNotes.Elements(Constants.Packaging.TemplateNodeName) - .Select(n => + .Select(n => + { + XElement alias = n.Element(Constants.Packaging.AliasNodeName); + if (alias == null) { - var alias = n.Element(Constants.Packaging.AliasNodeName); - if (alias == null) { throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeName + "\" element", "templateNotes"); } - string aliasStr = alias.Value; + throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeName + "\" element", + "templateNotes"); + } + string aliasStr = alias.Value; - ITemplate existingTemplate; + ITemplate existingTemplate; - if (_packageValidationHelper.TemplateExists(aliasStr, out existingTemplate)) - { - return new KeyValuePair(aliasStr, existingTemplate.Name); - } + if (PackageValidationHelper.TemplateExists(aliasStr, out existingTemplate)) + { + return existingTemplate; + } - return new KeyValuePair(aliasStr, null); - }) - .Where(kv => kv.Value != null); + return null; + }) + .Where(v => v != null).ToArray(); } - private IEnumerable> FindConflictingMacroAliases(XElement macroNodes) + private IMacro[] FindConflictingMacroAliases(XElement macroNodes) { return macroNodes.Elements(Constants.Packaging.MacroNodeName) - .Select(n => + .Select(n => + { + XElement xElement = n.Element(Constants.Packaging.AliasNodeName); + if (xElement == null) { - var xElement = n.Element(Constants.Packaging.AliasNodeName); - if (xElement == null) { throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeName + "\" element", "macroNodes"); } - string alias = xElement.Value; + throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeName + "\" element", + "macroNodes"); + } + string alias = xElement.Value; - IMacro existingMacro; - if (_packageValidationHelper.MacroExists(alias, out existingMacro)) - { - return new KeyValuePair(alias, existingMacro.Name); - } - - return new KeyValuePair(alias, null); - }) - .Where(kv => kv.Key != null && kv.Value != null); + IMacro existingMacro; + if (PackageValidationHelper.MacroExists(alias, out existingMacro)) + { + return existingMacro; + } + + return null; + }) + .Where(v => v != null).ToArray(); } - private bool FileNodeIsUnsecure(XElement fileNode) + private bool IsFileNodeUnsecure(FileInPackageInfo fileInPackageInfo) { - string basePath = HostingEnvironment.ApplicationPhysicalPath; - var orgName = fileNode.Element(Constants.Packaging.OrgnameNodeName); - if (orgName == null) { throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgnameNodeName + "\"", "fileNode"); } - - string destPath = GetFileName(basePath, orgName.Value); - // Should be done with regex :) - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) return true; - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) return true; + if (fileInPackageInfo.Directory.ToLower().Contains(IOHelper.DirSepChar + "app_code")) return true; + if (fileInPackageInfo.Directory.ToLower().Contains(IOHelper.DirSepChar + "bin")) return true; - return destPath.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase); + string extension = Path.GetExtension(fileInPackageInfo.Directory); + + return extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase); + } + + + private IEnumerable ExtractFileInPackageInfos(XElement filesElement) + { + if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName) == false) + { + throw new ArgumentException("the root element must be \"Files\"", "filesElement"); + } + + return filesElement.Elements(Constants.Packaging.FileNodeName) + .Select(e => + { + XElement guidElement = e.Element(Constants.Packaging.GuidNodeName); + if (guidElement == null) + { + throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"", + "filesElement"); + } + + XElement orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName); + if (orgPathElement == null) + { + throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"", + "filesElement"); + } + + XElement orgNameElement = e.Element(Constants.Packaging.OrgNameNodeName); + if (orgNameElement == null) + { + throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgNameNodeName + "\"", + "filesElement"); + } + + + return new FileInPackageInfo + { + FileNameInPackage = guidElement.Value, + FileName = PrepareAsFilePathElement(orgNameElement.Value), + RelativeDir = UpdatePathPlaceholders( + PrepareAsFilePathElement(orgPathElement.Value)), + DestinationRootDir = FullpathToRoot + }; + }).ToArray(); + } + + private static string PrepareAsFilePathElement(string pathElement) + { + return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\"); } @@ -390,20 +527,24 @@ namespace Umbraco.Core.Services { XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName); - if (infoElement == null) { throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element", "xRootElement"); } + if (infoElement == null) + { + throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element", + "xRootElement"); + } - var majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath); - var minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath); - var patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath); - var nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath); - var versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath); - var urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath); - var licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath); - var authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath); - var authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath); - var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath); + XElement majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath); + XElement minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath); + XElement patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath); + XElement nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath); + XElement versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath); + XElement urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath); + XElement licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath); + XElement authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath); + XElement authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath); + XElement readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath); - var controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName); + XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName); int val; @@ -413,7 +554,10 @@ namespace Umbraco.Core.Services Version = versionElement == null ? string.Empty : versionElement.Value, Url = urlElement == null ? string.Empty : urlElement.Value, License = licenseElement == null ? string.Empty : licenseElement.Value, - LicenseUrl = licenseElement == null ? string.Empty : licenseElement.HasAttributes ? licenseElement.AttributeValue("url") : string.Empty, + LicenseUrl = + licenseElement == null + ? string.Empty + : licenseElement.HasAttributes ? licenseElement.AttributeValue("url") : string.Empty, AuthorName = authorNameElement == null ? string.Empty : authorNameElement.Value, AuthorUrl = authorUrlElement == null ? string.Empty : authorUrlElement.Value, Readme = readmeElement == null ? string.Empty : readmeElement.Value, @@ -424,20 +568,8 @@ namespace Umbraco.Core.Services }; } - - /// - /// Gets the name of the file in the specified path. - /// Corrects possible problems with slashes that would result from a simple concatenation. - /// Can also be used to concatenate paths. - /// - /// The path. - /// Name of the file. - /// The name of the file in the specified path. - private static String GetFileName(string path, string fileName) + private static string UpdatePathPlaceholders(string path) { - // virtual dir support - fileName = IOHelper.FindFile(fileName); - if (path.Contains("[$")) { //this is experimental and undocumented... @@ -446,32 +578,38 @@ namespace Umbraco.Core.Services path = path.Replace("[$CONFIG]", SystemDirectories.Config); path = path.Replace("[$DATA]", SystemDirectories.Data); } - - //to support virtual dirs we try to lookup the file... - path = IOHelper.FindFile(path); - - Debug.Assert(path != null && path.Length >= 1); - Debug.Assert(fileName != null && fileName.Length >= 1); - - path = path.Replace('/', '\\'); - fileName = fileName.Replace('/', '\\'); - - // Does filename start with a slash? Does path end with one? - bool fileNameStartsWithSlash = (fileName[0] == Path.DirectorySeparatorChar); - bool pathEndsWithSlash = (path[path.Length - 1] == Path.DirectorySeparatorChar); - - // Path ends with a slash - if (pathEndsWithSlash) - { - if (fileNameStartsWithSlash == false) - // No double slash, just concatenate - return path + fileName; - return path + fileName.Substring(1); - } - if (fileNameStartsWithSlash) - // Required slash specified, just concatenate - return path + fileName; - return path + Path.DirectorySeparatorChar + fileName; + return path; } } + + public class FileInPackageInfo : IFileInPackageInfo + { + public string RelativePath + { + get { return Path.Combine(RelativeDir, FileName); } + } + + public string FileNameInPackage { get; set; } + public string RelativeDir { get; set; } + public string DestinationRootDir { private get; set; } + + public string Directory + { + get { return Path.Combine(DestinationRootDir, RelativeDir); } + } + + public string FullPath + { + get { return Path.Combine(DestinationRootDir, RelativePath); } + } + + public string FileName { get; set; } + } + + public interface IFileInPackageInfo + { + string RelativeDir { get; } + string RelativePath { get; } + string FileName { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/PackageValidationHelper.cs b/src/Umbraco.Core/Services/PackageValidationHelper.cs index e818781734..87495a4652 100644 --- a/src/Umbraco.Core/Services/PackageValidationHelper.cs +++ b/src/Umbraco.Core/Services/PackageValidationHelper.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Services else throw new ArgumentNullException("macroService"); } - public bool StylesheetExists(string styleSheetName, out Stylesheet existingStyleSheet) + public bool StylesheetExists(string styleSheetName, out IStylesheet existingStyleSheet) { existingStyleSheet = _fileService.GetStylesheetByName(styleSheetName); return existingStyleSheet != null; diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index f358e41bf3..5ffb45703f 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -22,6 +22,7 @@ namespace Umbraco.Core.Services private Lazy _fileService; private Lazy _localizationService; private Lazy _packagingService; + private Lazy _packageInstallerService; private Lazy _serverRegistrationService; private Lazy _entityService; private Lazy _relationService; @@ -31,7 +32,7 @@ namespace Umbraco.Core.Services private Lazy _memberTypeService; private Lazy _memberGroupService; private Lazy _notificationService; - + /// /// public ctor - will generally just be used for unit testing /// @@ -42,6 +43,7 @@ namespace Umbraco.Core.Services /// /// /// + /// /// /// /// @@ -59,7 +61,8 @@ namespace Umbraco.Core.Services IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, - IPackagingService packagingService, + IPackagingService packagingService, + IPackageInstallerService packageInstallerService, IEntityService entityService, IRelationService relationService, IMemberGroupService memberGroupService, @@ -79,6 +82,7 @@ namespace Umbraco.Core.Services _fileService = new Lazy(() => fileService); _localizationService = new Lazy(() => localizationService); _packagingService = new Lazy(() => packagingService); + _packageInstallerService = new Lazy(() => packageInstallerService); _entityService = new Lazy(() => entityService); _relationService = new Lazy(() => relationService); _sectionService = new Lazy(() => sectionService); @@ -152,6 +156,9 @@ namespace Umbraco.Core.Services if (_packagingService == null) _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _macroService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); + if (_packageInstallerService == null) + _packageInstallerService = new Lazy(() => new PackageInstallerService(_packagingService.Value, _macroService.Value, _fileService.Value)); + if (_entityService == null) _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); @@ -328,6 +335,10 @@ namespace Umbraco.Core.Services { get { return _memberGroupService.Value; } } - + + public IPackageInstallerService PackageInstallerService + { + get { return _packageInstallerService.Value; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ee90d31374..ab427d59e5 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -350,6 +350,7 @@ + @@ -364,9 +365,12 @@ - - + + + + + @@ -374,8 +378,8 @@ - + @@ -1020,7 +1024,6 @@ - @@ -1036,7 +1039,6 @@ - @@ -1046,7 +1048,6 @@ - @@ -1055,7 +1056,6 @@ - @@ -1065,9 +1065,7 @@ - - diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index f8810fe67f..eaa7b81850 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -44,6 +44,10 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), + new PackageInstallerService( + new Mock().Object, + new Mock().Object, + new Mock().Object), new Mock().Object, new RelationService( new Mock().Object, @@ -89,6 +93,10 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), + new PackageInstallerService( + new Mock().Object, + new Mock().Object, + new Mock().Object), new Mock().Object, new RelationService( new Mock().Object, diff --git a/src/Umbraco.Tests/Packages/Document_Type_Picker_1.1.umb b/src/Umbraco.Tests/Packages/Document_Type_Picker_1.1.umb new file mode 100644 index 0000000000..18449bd373 Binary files /dev/null and b/src/Umbraco.Tests/Packages/Document_Type_Picker_1.1.umb differ diff --git a/src/Umbraco.Tests/Services/PackageInstallerServiceTest.cs b/src/Umbraco.Tests/Services/PackageInstallerServiceTest.cs new file mode 100644 index 0000000000..f46e243d95 --- /dev/null +++ b/src/Umbraco.Tests/Services/PackageInstallerServiceTest.cs @@ -0,0 +1,82 @@ +using System.IO; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Services +{ + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + [TestFixture] + public class PackageInstallerServiceTest : BaseServiceTest + { + private const string DOCUMENT_TYPE_PICKER_UMB = "Document_Type_Picker_1.1.umb"; + private const string TEST_PACKAGES_DIR_NAME = "Packages"; + + [SetUp] + public override void Initialize() + { + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void PackageInstallerService_TestSomething() + { + // Arrange + var path = GetTestPackagePath(DOCUMENT_TYPE_PICKER_UMB); + + // Act + var packageMetaData = ServiceContext.PackageInstallerService.GetMetaData(path); + + // Assert + Assert.IsNotNull(packageMetaData); + } + + [Test] + public void PackageInstallerService_TestSomethingelse() + { + // Arrange + var path = GetTestPackagePath(DOCUMENT_TYPE_PICKER_UMB); + + // Act + var importIssues = ServiceContext.PackageInstallerService.FindPackageImportIssues(path); + + // Assert + Assert.IsNotNull(importIssues); + } + + + [Test] + public void PackageInstallerService_TestSomethingthered() + { + // Arrange + var path = GetTestPackagePath(DOCUMENT_TYPE_PICKER_UMB); + + // Act + var packageMetaData = ServiceContext.PackageInstallerService.InstallPackageFile(path, -1); + // Assert + IDataTypeDefinition dataTypeDefinitionById = ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById( + packageMetaData.DataTypesInstalled.Single().Id); + + Assert.IsNotNull(dataTypeDefinitionById); + + foreach (var result in packageMetaData.FilesInstalled.Select(fi => fi.Key)) + { + Assert.IsTrue(System.IO.File.Exists(result)); + System.IO.File.Delete(result); + } + } + + private static string GetTestPackagePath(string packageName) + { + string path = Path.Combine(Core.Configuration.GlobalSettings.FullpathToRoot, TEST_PACKAGES_DIR_NAME, packageName); + return path; + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 499ebaa336..acc47f85f6 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -322,6 +322,7 @@ + @@ -700,6 +701,7 @@ +