using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Web; using System.Xml.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.DI; using Umbraco.Core.IO; using Umbraco.Core.Media; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; namespace Umbraco.Core.Models { public static class ContentExtensions { #region IContent /// /// Returns true if this entity was just published as part of a recent save operation (i.e. it wasn't previously published) /// /// /// /// /// This is helpful for determining if the published event will execute during the saved event for a content item. /// internal static bool JustPublished(this IContent entity) { var dirty = (IRememberBeingDirty)entity; return dirty.WasPropertyDirty("Published") && entity.Published; } /// /// Determines whether the content should be persisted. /// /// The content. /// True is the content should be persisted, otherwise false. /// See remarks in overload. internal static bool RequiresSaving(this IContent entity) { return RequiresSaving(entity, ((Content) entity).PublishedState); } /// /// Determines whether the content should be persisted. /// /// The content. /// The published state of the content. /// True is the content should be persisted, otherwise false. /// /// This is called by the repository when persisting an existing content, to /// figure out whether it needs to persist the content at all. /// internal static bool RequiresSaving(this IContent entity, PublishedState publishedState) { // note: publishedState is always the entity's PublishedState except for tests var content = (Content) entity; var userPropertyChanged = content.IsAnyUserPropertyDirty(); var dirtyProps = content.GetDirtyProperties(); //var contentPropertyChanged = content.IsEntityDirty(); var contentPropertyChangedExceptPublished = dirtyProps.Any(x => x != "Published"); // we don't want to save (write to DB) if we are "saving" either a published content // (.Saving) or an unpublished content (.Unpublished) and strictly nothing has changed var noSave = (publishedState == PublishedState.Saving || publishedState == PublishedState.Unpublished) && userPropertyChanged == false && contentPropertyChangedExceptPublished == false; return noSave == false; } /// /// Determines whether a new version of the content should be created. /// /// The content. /// True if a new version should be created, otherwise false. /// See remarks in overload. internal static bool RequiresNewVersion(this IContent entity) { return RequiresNewVersion(entity, ((Content) entity).PublishedState); } /// /// Determines whether a new version of the content should be created. /// /// The content. /// The published state of the content. /// True if a new version should be created, otherwise false. /// /// This is called by the repository when persisting an existing content, to /// figure out whether it needs to create a new version for that content. /// A new version needs to be created when: /// * The publish status is changed /// * The language is changed /// * A content property is changed (? why ?) /// * The item is already published and is being published again and any property value is changed (to enable a rollback) /// internal static bool RequiresNewVersion(this IContent entity, PublishedState publishedState) { // note: publishedState is always the entity's PublishedState except for tests // read // http://issues.umbraco.org/issue/U4-2589 (save & publish & creating new versions) // http://issues.umbraco.org/issue/U4-3404 (pressing preview does save then preview) // http://issues.umbraco.org/issue/U4-5510 (previewing & creating new versions) // // slightly modifying the rules to make more sense (marked with CHANGE) // but should respect the result of the discussions in those issues // figure out whether .Language has changed // this language stuff was an old POC and should be removed var hasLanguageChanged = entity.IsPropertyDirty("Language"); if (hasLanguageChanged) return true; // language change => new version var content = (Content) entity; //var contentPropertyChanged = content2.IsEntityDirty(); var userPropertyChanged = content.IsAnyUserPropertyDirty(); var dirtyProps = content.GetDirtyProperties(); var contentPropertyChangedExceptPublished = dirtyProps.Any(x => x != "Published"); var wasPublished = content.PublishedOriginal; switch (publishedState) { case PublishedState.Publishing: // changed state, publishing either a published or an unpublished version: // DO create a new (published) version IF it was published already AND // anything has changed, else can reuse the current version return (contentPropertyChangedExceptPublished || userPropertyChanged) && wasPublished; case PublishedState.Unpublishing: // changed state, unpublishing a published version: // DO create a new (draft) version and preserve the (formerly) published // version for rollback purposes IF the version that's being saved is the // published version, else it's a draft that we can reuse return wasPublished; case PublishedState.Saving: // changed state, saving a published version: // DO create a new (draft) version and preserve the published version IF // anything has changed, else do NOT create a new version (pointless) return contentPropertyChangedExceptPublished || userPropertyChanged; case PublishedState.Published: // unchanged state, saving a published version: // (can happen eg when moving content, never otherwise) // do NOT create a new version as we're just saving after operations (eg // move) that cannot be rolled back anyway - ensure that's really it if (userPropertyChanged) throw new InvalidOperationException("Invalid PublishedState \"Published\" with user property changes."); return false; case PublishedState.Unpublished: // unchanged state, saving an unpublished version: // do NOT create a new version for user property changes, // BUT create a new version in case of content property changes, for // rollback purposes return contentPropertyChangedExceptPublished; default: throw new NotSupportedException(); } } /// /// Determines whether the database published flag should be cleared for versions /// other than this content version. /// /// The content. /// True if the published flag should be cleared, otherwise false. /// See remarks in overload. internal static bool RequiresClearPublishedFlag(this IContent entity) { var publishedState = ((Content) entity).PublishedState; var requiresNewVersion = entity.RequiresNewVersion(publishedState); return entity.RequiresClearPublishedFlag(publishedState, requiresNewVersion); } /// /// Determines whether the database published flag should be cleared for versions /// other than this content version. /// /// The content. /// The published state of the content. /// Indicates whether the content is a new version. /// True if the published flag should be cleared, otherwise false. /// /// This is called by the repository when persisting an existing content, to /// figure out whether it needs to clear the published flag for other versions. /// internal static bool RequiresClearPublishedFlag(this IContent entity, PublishedState publishedState, bool isNewVersion) { // note: publishedState is always the entity's PublishedState except for tests // new, published version => everything else must be cleared if (isNewVersion && entity.Published) return true; // if that entity was published then that entity has the flag and // it does not need to be cleared for other versions // NOT TRUE when unpublishing we create a NEW version //var wasPublished = ((Content)entity).PublishedOriginal; //if (wasPublished) // return false; // clear whenever we are publishing or unpublishing // publishing: because there might be a previously published version, which needs to be cleared // unpublishing: same - we might be a saved version, not the published one, which needs to be cleared return publishedState == PublishedState.Publishing || publishedState == PublishedState.Unpublishing; } /// /// Returns a list of the current contents ancestors, not including the content itself. /// /// Current content /// /// An enumerable list of objects public static IEnumerable Ancestors(this IContent content, IContentService contentService) { return contentService.GetAncestors(content); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable Ancestors(this IContent content) { return Current.Services.ContentService.GetAncestors(content); } /// /// Returns a list of the current contents children. /// /// Current content /// /// An enumerable list of objects public static IEnumerable Children(this IContent content, IContentService contentService) { return contentService.GetChildren(content.Id); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable Children(this IContent content) { return Current.Services.ContentService.GetChildren(content.Id); } /// /// Returns a list of the current contents descendants, not including the content itself. /// /// Current content /// /// An enumerable list of objects public static IEnumerable Descendants(this IContent content, IContentService contentService) { return contentService.GetDescendants(content); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable Descendants(this IContent content) { return Current.Services.ContentService.GetDescendants(content); } /// /// Returns the parent of the current content. /// /// Current content /// /// An object public static IContent Parent(this IContent content, IContentService contentService) { return contentService.GetById(content.ParentId); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IContent Parent(this IContent content) { return Current.Services.ContentService.GetById(content.ParentId); } #endregion #region IMedia /// /// Returns a list of the current medias ancestors, not including the media itself. /// /// Current media /// /// An enumerable list of objects public static IEnumerable Ancestors(this IMedia media, IMediaService mediaService) { return mediaService.GetAncestors(media); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable Ancestors(this IMedia media) { return Current.Services.MediaService.GetAncestors(media); } /// /// Returns a list of the current medias children. /// /// Current media /// /// An enumerable list of objects public static IEnumerable Children(this IMedia media, IMediaService mediaService) { return mediaService.GetChildren(media.Id); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable Children(this IMedia media) { return Current.Services.MediaService.GetChildren(media.Id); } /// /// Returns a list of the current medias descendants, not including the media itself. /// /// Current media /// /// An enumerable list of objects public static IEnumerable Descendants(this IMedia media, IMediaService mediaService) { return mediaService.GetDescendants(media); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IEnumerable Descendants(this IMedia media) { return Current.Services.MediaService.GetDescendants(media); } /// /// Returns the parent of the current media. /// /// Current media /// /// An object public static IMedia Parent(this IMedia media, IMediaService mediaService) { return mediaService.GetById(media.ParentId); } [Obsolete("Use the overload with the service reference instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static IMedia Parent(this IMedia media) { return Current.Services.MediaService.GetById(media.ParentId); } #endregion /// /// Removes characters that are not valide XML characters from all entity properties /// of type string. See: http://stackoverflow.com/a/961504/5018 /// /// /// /// If this is not done then the xml cache can get corrupt and it will throw YSODs upon reading it. /// /// public static void SanitizeEntityPropertiesForXmlStorage(this IContentBase entity) { entity.Name = entity.Name.ToValidXmlString(); foreach (var property in entity.Properties) { if (property.Value is string) { var value = (string)property.Value; property.Value = value.ToValidXmlString(); } } } /// /// Checks if the IContentBase has children /// /// /// /// /// /// This is a bit of a hack because we need to type check! /// internal static bool HasChildren(IContentBase content, ServiceContext services) { if (content is IContent) { return services.ContentService.HasChildren(content.Id); } if (content is IMedia) { return services.MediaService.HasChildren(content.Id); } return false; } /// /// Returns the children for the content base item /// /// /// /// /// /// This is a bit of a hack because we need to type check! /// internal static IEnumerable Children(IContentBase content, ServiceContext services) { if (content is IContent) { return services.ContentService.GetChildren(content.Id); } if (content is IMedia) { return services.MediaService.GetChildren(content.Id); } return null; } /// /// Returns properties that do not belong to a group /// /// /// public static IEnumerable GetNonGroupedProperties(this IContentBase content) { var propertyIdsInTabs = content.PropertyGroups.SelectMany(pg => pg.PropertyTypes); return content.Properties .Where(property => propertyIdsInTabs.Contains(property.PropertyType) == false) .OrderBy(x => x.PropertyType.SortOrder); } /// /// Returns the Property object for the given property group /// /// /// /// public static IEnumerable GetPropertiesForGroup(this IContentBase content, PropertyGroup propertyGroup) { //get the properties for the current tab return content.Properties .Where(property => propertyGroup.PropertyTypes .Select(propertyType => propertyType.Id) .Contains(property.PropertyTypeId)) .OrderBy(x => x.PropertyType.SortOrder); } /// /// Set property values by alias with an annonymous object /// public static void PropertyValues(this IContent content, object value) { if (value == null) throw new Exception("No properties has been passed in"); var propertyInfos = value.GetType().GetProperties(); foreach (var propertyInfo in propertyInfos) { //Check if a PropertyType with alias exists thus being a valid property var propertyType = content.PropertyTypes.FirstOrDefault(x => x.Alias == propertyInfo.Name); if (propertyType == null) throw new Exception( string.Format( "The property alias {0} is not valid, because no PropertyType with this alias exists", propertyInfo.Name)); //Check if a Property with the alias already exists in the collection thus being updated or inserted var item = content.Properties.FirstOrDefault(x => x.Alias == propertyInfo.Name); if (item != null) { item.Value = propertyInfo.GetValue(value, null); //Update item with newly added value content.Properties.Add(item); } else { //Create new Property to add to collection var property = propertyType.CreatePropertyFromValue(propertyInfo.GetValue(value, null)); content.Properties.Add(property); } } } #region SetValue for setting file contents /// /// Sets and uploads the file from a HttpPostedFileBase object as the property value /// /// to add property value to /// Alias of the property to save the value on /// The containing the file that will be uploaded /// public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, IDataTypeService dataTypeService) { // Ensure we get the filename without the path in IE in intranet mode // http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie var fileName = value.FileName; if (fileName.LastIndexOf(@"\") > 0) fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1); var name = IOHelper.SafeFileName( fileName.Substring(fileName.LastIndexOf(IOHelper.DirSepChar) + 1, fileName.Length - fileName.LastIndexOf(IOHelper.DirSepChar) - 1) .ToLower()); if (string.IsNullOrEmpty(name) == false) SetFileOnContent(content, propertyTypeAlias, name, value.InputStream, dataTypeService); } [Obsolete("Use the overload with the IDataTypeService parameter instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value) { content.SetValue(propertyTypeAlias, value, Current.Services.DataTypeService); } /// /// Sets and uploads the file from a HttpPostedFile object as the property value /// /// to add property value to /// Alias of the property to save the value on /// The containing the file that will be uploaded /// public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value, IDataTypeService dataTypeService) { SetValue(content, propertyTypeAlias, new HttpPostedFileWrapper(value), dataTypeService); } [Obsolete("Use the overload with the IDataTypeService parameter instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value) { SetValue(content, propertyTypeAlias, (HttpPostedFileBase)new HttpPostedFileWrapper(value)); } /// /// Sets and uploads the file from a HttpPostedFileWrapper object as the property value /// /// to add property value to /// Alias of the property to save the value on /// The containing the file that will be uploaded [Obsolete("There is no reason for this overload since HttpPostedFileWrapper inherits from HttpPostedFileBase")] [EditorBrowsable(EditorBrowsableState.Never)] public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileWrapper value) { SetValue(content, propertyTypeAlias, (HttpPostedFileBase)value); } /// /// Sets and uploads the file from a as the property value /// /// to add property value to /// Alias of the property to save the value on /// Name of the file /// to save to disk /// public static void SetValue(this IContentBase content, string propertyTypeAlias, string fileName, Stream fileStream, IDataTypeService dataTypeService) { var name = IOHelper.SafeFileName(fileName); if (string.IsNullOrEmpty(name) == false && fileStream != null) SetFileOnContent(content, propertyTypeAlias, name, fileStream, dataTypeService); } [Obsolete("Use the overload with the IDataTypeService parameter instead")] [EditorBrowsable(EditorBrowsableState.Never)] public static void SetValue(this IContentBase content, string propertyTypeAlias, string fileName, Stream fileStream) { content.SetValue(propertyTypeAlias, fileName, fileStream, Current.Services.DataTypeService); } private static void SetFileOnContent(IContentBase content, string propertyTypeAlias, string filename, Stream fileStream, IDataTypeService dataTypeService) { var property = content.Properties.FirstOrDefault(x => x.Alias == propertyTypeAlias); if (property == null) return; //TODO: ALl of this naming logic needs to be put into the ImageHelper and then we need to change FileUploadPropertyValueEditor to do the same! var numberedFolder = MediaSubfolderCounter.Current.Increment(); var fileName = UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories ? Path.Combine(numberedFolder.ToString(CultureInfo.InvariantCulture), filename) : numberedFolder + "-" + filename; var extension = Path.GetExtension(filename).Substring(1).ToLowerInvariant(); //the file size is the length of the stream in bytes var fileSize = fileStream.Length; var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); fs.AddFile(fileName, fileStream); //Check if file supports resizing and create thumbnails var supportsResizing = UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.InvariantContains(extension); //the config section used to auto-fill properties IImagingAutoFillUploadField uploadFieldConfigNode = null; //Check for auto fill of additional properties if (UmbracoConfig.For.UmbracoSettings().Content.ImageAutoFillProperties != null) { uploadFieldConfigNode = UmbracoConfig.For.UmbracoSettings().Content.ImageAutoFillProperties .FirstOrDefault(x => x.Alias == propertyTypeAlias); } if (supportsResizing) { //get the original image from the original stream if (fileStream.CanSeek) fileStream.Seek(0, 0); using (var originalImage = Image.FromStream(fileStream)) { var additionalSizes = new List(); //Look up Prevalues for this upload datatype - if it is an upload datatype - get additional configured sizes if (property.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias) { //Get Prevalues by the DataType's Id: property.PropertyType.DataTypeId var values = dataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeDefinitionId); var thumbnailSizes = values.FirstOrDefault(); //Additional thumbnails configured as prevalues on the DataType if (thumbnailSizes != null) { foreach (var thumb in thumbnailSizes.Split(new[] { ";", "," }, StringSplitOptions.RemoveEmptyEntries)) { int thumbSize; if (thumb != "" && int.TryParse(thumb, out thumbSize)) { additionalSizes.Add(thumbSize); } } } } ImageHelper.GenerateMediaThumbnails(fs, fileName, extension, originalImage, additionalSizes); //while the image is still open, we'll check if we need to auto-populate the image properties if (uploadFieldConfigNode != null) { content.SetValue(uploadFieldConfigNode.WidthFieldAlias, originalImage.Width.ToString(CultureInfo.InvariantCulture)); content.SetValue(uploadFieldConfigNode.HeightFieldAlias, originalImage.Height.ToString(CultureInfo.InvariantCulture)); } } } //if auto-fill is true, then fill the remaining, non-image properties if (uploadFieldConfigNode != null) { content.SetValue(uploadFieldConfigNode.LengthFieldAlias, fileSize.ToString(CultureInfo.InvariantCulture)); content.SetValue(uploadFieldConfigNode.ExtensionFieldAlias, extension); } //Set the value of the property to that of the uploaded file's url property.Value = fs.GetUrl(fileName); } #endregion #region User/Profile methods [Obsolete("Use the overload that declares the IUserService to use")] [EditorBrowsable(EditorBrowsableState.Never)] public static IProfile GetCreatorProfile(this IMedia media) { return Current.Services.UserService.GetProfileById(media.CreatorId); } /// /// Gets the for the Creator of this media item. /// public static IProfile GetCreatorProfile(this IMedia media, IUserService userService) { return userService.GetProfileById(media.CreatorId); } [Obsolete("Use the overload that declares the IUserService to use")] [EditorBrowsable(EditorBrowsableState.Never)] public static IProfile GetCreatorProfile(this IContentBase content) { return Current.Services.UserService.GetProfileById(content.CreatorId); } /// /// Gets the for the Creator of this content item. /// public static IProfile GetCreatorProfile(this IContentBase content, IUserService userService) { return userService.GetProfileById(content.CreatorId); } [Obsolete("Use the overload that declares the IUserService to use")] [EditorBrowsable(EditorBrowsableState.Never)] public static IProfile GetWriterProfile(this IContent content) { return Current.Services.UserService.GetProfileById(content.WriterId); } /// /// Gets the for the Writer of this content. /// public static IProfile GetWriterProfile(this IContent content, IUserService userService) { return userService.GetProfileById(content.WriterId); } #endregion /// /// Checks whether an item has any published versions /// /// /// True if the content has any published versiom otherwise False [Obsolete("Use the HasPublishedVersion property.", false)] public static bool HasPublishedVersion(this IContent content) { return content.HasPublishedVersion; } #region Tag methods /// /// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags. /// /// The content item to assign the tags to /// The property alias to assign the tags to /// The tags to assign /// True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones /// The group/category to assign the tags, the default value is "default" /// public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable tags, bool replaceTags, string tagGroup = "default") { content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup); } /// /// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags. /// /// The content item to assign the tags to /// The tag storage type in cache (default is csv) /// The property alias to assign the tags to /// The tags to assign /// True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones /// The group/category to assign the tags, the default value is "default" /// public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable tags, bool replaceTags, string tagGroup = "default") { var property = content.Properties[propertyTypeAlias]; if (property == null) { throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias); } property.SetTags(storageType, propertyTypeAlias, tags, replaceTags, tagGroup); } internal static void SetTags(this Property property, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable tags, bool replaceTags, string tagGroup = "default") { if (property == null) throw new ArgumentNullException("property"); var trimmedTags = tags.Select(x => x.Trim()).ToArray(); property.TagSupport.Enable = true; property.TagSupport.Tags = trimmedTags.Select(x => new Tuple(x, tagGroup)); property.TagSupport.Behavior = replaceTags ? PropertyTagBehavior.Replace : PropertyTagBehavior.Merge; //ensure the property value is set to the same thing if (replaceTags) { switch (storageType) { case TagCacheStorageType.Csv: property.Value = string.Join(",", trimmedTags); break; case TagCacheStorageType.Json: //json array property.Value = JsonConvert.SerializeObject(trimmedTags); break; } } else { switch (storageType) { case TagCacheStorageType.Csv: var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()); property.Value = string.Join(",", trimmedTags.Union(currTags)); break; case TagCacheStorageType.Json: var currJson = JsonConvert.DeserializeObject(property.Value.ToString()); //need to append the new ones foreach (var tag in trimmedTags) { currJson.Add(tag); } //json array property.Value = JsonConvert.SerializeObject(currJson); break; } } } /// /// Remove any of the tags specified in the collection from the property if they are currently assigned. /// /// /// /// /// The group/category that the tags are currently assigned to, the default value is "default" public static void RemoveTags(this IContentBase content, string propertyTypeAlias, IEnumerable tags, string tagGroup = "default") { var property = content.Properties[propertyTypeAlias]; if (property == null) { throw new IndexOutOfRangeException("No property exists with name " + propertyTypeAlias); } var trimmedTags = tags.Select(x => x.Trim()).ToArray(); property.TagSupport.Behavior = PropertyTagBehavior.Remove; property.TagSupport.Enable = true; property.TagSupport.Tags = trimmedTags.Select(x => new Tuple(x, tagGroup)); //set the property value var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim()); property.Value = string.Join(",", currTags.Except(trimmedTags)); } #endregion #region XML methods /// /// Creates the full xml representation for the object and all of it's descendants /// /// to generate xml for /// /// Xml representation of the passed in internal static XElement ToDeepXml(this IContent content, IPackagingService packagingService) { return packagingService.Export(content, true, raiseEvents: false); } [Obsolete("Use the overload that declares the IPackagingService to use")] public static XElement ToXml(this IContent content) { return Current.Services.PackagingService.Export(content, raiseEvents: false); } /// /// Creates the xml representation for the object /// /// to generate xml for /// /// Xml representation of the passed in public static XElement ToXml(this IContent content, IPackagingService packagingService) { return packagingService.Export(content, raiseEvents: false); } [Obsolete("Use the overload that declares the IPackagingService to use")] public static XElement ToXml(this IMedia media) { return Current.Services.PackagingService.Export(media, raiseEvents: false); } /// /// Creates the xml representation for the object /// /// to generate xml for /// /// Xml representation of the passed in public static XElement ToXml(this IMedia media, IPackagingService packagingService) { return packagingService.Export(media, raiseEvents: false); } /// /// Creates the full xml representation for the object and all of it's descendants /// /// to generate xml for /// /// Xml representation of the passed in internal static XElement ToDeepXml(this IMedia media, IPackagingService packagingService) { return packagingService.Export(media, true, raiseEvents: false); } [Obsolete("Use the overload that declares the IPackagingService to use")] public static XElement ToXml(this IContent content, bool isPreview) { //TODO Do a proper implementation of this //If current IContent is published we should get latest unpublished version return content.ToXml(); } /// /// Creates the xml representation for the object /// /// to generate xml for /// /// Boolean indicating whether the xml should be generated for preview /// Xml representation of the passed in public static XElement ToXml(this IContent content, IPackagingService packagingService, bool isPreview) { //TODO Do a proper implementation of this //If current IContent is published we should get latest unpublished version return content.ToXml(packagingService); } [Obsolete("Use the overload that declares the IPackagingService to use")] public static XElement ToXml(this IMember member) { return ((PackagingService)(Current.Services.PackagingService)).Export(member); } /// /// Creates the xml representation for the object /// /// to generate xml for /// /// Xml representation of the passed in public static XElement ToXml(this IMember member, IPackagingService packagingService) { return ((PackagingService)(packagingService)).Export(member); } #endregion #region Dirty public static IEnumerable GetDirtyUserProperties(this IContentBase entity) { return entity.Properties.Where(x => x.IsDirty()).Select(x => x.Alias); } public static bool IsAnyUserPropertyDirty(this IContentBase entity) { return entity.Properties.Any(x => x.IsDirty()); } public static bool WasAnyUserPropertyDirty(this IContentBase entity) { return entity.Properties.Any(x => x.WasDirty()); } #endregion } }