using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web; using System.Xml; using System.Xml.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.DependencyInjection; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Strings; namespace Umbraco.Core.Services { //TODO: Move the rest of the logic for the PackageService.Export methods to here! /// /// A helper class to serialize entities to XML /// internal class EntityXmlSerializer { /// /// Exports an item to xml as an /// /// /// /// /// Content to export /// /// Optional parameter indicating whether to include descendents /// containing the xml representation of the Content object public XElement Serialize( IContentService contentService, IDataTypeService dataTypeService, IUserService userService, IEnumerable urlSegmentProviders, IContent content, bool deep = false) { if (contentService == null) throw new ArgumentNullException("contentService"); if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); if (userService == null) throw new ArgumentNullException("userService"); if (content == null) throw new ArgumentNullException("content"); if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders"); //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = content.ContentType.Alias.ToSafeAliasWithForcingCheck(); var xml = Serialize(dataTypeService, content, content.GetUrlSegment(urlSegmentProviders), nodeName); xml.Add(new XAttribute("nodeType", content.ContentType.Id)); xml.Add(new XAttribute("creatorName", content.GetCreatorProfile(userService).Name)); xml.Add(new XAttribute("writerName", content.GetWriterProfile(userService).Name)); xml.Add(new XAttribute("writerID", content.WriterId)); xml.Add(new XAttribute("template", content.Template == null ? "0" : content.Template.Id.ToString(CultureInfo.InvariantCulture))); xml.Add(new XAttribute("nodeTypeAlias", content.ContentType.Alias)); if (deep) { var descendants = contentService.GetDescendants(content).ToArray(); var currentChildren = descendants.Where(x => x.ParentId == content.Id); AddChildXml(contentService, dataTypeService, userService, urlSegmentProviders, descendants, currentChildren, xml); } return xml; } /// /// Exports an item to xml as an /// /// /// /// /// Media to export /// /// Optional parameter indicating whether to include descendents /// containing the xml representation of the Media object public XElement Serialize( IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, IEnumerable urlSegmentProviders, IMedia media, bool deep = false) { if (mediaService == null) throw new ArgumentNullException("mediaService"); if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); if (userService == null) throw new ArgumentNullException("userService"); if (media == null) throw new ArgumentNullException("media"); if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders"); //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = media.ContentType.Alias.ToSafeAliasWithForcingCheck(); var xml = Serialize(dataTypeService, media, media.GetUrlSegment(urlSegmentProviders), nodeName); xml.Add(new XAttribute("nodeType", media.ContentType.Id)); xml.Add(new XAttribute("writerName", media.GetCreatorProfile(userService).Name)); xml.Add(new XAttribute("writerID", media.CreatorId)); xml.Add(new XAttribute("version", media.Version)); xml.Add(new XAttribute("template", 0)); xml.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias)); if (deep) { var descendants = mediaService.GetDescendants(media).ToArray(); var currentChildren = descendants.Where(x => x.ParentId == media.Id); AddChildXml(mediaService, dataTypeService, userService, urlSegmentProviders, descendants, currentChildren, xml); } return xml; } /// /// Exports an item to xml as an /// /// /// Member to export /// containing the xml representation of the Member object public XElement Serialize(IDataTypeService dataTypeService, IMember member) { //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = member.ContentType.Alias.ToSafeAliasWithForcingCheck(); var xml = Serialize(dataTypeService, member, "", nodeName); xml.Add(new XAttribute("nodeType", member.ContentType.Id)); xml.Add(new XAttribute("nodeTypeAlias", member.ContentType.Alias)); xml.Add(new XAttribute("loginName", member.Username)); xml.Add(new XAttribute("email", member.Email)); xml.Add(new XAttribute("icon", member.ContentType.Icon)); return xml; } public XElement Serialize(IDataTypeService dataTypeService, Property property) { var propertyType = property.PropertyType; var nodeName = property.Alias.ToSafeAlias(); var xElement = new XElement(nodeName); //Get the property editor for thsi property and let it convert it to the xml structure var propertyEditor = Current.PropertyEditors[property.PropertyType.PropertyEditorAlias]; if (propertyEditor != null) { var xmlValue = propertyEditor.ValueEditor.ConvertDbToXml(property, propertyType, dataTypeService); xElement.Add(xmlValue); } return xElement; } /// /// Exports an item to xml as an /// /// /// IDataTypeDefinition type to export /// containing the xml representation of the IDataTypeDefinition object public XElement Serialize(IDataTypeService dataTypeService, IDataTypeDefinition dataTypeDefinition) { var prevalues = new XElement("PreValues"); var prevalueList = dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeDefinition.Id) .FormatAsDictionary(); var sort = 0; foreach (var pv in prevalueList) { var prevalue = new XElement("PreValue"); prevalue.Add(new XAttribute("Id", pv.Value.Id)); prevalue.Add(new XAttribute("Value", pv.Value.Value ?? "")); prevalue.Add(new XAttribute("Alias", pv.Key)); prevalue.Add(new XAttribute("SortOrder", sort)); prevalues.Add(prevalue); sort++; } var xml = new XElement("DataType", prevalues); xml.Add(new XAttribute("Name", dataTypeDefinition.Name)); //The 'ID' when exporting is actually the property editor alias (in pre v7 it was the IDataType GUID id) xml.Add(new XAttribute("Id", dataTypeDefinition.PropertyEditorAlias)); xml.Add(new XAttribute("Definition", dataTypeDefinition.Key)); xml.Add(new XAttribute("DatabaseType", dataTypeDefinition.DatabaseType.ToString())); var folderNames = string.Empty; if (dataTypeDefinition.Level != 1) { //get url encoded folder names var folders = dataTypeService.GetContainers(dataTypeDefinition) .OrderBy(x => x.Level) .Select(x => HttpUtility.UrlEncode(x.Name)); folderNames = string.Join("/", folders.ToArray()); } if (string.IsNullOrWhiteSpace(folderNames) == false) xml.Add(new XAttribute("Folders", folderNames)); return xml; } public XElement Serialize(IDictionaryItem dictionaryItem) { var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey)); foreach (var translation in dictionaryItem.Translations) { xml.Add(new XElement("Value", new XAttribute("LanguageId", translation.Language.Id), new XAttribute("LanguageCultureAlias", translation.Language.IsoCode), new XCData(translation.Value))); } return xml; } public XElement Serialize(Stylesheet stylesheet) { var xml = new XElement("Stylesheet", new XElement("Name", stylesheet.Alias), new XElement("FileName", stylesheet.Path), new XElement("Content", new XCData(stylesheet.Content))); var props = new XElement("Properties"); xml.Add(props); foreach (var prop in stylesheet.Properties) { props.Add(new XElement("Property", new XElement("Name", prop.Name), new XElement("Alias", prop.Alias), new XElement("Value", prop.Value))); } return xml; } public XElement Serialize(ILanguage language) { var xml = new XElement("Language", new XAttribute("Id", language.Id), new XAttribute("CultureAlias", language.IsoCode), new XAttribute("FriendlyName", language.CultureName)); return xml; } public XElement Serialize(ITemplate template) { var xml = new XElement("Template"); xml.Add(new XElement("Name", template.Name)); xml.Add(new XElement("Alias", template.Alias)); xml.Add(new XElement("Design", new XCData(template.Content))); var concreteTemplate = template as Template; if (concreteTemplate != null && concreteTemplate.MasterTemplateId != null) { if (concreteTemplate.MasterTemplateId.IsValueCreated && concreteTemplate.MasterTemplateId.Value != default(int)) { xml.Add(new XElement("Master", concreteTemplate.MasterTemplateId.ToString())); xml.Add(new XElement("MasterAlias", concreteTemplate.MasterTemplateAlias)); } } return xml; } public XElement Serialize(IDataTypeService dataTypeService, IMediaType mediaType) { var info = new XElement("Info", new XElement("Name", mediaType.Name), new XElement("Alias", mediaType.Alias), new XElement("Icon", mediaType.Icon), new XElement("Thumbnail", mediaType.Thumbnail), new XElement("Description", mediaType.Description), new XElement("AllowAtRoot", mediaType.AllowedAsRoot.ToString())); var masterContentType = mediaType.CompositionAliases().FirstOrDefault(); if (masterContentType != null) info.Add(new XElement("Master", masterContentType)); var structure = new XElement("Structure"); foreach (var allowedType in mediaType.AllowedContentTypes) { structure.Add(new XElement("MediaType", allowedType.Alias)); } var genericProperties = new XElement("GenericProperties"); // actually, all of them foreach (var propertyType in mediaType.PropertyTypes) { var definition = dataTypeService.GetDataTypeDefinitionById(propertyType.DataTypeDefinitionId); var propertyGroup = propertyType.PropertyGroupId == null // true generic property ? null : mediaType.PropertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyGroupId.Value); var genericProperty = new XElement("GenericProperty", new XElement("Name", propertyType.Name), new XElement("Alias", propertyType.Alias), new XElement("Type", propertyType.PropertyEditorAlias), new XElement("Definition", definition.Key), new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name), new XElement("Mandatory", propertyType.Mandatory.ToString()), new XElement("Validation", propertyType.ValidationRegExp), new XElement("Description", new XCData(propertyType.Description))); genericProperties.Add(genericProperty); } var tabs = new XElement("Tabs"); foreach (var propertyGroup in mediaType.PropertyGroups) { var tab = new XElement("Tab", new XElement("Id", propertyGroup.Id.ToString(CultureInfo.InvariantCulture)), new XElement("Caption", propertyGroup.Name), new XElement("SortOrder", propertyGroup.SortOrder)); tabs.Add(tab); } var xml = new XElement("MediaType", info, structure, genericProperties, tabs); return xml; } public XElement Serialize(IMacro macro) { var xml = new XElement("macro"); xml.Add(new XElement("name", macro.Name)); xml.Add(new XElement("alias", macro.Alias)); xml.Add(new XElement("scriptType", macro.ControlType)); xml.Add(new XElement("scriptAssembly", macro.ControlAssembly)); xml.Add(new XElement("scriptingFile", macro.ScriptPath)); xml.Add(new XElement("xslt", macro.XsltPath)); xml.Add(new XElement("useInEditor", macro.UseInEditor.ToString())); xml.Add(new XElement("dontRender", macro.DontRender.ToString())); xml.Add(new XElement("refreshRate", macro.CacheDuration.ToString(CultureInfo.InvariantCulture))); xml.Add(new XElement("cacheByMember", macro.CacheByMember.ToString())); xml.Add(new XElement("cacheByPage", macro.CacheByPage.ToString())); var properties = new XElement("properties"); foreach (var property in macro.Properties) { properties.Add(new XElement("property", new XAttribute("name", property.Name), new XAttribute("alias", property.Alias), new XAttribute("sortOrder", property.SortOrder), new XAttribute("propertyType", property.EditorAlias))); } xml.Add(properties); return xml; } /// /// Exports an item to xml as an /// /// /// /// Content type to export /// containing the xml representation of the IContentType object public XElement Serialize(IDataTypeService dataTypeService, IContentTypeService contentTypeService, IContentType contentType) { var info = new XElement("Info", new XElement("Name", contentType.Name), new XElement("Alias", contentType.Alias), new XElement("Icon", contentType.Icon), new XElement("Thumbnail", contentType.Thumbnail), new XElement("Description", contentType.Description), new XElement("AllowAtRoot", contentType.AllowedAsRoot.ToString()), new XElement("IsListView", contentType.IsContainer.ToString())); var masterContentType = contentType.ContentTypeComposition.FirstOrDefault(x => x.Id == contentType.ParentId); if(masterContentType != null) info.Add(new XElement("Master", masterContentType.Alias)); var compositionsElement = new XElement("Compositions"); var compositions = contentType.ContentTypeComposition; foreach (var composition in compositions) { compositionsElement.Add(new XElement("Composition", composition.Alias)); } info.Add(compositionsElement); var allowedTemplates = new XElement("AllowedTemplates"); foreach (var template in contentType.AllowedTemplates) { allowedTemplates.Add(new XElement("Template", template.Alias)); } info.Add(allowedTemplates); if (contentType.DefaultTemplate != null && contentType.DefaultTemplate.Id != 0) info.Add(new XElement("DefaultTemplate", contentType.DefaultTemplate.Alias)); else info.Add(new XElement("DefaultTemplate", "")); var structure = new XElement("Structure"); foreach (var allowedType in contentType.AllowedContentTypes) { structure.Add(new XElement("DocumentType", allowedType.Alias)); } var genericProperties = new XElement("GenericProperties"); // actually, all of them foreach (var propertyType in contentType.PropertyTypes) { var definition = dataTypeService.GetDataTypeDefinitionById(propertyType.DataTypeDefinitionId); var propertyGroup = propertyType.PropertyGroupId == null // true generic property ? null : contentType.PropertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyGroupId.Value); var genericProperty = new XElement("GenericProperty", new XElement("Name", propertyType.Name), new XElement("Alias", propertyType.Alias), new XElement("Type", propertyType.PropertyEditorAlias), new XElement("Definition", definition.Key), new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name), new XElement("SortOrder", propertyType.SortOrder), new XElement("Mandatory", propertyType.Mandatory.ToString()), propertyType.ValidationRegExp != null ? new XElement("Validation", propertyType.ValidationRegExp) : null, propertyType.Description != null ? new XElement("Description", new XCData(propertyType.Description)) : null); genericProperties.Add(genericProperty); } var tabs = new XElement("Tabs"); foreach (var propertyGroup in contentType.PropertyGroups) { var tab = new XElement("Tab", new XElement("Id", propertyGroup.Id.ToString(CultureInfo.InvariantCulture)), new XElement("Caption", propertyGroup.Name), new XElement("SortOrder", propertyGroup.SortOrder)); tabs.Add(tab); } var xml = new XElement("DocumentType", info, structure, genericProperties, tabs); var folderNames = string.Empty; //don't add folders if this is a child doc type if (contentType.Level != 1 && masterContentType == null) { //get url encoded folder names var folders = contentTypeService.GetContainers(contentType) .OrderBy(x => x.Level) .Select(x => HttpUtility.UrlEncode(x.Name)); folderNames = string.Join("/", folders.ToArray()); } if (string.IsNullOrWhiteSpace(folderNames) == false) xml.Add(new XAttribute("Folders", folderNames)); return xml; } /// /// Used by Media Export to recursively add children /// /// /// /// /// /// /// /// private void AddChildXml(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, IEnumerable urlSegmentProviders, IMedia[] originalDescendants, IEnumerable currentChildren, XElement currentXml) { foreach (var child in currentChildren) { //add the child's xml var childXml = Serialize(mediaService, dataTypeService, userService, urlSegmentProviders, child); currentXml.Add(childXml); //copy local (out of closure) var c = child; //get this item's children var children = originalDescendants.Where(x => x.ParentId == c.Id); //recurse and add it's children to the child xml element AddChildXml(mediaService, dataTypeService, userService, urlSegmentProviders, originalDescendants, children, childXml); } } /// /// Part of the export of IContent and IMedia and IMember which is shared /// /// /// Base Content or Media to export /// /// Name of the node /// private XElement Serialize(IDataTypeService dataTypeService, IContentBase contentBase, string urlValue, string nodeName) { var xml = new XElement(nodeName, new XAttribute("id", contentBase.Id), new XAttribute("key", contentBase.Key), new XAttribute("parentID", contentBase.Level > 1 ? contentBase.ParentId : -1), new XAttribute("level", contentBase.Level), new XAttribute("creatorID", contentBase.CreatorId), new XAttribute("sortOrder", contentBase.SortOrder), new XAttribute("createDate", contentBase.CreateDate.ToString("s")), new XAttribute("updateDate", contentBase.UpdateDate.ToString("s")), new XAttribute("nodeName", contentBase.Name), new XAttribute("urlName", urlValue), new XAttribute("path", contentBase.Path), new XAttribute("isDoc", "")); foreach (var property in contentBase.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) { xml.Add(Serialize(dataTypeService, property)); } return xml; } /// /// Used by Content Export to recursively add children /// /// /// /// /// /// /// /// private void AddChildXml(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, IEnumerable urlSegmentProviders, IContent[] originalDescendants, IEnumerable currentChildren, XElement currentXml) { foreach (var child in currentChildren) { //add the child's xml var childXml = Serialize(contentService, dataTypeService, userService, urlSegmentProviders, child); currentXml.Add(childXml); //copy local (out of closure) var c = child; //get this item's children var children = originalDescendants.Where(x => x.ParentId == c.Id); //recurse and add it's children to the child xml element AddChildXml(contentService, dataTypeService, userService, urlSegmentProviders, originalDescendants, children, childXml); } } } }