Implementing PackagingService and moving Import Export methods to this service to have it specialized and centralized around packaging operations.

This commit is contained in:
Morten Christensen
2013-03-20 13:51:46 -01:00
parent ffe9953c43
commit d0787e92fe
14 changed files with 658 additions and 249 deletions

View File

@@ -8,6 +8,17 @@ namespace Umbraco.Core.Models
/// </summary>
public class ContentTypeSort : IValueObject
{
public ContentTypeSort()
{
}
public ContentTypeSort(Lazy<int> id, int sortOrder, string @alias)
{
Id = id;
SortOrder = sortOrder;
Alias = alias;
}
/// <summary>
/// Gets or sets the Id of the ContentType
/// </summary>

View File

@@ -328,8 +328,8 @@ namespace Umbraco.Core.Persistence.Repositories
public ITemplate Get(string alias)
{
var sql = GetBaseQuery(false);
sql.Where("cmsTemplate.alias = @Alias", new { Alias = alias });
var sql = GetBaseQuery(false)
.Where<TemplateDto>(x => x.Alias == alias);
var dto = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();

View File

@@ -1001,166 +1001,6 @@ namespace Umbraco.Core.Services
return content;
}
/// <summary>
/// Imports and saves package xml as <see cref="IContent"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <returns>An enumrable list of generated content</returns>
public IEnumerable<IContent> Import(XElement element)
{
var name = element.Name.LocalName;
if (name.Equals("DocumentSet"))
{
//This is a regular deep-structured import
var roots = from doc in element.Elements()
where (string) doc.Attribute("isDoc") == ""
select doc;
var contents = ParseRootXml(roots);
Save(contents);
return contents;
}
var attribute = element.Attribute("isDoc");
if (attribute != null)
{
//This is a single doc import
var elements = new List<XElement> { element };
var contents = ParseRootXml(elements);
Save(contents);
return contents;
}
throw new ArgumentException(
"The passed in XElement is not valid! It does not contain a root element called "+
"'DocumentSet' (for structured imports) nor is the first element a Document (for single document import).");
}
private IEnumerable<IContent> ParseRootXml(IEnumerable<XElement> roots)
{
var contentTypes = new Dictionary<string, IContentType>();
var contents = new List<IContent>();
foreach (var root in roots)
{
bool isLegacySchema = root.Name.LocalName.ToLowerInvariant().Equals("node");
string contentTypeAlias = isLegacySchema
? root.Attribute("nodeTypeAlias").Value
: root.Name.LocalName;
if (contentTypes.ContainsKey(contentTypeAlias) == false)
{
var contentType = FindContentTypeByAlias(contentTypeAlias);
contentTypes.Add(contentTypeAlias, contentType);
}
var content = CreateContentFromXml(root, contentTypes[contentTypeAlias], null, -1, isLegacySchema);
contents.Add(content);
var children = from child in root.Elements()
where (string)child.Attribute("isDoc") == ""
select child;
if(children.Any())
contents.AddRange(CreateContentFromXml(children, content, contentTypes, isLegacySchema));
}
return contents;
}
private IEnumerable<IContent> CreateContentFromXml(IEnumerable<XElement> children, IContent parent, Dictionary<string, IContentType> contentTypes, bool isLegacySchema)
{
var list = new List<IContent>();
foreach (var child in children)
{
string contentTypeAlias = isLegacySchema
? child.Attribute("nodeTypeAlias").Value
: child.Name.LocalName;
if (contentTypes.ContainsKey(contentTypeAlias) == false)
{
var contentType = FindContentTypeByAlias(contentTypeAlias);
contentTypes.Add(contentTypeAlias, contentType);
}
//Create and add the child to the list
var content = CreateContentFromXml(child, contentTypes[contentTypeAlias], parent, default(int), isLegacySchema);
list.Add(content);
//Recursive call
XElement child1 = child;
var grandChildren = from grand in child1.Elements()
where (string) grand.Attribute("isDoc") == ""
select grand;
if (grandChildren.Any())
list.AddRange(CreateContentFromXml(grandChildren, content, contentTypes, isLegacySchema));
}
return list;
}
private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId, bool isLegacySchema)
{
var id = element.Attribute("id").Value;
var level = element.Attribute("level").Value;
var sortOrder = element.Attribute("sortOrder").Value;
var nodeName = element.Attribute("nodeName").Value;
var path = element.Attribute("path").Value;
var template = element.Attribute("template").Value;
var properties = from property in element.Elements()
where property.Attribute("isDoc") == null
select property;
IContent content = parent == null
? new Content(nodeName, parentId, contentType)
{
Level = int.Parse(level),
SortOrder = int.Parse(sortOrder)
}
: new Content(nodeName, parent, contentType)
{
Level = int.Parse(level),
SortOrder = int.Parse(sortOrder)
};
foreach (var property in properties)
{
string propertyTypeAlias = isLegacySchema ? property.Attribute("alias").Value : property.Name.LocalName;
if(content.HasProperty(propertyTypeAlias))
content.SetValue(propertyTypeAlias, property.Value);
}
return content;
}
private IContentType FindContentTypeByAlias(string contentTypeAlias)
{
using (var repository = _repositoryFactory.CreateContentTypeRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContentType>.Builder.Where(x => x.Alias == contentTypeAlias);
var types = repository.GetByQuery(query);
if (!types.Any())
throw new Exception(
string.Format("No ContentType matching the passed in Alias: '{0}' was found",
contentTypeAlias));
var contentType = types.First();
if (contentType == null)
throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null",
contentTypeAlias));
return contentType;
}
}
public XElement Export(IContent content, bool deep = false)
{
throw new NotImplementedException();
}
#region Internal Methods
/// <summary>
@@ -1607,6 +1447,28 @@ namespace Umbraco.Core.Services
return true;
}
private IContentType FindContentTypeByAlias(string contentTypeAlias)
{
using (var repository = _repositoryFactory.CreateContentTypeRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContentType>.Builder.Where(x => x.Alias == contentTypeAlias);
var types = repository.GetByQuery(query);
if (!types.Any())
throw new Exception(
string.Format("No ContentType matching the passed in Alias: '{0}' was found",
contentTypeAlias));
var contentType = types.First();
if (contentType == null)
throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null",
contentTypeAlias));
return contentType;
}
}
#endregion
#region Event Handlers

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Xml.Linq;
@@ -474,61 +475,7 @@ namespace Umbraco.Core.Services
}
return dtd.ToString();
}
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <returns>An enumrable list of generated ContentTypes</returns>
public List<IContentType> Import(XElement element)
{
var name = element.Name.LocalName;
if (name.Equals("DocumentTypes") == false)
{
throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DocumentTypes'.");
}
var list = new List<IContentType>();
var documentTypes = from doc in element.Elements("DocumentType") select doc;
foreach (var documentType in documentTypes)
{
//TODO Check if the ContentType already exists by looking up the alias
list.Add(CreateContentTypeFromXml(documentType));
}
Save(list);
return list;
}
private IContentType CreateContentTypeFromXml(XElement documentType)
{
var infoElement = documentType.Element("Info");
var name = infoElement.Element("Name").Value;
var alias = infoElement.Element("Alias").Value;
var masterElement = infoElement.Element("Master");//Name of the master corresponds to the parent
var icon = infoElement.Element("Icon").Value;
var thumbnail = infoElement.Element("Thumbnail").Value;
var description = infoElement.Element("Description").Value;
var allowAtRoot = infoElement.Element("AllowAtRoot").Value;
var defaultTemplate = infoElement.Element("DefaultTemplate").Value;
var allowedTemplatesElement = infoElement.Elements("AllowedTemplates");
var structureElement = documentType.Element("Structure");
var genericPropertiesElement = documentType.Element("GenericProperties");
var tabElement = documentType.Element("Tab");
var contentType = new ContentType(-1)
{
Alias = alias,
Name = name,
Icon = icon,
Thumbnail = thumbnail,
AllowedAsRoot = allowAtRoot.ToLowerInvariant().Equals("true"),
Description = description
};
return contentType;
}
#region Event Handlers
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core.Auditing;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
@@ -19,6 +20,7 @@ namespace Umbraco.Core.Services
{
private readonly RepositoryFactory _repositoryFactory;
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public DataTypeService()
: this(new RepositoryFactory())
@@ -121,18 +123,84 @@ namespace Umbraco.Core.Services
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
return;
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
{
dataTypeDefinition.CreatorId = userId;
repository.AddOrUpdate(dataTypeDefinition);
uow.Commit();
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false), this);
}
using (new WriteLock(Locker))
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
{
dataTypeDefinition.CreatorId = userId;
repository.AddOrUpdate(dataTypeDefinition);
uow.Commit();
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id);
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition, false), this);
}
}
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id);
}
/// <summary>
/// Saves a collection of <see cref="IDataTypeDefinition"/>
/// </summary>
/// <param name="dataTypeDefinitions"><see cref="IDataTypeDefinition"/> to save</param>
/// <param name="userId">Id of the user issueing the save</param>
public void Save(IEnumerable<IDataTypeDefinition> dataTypeDefinitions, int userId = 0)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions), this))
return;
using (new WriteLock(Locker))
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateDataTypeDefinitionRepository(uow))
{
foreach (var dataTypeDefinition in dataTypeDefinitions)
{
dataTypeDefinition.CreatorId = userId;
repository.AddOrUpdate(dataTypeDefinition);
}
uow.Commit();
Saved.RaiseEvent(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinitions, false), this);
}
}
Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1);
}
/// <summary>
/// Saves a list of PreValues for a given DataTypeDefinition
/// </summary>
/// <param name="id">Id of the DataTypeDefinition to save PreValues for</param>
/// <param name="values">List of string values to save</param>
public void SavePreValues(int id, IEnumerable<string> values)
{
using (new WriteLock(Locker))
{
using (var uow = _uowProvider.GetUnitOfWork())
{
var sortOrderObj =
uow.Database.ExecuteScalar<object>(
"SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = id });
int sortOrder;
if (sortOrderObj == null || int.TryParse(sortOrderObj.ToString(), out sortOrder) == false)
{
sortOrder = 1;
}
using (var transaction = uow.Database.GetTransaction())
{
foreach (var value in values)
{
var dto = new DataTypePreValueDto { DataTypeNodeId = id, Value = value, SortOrder = sortOrder };
uow.Database.Insert(dto);
sortOrder++;
}
transaction.Complete();
}
}
}
}
/// <summary>

View File

@@ -288,12 +288,5 @@ namespace Umbraco.Core.Services
/// <param name="content"><see cref="IContent"/> to check if anscestors are published</param>
/// <returns>True if the Content can be published, otherwise False</returns>
bool IsPublishable(IContent content);
/// <summary>
/// Imports and saves package xml as <see cref="IContent"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <returns>An enumrable list of generated content</returns>
IEnumerable<IContent> Import(XElement element);
}
}

View File

@@ -150,12 +150,5 @@ namespace Umbraco.Core.Services
/// <param name="id">Id of the <see cref="IMediaType"/></param>
/// <returns>True if the media type has any children otherwise False</returns>
bool MediaTypeHasChildren(int id);
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <returns>An enumrable list of generated ContentTypes</returns>
List<IContentType> Import(XElement element);
}
}

View File

@@ -38,6 +38,13 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the user issueing the save</param>
void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0);
/// <summary>
/// Saves a collection of <see cref="IDataTypeDefinition"/>
/// </summary>
/// <param name="dataTypeDefinitions"><see cref="IDataTypeDefinition"/> to save</param>
/// <param name="userId">Id of the user issueing the save</param>
void Save(IEnumerable<IDataTypeDefinition> dataTypeDefinitions, int userId = 0);
/// <summary>
/// Deletes an <see cref="IDataTypeDefinition"/>
/// </summary>
@@ -75,5 +82,12 @@ namespace Umbraco.Core.Services
/// <param name="id">Id of the <see cref="IDataTypeDefinition"/> to retrieve prevalues from</param>
/// <returns>An enumerable list of string values</returns>
IEnumerable<string> GetPreValuesByDataTypeId(int id);
/// <summary>
/// Saves a list of PreValues for a given DataTypeDefinition
/// </summary>
/// <param name="id">Id of the DataTypeDefinition to save PreValues for</param>
/// <param name="values">List of string values to save</param>
void SavePreValues(int id, IEnumerable<string> values);
}
}

View File

@@ -0,0 +1,499 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
public class PackagingService : IService
{
private readonly IContentService _contentService;
private readonly IContentTypeService _contentTypeService;
private readonly IMediaService _mediaService;
private readonly IDataTypeService _dataTypeService;
private readonly IFileService _fileService;
private readonly RepositoryFactory _repositoryFactory;
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private Dictionary<string, IContentType> _importedContentTypes;
//Support recursive locks because some of the methods that require locking call other methods that require locking.
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public PackagingService(IContentService contentService, IContentTypeService contentTypeService, IMediaService mediaService, IDataTypeService dataTypeService, IFileService fileService, RepositoryFactory repositoryFactory, IDatabaseUnitOfWorkProvider uowProvider)
{
_contentService = contentService;
_contentTypeService = contentTypeService;
_mediaService = mediaService;
_dataTypeService = dataTypeService;
_fileService = fileService;
_repositoryFactory = repositoryFactory;
_uowProvider = uowProvider;
_importedContentTypes = new Dictionary<string, IContentType>();
}
#region Content
/// <summary>
/// Imports and saves package xml as <see cref="IContent"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId"></param>
/// <returns>An enumrable list of generated content</returns>
public IEnumerable<IContent> ImportContent(XElement element, int userId = 0)
{
var name = element.Name.LocalName;
if (name.Equals("DocumentSet"))
{
//This is a regular deep-structured import
var roots = from doc in element.Elements()
where (string)doc.Attribute("isDoc") == ""
select doc;
var contents = ParseDocumentRootXml(roots);
_contentService.Save(contents, userId);
return contents;
}
var attribute = element.Attribute("isDoc");
if (attribute != null)
{
//This is a single doc import
var elements = new List<XElement> { element };
var contents = ParseDocumentRootXml(elements);
_contentService.Save(contents, userId);
return contents;
}
throw new ArgumentException(
"The passed in XElement is not valid! It does not contain a root element called " +
"'DocumentSet' (for structured imports) nor is the first element a Document (for single document import).");
}
private IEnumerable<IContent> ParseDocumentRootXml(IEnumerable<XElement> roots)
{
var contents = new List<IContent>();
foreach (var root in roots)
{
bool isLegacySchema = root.Name.LocalName.ToLowerInvariant().Equals("node");
string contentTypeAlias = isLegacySchema
? root.Attribute("nodeTypeAlias").Value
: root.Name.LocalName;
if (_importedContentTypes.ContainsKey(contentTypeAlias) == false)
{
var contentType = FindContentTypeByAlias(contentTypeAlias);
_importedContentTypes.Add(contentTypeAlias, contentType);
}
var content = CreateContentFromXml(root, _importedContentTypes[contentTypeAlias], null, -1, isLegacySchema);
contents.Add(content);
var children = from child in root.Elements()
where (string)child.Attribute("isDoc") == ""
select child;
if (children.Any())
contents.AddRange(CreateContentFromXml(children, content, isLegacySchema));
}
return contents;
}
private IEnumerable<IContent> CreateContentFromXml(IEnumerable<XElement> children, IContent parent, bool isLegacySchema)
{
var list = new List<IContent>();
foreach (var child in children)
{
string contentTypeAlias = isLegacySchema
? child.Attribute("nodeTypeAlias").Value
: child.Name.LocalName;
if (_importedContentTypes.ContainsKey(contentTypeAlias) == false)
{
var contentType = FindContentTypeByAlias(contentTypeAlias);
_importedContentTypes.Add(contentTypeAlias, contentType);
}
//Create and add the child to the list
var content = CreateContentFromXml(child, _importedContentTypes[contentTypeAlias], parent, default(int), isLegacySchema);
list.Add(content);
//Recursive call
XElement child1 = child;
var grandChildren = from grand in child1.Elements()
where (string)grand.Attribute("isDoc") == ""
select grand;
if (grandChildren.Any())
list.AddRange(CreateContentFromXml(grandChildren, content, isLegacySchema));
}
return list;
}
private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId, bool isLegacySchema)
{
var id = element.Attribute("id").Value;
var level = element.Attribute("level").Value;
var sortOrder = element.Attribute("sortOrder").Value;
var nodeName = element.Attribute("nodeName").Value;
var path = element.Attribute("path").Value;
var template = element.Attribute("template").Value;
var properties = from property in element.Elements()
where property.Attribute("isDoc") == null
select property;
IContent content = parent == null
? new Content(nodeName, parentId, contentType)
{
Level = int.Parse(level),
SortOrder = int.Parse(sortOrder)
}
: new Content(nodeName, parent, contentType)
{
Level = int.Parse(level),
SortOrder = int.Parse(sortOrder)
};
foreach (var property in properties)
{
string propertyTypeAlias = isLegacySchema ? property.Attribute("alias").Value : property.Name.LocalName;
if (content.HasProperty(propertyTypeAlias))
content.SetValue(propertyTypeAlias, property.Value);
}
return content;
}
public XElement Export(IContent content, bool deep = false)
{
throw new NotImplementedException();
}
#endregion
#region ContentTypes
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId"></param>
/// <returns>An enumrable list of generated ContentTypes</returns>
public IEnumerable<IContentType> ImportContentTypes(XElement element, int userId = 0)
{
var name = element.Name.LocalName;
if (name.Equals("DocumentTypes") == false)
{
throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DocumentTypes'.");
}
_importedContentTypes = new Dictionary<string, IContentType>();
var documentTypes = (from doc in element.Elements("DocumentType") select doc).ToList();
//NOTE it might be an idea to sort the doctype XElements based on dependencies
//before creating the doc types - should also allow for a better structure/inheritance support.
foreach (var documentType in documentTypes)
{
var alias = documentType.Element("Info").Element("Alias").Value;
if (_importedContentTypes.ContainsKey(alias) == false)
{
var contentType = _contentTypeService.GetContentType(alias);
_importedContentTypes.Add(alias, contentType == null
? CreateContentTypeFromXml(documentType)
: UpdateContentTypeFromXml(documentType, contentType));
}
}
var list = _importedContentTypes.Select(x => x.Value).ToList();
_contentTypeService.Save(list, userId);
var updatedContentTypes = new List<IContentType>();
//Update the structure here - we can't do it untill all DocTypes have been created
foreach (var documentType in documentTypes)
{
var alias = documentType.Element("Info").Element("Alias").Value;
var structureElement = documentType.Element("Structure");
//Ensure that we only update ContentTypes which has actual structure-elements
if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue;
var updated = UpdateContentTypesStructure(_importedContentTypes[alias], structureElement);
updatedContentTypes.Add(updated);
}
//Update ContentTypes with a newly added structure/list of allowed children
if(updatedContentTypes.Any())
_contentTypeService.Save(updatedContentTypes, userId);
return list;
}
private IContentType CreateContentTypeFromXml(XElement documentType)
{
var infoElement = documentType.Element("Info");
//Name of the master corresponds to the parent
var masterElement = infoElement.Element("Master");
IContentType parent = null;
if (masterElement != null)
{
var masterAlias = masterElement.Value;
parent = _importedContentTypes.ContainsKey(masterAlias)
? _importedContentTypes[masterAlias]
: _contentTypeService.GetContentType(masterAlias);
}
var contentType = parent == null
? new ContentType(-1)
{
Alias = infoElement.Element("Alias").Value
}
: new ContentType(parent)
{
Alias = infoElement.Element("Alias").Value
};
return UpdateContentTypeFromXml(documentType, contentType);
}
private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType)
{
var infoElement = documentType.Element("Info");
var defaultTemplateElement = infoElement.Element("DefaultTemplate");
contentType.Name = infoElement.Element("Name").Value;
contentType.Icon = infoElement.Element("Icon").Value;
contentType.Thumbnail = infoElement.Element("Thumbnail").Value;
contentType.Description = infoElement.Element("Description").Value;
contentType.AllowedAsRoot = infoElement.Element("AllowAtRoot").Value.ToLowerInvariant().Equals("true");
UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement);
UpdateContentTypesTabs(contentType, documentType.Element("Tab"));
UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties"));
return contentType;
}
private void UpdateContentTypesAllowedTemplates(IContentType contentType,
XElement allowedTemplatesElement, XElement defaultTemplateElement)
{
if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any())
{
var allowedTemplates = contentType.AllowedTemplates.ToList();
foreach (var templateElement in allowedTemplatesElement.Elements("Template"))
{
var alias = templateElement.Value;
var template = _fileService.GetTemplate(alias);
if (template != null)
{
allowedTemplates.Add(template);
}
else
{
LogHelper.Warn<PackagingService>(
string.Format(
"Packager: Error handling allowed templates. Template with alias '{0}' could not be found.",
alias));
}
}
contentType.AllowedTemplates = allowedTemplates;
}
var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value);
if (defaultTemplate != null)
{
contentType.SetDefaultTemplate(defaultTemplate);
}
else
{
LogHelper.Warn<PackagingService>(
string.Format(
"Packager: Error handling default template. Default template with alias '{0}' could not be found.",
defaultTemplateElement.Value));
}
}
private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement)
{
if(tabElement == null)
return;
var tabs = tabElement.Elements("Tab");
foreach (var tab in tabs)
{
var id = tab.Element("Id").Value;//Do we need to use this for tracking?
var caption = tab.Element("Caption").Value;
if (contentType.PropertyGroups.Contains(caption) == false)
{
contentType.AddPropertyGroup(caption);
}
}
}
private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement)
{
var properties = genericPropertiesElement.Elements("GenericProperty");
foreach (var property in properties)
{
var dataTypeId = new Guid(property.Element("Type").Value);//The DataType's Control Id
var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition
var dataTypeDefinition = _dataTypeService.GetDataTypeDefinitionById(dataTypeDefinitionId);
//If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id
//We look up a DataTypeDefinition that matches
if (dataTypeDefinition == null || dataTypeDefinition.ControlId != dataTypeId)
{
var dataTypeDefinitions = _dataTypeService.GetDataTypeDefinitionByControlId(dataTypeId);
if (dataTypeDefinitions != null && dataTypeDefinitions.Any())
{
dataTypeDefinition = dataTypeDefinitions.First();
}
else
{
throw new Exception(
String.Format(
"Packager: Error handling creation of PropertyType '{0}'. Could not find DataTypeDefintion with unique id '{1}' nor one referencing the DataType with control id '{2}'.",
property.Element("Name").Value, dataTypeDefinitionId, dataTypeId));
}
}
var propertyType = new PropertyType(dataTypeDefinition)
{
Alias = property.Element("Alias").Value,
Name = property.Element("Name").Value,
Description = property.Element("Description").Value,
Mandatory = property.Element("Mandatory").Value.ToLowerInvariant().Equals("true"),
ValidationRegExp = property.Element("Validation").Value
};
var helpTextElement = property.Element("HelpText");
if (helpTextElement != null)
{
propertyType.HelpText = helpTextElement.Value;
}
var tab = property.Element("Tab").Value;
if (string.IsNullOrEmpty(tab))
{
contentType.AddPropertyType(propertyType);
}
else
{
contentType.AddPropertyType(propertyType, tab);
}
}
}
private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement)
{
var allowedChildren = contentType.AllowedContentTypes.ToList();
int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0;
foreach (var element in structureElement.Elements("DocumentType"))
{
var alias = element.Value;
if (_importedContentTypes.ContainsKey(alias))
{
var allowedChild = _importedContentTypes[alias];
allowedChildren.Add(new ContentTypeSort(new Lazy<int>(() => allowedChild.Id), sortOrder, allowedChild.Alias));
sortOrder++;
}
else
{
LogHelper.Warn<PackagingService>(
string.Format(
"Packager: Error handling DocumentType structure. DocumentType with alias '{0}' could not be found and was not added to the structure for '{1}'.",
alias, contentType.Alias));
}
}
contentType.AllowedContentTypes = allowedChildren;
return contentType;
}
private IContentType FindContentTypeByAlias(string contentTypeAlias)
{
using (var repository = _repositoryFactory.CreateContentTypeRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContentType>.Builder.Where(x => x.Alias == contentTypeAlias);
var types = repository.GetByQuery(query);
if (!types.Any())
throw new Exception(
string.Format("No ContentType matching the passed in Alias: '{0}' was found",
contentTypeAlias));
var contentType = types.First();
if (contentType == null)
throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null",
contentTypeAlias));
return contentType;
}
}
#endregion
#region DataTypes
/// <summary>
/// Imports and saves package xml as <see cref="IDataTypeDefinition"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId"></param>
/// <returns>An enumrable list of generated DataTypeDefinitions</returns>
public IEnumerable<IDataTypeDefinition> ImportDataTypeDefinitions(XElement element, int userId = 0)
{
var name = element.Name.LocalName;
if (name.Equals("DataTypes") == false)
{
throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DataTypes'.");
}
var dataTypes = new Dictionary<string, IDataTypeDefinition>();
var dataTypeElements = element.Elements("DataType").ToList();
foreach (var dataTypeElement in dataTypeElements)
{
var dataTypeDefinitionName = dataTypeElement.Attribute("Name").Value;
var dataTypeId = new Guid(dataTypeElement.Attribute("Id").Value);
var dataTypeDefinitionId = new Guid(dataTypeElement.Attribute("Definition").Value);
var definition = _dataTypeService.GetDataTypeDefinitionById(dataTypeDefinitionId);
if (definition == null)
{
var dataTypeDefinition = new DataTypeDefinition(-1, dataTypeId) { Key = dataTypeDefinitionId, Name = dataTypeDefinitionName };
dataTypes.Add(dataTypeDefinitionName, dataTypeDefinition);
}
}
var list = dataTypes.Select(x => x.Value).ToList();
_dataTypeService.Save(list, userId);
SavePrevaluesFromXml(list, dataTypeElements);
return list;
}
private void SavePrevaluesFromXml(List<IDataTypeDefinition> dataTypes, IEnumerable<XElement> dataTypeElements)
{
foreach (var dataTypeElement in dataTypeElements)
{
var prevaluesElement = dataTypeElement.Element("PreValues");
if (prevaluesElement == null) continue;
var dataTypeDefinitionName = dataTypeElement.Attribute("Name").Value;
var dataTypeDefinition = dataTypes.First(x => x.Name == dataTypeDefinitionName);
var values = prevaluesElement.Elements("PreValue").Select(prevalue => prevalue.Attribute("Value").Value).ToList();
_dataTypeService.SavePreValues(dataTypeDefinition.Id, values);
}
}
#endregion
}
}

View File

@@ -20,6 +20,7 @@ namespace Umbraco.Core.Services
private Lazy<DataTypeService> _dataTypeService;
private Lazy<FileService> _fileService;
private Lazy<LocalizationService> _localizationService;
private Lazy<PackagingService> _packagingService;
/// <summary>
/// Constructor
@@ -70,6 +71,9 @@ namespace Umbraco.Core.Services
if(_localizationService == null)
_localizationService = new Lazy<LocalizationService>(() => new LocalizationService(provider, repositoryFactory.Value));
if(_packagingService == null)
_packagingService = new Lazy<PackagingService>(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, repositoryFactory.Value, provider));
}
/// <summary>
@@ -120,6 +124,14 @@ namespace Umbraco.Core.Services
get { return _mediaService.Value; }
}
/// <summary>
/// Gets the <see cref="PackagingService"/>
/// </summary>
public PackagingService PackagingService
{
get { return _packagingService.Value; }
}
/// <summary>
/// Gets the <see cref="IMacroService"/>
/// </summary>

View File

@@ -654,6 +654,7 @@
<Compile Include="Services\LocalizationService.cs" />
<Compile Include="Services\MacroService.cs" />
<Compile Include="Services\MediaService.cs" />
<Compile Include="Services\PackagingService.cs" />
<Compile Include="Services\ServiceContext.cs" />
<Compile Include="Services\UserService.cs" />
<Compile Include="TypeExtensions.cs" />

View File

@@ -26,16 +26,21 @@ namespace Umbraco.Tests.Services.Importing
string strXml = ImportResources.package;
var xml = XElement.Parse(strXml);
var element = xml.Descendants("DocumentTypes").First();
var contentTypeService = ServiceContext.ContentTypeService;
var dataTypeElement = xml.Descendants("DataTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var contentTypes = contentTypeService.Import(element);
var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var contentTypes = packagingService.ImportContentTypes(element);
var numberOfDocTypes = (from doc in element.Elements("DocumentType") select doc).Count();
// Assert
Assert.That(dataTypeDefinitions, Is.Not.Null);
Assert.That(dataTypeDefinitions.Any(), Is.True);
Assert.That(contentTypes, Is.Not.Null);
Assert.That(contentTypes.Any(), Is.True);
Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes));
Assert.That(contentTypes.Count(x => x.ParentId == -1), Is.EqualTo(1));
}
[Test]
@@ -44,20 +49,22 @@ namespace Umbraco.Tests.Services.Importing
// Arrange
string strXml = ImportResources.package;
var xml = XElement.Parse(strXml);
var dataTypeElement = xml.Descendants("DataTypes").First();
var docTypesElement = xml.Descendants("DocumentTypes").First();
var element = xml.Descendants("DocumentSet").First();
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var packagingService = ServiceContext.PackagingService;
// Act
var contentTypes = contentTypeService.Import(docTypesElement);
var contents = contentService.Import(element);
var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var contentTypes = packagingService.ImportContentTypes(docTypesElement);
var contents = packagingService.ImportContent(element);
var numberOfDocs = (from doc in element.Descendants()
where (string) doc.Attribute("isDoc") == ""
select doc).Count();
// Assert
Assert.That(contents, Is.Not.Null);
Assert.That(dataTypeDefinitions.Any(), Is.True);
Assert.That(contentTypes.Any(), Is.True);
Assert.That(contents.Any(), Is.True);
Assert.That(contents.Count(), Is.EqualTo(numberOfDocs));

View File

@@ -424,7 +424,9 @@
<Content Include="Migrations\SqlScripts\SqlCe-SchemaAndData-4110.sql" />
<Content Include="Migrations\SqlScripts\SqlCeTotal-480.sql" />
<Content Include="Migrations\SqlScripts\SqlServerTotal-480.sql" />
<Content Include="Services\Importing\package.xml" />
<Content Include="Services\Importing\package.xml">
<SubType>Designer</SubType>
</Content>
<Content Include="TestHelpers\ExamineHelpers\media.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@@ -762,7 +762,7 @@ namespace umbraco.cms.businesslogic.packager
tabNames += tabs[t].Caption + ";";
//So the Tab is added to the DocumentType and then to this Hashtable, but its never used anywhere - WHY?
Hashtable ht = new Hashtable();
foreach (XmlNode t in n.SelectNodes("Tabs/Tab"))
{