2017-12-28 09:18:09 +01:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Net.Http ;
using System.Text.RegularExpressions ;
using System.Web ;
using System.Xml.Linq ;
2018-02-05 17:48:54 +01:00
using Umbraco.Core.Collections ;
2017-12-28 09:18:09 +01:00
using Umbraco.Core.Configuration ;
using Umbraco.Core.Configuration.UmbracoSettings ;
using Umbraco.Core.Events ;
using Umbraco.Core.Exceptions ;
using Umbraco.Core.IO ;
using Umbraco.Core.Logging ;
using Umbraco.Core.Models ;
2018-01-15 11:32:30 +01:00
using Umbraco.Core.Models.Entities ;
2017-12-28 09:18:09 +01:00
using Umbraco.Core.Models.Packaging ;
using Umbraco.Core.Packaging ;
using Umbraco.Core.Packaging.Models ;
using Umbraco.Core.Persistence.Dtos ;
using Umbraco.Core.Persistence.Querying ;
using Umbraco.Core.Persistence.Repositories ;
2018-01-20 12:09:15 +01:00
using Umbraco.Core.PropertyEditors ;
2017-12-28 09:18:09 +01:00
using Umbraco.Core.Scoping ;
using Umbraco.Core.Strings ;
using Content = Umbraco . Core . Models . Content ;
namespace Umbraco.Core.Services.Implement
{
/// <summary>
/// Represents the Packaging Service, which provides import/export functionality for the Core models of the API
/// using xml representation. This is primarily used by the Package functionality.
/// </summary>
public class PackagingService : IPackagingService
{
private readonly ILogger _logger ;
private readonly IContentService _contentService ;
private readonly IContentTypeService _contentTypeService ;
private readonly IMediaService _mediaService ;
private readonly IMacroService _macroService ;
private readonly IDataTypeService _dataTypeService ;
private readonly IFileService _fileService ;
private readonly ILocalizationService _localizationService ;
private readonly IEntityService _entityService ;
private readonly IScopeProvider _scopeProvider ;
private readonly IEnumerable < IUrlSegmentProvider > _urlSegmentProviders ;
private Dictionary < string , IContentType > _importedContentTypes ;
private IPackageInstallation _packageInstallation ;
private readonly IUserService _userService ;
private readonly IAuditRepository _auditRepository ;
private readonly IContentTypeRepository _contentTypeRepository ;
2018-01-20 12:09:15 +01:00
private readonly PropertyEditorCollection _propertyEditors ;
2018-10-01 14:32:46 +02:00
private static HttpClient _httpClient ;
2017-12-28 09:18:09 +01:00
public PackagingService (
ILogger logger ,
IContentService contentService ,
IContentTypeService contentTypeService ,
IMediaService mediaService ,
IMacroService macroService ,
IDataTypeService dataTypeService ,
IFileService fileService ,
ILocalizationService localizationService ,
IEntityService entityService ,
IUserService userService ,
IScopeProvider scopeProvider ,
IEnumerable < IUrlSegmentProvider > urlSegmentProviders ,
2018-02-16 12:00:45 +01:00
IAuditRepository auditRepository , IContentTypeRepository contentTypeRepository ,
PropertyEditorCollection propertyEditors )
2017-12-28 09:18:09 +01:00
{
_logger = logger ;
_contentService = contentService ;
_contentTypeService = contentTypeService ;
_mediaService = mediaService ;
_macroService = macroService ;
_dataTypeService = dataTypeService ;
_fileService = fileService ;
_localizationService = localizationService ;
_entityService = entityService ;
_scopeProvider = scopeProvider ;
_urlSegmentProviders = urlSegmentProviders ;
_auditRepository = auditRepository ;
_contentTypeRepository = contentTypeRepository ;
2018-01-20 12:09:15 +01:00
_propertyEditors = propertyEditors ;
2017-12-28 09:18:09 +01:00
_userService = userService ;
_importedContentTypes = new Dictionary < string , IContentType > ( ) ;
}
protected IQuery < T > Query < T > ( ) = > _scopeProvider . SqlContext . Query < T > ( ) ;
#region Content
/// <summary>
/// Exports an <see cref="IContent"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="content">Content to export</param>
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Content object</returns>
public XElement Export ( IContent content , bool deep = false , bool raiseEvents = true )
{
var nodeName = content . ContentType . Alias . ToSafeAliasWithForcingCheck ( ) ;
if ( raiseEvents )
{
if ( ExportingContent . IsRaisedEventCancelled ( new ExportEventArgs < IContent > ( content , nodeName ) , this ) )
return new XElement ( nodeName ) ;
}
const bool published = false ; // fixme - what shall we export?
var xml = EntityXmlSerializer . Serialize ( _contentService , _dataTypeService , _userService , _localizationService , _urlSegmentProviders , content , published , deep ) ;
if ( raiseEvents )
ExportedContent . RaiseEvent ( new ExportEventArgs < IContent > ( content , xml , false ) , this ) ;
return xml ;
}
/// <summary>
/// Imports and saves package xml as <see cref="IContent"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="parentId">Optional parent Id for the content being imported</param>
/// <param name="userId">Optional Id of the user performing the import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated content</returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < IContent > ImportContent ( XElement element , int parentId = - 1 , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
if ( ImportingContent . IsRaisedEventCancelled ( new ImportEventArgs < IContent > ( element ) , this ) )
return Enumerable . Empty < IContent > ( ) ;
}
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 ;
2018-02-09 14:59:41 +01:00
var contents = ParseDocumentRootXml ( roots , parentId ) . ToList ( ) ;
2017-12-28 09:18:09 +01:00
if ( contents . Any ( ) )
_contentService . Save ( contents , userId ) ;
if ( raiseEvents )
ImportedContent . RaiseEvent ( new ImportEventArgs < IContent > ( contents , element , false ) , this ) ;
return contents ;
}
var attribute = element . Attribute ( "isDoc" ) ;
if ( attribute ! = null )
{
//This is a single doc import
var elements = new List < XElement > { element } ;
2018-02-09 14:59:41 +01:00
var contents = ParseDocumentRootXml ( elements , parentId ) . ToList ( ) ;
2017-12-28 09:18:09 +01:00
if ( contents . Any ( ) )
_contentService . Save ( contents , userId ) ;
if ( raiseEvents )
ImportedContent . RaiseEvent ( new ImportEventArgs < IContent > ( contents , element , false ) , this ) ;
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 , int parentId )
{
var contents = new List < IContent > ( ) ;
foreach ( var root in roots )
{
var contentTypeAlias = root . Name . LocalName ;
if ( _importedContentTypes . ContainsKey ( contentTypeAlias ) = = false )
{
var contentType = FindContentTypeByAlias ( contentTypeAlias ) ;
_importedContentTypes . Add ( contentTypeAlias , contentType ) ;
}
var content = CreateContentFromXml ( root , _importedContentTypes [ contentTypeAlias ] , null , parentId ) ;
contents . Add ( content ) ;
2018-02-09 14:59:41 +01:00
var children = ( from child in root . Elements ( )
2017-12-28 09:18:09 +01:00
where ( string ) child . Attribute ( "isDoc" ) = = ""
2018-02-09 14:59:41 +01:00
select child )
. ToList ( ) ;
2017-12-28 09:18:09 +01:00
if ( children . Any ( ) )
contents . AddRange ( CreateContentFromXml ( children , content ) ) ;
}
return contents ;
}
private IEnumerable < IContent > CreateContentFromXml ( IEnumerable < XElement > children , IContent parent )
{
var list = new List < IContent > ( ) ;
foreach ( var child in children )
{
string contentTypeAlias = 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 ) ) ;
list . Add ( content ) ;
//Recursive call
XElement child1 = child ;
2018-02-09 14:59:41 +01:00
var grandChildren = ( from grand in child1 . Elements ( )
where ( string ) grand . Attribute ( "isDoc" ) = = ""
select grand ) . ToList ( ) ;
2017-12-28 09:18:09 +01:00
if ( grandChildren . Any ( ) )
list . AddRange ( CreateContentFromXml ( grandChildren , content ) ) ;
}
return list ;
}
private IContent CreateContentFromXml ( XElement element , IContentType contentType , IContent parent , int parentId )
{
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 ;
//TODO: Shouldn't we be using this value???
var template = element . Attribute ( "template" ) . Value ;
var key = Guid . Empty ;
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 )
} ;
if ( element . Attribute ( "key" ) ! = null & & Guid . TryParse ( element . Attribute ( "key" ) . Value , out key ) )
{
// update the Guid (for UDI support)
content . Key = key ;
}
using ( var scope = _scopeProvider . CreateScope ( autoComplete : true ) )
{
foreach ( var property in properties )
{
string propertyTypeAlias = property . Name . LocalName ;
if ( content . HasProperty ( propertyTypeAlias ) )
{
var propertyValue = property . Value ;
var propertyType = contentType . PropertyTypes . FirstOrDefault ( pt = > pt . Alias = = propertyTypeAlias ) ;
//TODO: It would be heaps nicer if we didn't have to hard code references to specific property editors
// we'd have to modify the packaging format to denote how to parse/store the value instead of relying on this
if ( propertyType ! = null )
{
2018-01-26 17:55:20 +01:00
// fixme - wtf is this very specific thing here?!
//if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.CheckBoxList)
//{
2017-12-28 09:18:09 +01:00
2018-01-26 17:55:20 +01:00
// //TODO: We need to refactor this so the packager isn't making direct db calls for an 'edge' case
// var database = scope.Database;
// var dtos = database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = propertyType.DataTypeId });
2017-12-28 09:18:09 +01:00
2018-01-26 17:55:20 +01:00
// var propertyValueList = new List<string>();
// foreach (var preValue in propertyValue.Split(','))
// {
// propertyValueList.Add(dtos.Single(x => x.Value == preValue).Id.ToString(CultureInfo.InvariantCulture));
// }
2017-12-28 09:18:09 +01:00
2018-01-26 17:55:20 +01:00
// propertyValue = string.Join(",", propertyValueList.ToArray());
2017-12-28 09:18:09 +01:00
2018-01-26 17:55:20 +01:00
//}
2017-12-28 09:18:09 +01:00
}
//set property value
content . SetValue ( propertyTypeAlias , propertyValue ) ;
}
}
}
return content ;
}
#endregion
#region ContentTypes
/// <summary>
/// Exports an <see cref="IContentType"/> to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="contentType">ContentType to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ContentType item.</returns>
public XElement Export ( IContentType contentType , bool raiseEvents = true )
{
if ( raiseEvents )
{
if ( ExportingContentType . IsRaisedEventCancelled ( new ExportEventArgs < IContentType > ( contentType , "DocumentType" ) , this ) )
return new XElement ( "DocumentType" ) ;
}
var exporter = new EntityXmlSerializer ( ) ;
var xml = exporter . Serialize ( _dataTypeService , _contentTypeService , contentType ) ;
if ( raiseEvents )
ExportedContentType . RaiseEvent ( new ExportEventArgs < IContentType > ( contentType , xml , false ) , this ) ;
return xml ;
}
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin).</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated ContentTypes</returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < IContentType > ImportContentTypes ( XElement element , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
return ImportContentTypes ( element , true , userId ) ;
}
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="importStructure">Boolean indicating whether or not to import the </param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin).</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated ContentTypes</returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < IContentType > ImportContentTypes ( XElement element , bool importStructure , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
if ( ImportingContentType . IsRaisedEventCancelled ( new ImportEventArgs < IContentType > ( element ) , this ) )
return Enumerable . Empty < IContentType > ( ) ;
}
var name = element . Name . LocalName ;
if ( name . Equals ( "DocumentTypes" ) = = false & & name . Equals ( "DocumentType" ) = = false )
{
throw new ArgumentException ( "The passed in XElement is not valid! It does not contain a root element called 'DocumentTypes' for multiple imports or 'DocumentType' for a single import." ) ;
}
_importedContentTypes = new Dictionary < string , IContentType > ( ) ;
var unsortedDocumentTypes = name . Equals ( "DocumentTypes" )
? ( from doc in element . Elements ( "DocumentType" ) select doc ) . ToList ( )
: new List < XElement > { element } ;
//When you are importing a single doc type we have to assume that the depedencies are already there.
//Otherwise something like uSync won't work.
var graph = new TopoGraph < string , TopoGraph . Node < string , XElement > > ( x = > x . Key , x = > x . Dependencies ) ;
var isSingleDocTypeImport = unsortedDocumentTypes . Count = = 1 ;
var importedFolders = CreateContentTypeFolderStructure ( unsortedDocumentTypes ) ;
if ( isSingleDocTypeImport = = false )
{
//NOTE Here we sort the doctype XElements based on dependencies
//before creating the doc types - this should also allow for a better structure/inheritance support.
foreach ( var documentType in unsortedDocumentTypes )
{
var elementCopy = documentType ;
var infoElement = elementCopy . Element ( "Info" ) ;
var dependencies = new HashSet < string > ( ) ;
//Add the Master as a dependency
if ( string . IsNullOrEmpty ( ( string ) infoElement . Element ( "Master" ) ) = = false )
{
dependencies . Add ( infoElement . Element ( "Master" ) . Value ) ;
}
//Add compositions as dependencies
var compositionsElement = infoElement . Element ( "Compositions" ) ;
if ( compositionsElement ! = null & & compositionsElement . HasElements )
{
var compositions = compositionsElement . Elements ( "Composition" ) ;
if ( compositions . Any ( ) )
{
foreach ( var composition in compositions )
{
dependencies . Add ( composition . Value ) ;
}
}
}
graph . AddItem ( TopoGraph . CreateNode ( infoElement . Element ( "Alias" ) . Value , elementCopy , dependencies . ToArray ( ) ) ) ;
}
}
//Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921
var documentTypes = isSingleDocTypeImport
? unsortedDocumentTypes . ToList ( )
: graph . GetSortedItems ( ) . Select ( x = > x . Item ) . ToList ( ) ;
//Iterate the sorted document types and create them as IContentType objects
foreach ( var documentType in documentTypes )
{
var alias = documentType . Element ( "Info" ) . Element ( "Alias" ) . Value ;
if ( _importedContentTypes . ContainsKey ( alias ) = = false )
{
var contentType = _contentTypeService . Get ( alias ) ;
_importedContentTypes . Add ( alias , contentType = = null
? CreateContentTypeFromXml ( documentType )
: UpdateContentTypeFromXml ( documentType , contentType ) ) ;
}
}
foreach ( var contentType in _importedContentTypes )
{
var ct = contentType . Value ;
if ( importedFolders . ContainsKey ( ct . Alias ) )
{
ct . ParentId = importedFolders [ ct . Alias ] ;
}
}
//Save the newly created/updated IContentType objects
var list = _importedContentTypes . Select ( x = > x . Value ) . ToList ( ) ;
_contentTypeService . Save ( list , userId ) ;
//Now we can finish the import by updating the 'structure',
//which requires the doc types to be saved/available in the db
if ( importStructure )
{
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 ) ;
}
if ( raiseEvents )
ImportedContentType . RaiseEvent ( new ImportEventArgs < IContentType > ( list , element , false ) , this ) ;
return list ;
}
private Dictionary < string , int > CreateContentTypeFolderStructure ( IEnumerable < XElement > unsortedDocumentTypes )
{
var importedFolders = new Dictionary < string , int > ( ) ;
foreach ( var documentType in unsortedDocumentTypes )
{
var foldersAttribute = documentType . Attribute ( "Folders" ) ;
var infoElement = documentType . Element ( "Info" ) ;
if ( foldersAttribute ! = null & & infoElement ! = null
//don't import any folder if this is a child doc type - the parent doc type will need to
//exist which contains it's folders
& & ( ( string ) infoElement . Element ( "Master" ) ) . IsNullOrWhiteSpace ( ) )
{
var alias = documentType . Element ( "Info" ) . Element ( "Alias" ) . Value ;
var folders = foldersAttribute . Value . Split ( '/' ) ;
var rootFolder = HttpUtility . UrlDecode ( folders [ 0 ] ) ;
//level 1 = root level folders, there can only be one with the same name
var current = _contentTypeService . GetContainers ( rootFolder , 1 ) . FirstOrDefault ( ) ;
if ( current = = null )
{
var tryCreateFolder = _contentTypeService . CreateContainer ( - 1 , rootFolder ) ;
if ( tryCreateFolder = = false )
{
2018-08-17 15:41:58 +01:00
_logger . Error < PackagingService > ( tryCreateFolder . Exception , "Could not create folder: {FolderName}" , rootFolder ) ;
2017-12-28 09:18:09 +01:00
throw tryCreateFolder . Exception ;
}
var rootFolderId = tryCreateFolder . Result . Entity . Id ;
current = _contentTypeService . GetContainer ( rootFolderId ) ;
}
importedFolders . Add ( alias , current . Id ) ;
for ( var i = 1 ; i < folders . Length ; i + + )
{
var folderName = HttpUtility . UrlDecode ( folders [ i ] ) ;
current = CreateContentTypeChildFolder ( folderName , current ) ;
importedFolders [ alias ] = current . Id ;
}
}
}
return importedFolders ;
}
private EntityContainer CreateContentTypeChildFolder ( string folderName , IUmbracoEntity current )
{
var children = _entityService . GetChildren ( current . Id ) . ToArray ( ) ;
var found = children . Any ( x = > x . Name . InvariantEquals ( folderName ) ) ;
if ( found )
{
var containerId = children . Single ( x = > x . Name . InvariantEquals ( folderName ) ) . Id ;
return _contentTypeService . GetContainer ( containerId ) ;
}
var tryCreateFolder = _contentTypeService . CreateContainer ( current . Id , folderName ) ;
if ( tryCreateFolder = = false )
{
2018-08-17 15:41:58 +01:00
_logger . Error < PackagingService > ( tryCreateFolder . Exception , "Could not create folder: {FolderName}" , folderName ) ;
2017-12-28 09:18:09 +01:00
throw tryCreateFolder . Exception ;
}
return _contentTypeService . GetContainer ( tryCreateFolder . Result . Entity . Id ) ;
}
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 . Get ( masterAlias ) ;
}
var alias = infoElement . Element ( "Alias" ) . Value ;
var contentType = parent = = null
? new ContentType ( - 1 ) { Alias = alias }
: new ContentType ( parent , alias ) ;
if ( parent ! = null )
contentType . AddContentType ( parent ) ;
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 ;
//NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it.
var allowAtRoot = infoElement . Element ( "AllowAtRoot" ) ;
if ( allowAtRoot ! = null )
contentType . AllowedAsRoot = allowAtRoot . Value . InvariantEquals ( "true" ) ;
//NOTE IsListView is a new property in the package xml so we need to verify it exists before using it.
var isListView = infoElement . Element ( "IsListView" ) ;
if ( isListView ! = null )
contentType . IsContainer = isListView . Value . InvariantEquals ( "true" ) ;
//Name of the master corresponds to the parent and we need to ensure that the Parent Id is set
var masterElement = infoElement . Element ( "Master" ) ;
if ( masterElement ! = null )
{
var masterAlias = masterElement . Value ;
IContentType parent = _importedContentTypes . ContainsKey ( masterAlias )
? _importedContentTypes [ masterAlias ]
: _contentTypeService . Get ( masterAlias ) ;
2018-01-15 11:32:30 +01:00
contentType . SetParent ( parent ) ;
2017-12-28 09:18:09 +01:00
}
//Update Compositions on the ContentType to ensure that they are as is defined in the package xml
var compositionsElement = infoElement . Element ( "Compositions" ) ;
if ( compositionsElement ! = null & & compositionsElement . HasElements )
{
var compositions = compositionsElement . Elements ( "Composition" ) ;
if ( compositions . Any ( ) )
{
foreach ( var composition in compositions )
{
var compositionAlias = composition . Value ;
var compositionContentType = _importedContentTypes . ContainsKey ( compositionAlias )
? _importedContentTypes [ compositionAlias ]
: _contentTypeService . Get ( compositionAlias ) ;
var added = contentType . AddContentType ( compositionContentType ) ;
}
}
}
UpdateContentTypesAllowedTemplates ( contentType , infoElement . Element ( "AllowedTemplates" ) , defaultTemplateElement ) ;
UpdateContentTypesTabs ( contentType , documentType . Element ( "Tabs" ) ) ;
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 . ToSafeAlias ( ) ) ;
if ( template ! = null )
{
if ( allowedTemplates . Any ( x = > x . Id = = template . Id ) ) continue ;
allowedTemplates . Add ( template ) ;
}
else
{
2018-08-14 15:08:32 +01:00
_logger . Warn < PackagingService > ( "Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found." , alias ) ;
2017-12-28 09:18:09 +01:00
}
}
contentType . AllowedTemplates = allowedTemplates ;
}
if ( string . IsNullOrEmpty ( ( string ) defaultTemplateElement ) = = false )
{
var defaultTemplate = _fileService . GetTemplate ( defaultTemplateElement . Value . ToSafeAlias ( ) ) ;
if ( defaultTemplate ! = null )
{
contentType . SetDefaultTemplate ( defaultTemplate ) ;
}
else
{
2018-08-14 15:08:32 +01:00
_logger . Warn < PackagingService > ( "Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found." , defaultTemplateElement . Value ) ;
2017-12-28 09:18:09 +01:00
}
}
}
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 ) ;
}
int sortOrder ;
if ( tab . Element ( "SortOrder" ) ! = null & & int . TryParse ( tab . Element ( "SortOrder" ) . Value , out sortOrder ) )
{
// Override the sort order with the imported value
contentType . PropertyGroups [ caption ] . SortOrder = sortOrder ;
}
}
}
private void UpdateContentTypesProperties ( IContentType contentType , XElement genericPropertiesElement )
{
var properties = genericPropertiesElement . Elements ( "GenericProperty" ) ;
foreach ( var property in properties )
{
var dataTypeDefinitionId = new Guid ( property . Element ( "Definition" ) . Value ) ; //Unique Id for a DataTypeDefinition
2018-01-15 17:32:45 +01:00
var dataTypeDefinition = _dataTypeService . GetDataType ( dataTypeDefinitionId ) ;
2017-12-28 09:18:09 +01:00
//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
//get the alias as a string for use below
var propertyEditorAlias = property . Element ( "Type" ) . Value . Trim ( ) ;
//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 )
{
2018-01-15 17:32:45 +01:00
var dataTypeDefinitions = _dataTypeService . GetByEditorAlias ( propertyEditorAlias ) ;
2017-12-28 09:18:09 +01:00
if ( dataTypeDefinitions ! = null & & dataTypeDefinitions . Any ( ) )
{
dataTypeDefinition = dataTypeDefinitions . FirstOrDefault ( ) ;
}
}
2018-01-10 12:48:51 +01:00
else if ( dataTypeDefinition . EditorAlias ! = propertyEditorAlias )
2017-12-28 09:18:09 +01:00
{
2018-01-15 17:32:45 +01:00
var dataTypeDefinitions = _dataTypeService . GetByEditorAlias ( propertyEditorAlias ) ;
2017-12-28 09:18:09 +01:00
if ( dataTypeDefinitions ! = null & & dataTypeDefinitions . Any ( ) )
{
dataTypeDefinition = dataTypeDefinitions . FirstOrDefault ( ) ;
}
}
// For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently.
// This means that the property will not be created.
if ( dataTypeDefinition = = null )
{
2018-08-14 15:08:32 +01:00
_logger . Warn < PackagingService > ( "Packager: Error handling creation of PropertyType '{PropertyType}'. Could not find DataTypeDefintion with unique id '{DataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{PropertyEditorAlias}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists." ,
property . Element ( "Name" ) . Value , dataTypeDefinitionId , property . Element ( "Type" ) . Value . Trim ( ) ) ;
2017-12-28 09:18:09 +01:00
//convert to a label!
2018-01-20 12:09:15 +01:00
dataTypeDefinition = _dataTypeService . GetByEditorAlias ( Constants . PropertyEditors . Aliases . NoEdit ) . FirstOrDefault ( ) ;
2017-12-28 09:18:09 +01:00
//if for some odd reason this isn't there then ignore
if ( dataTypeDefinition = = null ) continue ;
}
var sortOrder = 0 ;
var sortOrderElement = property . Element ( "SortOrder" ) ;
if ( sortOrderElement ! = null )
int . TryParse ( sortOrderElement . Value , out sortOrder ) ;
var propertyType = new PropertyType ( dataTypeDefinition , property . Element ( "Alias" ) . Value )
{
Name = property . Element ( "Name" ) . Value ,
Description = ( string ) property . Element ( "Description" ) ,
Mandatory = property . Element ( "Mandatory" ) ! = null ? property . Element ( "Mandatory" ) . Value . ToLowerInvariant ( ) . Equals ( "true" ) : false ,
ValidationRegExp = ( string ) property . Element ( "Validation" ) ,
SortOrder = sortOrder
} ;
var tab = ( string ) property . Element ( "Tab" ) ;
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 ;
2018-03-22 17:41:13 +01:00
var allowedChild = _importedContentTypes . ContainsKey ( alias ) ? _importedContentTypes [ alias ] : _contentTypeService . Get ( alias ) ;
if ( allowedChild = = null )
2017-12-28 09:18:09 +01:00
{
2018-08-14 15:08:32 +01:00
_logger . Warn < PackagingService > (
"Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'." ,
alias , contentType . Alias ) ;
2018-03-22 17:41:13 +01:00
continue ;
2017-12-28 09:18:09 +01:00
}
2018-03-22 17:41:13 +01:00
if ( allowedChildren . Any ( x = > x . Id . IsValueCreated & & x . Id . Value = = allowedChild . Id ) ) continue ;
allowedChildren . Add ( new ContentTypeSort ( new Lazy < int > ( ( ) = > allowedChild . Id ) , sortOrder , allowedChild . Alias ) ) ;
sortOrder + + ;
2017-12-28 09:18:09 +01:00
}
contentType . AllowedContentTypes = allowedChildren ;
return contentType ;
}
/// <summary>
/// Used during Content import to ensure that the ContentType of a content item exists
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
private IContentType FindContentTypeByAlias ( string contentTypeAlias )
{
using ( var scope = _scopeProvider . CreateScope ( ) )
{
var query = Query < IContentType > ( ) . Where ( x = > x . Alias = = contentTypeAlias ) ;
var contentType = _contentTypeRepository . Get ( query ) . FirstOrDefault ( ) ;
if ( contentType = = null )
throw new Exception ( $"ContentType matching the passed in Alias: '{contentTypeAlias}' was null" ) ;
scope . Complete ( ) ;
return contentType ;
}
}
#endregion
#region DataTypes
/// <summary>
/// Exports a list of Data Types
/// </summary>
/// <param name="dataTypeDefinitions">List of data types to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition objects</returns>
2018-01-10 12:48:51 +01:00
public XElement Export ( IEnumerable < IDataType > dataTypeDefinitions , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
var container = new XElement ( "DataTypes" ) ;
foreach ( var dataTypeDefinition in dataTypeDefinitions )
{
container . Add ( Export ( dataTypeDefinition , raiseEvents ) ) ;
}
return container ;
}
/// <summary>
/// Exports a single Data Type
/// </summary>
2018-01-10 12:48:51 +01:00
/// <param name="dataType">Data type to export</param>
2017-12-28 09:18:09 +01:00
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition object</returns>
2018-01-10 12:48:51 +01:00
public XElement Export ( IDataType dataType , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
2018-01-10 12:48:51 +01:00
if ( ExportingDataType . IsRaisedEventCancelled ( new ExportEventArgs < IDataType > ( dataType , "DataType" ) , this ) )
2017-12-28 09:18:09 +01:00
return new XElement ( "DataType" ) ;
}
var exporter = new EntityXmlSerializer ( ) ;
2018-01-10 12:48:51 +01:00
var xml = exporter . Serialize ( _dataTypeService , dataType ) ;
2017-12-28 09:18:09 +01:00
if ( raiseEvents )
2018-01-10 12:48:51 +01:00
ExportedDataType . RaiseEvent ( new ExportEventArgs < IDataType > ( dataType , xml , false ) , this ) ;
2017-12-28 09:18:09 +01:00
return xml ;
}
/// <summary>
2018-01-10 12:48:51 +01:00
/// Imports and saves package xml as <see cref="IDataType"/>
2017-12-28 09:18:09 +01:00
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the user</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated DataTypeDefinitions</returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < IDataType > ImportDataTypeDefinitions ( XElement element , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
2018-01-10 12:48:51 +01:00
if ( ImportingDataType . IsRaisedEventCancelled ( new ImportEventArgs < IDataType > ( element ) , this ) )
return Enumerable . Empty < IDataType > ( ) ;
2017-12-28 09:18:09 +01:00
}
var name = element . Name . LocalName ;
if ( name . Equals ( "DataTypes" ) = = false & & name . Equals ( "DataType" ) = = false )
{
throw new ArgumentException ( "The passed in XElement is not valid! It does not contain a root element called 'DataTypes' for multiple imports or 'DataType' for a single import." ) ;
}
2018-02-07 13:35:59 +01:00
var dataTypes = new List < IDataType > ( ) ;
2017-12-28 09:18:09 +01:00
var dataTypeElements = name . Equals ( "DataTypes" )
? ( from doc in element . Elements ( "DataType" ) select doc ) . ToList ( )
: new List < XElement > { element } ;
var importedFolders = CreateDataTypeFolderStructure ( dataTypeElements ) ;
foreach ( var dataTypeElement in dataTypeElements )
{
var dataTypeDefinitionName = dataTypeElement . Attribute ( "Name" ) . Value ;
var dataTypeDefinitionId = new Guid ( dataTypeElement . Attribute ( "Definition" ) . Value ) ;
var databaseTypeAttribute = dataTypeElement . Attribute ( "DatabaseType" ) ;
var parentId = - 1 ;
if ( importedFolders . ContainsKey ( dataTypeDefinitionName ) )
parentId = importedFolders [ dataTypeDefinitionName ] ;
2018-01-15 17:32:45 +01:00
var definition = _dataTypeService . GetDataType ( dataTypeDefinitionId ) ;
2017-12-28 09:18:09 +01:00
//If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml
if ( definition = = null )
{
var databaseType = databaseTypeAttribute ! = null
2018-01-24 11:44:44 +01:00
? databaseTypeAttribute . Value . EnumParse < ValueStorageType > ( true )
: ValueStorageType . Ntext ;
2017-12-28 09:18:09 +01:00
2018-02-07 13:35:59 +01:00
// the Id field is actually the string property editor Alias
// however, the actual editor with this alias could be installed with the package, and
// therefore not yet part of the _propertyEditors collection, so we cannot try and get
// the actual editor - going with a void editor
var editorAlias = dataTypeElement . Attribute ( "Id" ) ? . Value ? . Trim ( ) ;
if ( ! _propertyEditors . TryGet ( editorAlias , out var editor ) )
editor = new VoidEditor ( _logger ) { Alias = editorAlias } ;
2018-02-09 14:59:41 +01:00
var dataType = new DataType ( editor )
2017-12-28 09:18:09 +01:00
{
Key = dataTypeDefinitionId ,
Name = dataTypeDefinitionName ,
DatabaseType = databaseType ,
ParentId = parentId
} ;
2018-02-09 14:59:41 +01:00
var configurationAttributeValue = dataTypeElement . Attribute ( "Configuration" ) ? . Value ;
if ( ! string . IsNullOrWhiteSpace ( configurationAttributeValue ) )
2018-03-16 09:06:44 +01:00
dataType . Configuration = editor . GetConfigurationEditor ( ) . FromDatabase ( configurationAttributeValue ) ;
2018-02-07 13:35:59 +01:00
2018-02-09 14:59:41 +01:00
dataTypes . Add ( dataType ) ;
2017-12-28 09:18:09 +01:00
}
else
{
definition . ParentId = parentId ;
_dataTypeService . Save ( definition , userId ) ;
}
}
2018-02-07 13:35:59 +01:00
if ( dataTypes . Count > 0 )
2017-12-28 09:18:09 +01:00
{
2018-02-07 13:35:59 +01:00
_dataTypeService . Save ( dataTypes , userId , true ) ;
2017-12-28 09:18:09 +01:00
}
if ( raiseEvents )
2018-02-07 13:35:59 +01:00
ImportedDataType . RaiseEvent ( new ImportEventArgs < IDataType > ( dataTypes , element , false ) , this ) ;
2017-12-28 09:18:09 +01:00
2018-02-07 13:35:59 +01:00
return dataTypes ;
2017-12-28 09:18:09 +01:00
}
private Dictionary < string , int > CreateDataTypeFolderStructure ( IEnumerable < XElement > datatypeElements )
{
var importedFolders = new Dictionary < string , int > ( ) ;
foreach ( var datatypeElement in datatypeElements )
{
var foldersAttribute = datatypeElement . Attribute ( "Folders" ) ;
if ( foldersAttribute ! = null )
{
var name = datatypeElement . Attribute ( "Name" ) . Value ;
var folders = foldersAttribute . Value . Split ( '/' ) ;
var rootFolder = HttpUtility . UrlDecode ( folders [ 0 ] ) ;
//there will only be a single result by name for level 1 (root) containers
var current = _dataTypeService . GetContainers ( rootFolder , 1 ) . FirstOrDefault ( ) ;
if ( current = = null )
{
var tryCreateFolder = _dataTypeService . CreateContainer ( - 1 , rootFolder ) ;
if ( tryCreateFolder = = false )
{
2018-08-17 15:41:58 +01:00
_logger . Error < PackagingService > ( tryCreateFolder . Exception , "Could not create folder: {FolderName}" , rootFolder ) ;
2017-12-28 09:18:09 +01:00
throw tryCreateFolder . Exception ;
}
current = _dataTypeService . GetContainer ( tryCreateFolder . Result . Entity . Id ) ;
}
importedFolders . Add ( name , current . Id ) ;
for ( var i = 1 ; i < folders . Length ; i + + )
{
var folderName = HttpUtility . UrlDecode ( folders [ i ] ) ;
current = CreateDataTypeChildFolder ( folderName , current ) ;
importedFolders [ name ] = current . Id ;
}
}
}
return importedFolders ;
}
private EntityContainer CreateDataTypeChildFolder ( string folderName , IUmbracoEntity current )
{
var children = _entityService . GetChildren ( current . Id ) . ToArray ( ) ;
var found = children . Any ( x = > x . Name . InvariantEquals ( folderName ) ) ;
if ( found )
{
var containerId = children . Single ( x = > x . Name . InvariantEquals ( folderName ) ) . Id ;
return _dataTypeService . GetContainer ( containerId ) ;
}
var tryCreateFolder = _dataTypeService . CreateContainer ( current . Id , folderName ) ;
if ( tryCreateFolder = = false )
{
2018-08-17 15:41:58 +01:00
_logger . Error < PackagingService > ( tryCreateFolder . Exception , "Could not create folder: {FolderName}" , folderName ) ;
2017-12-28 09:18:09 +01:00
throw tryCreateFolder . Exception ;
}
return _dataTypeService . GetContainer ( tryCreateFolder . Result . Entity . Id ) ;
}
#endregion
#region Dictionary Items
/// <summary>
/// Exports a list of <see cref="IDictionaryItem"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">List of dictionary items to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem objects</returns>
public XElement Export ( IEnumerable < IDictionaryItem > dictionaryItem , bool includeChildren = true , bool raiseEvents = true )
{
var xml = new XElement ( "DictionaryItems" ) ;
foreach ( var item in dictionaryItem )
{
xml . Add ( Export ( item , includeChildren , raiseEvents ) ) ;
}
return xml ;
}
/// <summary>
/// Exports a single <see cref="IDictionaryItem"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">Dictionary Item to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem object</returns>
public XElement Export ( IDictionaryItem dictionaryItem , bool includeChildren , bool raiseEvents = true )
{
if ( raiseEvents )
{
if ( ExportingDictionaryItem . IsRaisedEventCancelled ( new ExportEventArgs < IDictionaryItem > ( dictionaryItem , "DictionaryItem" ) , this ) )
return new XElement ( "DictionaryItem" ) ;
}
var exporter = new EntityXmlSerializer ( ) ;
var xml = exporter . Serialize ( dictionaryItem ) ;
if ( includeChildren )
{
var children = _localizationService . GetDictionaryItemChildren ( dictionaryItem . Key ) ;
foreach ( var child in children )
{
xml . Add ( Export ( child , true ) ) ;
}
}
if ( raiseEvents )
ExportedDictionaryItem . RaiseEvent ( new ExportEventArgs < IDictionaryItem > ( dictionaryItem , xml , false ) , this ) ;
return xml ;
}
/// <summary>
/// Imports and saves the 'DictionaryItems' part of the package xml as a list of <see cref="IDictionaryItem"/>
/// </summary>
/// <param name="dictionaryItemElementList">Xml to import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of dictionary items</returns>
public IEnumerable < IDictionaryItem > ImportDictionaryItems ( XElement dictionaryItemElementList , bool raiseEvents = true )
{
if ( raiseEvents )
{
if ( ImportingDictionaryItem . IsRaisedEventCancelled ( new ImportEventArgs < IDictionaryItem > ( dictionaryItemElementList ) , this ) )
return Enumerable . Empty < IDictionaryItem > ( ) ;
}
var languages = _localizationService . GetAllLanguages ( ) . ToList ( ) ;
return ImportDictionaryItems ( dictionaryItemElementList , languages , raiseEvents ) ;
}
private IEnumerable < IDictionaryItem > ImportDictionaryItems ( XElement dictionaryItemElementList , List < ILanguage > languages , bool raiseEvents , Guid ? parentId = null )
{
var items = new List < IDictionaryItem > ( ) ;
foreach ( var dictionaryItemElement in dictionaryItemElementList . Elements ( "DictionaryItem" ) )
items . AddRange ( ImportDictionaryItem ( dictionaryItemElement , languages , raiseEvents , parentId ) ) ;
if ( raiseEvents )
ImportedDictionaryItem . RaiseEvent ( new ImportEventArgs < IDictionaryItem > ( items , dictionaryItemElementList , false ) , this ) ;
return items ;
}
private IEnumerable < IDictionaryItem > ImportDictionaryItem ( XElement dictionaryItemElement , List < ILanguage > languages , bool raiseEvents , Guid ? parentId )
{
var items = new List < IDictionaryItem > ( ) ;
IDictionaryItem dictionaryItem ;
var key = dictionaryItemElement . Attribute ( "Key" ) . Value ;
if ( _localizationService . DictionaryItemExists ( key ) )
dictionaryItem = GetAndUpdateDictionaryItem ( key , dictionaryItemElement , languages ) ;
else
dictionaryItem = CreateNewDictionaryItem ( key , dictionaryItemElement , languages , parentId ) ;
_localizationService . Save ( dictionaryItem ) ;
items . Add ( dictionaryItem ) ;
items . AddRange ( ImportDictionaryItems ( dictionaryItemElement , languages , raiseEvents , dictionaryItem . Key ) ) ;
return items ;
}
private IDictionaryItem GetAndUpdateDictionaryItem ( string key , XElement dictionaryItemElement , List < ILanguage > languages )
{
var dictionaryItem = _localizationService . GetDictionaryItemByKey ( key ) ;
var translations = dictionaryItem . Translations . ToList ( ) ;
foreach ( var valueElement in dictionaryItemElement . Elements ( "Value" ) . Where ( v = > DictionaryValueIsNew ( translations , v ) ) )
AddDictionaryTranslation ( translations , valueElement , languages ) ;
dictionaryItem . Translations = translations ;
return dictionaryItem ;
}
private static DictionaryItem CreateNewDictionaryItem ( string key , XElement dictionaryItemElement , List < ILanguage > languages , Guid ? parentId )
{
var dictionaryItem = parentId . HasValue ? new DictionaryItem ( parentId . Value , key ) : new DictionaryItem ( key ) ;
var translations = new List < IDictionaryTranslation > ( ) ;
foreach ( var valueElement in dictionaryItemElement . Elements ( "Value" ) )
AddDictionaryTranslation ( translations , valueElement , languages ) ;
dictionaryItem . Translations = translations ;
return dictionaryItem ;
}
private static bool DictionaryValueIsNew ( IEnumerable < IDictionaryTranslation > translations , XElement valueElement )
{
return translations . All ( t = >
String . Compare ( t . Language . IsoCode , valueElement . Attribute ( "LanguageCultureAlias" ) . Value ,
StringComparison . InvariantCultureIgnoreCase ) ! = 0
) ;
}
private static void AddDictionaryTranslation ( ICollection < IDictionaryTranslation > translations , XElement valueElement , IEnumerable < ILanguage > languages )
{
var languageId = valueElement . Attribute ( "LanguageCultureAlias" ) . Value ;
var language = languages . SingleOrDefault ( l = > l . IsoCode = = languageId ) ;
if ( language = = null )
return ;
var translation = new DictionaryTranslation ( language , valueElement . Value ) ;
translations . Add ( translation ) ;
}
#endregion
#region Files
#endregion
#region Languages
/// <summary>
/// Exports a list of <see cref="ILanguage"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="languages">List of Languages to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ILanguage objects</returns>
public XElement Export ( IEnumerable < ILanguage > languages , bool raiseEvents = true )
{
var xml = new XElement ( "Languages" ) ;
foreach ( var language in languages )
{
xml . Add ( Export ( language , raiseEvents ) ) ;
}
return xml ;
}
/// <summary>
/// Exports a single <see cref="ILanguage"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="language">Language to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ILanguage object</returns>
public XElement Export ( ILanguage language , bool raiseEvents = true )
{
if ( raiseEvents )
{
if ( ExportingLanguage . IsRaisedEventCancelled ( new ExportEventArgs < ILanguage > ( language , "Language" ) , this ) )
return new XElement ( "Language" ) ;
}
var exporter = new EntityXmlSerializer ( ) ;
var xml = exporter . Serialize ( language ) ;
if ( raiseEvents )
ExportedLanguage . RaiseEvent ( new ExportEventArgs < ILanguage > ( language , xml , false ) , this ) ;
return xml ;
}
/// <summary>
/// Imports and saves the 'Languages' part of a package xml as a list of <see cref="ILanguage"/>
/// </summary>
/// <param name="languageElementList">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of generated languages</returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < ILanguage > ImportLanguages ( XElement languageElementList , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
if ( ImportingLanguage . IsRaisedEventCancelled ( new ImportEventArgs < ILanguage > ( languageElementList ) , this ) )
return Enumerable . Empty < ILanguage > ( ) ;
}
var list = new List < ILanguage > ( ) ;
foreach ( var languageElement in languageElementList . Elements ( "Language" ) )
{
var isoCode = languageElement . Attribute ( "CultureAlias" ) . Value ;
var existingLanguage = _localizationService . GetLanguageByIsoCode ( isoCode ) ;
if ( existingLanguage = = null )
{
var langauge = new Language ( isoCode )
{
CultureName = languageElement . Attribute ( "FriendlyName" ) . Value
} ;
_localizationService . Save ( langauge ) ;
list . Add ( langauge ) ;
}
}
if ( raiseEvents )
ImportedLanguage . RaiseEvent ( new ImportEventArgs < ILanguage > ( list , languageElementList , false ) , this ) ;
return list ;
}
#endregion
#region Macros
/// <summary>
/// Imports and saves the 'Macros' part of a package xml as a list of <see cref="IMacro"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns></returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < IMacro > ImportMacros ( XElement element , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
if ( ImportingMacro . IsRaisedEventCancelled ( new ImportEventArgs < IMacro > ( element ) , this ) )
return Enumerable . Empty < IMacro > ( ) ;
}
var name = element . Name . LocalName ;
if ( name . Equals ( "Macros" ) = = false & & name . Equals ( "macro" ) = = false )
{
throw new ArgumentException ( "The passed in XElement is not valid! It does not contain a root element called 'Macros' for multiple imports or 'macro' for a single import." ) ;
}
var macroElements = name . Equals ( "Macros" )
? ( from doc in element . Elements ( "macro" ) select doc ) . ToList ( )
: new List < XElement > { element } ;
var macros = macroElements . Select ( ParseMacroElement ) . ToList ( ) ;
foreach ( var macro in macros )
{
var existing = _macroService . GetByAlias ( macro . Alias ) ;
if ( existing ! = null )
macro . Id = existing . Id ;
_macroService . Save ( macro , userId ) ;
}
if ( raiseEvents )
ImportedMacro . RaiseEvent ( new ImportEventArgs < IMacro > ( macros , element , false ) , this ) ;
return macros ;
}
private IMacro ParseMacroElement ( XElement macroElement )
{
var macroName = macroElement . Element ( "name" ) . Value ;
var macroAlias = macroElement . Element ( "alias" ) . Value ;
2018-05-01 12:12:26 +10:00
var macroType = Enum < MacroTypes > . Parse ( macroElement . Element ( "macroType" ) . Value ) ;
var macroSource = macroElement . Element ( "macroSource" ) . Value ;
2017-12-28 09:18:09 +01:00
//Following xml elements are treated as nullable properties
var useInEditorElement = macroElement . Element ( "useInEditor" ) ;
var useInEditor = false ;
if ( useInEditorElement ! = null & & string . IsNullOrEmpty ( ( string ) useInEditorElement ) = = false )
{
useInEditor = bool . Parse ( useInEditorElement . Value ) ;
}
var cacheDurationElement = macroElement . Element ( "refreshRate" ) ;
var cacheDuration = 0 ;
if ( cacheDurationElement ! = null & & string . IsNullOrEmpty ( ( string ) cacheDurationElement ) = = false )
{
cacheDuration = int . Parse ( cacheDurationElement . Value ) ;
}
var cacheByMemberElement = macroElement . Element ( "cacheByMember" ) ;
var cacheByMember = false ;
if ( cacheByMemberElement ! = null & & string . IsNullOrEmpty ( ( string ) cacheByMemberElement ) = = false )
{
cacheByMember = bool . Parse ( cacheByMemberElement . Value ) ;
}
var cacheByPageElement = macroElement . Element ( "cacheByPage" ) ;
var cacheByPage = false ;
if ( cacheByPageElement ! = null & & string . IsNullOrEmpty ( ( string ) cacheByPageElement ) = = false )
{
cacheByPage = bool . Parse ( cacheByPageElement . Value ) ;
}
var dontRenderElement = macroElement . Element ( "dontRender" ) ;
var dontRender = true ;
if ( dontRenderElement ! = null & & string . IsNullOrEmpty ( ( string ) dontRenderElement ) = = false )
{
dontRender = bool . Parse ( dontRenderElement . Value ) ;
}
var existingMacro = _macroService . GetByAlias ( macroAlias ) as Macro ;
2018-05-01 12:12:26 +10:00
var macro = existingMacro ? ? new Macro ( macroAlias , macroName , macroSource , macroType ,
2017-12-28 09:18:09 +01:00
cacheByPage , cacheByMember , dontRender , useInEditor , cacheDuration ) ;
var properties = macroElement . Element ( "properties" ) ;
if ( properties ! = null )
{
int sortOrder = 0 ;
foreach ( var property in properties . Elements ( ) )
{
var propertyName = property . Attribute ( "name" ) . Value ;
var propertyAlias = property . Attribute ( "alias" ) . Value ;
var editorAlias = property . Attribute ( "propertyType" ) . Value ;
var sortOrderAttribute = property . Attribute ( "sortOrder" ) ;
if ( sortOrderAttribute ! = null )
{
sortOrder = int . Parse ( sortOrderAttribute . Value ) ;
}
if ( macro . Properties . Any ( x = > string . Equals ( x . Alias , propertyAlias , StringComparison . OrdinalIgnoreCase ) ) ) continue ;
macro . Properties . Add ( new MacroProperty ( propertyAlias , propertyName , sortOrder , editorAlias ) ) ;
sortOrder + + ;
}
}
return macro ;
}
/// <summary>
/// Exports a list of <see cref="IMacro"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="macros">Macros to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IMacro objects</returns>
public XElement Export ( IEnumerable < IMacro > macros , bool raiseEvents = true )
{
var xml = new XElement ( "Macros" ) ;
foreach ( var item in macros )
{
xml . Add ( Export ( item , raiseEvents ) ) ;
}
return xml ;
}
/// <summary>
/// Exports a single <see cref="IMacro"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="macro">Macro to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IMacro object</returns>
public XElement Export ( IMacro macro , bool raiseEvents = true )
{
if ( raiseEvents )
{
if ( ExportingMacro . IsRaisedEventCancelled ( new ExportEventArgs < IMacro > ( macro , "macro" ) , this ) )
return new XElement ( "macro" ) ;
}
var exporter = new EntityXmlSerializer ( ) ;
var xml = exporter . Serialize ( macro ) ;
if ( raiseEvents )
ExportedMacro . RaiseEvent ( new ExportEventArgs < IMacro > ( macro , xml , false ) , this ) ;
return xml ;
}
#endregion
#region Members
/// <summary>
/// Exports an <see cref="IMedia"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="member">Member to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Member object</returns>
public XElement Export ( IMember member )
{
return EntityXmlSerializer . Serialize ( _dataTypeService , _localizationService , member ) ;
}
#endregion
#region Media
/// <summary>
/// Exports an <see cref="IMedia"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="media">Media to export</param>
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Media object</returns>
public XElement Export ( IMedia media , bool deep = false , bool raiseEvents = true )
{
var nodeName = media . ContentType . Alias . ToSafeAliasWithForcingCheck ( ) ;
if ( raiseEvents )
{
if ( ExportingMedia . IsRaisedEventCancelled ( new ExportEventArgs < IMedia > ( media , nodeName ) , this ) )
return new XElement ( nodeName ) ;
}
var xml = EntityXmlSerializer . Serialize ( _mediaService , _dataTypeService , _userService , _localizationService , _urlSegmentProviders , media , deep ) ;
if ( raiseEvents )
ExportedMedia . RaiseEvent ( new ExportEventArgs < IMedia > ( media , xml , false ) , this ) ;
return xml ;
}
#endregion
#region MediaTypes
/// <summary>
/// Exports an <see cref="IMediaType"/> to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="mediaType">MediaType to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the MediaType item.</returns>
internal XElement Export ( IMediaType mediaType )
{
var exporter = new EntityXmlSerializer ( ) ;
var xml = exporter . Serialize ( _dataTypeService , mediaType ) ;
return xml ;
}
#endregion
#region Package Manifest
#endregion
#region Package Files
/// <summary>
/// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file
/// </summary>
/// <param name="packageId"></param>
/// <param name="umbracoVersion"></param>
/// /// <param name="userId">The current user id performing the operation</param>
/// <returns></returns>
public string FetchPackageFile ( Guid packageId , Version umbracoVersion , int userId )
{
using ( var scope = _scopeProvider . CreateScope ( ) )
{
//includeHidden = true because we don't care if it's hidden we want to get the file regardless
2018-04-30 23:25:07 +10:00
var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true" ;
2017-12-28 09:18:09 +01:00
byte [ ] bytes ;
try
{
2018-10-02 11:14:04 +02:00
if ( _httpClient = = null )
{
_httpClient = new HttpClient ( ) ;
}
bytes = _httpClient . GetByteArrayAsync ( url ) . GetAwaiter ( ) . GetResult ( ) ;
2017-12-28 09:18:09 +01:00
}
catch ( HttpRequestException ex )
{
throw new ConnectionException ( "An error occuring downloading the package from " + url , ex ) ;
}
//successfull
if ( bytes . Length > 0 )
{
var packagePath = IOHelper . MapPath ( SystemDirectories . Packages ) ;
// Check for package directory
if ( Directory . Exists ( packagePath ) = = false )
Directory . CreateDirectory ( packagePath ) ;
var packageFilePath = Path . Combine ( packagePath , packageId + ".umb" ) ;
using ( var fs1 = new FileStream ( packageFilePath , FileMode . Create ) )
{
fs1 . Write ( bytes , 0 , bytes . Length ) ;
return "packages\\" + packageId + ".umb" ;
}
}
2018-04-30 23:25:07 +10:00
Audit ( AuditType . PackagerInstall , $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}" , userId , - 1 ) ;
2017-12-28 09:18:09 +01:00
return null ;
}
}
private void Audit ( AuditType type , string message , int userId , int objectId )
{
_auditRepository . Save ( new AuditItem ( objectId , message , type , userId ) ) ;
}
#endregion
#region Templates
/// <summary>
/// Imports and saves package xml as <see cref="ITemplate"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional user id</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated Templates</returns>
2018-05-31 23:12:48 +10:00
public IEnumerable < ITemplate > ImportTemplates ( XElement element , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
if ( ImportingTemplate . IsRaisedEventCancelled ( new ImportEventArgs < ITemplate > ( element ) , this ) )
return Enumerable . Empty < ITemplate > ( ) ;
}
var name = element . Name . LocalName ;
if ( name . Equals ( "Templates" ) = = false & & name . Equals ( "Template" ) = = false )
{
throw new ArgumentException ( "The passed in XElement is not valid! It does not contain a root element called 'Templates' for multiple imports or 'Template' for a single import." ) ;
}
var templates = new List < ITemplate > ( ) ;
var templateElements = name . Equals ( "Templates" )
? ( from doc in element . Elements ( "Template" ) select doc ) . ToList ( )
: new List < XElement > { element } ;
var graph = new TopoGraph < string , TopoGraph . Node < string , XElement > > ( x = > x . Key , x = > x . Dependencies ) ;
foreach ( XElement tempElement in templateElements )
{
var dependencies = new List < string > ( ) ;
var elementCopy = tempElement ;
//Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting.
if ( string . IsNullOrEmpty ( ( string ) elementCopy . Element ( "Master" ) ) = = false & &
templateElements . Any ( x = > ( string ) x . Element ( "Alias" ) = = ( string ) elementCopy . Element ( "Master" ) ) )
{
dependencies . Add ( ( string ) elementCopy . Element ( "Master" ) ) ;
}
else if ( string . IsNullOrEmpty ( ( string ) elementCopy . Element ( "Master" ) ) = = false & &
templateElements . Any ( x = > ( string ) x . Element ( "Alias" ) = = ( string ) elementCopy . Element ( "Master" ) ) = = false )
{
2018-08-16 12:00:12 +01:00
_logger . Info < PackagingService > (
"Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored." ,
( string ) elementCopy . Element ( "Alias" ) ,
( string ) elementCopy . Element ( "Master" ) ) ;
2017-12-28 09:18:09 +01:00
}
graph . AddItem ( TopoGraph . CreateNode ( ( string ) elementCopy . Element ( "Alias" ) , elementCopy , dependencies ) ) ;
}
//Sort templates by dependencies to a potential master template
var sorted = graph . GetSortedItems ( ) ;
foreach ( var item in sorted )
{
var templateElement = item . Item ;
var templateName = templateElement . Element ( "Name" ) . Value ;
var alias = templateElement . Element ( "Alias" ) . Value ;
var design = templateElement . Element ( "Design" ) . Value ;
var masterElement = templateElement . Element ( "Master" ) ;
var isMasterPage = IsMasterPageSyntax ( design ) ;
var path = isMasterPage ? MasterpagePath ( alias ) : ViewPath ( alias ) ;
var existingTemplate = _fileService . GetTemplate ( alias ) as Template ;
var template = existingTemplate ? ? new Template ( templateName , alias ) ;
template . Content = design ;
if ( masterElement ! = null & & string . IsNullOrEmpty ( ( string ) masterElement ) = = false )
{
template . MasterTemplateAlias = masterElement . Value ;
var masterTemplate = templates . FirstOrDefault ( x = > x . Alias = = masterElement . Value ) ;
if ( masterTemplate ! = null )
template . MasterTemplateId = new Lazy < int > ( ( ) = > masterTemplate . Id ) ;
}
templates . Add ( template ) ;
}
if ( templates . Any ( ) )
_fileService . SaveTemplate ( templates , userId ) ;
if ( raiseEvents )
ImportedTemplate . RaiseEvent ( new ImportEventArgs < ITemplate > ( templates , element , false ) , this ) ;
return templates ;
}
2018-05-31 23:12:48 +10:00
public IEnumerable < IFile > ImportStylesheets ( XElement element , int userId = 0 , bool raiseEvents = true )
2017-12-28 09:18:09 +01:00
{
if ( raiseEvents )
{
if ( ImportingStylesheets . IsRaisedEventCancelled ( new ImportEventArgs < IFile > ( element ) , this ) )
return Enumerable . Empty < IFile > ( ) ;
}
IEnumerable < IFile > styleSheets = Enumerable . Empty < IFile > ( ) ;
if ( element . Elements ( ) . Any ( ) )
throw new NotImplementedException ( "This needs to be implimentet" ) ;
if ( raiseEvents )
ImportingStylesheets . RaiseEvent ( new ImportEventArgs < IFile > ( styleSheets , element , false ) , this ) ;
return styleSheets ;
}
private bool IsMasterPageSyntax ( string code )
{
return Regex . IsMatch ( code , @"<%@\s*Master" , RegexOptions . IgnoreCase ) | |
code . InvariantContains ( "<umbraco:Item" ) | | code . InvariantContains ( "<asp:" ) | | code . InvariantContains ( "<umbraco:Macro" ) ;
}
private string ViewPath ( string alias )
{
return SystemDirectories . MvcViews + "/" + alias . Replace ( " " , "" ) + ".cshtml" ;
}
private string MasterpagePath ( string alias )
{
return IOHelper . MapPath ( SystemDirectories . Masterpages + "/" + alias . Replace ( " " , "" ) + ".master" ) ;
}
/// <summary>
/// Exports a list of <see cref="ITemplate"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="templates">List of Templates to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ITemplate objects</returns>
public XElement Export ( IEnumerable < ITemplate > templates , bool raiseEvents = true )
{
var xml = new XElement ( "Templates" ) ;
foreach ( var item in templates )
{
xml . Add ( Export ( item , raiseEvents ) ) ;
}
return xml ;
}
/// <summary>
/// Exports a single <see cref="ITemplate"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="template">Template to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ITemplate object</returns>
public XElement Export ( ITemplate template , bool raiseEvents = true )
{
if ( raiseEvents )
{
if ( ExportingTemplate . IsRaisedEventCancelled ( new ExportEventArgs < ITemplate > ( template , "Template" ) , this ) )
return new XElement ( "Template" ) ;
}
var exporter = new EntityXmlSerializer ( ) ;
var xml = exporter . Serialize ( template ) ;
if ( raiseEvents )
ExportedTemplate . RaiseEvent ( new ExportEventArgs < ITemplate > ( template , xml , false ) , this ) ;
return xml ;
}
#endregion
#region Stylesheets
#endregion
#region Installation
internal IPackageInstallation PackageInstallation
{
private get { return _packageInstallation ? ? new PackageInstallation ( this , _macroService , _fileService , new PackageExtraction ( ) ) ; }
set { _packageInstallation = value ; }
}
2018-05-31 23:12:48 +10:00
internal InstallationSummary InstallPackage ( string packageFilePath , int userId = 0 , bool raiseEvents = false )
2017-12-28 09:18:09 +01:00
{
2018-03-22 17:41:13 +01:00
var metaData = GetPackageMetaData ( packageFilePath ) ;
2017-12-28 09:18:09 +01:00
if ( raiseEvents )
{
if ( ImportingPackage . IsRaisedEventCancelled ( new ImportPackageEventArgs < string > ( packageFilePath , metaData ) , this ) )
{
var initEmpty = new InstallationSummary ( ) . InitEmpty ( ) ;
initEmpty . MetaData = metaData ;
return initEmpty ;
}
}
var installationSummary = PackageInstallation . InstallPackage ( packageFilePath , userId ) ;
if ( raiseEvents )
{
2018-03-22 17:41:13 +01:00
ImportedPackage . RaiseEvent ( new ImportPackageEventArgs < InstallationSummary > ( installationSummary , metaData , false ) , this ) ;
2017-12-28 09:18:09 +01:00
}
return installationSummary ;
}
internal PreInstallWarnings GetPackageWarnings ( string packageFilePath )
{
return PackageInstallation . GetPreInstallWarnings ( packageFilePath ) ;
}
internal MetaData GetPackageMetaData ( string packageFilePath )
{
return PackageInstallation . GetMetaData ( packageFilePath ) ;
}
#endregion
#region Package Building
#endregion
/// <summary>
/// This method can be used to trigger the 'ImportedPackage' event when a package is installed by something else but this service.
/// </summary>
/// <param name="args"></param>
internal static void OnImportedPackage ( ImportPackageEventArgs < InstallationSummary > args )
{
ImportedPackage . RaiseEvent ( args , null ) ;
}
/// <summary>
/// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service.
/// </summary>
/// <param name="args"></param>
internal static void OnUninstalledPackage ( UninstallPackageEventArgs < UninstallationSummary > args )
{
UninstalledPackage . RaiseEvent ( args , null ) ;
}
#region Event Handlers
/// <summary>
/// Occurs before Importing Content
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IContent > > ImportingContent ;
/// <summary>
/// Occurs after Content is Imported and Saved
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IContent > > ImportedContent ;
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IContent > > ExportingContent ;
/// <summary>
/// Occurs after Content is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IContent > > ExportedContent ;
/// <summary>
/// Occurs before Exporting Media
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IMedia > > ExportingMedia ;
/// <summary>
/// Occurs after Media is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IMedia > > ExportedMedia ;
/// <summary>
/// Occurs before Importing ContentType
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IContentType > > ImportingContentType ;
/// <summary>
/// Occurs after ContentType is Imported and Saved
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IContentType > > ImportedContentType ;
/// <summary>
/// Occurs before Exporting ContentType
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IContentType > > ExportingContentType ;
/// <summary>
/// Occurs after ContentType is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IContentType > > ExportedContentType ;
/// <summary>
/// Occurs before Importing DataType
/// </summary>
2018-01-10 12:48:51 +01:00
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IDataType > > ImportingDataType ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Occurs after DataType is Imported and Saved
/// </summary>
2018-01-10 12:48:51 +01:00
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IDataType > > ImportedDataType ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Occurs before Exporting DataType
/// </summary>
2018-01-10 12:48:51 +01:00
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IDataType > > ExportingDataType ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Occurs after DataType is Exported to Xml
/// </summary>
2018-01-10 12:48:51 +01:00
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IDataType > > ExportedDataType ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Occurs before Importing DictionaryItem
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IDictionaryItem > > ImportingDictionaryItem ;
/// <summary>
/// Occurs after DictionaryItem is Imported and Saved
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IDictionaryItem > > ImportedDictionaryItem ;
/// <summary>
/// Occurs before Exporting DictionaryItem
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IDictionaryItem > > ExportingDictionaryItem ;
/// <summary>
/// Occurs after DictionaryItem is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IDictionaryItem > > ExportedDictionaryItem ;
/// <summary>
/// Occurs before Importing Macro
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IMacro > > ImportingMacro ;
/// <summary>
/// Occurs after Macro is Imported and Saved
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IMacro > > ImportedMacro ;
/// <summary>
/// Occurs before Exporting Macro
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IMacro > > ExportingMacro ;
/// <summary>
/// Occurs after Macro is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < IMacro > > ExportedMacro ;
/// <summary>
/// Occurs before Importing Language
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < ILanguage > > ImportingLanguage ;
/// <summary>
/// Occurs after Language is Imported and Saved
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < ILanguage > > ImportedLanguage ;
/// <summary>
/// Occurs before Exporting Language
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < ILanguage > > ExportingLanguage ;
/// <summary>
/// Occurs after Language is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < ILanguage > > ExportedLanguage ;
/// <summary>
/// Occurs before Importing Template
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < ITemplate > > ImportingTemplate ;
/// <summary>
/// Occurs before Importing Stylesheets
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < IFile > > ImportingStylesheets ;
/// <summary>
/// Occurs after Template is Imported and Saved
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportEventArgs < ITemplate > > ImportedTemplate ;
/// <summary>
/// Occurs before Exporting Template
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < ITemplate > > ExportingTemplate ;
/// <summary>
/// Occurs after Template is Exported to Xml
/// </summary>
public static event TypedEventHandler < IPackagingService , ExportEventArgs < ITemplate > > ExportedTemplate ;
/// <summary>
/// Occurs before Importing umbraco package
/// </summary>
internal static event TypedEventHandler < IPackagingService , ImportPackageEventArgs < string > > ImportingPackage ;
/// <summary>
/// Occurs after a package is imported
/// </summary>
public static event TypedEventHandler < IPackagingService , ImportPackageEventArgs < InstallationSummary > > ImportedPackage ;
/// <summary>
/// Occurs after a package is uninstalled
/// </summary>
public static event TypedEventHandler < IPackagingService , UninstallPackageEventArgs < UninstallationSummary > > UninstalledPackage ;
#endregion
}
}