2012-11-07 13:56:52 -01:00
using System ;
2012-10-03 12:51:32 -02:00
using System.Collections.Generic ;
2015-05-04 11:58:00 +10:00
using System.ComponentModel ;
2013-02-09 11:12:51 -01:00
using System.Globalization ;
2017-01-05 10:29:03 +11:00
using System.IO ;
2012-10-03 12:51:32 -02:00
using System.Linq ;
2013-03-19 23:46:13 +06:00
using System.Threading ;
2016-08-05 17:41:00 +02:00
using System.Xml ;
2017-02-03 12:23:32 +11:00
using System.Xml.Linq ;
2012-12-15 10:43:03 +05:00
using Umbraco.Core.Events ;
2012-10-29 14:28:16 -01:00
using Umbraco.Core.Logging ;
2012-10-03 12:51:32 -02:00
using Umbraco.Core.Models ;
2013-09-10 13:38:45 +10:00
using Umbraco.Core.Models.Membership ;
2012-12-14 15:19:54 -01:00
using Umbraco.Core.Models.Rdbms ;
2012-10-03 12:51:32 -02:00
using Umbraco.Core.Persistence ;
2015-01-13 13:33:39 +11:00
2014-08-12 08:15:43 +01:00
using Umbraco.Core.Persistence.DatabaseModelDefinitions ;
2012-10-10 12:13:23 -02:00
using Umbraco.Core.Persistence.Querying ;
2014-09-10 15:07:20 +10:00
using Umbraco.Core.Persistence.Repositories ;
2012-10-10 12:13:23 -02:00
using Umbraco.Core.Persistence.UnitOfWork ;
2012-11-06 10:47:14 -01:00
using Umbraco.Core.Publishing ;
2017-01-26 18:30:00 +01:00
using Umbraco.Core.Scoping ;
2012-10-03 12:51:32 -02:00
2012-11-12 07:40:11 -01:00
namespace Umbraco.Core.Services
2012-10-03 12:51:32 -02:00
{
2013-02-13 04:22:46 +06:00
/// <summary>
2013-04-23 12:49:33 -02:00
/// Represents the Content Service, which is an easy access to operations involving <see cref="IContent"/>
/// </summary>
2017-01-19 15:37:17 +11:00
public class ContentService : ScopeRepositoryService , IContentService , IContentServiceOperations
2013-04-23 12:49:33 -02:00
{
2017-01-25 16:49:44 +11:00
private readonly IPublishingStrategy2 _publishingStrategy ;
2014-04-28 17:28:40 +10:00
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer ( ) ;
private readonly IDataTypeService _dataTypeService ;
2014-09-30 18:46:02 +10:00
private readonly IUserService _userService ;
2014-04-28 17:28:40 +10:00
2017-01-26 18:30:00 +01:00
//Support recursive locks because some of the methods that require locking call other methods that require locking.
2013-03-19 23:46:13 +06:00
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
2013-04-23 12:49:33 -02:00
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim ( LockRecursionPolicy . SupportsRecursion ) ;
2012-12-11 23:19:07 +05:00
2015-07-23 20:04:40 +02:00
public ContentService (
2015-07-29 15:12:12 +02:00
IDatabaseUnitOfWorkProvider provider ,
RepositoryFactory repositoryFactory ,
2015-07-23 20:04:40 +02:00
ILogger logger ,
IEventMessagesFactory eventMessagesFactory ,
2015-07-29 15:12:12 +02:00
IDataTypeService dataTypeService ,
2015-07-23 20:04:40 +02:00
IUserService userService )
: base ( provider , repositoryFactory , logger , eventMessagesFactory )
2014-04-28 17:28:40 +10:00
{
2015-01-19 18:37:48 +11:00
if ( dataTypeService = = null ) throw new ArgumentNullException ( "dataTypeService" ) ;
2015-07-29 15:12:12 +02:00
if ( userService = = null ) throw new ArgumentNullException ( "userService" ) ;
2017-02-07 14:08:55 +11:00
_publishingStrategy = new PublishingStrategy ( eventMessagesFactory , logger ) ;
2014-04-28 17:28:40 +10:00
_dataTypeService = dataTypeService ;
2014-09-30 18:46:02 +10:00
_userService = userService ;
2013-04-23 12:49:33 -02:00
}
2012-10-10 12:13:23 -02:00
2016-10-27 17:50:52 +02:00
#region Static Queries
2016-12-08 12:48:44 +11:00
private IQuery < IContent > _notTrashedQuery ;
2016-10-27 17:50:52 +02:00
#endregion
2014-10-23 18:31:08 +10:00
public int CountPublished ( string contentTypeAlias = null )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-10-23 18:31:08 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-10-23 18:31:08 +10:00
return repository . CountPublished ( ) ;
}
}
2014-08-05 12:37:20 -06:00
public int Count ( string contentTypeAlias = null )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-08-20 17:01:12 +02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-08-05 12:37:20 -06:00
return repository . Count ( contentTypeAlias ) ;
}
}
public int CountChildren ( int parentId , string contentTypeAlias = null )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-08-05 12:37:20 -06:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-08-05 12:37:20 -06:00
return repository . CountChildren ( parentId , contentTypeAlias ) ;
}
}
public int CountDescendants ( int parentId , string contentTypeAlias = null )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-08-05 12:37:20 -06:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-08-05 12:37:20 -06:00
return repository . CountDescendants ( parentId , contentTypeAlias ) ;
}
}
2014-07-10 12:24:36 +10:00
/// <summary>
/// Used to bulk update the permissions set for a content item. This will replace all permissions
/// assigned to an entity with a list of user id & permission pairs.
/// </summary>
/// <param name="permissionSet"></param>
public void ReplaceContentPermissions ( EntityPermissionSet permissionSet )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2014-07-10 12:24:36 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-07-10 12:24:36 +10:00
repository . ReplaceContentPermissions ( permissionSet ) ;
2017-01-25 13:34:41 +01:00
uow . Commit ( ) ;
2014-07-10 12:24:36 +10:00
}
}
2014-02-17 15:33:17 +11:00
/// <summary>
/// Assigns a single permission to the current content item for the specified user ids
/// </summary>
/// <param name="entity"></param>
/// <param name="permission"></param>
/// <param name="userIds"></param>
2014-02-21 15:55:56 +11:00
public void AssignContentPermission ( IContent entity , char permission , IEnumerable < int > userIds )
2014-01-08 15:42:49 +11:00
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-09-10 13:38:45 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-02-21 15:55:56 +11:00
repository . AssignEntityPermission ( entity , permission , userIds ) ;
2017-01-25 13:34:41 +01:00
uow . Commit ( ) ;
2013-09-10 13:38:45 +10:00
}
}
2014-02-17 15:33:17 +11:00
/// <summary>
/// Gets the list of permissions for the content item
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
public IEnumerable < EntityPermission > GetPermissionsForEntity ( IContent content )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-02-17 15:33:17 +11:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-02-17 15:33:17 +11:00
return repository . GetPermissionsForEntity ( content . Id ) ;
}
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Creates an <see cref="IContent"/> object using the alias of the <see cref="IContentType"/>
2013-05-27 08:45:07 -02:00
/// that this Content should based on.
2013-04-23 12:49:33 -02:00
/// </summary>
2013-05-27 08:45:07 -02:00
/// <remarks>
/// Note that using this method will simply return a new IContent without any identity
/// as it has not yet been persisted. It is intended as a shortcut to creating new content objects
/// that does not invoke a save operation against the database.
/// </remarks>
2013-01-15 10:12:23 -01:00
/// <param name="name">Name of the Content object</param>
2013-04-23 12:49:33 -02:00
/// <param name="parentId">Id of Parent for the new Content</param>
/// <param name="contentTypeAlias">Alias of the <see cref="IContentType"/></param>
/// <param name="userId">Optional id of the user creating the content</param>
/// <returns><see cref="IContent"/></returns>
public IContent CreateContent ( string name , int parentId , string contentTypeAlias , int userId = 0 )
{
var contentType = FindContentTypeByAlias ( contentTypeAlias ) ;
2013-05-27 08:45:07 -02:00
var content = new Content ( name , parentId , contentType ) ;
2015-11-18 15:52:32 +01:00
var parent = GetById ( content . ParentId ) ;
content . Path = string . Concat ( parent . IfNotNull ( x = > x . Path , content . ParentId . ToString ( ) ) , "," , content . Id ) ;
2017-01-27 11:42:25 +01:00
// fixme - why are we creating a UOW here and not a SCOPE FFS!
using ( var uow = UowProvider . GetUnitOfWork ( /*commit: true*/ ) ) // FIXME + we are WRITING audit info FFS!
2013-04-23 12:49:33 -02:00
{
2017-01-26 18:30:00 +01:00
// fixme
2017-01-27 14:12:53 +01:00
if ( uow . Events . DispatchCancelable ( Creating , this , new NewEventArgs < IContent > ( content , contentTypeAlias , parentId ) ) )
2017-01-26 18:30:00 +01:00
//if (Creating.IsRaisedEventCancelled(new NewEventArgs<IContent>(content, contentTypeAlias, parentId), this, uow.Events))
{
content . WasCancelled = true ;
return content ;
}
2012-12-15 11:04:03 -01:00
2017-01-26 18:30:00 +01:00
content . CreatorId = userId ;
content . WriterId = userId ;
2012-12-15 11:04:03 -01:00
2017-01-26 18:30:00 +01:00
// fixme
uow . Events . Dispatch ( Created , this , new NewEventArgs < IContent > ( content , false , contentTypeAlias , parentId ) ) ;
//Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId), this, uow.Events);
2012-12-21 04:59:51 +05:00
2017-01-27 11:42:25 +01:00
// fixme
2017-01-25 13:34:41 +01:00
var auditRepo = RepositoryFactory . CreateAuditRepository ( uow ) ;
2015-01-19 15:12:34 +11:00
auditRepo . AddOrUpdate ( new AuditItem ( content . Id , string . Format ( "Content '{0}' was created" , name ) , AuditType . New , content . CreatorId ) ) ;
uow . Commit ( ) ;
}
2017-02-03 12:23:32 +11:00
2017-01-27 11:42:25 +01:00
// fixme duplicate + NOOOOO not another UOW!
//Audit(AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id);
2012-12-15 11:04:03 -01:00
2013-04-23 12:49:33 -02:00
return content ;
}
2012-12-11 23:19:07 +05:00
2013-01-29 12:45:42 -01:00
/// <summary>
/// Creates an <see cref="IContent"/> object using the alias of the <see cref="IContentType"/>
2013-05-27 08:45:07 -02:00
/// that this Content should based on.
2013-01-29 12:45:42 -01:00
/// </summary>
2013-05-27 08:45:07 -02:00
/// <remarks>
/// Note that using this method will simply return a new IContent without any identity
/// as it has not yet been persisted. It is intended as a shortcut to creating new content objects
/// that does not invoke a save operation against the database.
/// </remarks>
2013-01-29 12:45:42 -01:00
/// <param name="name">Name of the Content object</param>
/// <param name="parent">Parent <see cref="IContent"/> object for the new Content</param>
/// <param name="contentTypeAlias">Alias of the <see cref="IContentType"/></param>
/// <param name="userId">Optional id of the user creating the content</param>
/// <returns><see cref="IContent"/></returns>
public IContent CreateContent ( string name , IContent parent , string contentTypeAlias , int userId = 0 )
{
2015-11-18 15:52:32 +01:00
if ( parent = = null ) throw new ArgumentNullException ( "parent" ) ;
2013-03-22 12:13:06 -01:00
var contentType = FindContentTypeByAlias ( contentTypeAlias ) ;
var content = new Content ( name , parent , contentType ) ;
2015-11-17 16:49:01 +01:00
content . Path = string . Concat ( parent . Path , "," , content . Id ) ;
2013-01-29 12:45:42 -01:00
2017-01-19 17:18:06 +11:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-03-22 12:13:06 -01:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Creating , this , new NewEventArgs < IContent > ( content , contentTypeAlias , parent ) ) )
2017-01-19 17:18:06 +11:00
{
content . WasCancelled = true ;
return content ;
}
2013-01-29 12:45:42 -01:00
2017-01-19 17:18:06 +11:00
content . CreatorId = userId ;
content . WriterId = userId ;
2017-02-03 12:23:32 +11:00
2017-01-27 14:12:53 +01:00
uow . Events . Dispatch ( Created , this , new NewEventArgs < IContent > ( content , false , contentTypeAlias , parent ) ) ;
2017-01-23 22:16:27 +11:00
}
2013-01-29 12:45:42 -01:00
2017-01-27 11:42:25 +01:00
// MOVE into the UOW + COMMIT the f*cking thing
2017-01-23 22:16:27 +11:00
Audit ( AuditType . New , string . Format ( "Content '{0}' was created" , name ) , content . CreatorId , content . Id ) ;
2013-01-29 12:45:42 -01:00
2017-01-23 22:16:27 +11:00
return content ;
2013-01-29 12:45:42 -01:00
}
2013-05-27 08:45:07 -02:00
/// <summary>
/// Creates and saves an <see cref="IContent"/> object using the alias of the <see cref="IContentType"/>
/// that this Content should based on.
/// </summary>
/// <remarks>
/// This method returns an <see cref="IContent"/> object that has been persisted to the database
/// and therefor has an identity.
/// </remarks>
/// <param name="name">Name of the Content object</param>
/// <param name="parentId">Id of Parent for the new Content</param>
/// <param name="contentTypeAlias">Alias of the <see cref="IContentType"/></param>
/// <param name="userId">Optional id of the user creating the content</param>
/// <returns><see cref="IContent"/></returns>
public IContent CreateContentWithIdentity ( string name , int parentId , string contentTypeAlias , int userId = 0 )
{
var contentType = FindContentTypeByAlias ( contentTypeAlias ) ;
2016-02-26 14:30:32 +00:00
var content = new Content ( name , parentId , contentType ) ;
2013-05-27 08:45:07 -02:00
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-05-27 08:45:07 -02:00
{
2017-01-26 18:30:00 +01:00
//NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found
// out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now.
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Creating , this , new NewEventArgs < IContent > ( content , contentTypeAlias , parentId ) ) )
2017-01-26 18:30:00 +01:00
{
content . WasCancelled = true ;
return content ;
}
2013-05-27 08:45:07 -02:00
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Saving , this , new SaveEventArgs < IContent > ( content ) ) )
2017-01-26 18:30:00 +01:00
{
content . WasCancelled = true ;
return content ;
}
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-05-27 08:45:07 -02:00
content . CreatorId = userId ;
content . WriterId = userId ;
repository . AddOrUpdate ( content ) ;
2014-04-29 11:11:48 +10:00
//Generate a new preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-05-27 08:45:07 -02:00
uow . Commit ( ) ;
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Saved , this , new SaveEventArgs < IContent > ( content , false ) ) ;
uow . Events . Dispatch ( Created , this , new NewEventArgs < IContent > ( content , false , contentTypeAlias , parentId ) ) ;
2017-01-19 17:18:06 +11:00
}
2013-05-27 08:45:07 -02:00
2015-01-19 15:12:34 +11:00
Audit ( AuditType . New , string . Format ( "Content '{0}' was created with Id {1}" , name , content . Id ) , content . CreatorId , content . Id ) ;
2013-05-27 08:45:07 -02:00
return content ;
}
/// <summary>
/// Creates and saves an <see cref="IContent"/> object using the alias of the <see cref="IContentType"/>
/// that this Content should based on.
/// </summary>
/// <remarks>
/// This method returns an <see cref="IContent"/> object that has been persisted to the database
/// and therefor has an identity.
/// </remarks>
/// <param name="name">Name of the Content object</param>
/// <param name="parent">Parent <see cref="IContent"/> object for the new Content</param>
/// <param name="contentTypeAlias">Alias of the <see cref="IContentType"/></param>
/// <param name="userId">Optional id of the user creating the content</param>
/// <returns><see cref="IContent"/></returns>
public IContent CreateContentWithIdentity ( string name , IContent parent , string contentTypeAlias , int userId = 0 )
{
2015-11-18 16:04:01 +01:00
if ( parent = = null ) throw new ArgumentNullException ( "parent" ) ;
2013-05-27 08:45:07 -02:00
var contentType = FindContentTypeByAlias ( contentTypeAlias ) ;
var content = new Content ( name , parent , contentType ) ;
2017-01-26 18:30:00 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-05-27 08:45:07 -02:00
{
2017-01-26 18:30:00 +01:00
//NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found
// out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now.
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Creating , this , new NewEventArgs < IContent > ( content , contentTypeAlias , parent ) ) )
2017-01-26 18:30:00 +01:00
{
content . WasCancelled = true ;
return content ;
}
2013-05-27 08:45:07 -02:00
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Saving , this , new SaveEventArgs < IContent > ( content ) ) )
2017-01-26 18:30:00 +01:00
{
content . WasCancelled = true ;
return content ;
}
2014-03-06 17:35:47 +11:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-05-27 08:45:07 -02:00
content . CreatorId = userId ;
content . WriterId = userId ;
repository . AddOrUpdate ( content ) ;
2014-04-29 11:11:48 +10:00
//Generate a new preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-05-27 08:45:07 -02:00
uow . Commit ( ) ;
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Saved , this , new SaveEventArgs < IContent > ( content , false ) ) ;
uow . Events . Dispatch ( Created , this , new NewEventArgs < IContent > ( content , false , contentTypeAlias , parent ) ) ;
2017-01-19 17:18:06 +11:00
}
2013-05-27 08:45:07 -02:00
2015-01-19 15:12:34 +11:00
Audit ( AuditType . New , string . Format ( "Content '{0}' was created with Id {1}" , name , content . Id ) , content . CreatorId , content . Id ) ;
2013-05-27 08:45:07 -02:00
return content ;
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Gets an <see cref="IContent"/> object by Id
/// </summary>
/// <param name="id">Id of the Content to retrieve</param>
/// <returns><see cref="IContent"/></returns>
public IContent GetById ( int id )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
return repository . Get ( id ) ;
}
}
2013-06-11 14:43:36 +02:00
/// <summary>
/// Gets an <see cref="IContent"/> object by Id
/// </summary>
/// <param name="ids">Ids of the Content to retrieve</param>
/// <returns><see cref="IContent"/></returns>
2013-10-09 15:21:01 +11:00
public IEnumerable < IContent > GetByIds ( IEnumerable < int > ids )
2013-06-11 14:43:36 +02:00
{
2016-10-05 11:20:48 +02:00
var idsArray = ids . ToArray ( ) ;
if ( idsArray . Length = = 0 ) return Enumerable . Empty < IContent > ( ) ;
2015-07-24 18:36:07 +02:00
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-06-11 14:43:36 +02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2016-10-05 11:20:48 +02:00
//ensure that the result has the order based on the ids passed in
var result = repository . GetAll ( idsArray ) ;
var content = result . ToDictionary ( x = > x . Id , x = > x ) ;
var sortedResult = idsArray . Select ( x = >
{
IContent c ;
return content . TryGetValue ( x , out c ) ? c : null ;
} ) . WhereNotNull ( ) ;
2017-01-25 13:34:41 +01:00
uow . Commit ( ) ;
2016-10-05 11:20:48 +02:00
return sortedResult ;
2013-06-11 14:43:36 +02:00
}
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Gets an <see cref="IContent"/> object by its 'UniqueId'
/// </summary>
/// <param name="key">Guid key of the Content to retrieve</param>
/// <returns><see cref="IContent"/></returns>
public IContent GetById ( Guid key )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
var query = Query < IContent > . Builder . Where ( x = > x . Key = = key ) ;
var contents = repository . GetByQuery ( query ) ;
return contents . SingleOrDefault ( ) ;
}
}
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by the Id of the <see cref="IContentType"/>
/// </summary>
/// <param name="id">Id of the <see cref="IContentType"/></param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetContentOfContentType ( int id )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
var query = Query < IContent > . Builder . Where ( x = > x . ContentTypeId = = id ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2013-04-23 12:49:33 -02:00
}
}
2013-07-29 15:49:56 +10:00
internal IEnumerable < IContent > GetPublishedContentOfContentType ( int id )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-07-29 15:49:56 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-07-29 15:49:56 +10:00
var query = Query < IContent > . Builder . Where ( x = > x . ContentTypeId = = id ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByPublishedVersion ( query ) ;
2013-07-29 15:49:56 +10:00
}
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Level
/// </summary>
/// <param name="level">The level to retrieve Content from</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetByLevel ( int level )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2017-01-23 22:16:27 +11:00
var query = Query < IContent > . Builder . Where ( x = > x . Level = = level & & x . Path . StartsWith ( Constants . System . RecycleBinContent . ToInvariantString ( ) ) = = false ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2013-04-23 12:49:33 -02:00
}
}
2012-12-11 23:19:07 +05:00
2012-12-17 13:37:36 -01:00
/// <summary>
/// Gets a specific version of an <see cref="IContent"/> item.
/// </summary>
/// <param name="versionId">Id of the version to retrieve</param>
/// <returns>An <see cref="IContent"/> item</returns>
public IContent GetByVersion ( Guid versionId )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2012-12-17 13:37:36 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2012-12-17 13:37:36 -01:00
return repository . GetByVersion ( versionId ) ;
}
}
2015-01-19 15:12:34 +11:00
2012-12-17 13:37:36 -01:00
/// <summary>
/// Gets a collection of an <see cref="IContent"/> objects versions by Id
/// </summary>
/// <param name="id"></param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetVersions ( int id )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2012-12-17 13:37:36 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2017-01-27 11:42:25 +01:00
return repository . GetAllVersions ( id ) ;
2012-12-17 13:37:36 -01:00
}
}
2016-10-05 18:30:03 +02:00
/// <summary>
/// Gets a list of all version Ids for the given content item ordered so latest is first
/// </summary>
/// <param name="id"></param>
/// <param name="maxRows">The maximum number of rows to return</param>
/// <returns></returns>
public IEnumerable < Guid > GetVersionIds ( int id , int maxRows )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2016-10-05 18:30:03 +02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2017-01-27 11:42:25 +01:00
return repository . GetVersionIds ( id , maxRows ) ;
2016-10-05 18:30:03 +02:00
}
}
2013-02-09 10:58:21 -01:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects, which are ancestors of the current content.
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> to retrieve ancestors for</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetAncestors ( int id )
{
var content = GetById ( id ) ;
return GetAncestors ( content ) ;
}
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects, which are ancestors of the current content.
/// </summary>
/// <param name="content"><see cref="IContent"/> to retrieve ancestors for</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
2014-01-09 10:33:35 +11:00
public IEnumerable < IContent > GetAncestors ( IContent content )
{
2015-10-29 12:05:46 +01:00
//null check otherwise we get exceptions
if ( content . Path . IsNullOrWhiteSpace ( ) ) return Enumerable . Empty < IContent > ( ) ;
2015-01-04 21:02:20 +00:00
var ids = content . Path . Split ( ',' ) . Where ( x = > x ! = Constants . System . Root . ToInvariantString ( ) & & x ! = content . Id . ToString ( CultureInfo . InvariantCulture ) ) . Select ( int . Parse ) . ToArray ( ) ;
2013-02-09 11:12:51 -01:00
if ( ids . Any ( ) = = false )
return new List < IContent > ( ) ;
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-02-09 10:58:21 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-02-09 10:58:21 -01:00
return repository . GetAll ( ids ) ;
}
2014-01-09 10:33:35 +11:00
}
2013-02-09 10:58:21 -01:00
2014-01-09 10:33:35 +11:00
/// <summary>
2013-04-23 12:49:33 -02:00
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetChildren ( int id )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
var query = Query < IContent > . Builder . Where ( x = > x . ParentId = = id ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) . OrderBy ( x = > x . SortOrder ) ;
2013-04-23 12:49:33 -02:00
}
}
2012-12-11 23:19:07 +05:00
2015-05-04 11:58:00 +10:00
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable < IContent > GetPagedChildren ( int id , int pageIndex , int pageSize , out int totalChildren ,
string orderBy , Direction orderDirection , string filter = "" )
{
2016-04-05 16:00:49 +02:00
long total ;
var result = GetPagedChildren ( id , Convert . ToInt64 ( pageIndex ) , pageSize , out total , orderBy , orderDirection , true , filter ) ;
totalChildren = Convert . ToInt32 ( total ) ;
return result ;
2015-05-04 11:58:00 +10:00
}
2016-04-06 13:23:39 +02:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
/// <param name="pageIndex">Page index (zero based)</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalChildren">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetPagedChildren ( int id , long pageIndex , int pageSize , out long totalChildren ,
string orderBy , Direction orderDirection , string filter = "" )
{
return GetPagedChildren ( id , pageIndex , pageSize , out totalChildren , orderBy , orderDirection , true , filter ) ;
}
2014-08-12 08:15:43 +01:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Children from</param>
2014-09-16 23:21:27 +10:00
/// <param name="pageIndex">Page index (zero based)</param>
2014-08-12 08:15:43 +01:00
/// <param name="pageSize">Page size</param>
2014-08-25 16:58:01 +10:00
/// <param name="totalChildren">Total records query would return without paging</param>
2014-08-12 08:15:43 +01:00
/// <param name="orderBy">Field to order by</param>
2014-08-25 16:58:01 +10:00
/// <param name="orderDirection">Direction to order by</param>
2014-12-13 22:00:32 +01:00
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
2014-08-13 20:02:51 +01:00
/// <param name="filter">Search text filter</param>
2014-08-12 08:15:43 +01:00
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
2015-05-04 11:58:00 +10:00
public IEnumerable < IContent > GetPagedChildren ( int id , long pageIndex , int pageSize , out long totalChildren ,
2016-04-06 13:23:39 +02:00
string orderBy , Direction orderDirection , bool orderBySystemField , string filter )
2014-08-12 08:15:43 +01:00
{
2015-03-24 12:30:28 +11:00
Mandate . ParameterCondition ( pageIndex > = 0 , "pageIndex" ) ;
2014-08-24 23:37:10 +02:00
Mandate . ParameterCondition ( pageSize > 0 , "pageSize" ) ;
2017-01-25 13:34:41 +01:00
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-08-12 08:15:43 +01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-01-19 15:12:34 +11:00
2014-09-30 15:13:10 +10:00
var query = Query < IContent > . Builder ;
2015-01-04 21:02:20 +00:00
//if the id is System Root, then just get all
if ( id ! = Constants . System . Root )
2014-09-30 15:13:10 +10:00
{
query . Where ( x = > x . ParentId = = id ) ;
}
2016-05-26 15:30:40 +02:00
IQuery < IContent > filterQuery = null ;
if ( filter . IsNullOrWhiteSpace ( ) = = false )
{
filterQuery = Query < IContent > . Builder . Where ( x = > x . Name . Contains ( filter ) ) ;
}
2017-01-27 11:42:25 +01:00
return repository . GetPagedResultsByQuery ( query , pageIndex , pageSize , out totalChildren , orderBy , orderDirection , orderBySystemField , filterQuery ) ;
2014-09-30 15:13:10 +10:00
}
}
2015-05-04 11:58:00 +10:00
[Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
2017-01-24 00:35:17 +11:00
public IEnumerable < IContent > GetPagedDescendants ( int id , int pageIndex , int pageSize , out int totalChildren , string orderBy = "path" , Direction orderDirection = Direction . Ascending , string filter = "" )
2015-05-04 11:58:00 +10:00
{
2016-04-05 16:00:49 +02:00
long total ;
var result = GetPagedDescendants ( id , Convert . ToInt64 ( pageIndex ) , pageSize , out total , orderBy , orderDirection , true , filter ) ;
totalChildren = Convert . ToInt32 ( total ) ;
return result ;
2015-05-04 11:58:00 +10:00
}
2016-04-06 13:23:39 +02:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Descendants from</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalChildren">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter">Search text filter</param>
2017-01-26 18:30:00 +01:00
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
2017-01-24 00:35:17 +11:00
public IEnumerable < IContent > GetPagedDescendants ( int id , long pageIndex , int pageSize , out long totalChildren , string orderBy = "path" , Direction orderDirection = Direction . Ascending , string filter = "" )
2016-04-06 13:23:39 +02:00
{
return GetPagedDescendants ( id , pageIndex , pageSize , out totalChildren , orderBy , orderDirection , true , filter ) ;
}
2014-09-30 15:13:10 +10:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Descendants from</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalChildren">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
2014-12-13 22:00:32 +01:00
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
2014-09-30 15:13:10 +10:00
/// <param name="filter">Search text filter</param>
2017-01-26 18:30:00 +01:00
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
2016-04-06 13:23:39 +02:00
public IEnumerable < IContent > GetPagedDescendants ( int id , long pageIndex , int pageSize , out long totalChildren , string orderBy , Direction orderDirection , bool orderBySystemField , string filter )
2014-09-30 15:13:10 +10:00
{
2015-03-24 12:30:28 +11:00
Mandate . ParameterCondition ( pageIndex > = 0 , "pageIndex" ) ;
2014-09-30 15:13:10 +10:00
Mandate . ParameterCondition ( pageSize > 0 , "pageSize" ) ;
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2014-09-30 15:13:10 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-09-30 15:13:10 +10:00
var query = Query < IContent > . Builder ;
2015-01-04 21:02:20 +00:00
//if the id is System Root, then just get all
if ( id ! = Constants . System . Root )
2014-09-30 15:13:10 +10:00
{
query . Where ( x = > x . Path . SqlContains ( string . Format ( ",{0}," , id ) , TextColumnType . NVarchar ) ) ;
}
2016-05-26 15:30:40 +02:00
IQuery < IContent > filterQuery = null ;
if ( filter . IsNullOrWhiteSpace ( ) = = false )
{
filterQuery = Query < IContent > . Builder . Where ( x = > x . Name . Contains ( filter ) ) ;
}
2017-01-27 11:42:25 +01:00
return repository . GetPagedResultsByQuery ( query , pageIndex , pageSize , out totalChildren , orderBy , orderDirection , orderBySystemField , filterQuery ) ;
2016-05-26 15:30:40 +02:00
}
}
2014-08-12 08:15:43 +01:00
2016-05-26 15:30:40 +02:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Descendants from</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalChildren">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="orderBySystemField">Flag to indicate when ordering by system field</param>
/// <param name="filter">Search filter</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetPagedDescendants ( int id , long pageIndex , int pageSize , out long totalChildren , string orderBy , Direction orderDirection , bool orderBySystemField , IQuery < IContent > filter )
{
Mandate . ParameterCondition ( pageIndex > = 0 , "pageIndex" ) ;
Mandate . ParameterCondition ( pageSize > 0 , "pageSize" ) ;
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2016-05-26 15:30:40 +02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2016-05-26 15:30:40 +02:00
var query = Query < IContent > . Builder ;
//if the id is System Root, then just get all
if ( id ! = Constants . System . Root )
{
query . Where ( x = > x . Path . SqlContains ( string . Format ( ",{0}," , id ) , TextColumnType . NVarchar ) ) ;
}
2017-01-27 11:42:25 +01:00
return repository . GetPagedResultsByQuery ( query , pageIndex , pageSize , out totalChildren , orderBy , orderDirection , orderBySystemField , filter ) ;
2014-08-12 08:15:43 +01:00
}
}
2012-12-14 15:19:54 -01:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by its name or partial name
/// </summary>
/// <param name="parentId">Id of the Parent to retrieve Children from</param>
/// <param name="name">Full or partial name of the children</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetChildrenByName ( int parentId , string name )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2012-12-14 15:19:54 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2012-12-14 15:19:54 -01:00
var query = Query < IContent > . Builder . Where ( x = > x . ParentId = = parentId & & x . Name . Contains ( name ) ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2012-12-14 15:19:54 -01:00
}
}
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="id">Id of the Parent to retrieve Descendants from</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetDescendants ( int id )
{
var content = GetById ( id ) ;
2014-09-26 11:19:54 +10:00
if ( content = = null )
{
return Enumerable . Empty < IContent > ( ) ;
}
2012-12-14 15:19:54 -01:00
return GetDescendants ( content ) ;
}
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects by Parent Id
/// </summary>
/// <param name="content"><see cref="IContent"/> item to retrieve Descendants from</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetDescendants ( IContent content )
{
2017-01-05 10:29:03 +11:00
//This is a check to ensure that the path is correct for this entity to avoid problems like: http://issues.umbraco.org/issue/U4-9336 due to data corruption
if ( content . ValidatePath ( ) = = false )
throw new InvalidDataException ( string . Format ( "The content item {0} has an invalid path: {1} with parentID: {2}" , content . Id , content . Path , content . ParentId ) ) ;
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2012-12-14 15:19:54 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-10-08 17:23:14 +11:00
var pathMatch = content . Path + "," ;
var query = Query < IContent > . Builder . Where ( x = > x . Path . StartsWith ( pathMatch ) & & x . Id ! = content . Id ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2012-12-14 15:19:54 -01:00
}
}
/// <summary>
2013-02-09 10:58:21 -01:00
/// Gets the parent of the current content as an <see cref="IContent"/> item.
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> to retrieve the parent from</param>
/// <returns>Parent <see cref="IContent"/> object</returns>
public IContent GetParent ( int id )
{
var content = GetById ( id ) ;
return GetParent ( content ) ;
}
/// <summary>
/// Gets the parent of the current content as an <see cref="IContent"/> item.
/// </summary>
/// <param name="content"><see cref="IContent"/> to retrieve the parent from</param>
/// <returns>Parent <see cref="IContent"/> object</returns>
public IContent GetParent ( IContent content )
{
2015-01-04 21:02:20 +00:00
if ( content . ParentId = = Constants . System . Root | | content . ParentId = = Constants . System . RecycleBinContent )
2013-02-09 11:12:51 -01:00
return null ;
2013-02-09 10:58:21 -01:00
return GetById ( content . ParentId ) ;
}
2014-01-09 10:33:35 +11:00
/// <summary>
2012-12-14 15:19:54 -01:00
/// Gets the published version of an <see cref="IContent"/> item
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> to retrieve version from</param>
/// <returns>An <see cref="IContent"/> item</returns>
public IContent GetPublishedVersion ( int id )
{
2012-12-15 11:04:03 -01:00
var version = GetVersions ( id ) ;
return version . FirstOrDefault ( x = > x . Published = = true ) ;
2012-12-14 15:19:54 -01:00
}
2015-02-10 15:28:48 +01:00
/// <summary>
/// Gets the published version of a <see cref="IContent"/> item.
/// </summary>
/// <param name="content">The content item.</param>
/// <returns>The published version, if any; otherwise, null.</returns>
public IContent GetPublishedVersion ( IContent content )
{
2015-02-10 19:15:23 +01:00
if ( content . Published ) return content ;
2015-02-10 15:28:48 +01:00
return content . HasPublishedVersion
? GetByVersion ( content . PublishedVersionGuid )
: null ;
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects, which reside at the first level / root
/// </summary>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetRootContent ( )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-01-04 21:02:20 +00:00
var query = Query < IContent > . Builder . Where ( x = > x . ParentId = = Constants . System . Root ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2013-04-23 12:49:33 -02:00
}
}
2013-07-29 15:49:56 +10:00
/// <summary>
/// Gets all published content items
/// </summary>
/// <returns></returns>
internal IEnumerable < IContent > GetAllPublished ( )
{
2016-12-08 12:48:44 +11:00
//create it once if it is needed (no need for locking here)
if ( _notTrashedQuery = = null )
{
_notTrashedQuery = Query < IContent > . Builder . Where ( x = > x . Trashed = = false ) ;
}
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-07-29 15:49:56 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2016-10-27 17:50:52 +02:00
return repository . GetByPublishedVersion ( _notTrashedQuery ) ;
2013-07-29 15:49:56 +10:00
}
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects, which has an expiration date less than or equal to today.
/// </summary>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetContentForExpiration ( )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
var query = Query < IContent > . Builder . Where ( x = > x . Published & & x . ExpireDate < = DateTime . Now ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2013-04-23 12:49:33 -02:00
}
}
/// <summary>
/// Gets a collection of <see cref="IContent"/> objects, which has a release date less than or equal to today.
/// </summary>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetContentForRelease ( )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
var query = Query < IContent > . Builder . Where ( x = > x . Published = = false & & x . ReleaseDate < = DateTime . Now ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2013-04-23 12:49:33 -02:00
}
}
/// <summary>
/// Gets a collection of an <see cref="IContent"/> objects, which resides in the Recycle Bin
/// </summary>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable < IContent > GetContentInRecycleBin ( )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-01-04 21:02:20 +00:00
var query = Query < IContent > . Builder . Where ( x = > x . Path . Contains ( Constants . System . RecycleBinContent . ToInvariantString ( ) ) ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByQuery ( query ) ;
2013-04-23 12:49:33 -02:00
}
}
2012-12-11 23:19:07 +05:00
2015-07-29 15:12:12 +02:00
2015-07-24 11:44:09 +02:00
2012-12-14 15:19:54 -01:00
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any children
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/></param>
/// <returns>True if the content has any children otherwise False</returns>
public bool HasChildren ( int id )
2013-08-06 18:42:36 +10:00
{
return CountChildren ( id ) > 0 ;
}
internal int CountChildren ( int id )
2012-12-14 15:19:54 -01:00
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2012-12-14 15:19:54 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2012-12-14 15:19:54 -01:00
var query = Query < IContent > . Builder . Where ( x = > x . ParentId = = id ) ;
2017-01-27 11:42:25 +01:00
return repository . Count ( query ) ;
2012-12-14 15:19:54 -01:00
}
}
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any published versions
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/></param>
/// <returns>True if the content has any published version otherwise False</returns>
public bool HasPublishedVersion ( int id )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2012-12-14 15:19:54 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-01-29 08:04:27 -01:00
var query = Query < IContent > . Builder . Where ( x = > x . Published = = true & & x . Id = = id & & x . Trashed = = false ) ;
2017-01-27 11:42:25 +01:00
return repository . Count ( query ) > 0 ;
2012-12-14 15:19:54 -01:00
}
}
2012-12-18 11:46:05 -01:00
/// <summary>
/// Checks if the passed in <see cref="IContent"/> can be published based on the anscestors publish state.
/// </summary>
/// <param name="content"><see cref="IContent"/> to check if anscestors are published</param>
/// <returns>True if the Content can be published, otherwise False</returns>
public bool IsPublishable ( IContent content )
{
//If the passed in content has yet to be saved we "fallback" to checking the Parent
//because if the Parent is publishable then the current content can be Saved and Published
if ( content . HasIdentity = = false )
{
IContent parent = GetById ( content . ParentId ) ;
return IsPublishable ( parent , true ) ;
}
return IsPublishable ( content , false ) ;
}
2013-03-19 23:46:13 +06:00
/// <summary>
2017-01-26 18:30:00 +01:00
/// This will rebuild the xml structures for content in the database.
2013-03-20 00:19:36 +06:00
/// </summary>
2013-03-20 20:53:12 +06:00
/// <param name="userId">This is not used for anything</param>
2013-03-20 00:19:36 +06:00
/// <returns>True if publishing succeeded, otherwise False</returns>
2013-03-19 23:46:13 +06:00
/// <remarks>
2017-01-26 18:30:00 +01:00
/// This is used for when a document type alias or a document type property is changed, the xml will need to
2013-03-19 23:46:13 +06:00
/// be regenerated.
/// </remarks>
2013-03-20 00:19:36 +06:00
public bool RePublishAll ( int userId = 0 )
{
2013-03-19 23:46:13 +06:00
try
{
2013-04-25 22:13:05 -10:00
RebuildXmlStructures ( ) ;
2013-03-19 23:46:13 +06:00
return true ;
}
catch ( Exception ex )
{
2015-01-19 18:37:48 +11:00
Logger . Error < ContentService > ( "An error occurred executing RePublishAll" , ex ) ;
2013-03-19 23:46:13 +06:00
return false ;
2014-01-09 10:33:35 +11:00
}
2013-03-20 00:19:36 +06:00
}
2012-12-11 23:19:07 +05:00
2013-03-20 20:53:12 +06:00
/// <summary>
2017-01-26 18:30:00 +01:00
/// This will rebuild the xml structures for content in the database.
2013-03-20 20:53:12 +06:00
/// </summary>
/// <param name="contentTypeIds">
/// If specified will only rebuild the xml for the content type's specified, otherwise will update the structure
/// for all published content.
/// </param>
internal void RePublishAll ( params int [ ] contentTypeIds )
{
try
{
2013-04-25 22:13:05 -10:00
RebuildXmlStructures ( contentTypeIds ) ;
2013-03-20 20:53:12 +06:00
}
catch ( Exception ex )
{
2015-01-19 18:37:48 +11:00
Logger . Error < ContentService > ( "An error occurred executing RePublishAll" , ex ) ;
2014-01-09 10:33:35 +11:00
}
2013-03-20 20:53:12 +06:00
}
/// <summary>
2012-12-14 15:19:54 -01:00
/// Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
2013-01-25 08:58:21 -01:00
public bool Publish ( IContent content , int userId = 0 )
2012-12-14 15:19:54 -01:00
{
2013-02-11 20:07:23 +06:00
var result = SaveAndPublishDo ( content , userId ) ;
2015-01-19 18:37:48 +11:00
Logger . Info < ContentService > ( "Call was made to ContentService.Publish, use PublishWithStatus instead since that method will provide more detailed information on the outcome" ) ;
2014-08-20 17:01:12 +02:00
return result . Success ;
2012-12-14 15:19:54 -01:00
}
2015-07-29 15:12:12 +02:00
/// <summary>
/// Publishes a <see cref="IContent"/> object and all its children
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish along with its children</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="includeUnpublished"></param>
/// <returns>The list of statuses for all published items</returns>
IEnumerable < Attempt < PublishStatus > > IContentServiceOperations . PublishWithChildren ( IContent content , int userId , bool includeUnpublished )
{
return PublishWithChildrenDo ( content , userId , includeUnpublished ) ;
}
/// <summary>
/// Saves and Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save and publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
Attempt < PublishStatus > IContentServiceOperations . SaveAndPublish ( IContent content , int userId , bool raiseEvents )
{
return SaveAndPublishDo ( content , userId , raiseEvents ) ;
}
/// <summary>
/// Deletes an <see cref="IContent"/> object by moving it to the Recycle Bin
/// </summary>
/// <remarks>Move an item to the Recycle Bin will result in the item being unpublished</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
Attempt < OperationStatus > IContentServiceOperations . MoveToRecycleBin ( IContent content , int userId )
{
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2015-07-29 15:12:12 +02:00
using ( new WriteLock ( Locker ) )
{
2017-01-26 18:30:00 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
{
//Hack: this ensures that the entity's path is valid and if not it fixes/persists it
//see: http://issues.umbraco.org/issue/U4-9336
content . EnsureValidPath ( Logger , entity = > GetById ( entity . ParentId ) , QuickUpdate ) ;
var originalPath = content . Path ;
2017-02-07 14:08:55 +11:00
if ( uow . Events . DispatchCancelable ( Trashing , this , new MoveEventArgs < IContent > ( evtMsgs , new MoveEventInfo < IContent > ( content , originalPath , Constants . System . RecycleBinContent ) ) , "Trashing" ) )
2017-01-26 18:30:00 +01:00
{
return OperationStatus . Cancelled ( evtMsgs ) ;
}
var moveInfo = new List < MoveEventInfo < IContent > >
{
new MoveEventInfo < IContent > ( content , originalPath , Constants . System . RecycleBinContent )
} ;
//Make sure that published content is unpublished before being moved to the Recycle Bin
if ( HasPublishedVersion ( content . Id ) )
{
//TODO: this shouldn't be a 'sub operation', and if it needs to be it cannot raise events and cannot be cancelled!
UnPublish ( content , userId ) ;
}
//Unpublish descendents of the content item that is being moved to trash
var descendants = GetDescendants ( content ) . OrderBy ( x = > x . Level ) . ToList ( ) ;
foreach ( var descendant in descendants )
{
//TODO: this shouldn't be a 'sub operation', and if it needs to be it cannot raise events and cannot be cancelled!
UnPublish ( descendant , userId ) ;
}
2015-07-29 15:12:12 +02:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-07-29 15:12:12 +02:00
content . WriterId = userId ;
content . ChangeTrashedState ( true ) ;
repository . AddOrUpdate ( content ) ;
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
foreach ( var descendant in descendants )
{
moveInfo . Add ( new MoveEventInfo < IContent > ( descendant , descendant . Path , descendant . ParentId ) ) ;
descendant . WriterId = userId ;
descendant . ChangeTrashedState ( true , descendant . ParentId ) ;
repository . AddOrUpdate ( descendant ) ;
}
2017-02-07 14:08:55 +11:00
uow . Commit ( ) ;
uow . Events . Dispatch ( Trashed , this , new MoveEventArgs < IContent > ( false , evtMsgs , moveInfo . ToArray ( ) ) , "Trashed" ) ;
2017-01-26 18:30:00 +01:00
// fixme just writing to DB, no need for an event, ok here
Audit ( AuditType . Move , "Move Content to Recycle Bin performed by user" , userId , content . Id ) ;
2015-07-29 15:12:12 +02:00
}
2017-02-03 12:23:32 +11:00
2017-01-26 18:30:00 +01:00
// fixme the WHOLE thing should move to the UOW!
//Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
2015-07-29 15:12:12 +02:00
2016-01-27 15:22:58 +01:00
return OperationStatus . Success ( evtMsgs ) ;
2015-07-29 15:12:12 +02:00
}
}
/// <summary>
/// UnPublishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if unpublishing succeeded, otherwise False</returns>
Attempt < UnPublishStatus > IContentServiceOperations . UnPublish ( IContent content , int userId )
{
return UnPublishDo ( content , false , userId ) ;
}
2013-10-10 09:35:23 +11:00
/// <summary>
/// Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
public Attempt < PublishStatus > PublishWithStatus ( IContent content , int userId = 0 )
{
2015-07-29 15:12:12 +02:00
return ( ( IContentServiceOperations ) this ) . Publish ( content , userId ) ;
2013-10-10 09:35:23 +11:00
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Publishes a <see cref="IContent"/> object and all its children
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish along with its children</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
2013-10-10 09:35:23 +11:00
[Obsolete("Use PublishWithChildrenWithStatus instead, that method will provide more detailed information on the outcome and also allows the includeUnpublished flag")]
2013-04-23 12:49:33 -02:00
public bool PublishWithChildren ( IContent content , int userId = 0 )
{
2013-02-15 20:53:44 +06:00
var result = PublishWithChildrenDo ( content , userId , true ) ;
2013-10-10 09:35:23 +11:00
2013-02-09 04:33:56 +06:00
//This used to just return false only when the parent content failed, otherwise would always return true so we'll
// do the same thing for the moment
2015-07-29 15:12:12 +02:00
if ( result . All ( x = > x . Result . ContentItem . Id ! = content . Id ) )
2013-10-10 09:35:23 +11:00
return false ;
2013-02-15 20:53:44 +06:00
2014-08-20 17:01:12 +02:00
return result . Single ( x = > x . Result . ContentItem . Id = = content . Id ) . Success ;
2013-10-10 09:35:23 +11:00
}
/// <summary>
/// Publishes a <see cref="IContent"/> object and all its children
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish along with its children</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="includeUnpublished">set to true if you want to also publish children that are currently unpublished</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
public IEnumerable < Attempt < PublishStatus > > PublishWithChildrenWithStatus ( IContent content , int userId = 0 , bool includeUnpublished = false )
{
2015-07-29 15:12:12 +02:00
return ( ( IContentServiceOperations ) this ) . PublishWithChildren ( content , userId , includeUnpublished ) ;
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// UnPublishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if unpublishing succeeded, otherwise False</returns>
public bool UnPublish ( IContent content , int userId = 0 )
{
2016-02-26 14:30:32 +00:00
return ( ( IContentServiceOperations ) this ) . UnPublish ( content , userId ) . Success ;
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Saves and Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save and publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
2013-01-29 12:45:42 -01:00
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
2013-04-23 12:49:33 -02:00
/// <returns>True if publishing succeeded, otherwise False</returns>
2013-10-10 09:35:23 +11:00
[Obsolete("Use SaveAndPublishWithStatus instead, that method will provide more detailed information on the outcome")]
2013-01-29 12:45:42 -01:00
public bool SaveAndPublish ( IContent content , int userId = 0 , bool raiseEvents = true )
2013-04-23 12:49:33 -02:00
{
2013-02-11 20:07:23 +06:00
var result = SaveAndPublishDo ( content , userId , raiseEvents ) ;
2014-08-20 17:01:12 +02:00
return result . Success ;
2013-04-23 12:49:33 -02:00
}
2012-12-11 12:03:36 +05:00
2013-10-10 09:35:23 +11:00
/// <summary>
/// Saves and Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save and publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
public Attempt < PublishStatus > SaveAndPublishWithStatus ( IContent content , int userId = 0 , bool raiseEvents = true )
{
2015-07-29 15:12:12 +02:00
return ( ( IContentServiceOperations ) this ) . SaveAndPublish ( content , userId , raiseEvents ) ;
2013-10-10 09:35:23 +11:00
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
2013-01-29 12:45:42 -01:00
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
2015-07-29 15:12:12 +02:00
public void Save ( IContent content , int userId = 0 , bool raiseEvents = true )
2013-04-23 12:49:33 -02:00
{
2015-07-29 15:12:12 +02:00
( ( IContentServiceOperations ) this ) . Save ( content , userId , raiseEvents ) ;
2013-04-23 12:49:33 -02:00
}
2012-11-14 11:20:21 -01:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Saves a collection of <see cref="IContent"/> objects.
2017-01-26 18:30:00 +01:00
/// </summary>
2013-04-23 12:49:33 -02:00
/// <param name="contents">Collection of <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
2017-01-26 18:30:00 +01:00
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
2015-07-29 15:12:12 +02:00
Attempt < OperationStatus > IContentServiceOperations . Save ( IEnumerable < IContent > contents , int userId , bool raiseEvents )
2013-04-23 12:49:33 -02:00
{
2014-04-29 11:11:48 +10:00
var asArray = contents . ToArray ( ) ;
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2017-01-26 18:30:00 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-04-23 12:49:33 -02:00
{
2017-01-26 18:30:00 +01:00
if ( raiseEvents )
2015-07-24 11:44:09 +02:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Saving , this , new SaveEventArgs < IContent > ( asArray , evtMsgs ) ) )
2017-01-26 18:30:00 +01:00
{
return OperationStatus . Cancelled ( evtMsgs ) ;
}
2015-07-24 11:44:09 +02:00
}
2017-01-23 22:16:27 +11:00
2017-01-26 18:30:00 +01:00
using ( new WriteLock ( Locker ) )
2013-04-23 12:49:33 -02:00
{
2017-01-26 18:30:00 +01:00
var containsNew = asArray . Any ( x = > x . HasIdentity = = false ) ;
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
if ( containsNew )
{
2014-04-29 11:11:48 +10:00
foreach ( var content in asArray )
2013-04-23 12:49:33 -02:00
{
content . WriterId = userId ;
//Only change the publish state if the "previous" version was actually published
if ( content . Published )
content . ChangePublishedState ( PublishedState . Saved ) ;
repository . AddOrUpdate ( content ) ;
2014-04-29 11:11:48 +10:00
//add or update preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-04-23 12:49:33 -02:00
}
}
else
{
2014-04-29 11:11:48 +10:00
foreach ( var content in asArray )
2013-04-23 12:49:33 -02:00
{
content . WriterId = userId ;
repository . AddOrUpdate ( content ) ;
2014-04-29 11:11:48 +10:00
//add or update preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2014-08-20 17:01:12 +02:00
}
2013-04-23 12:49:33 -02:00
}
2014-04-29 11:11:48 +10:00
uow . Commit ( ) ;
2013-04-23 12:49:33 -02:00
}
if ( raiseEvents )
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Saved , this , new SaveEventArgs < IContent > ( asArray , false , evtMsgs ) ) ;
2013-04-23 12:49:33 -02:00
2015-01-19 15:12:34 +11:00
Audit ( AuditType . Save , "Bulk Save content performed by user" , userId = = - 1 ? 0 : userId , Constants . System . Root ) ;
2015-07-24 11:44:09 +02:00
2016-01-27 15:22:58 +01:00
return OperationStatus . Success ( evtMsgs ) ;
2013-04-23 12:49:33 -02:00
}
}
2015-07-29 15:12:12 +02:00
/// <summary>
/// Permanently deletes an <see cref="IContent"/> object.
/// </summary>
/// <remarks>
/// This method will also delete associated media files, child content and possibly associated domains.
/// </remarks>
/// <remarks>Please note that this method will completely remove the Content from the database</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
Attempt < OperationStatus > IContentServiceOperations . Delete ( IContent content , int userId )
{
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2015-07-29 15:12:12 +02:00
using ( new WriteLock ( Locker ) )
{
2017-01-26 18:30:00 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2015-07-29 15:12:12 +02:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Deleting , this , new DeleteEventArgs < IContent > ( content , evtMsgs ) , "Deleting" ) )
2017-01-26 18:30:00 +01:00
{
return OperationStatus . Cancelled ( evtMsgs ) ;
}
2015-07-29 15:12:12 +02:00
2017-01-26 18:30:00 +01:00
//Make sure that published content is unpublished before being deleted
if ( HasPublishedVersion ( content . Id ) )
{
UnPublish ( content , userId ) ;
}
2015-07-29 15:12:12 +02:00
2017-01-26 18:30:00 +01:00
//Delete children before deleting the 'possible parent'
var children = GetChildren ( content . Id ) ;
foreach ( var child in children )
{
Delete ( child , userId ) ;
}
2015-07-29 15:12:12 +02:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-07-29 15:12:12 +02:00
repository . Delete ( content ) ;
uow . Commit ( ) ;
2016-02-26 14:30:32 +00:00
2015-07-29 15:12:12 +02:00
var args = new DeleteEventArgs < IContent > ( content , false , evtMsgs ) ;
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Deleted , this , args , "Deleted" ) ; // fixme why the event name?!
2016-02-26 14:30:32 +00:00
2015-07-29 15:12:12 +02:00
//remove any flagged media files
repository . DeleteMediaFiles ( args . MediaFilesToDelete ) ;
}
Audit ( AuditType . Delete , "Delete Content performed by user" , userId , content . Id ) ;
2016-01-27 15:22:58 +01:00
return OperationStatus . Success ( evtMsgs ) ;
2015-07-29 15:12:12 +02:00
}
}
/// <summary>
/// Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>The published status attempt</returns>
Attempt < PublishStatus > IContentServiceOperations . Publish ( IContent content , int userId )
{
return SaveAndPublishDo ( content , userId ) ;
}
2015-07-24 11:44:09 +02:00
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
2015-07-29 15:12:12 +02:00
Attempt < OperationStatus > IContentServiceOperations . Save ( IContent content , int userId , bool raiseEvents )
2015-07-24 11:44:09 +02:00
{
2015-07-29 15:12:12 +02:00
return Save ( content , true , userId , raiseEvents ) ;
2015-07-24 11:44:09 +02:00
}
/// <summary>
/// Saves a collection of <see cref="IContent"/> objects.
/// </summary>
/// <remarks>
/// If the collection of content contains new objects that references eachother by Id or ParentId,
/// then use the overload Save method with a collection of Lazy <see cref="IContent"/>.
/// </remarks>
/// <param name="contents">Collection of <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public void Save ( IEnumerable < IContent > contents , int userId = 0 , bool raiseEvents = true )
{
2015-07-29 15:12:12 +02:00
( ( IContentServiceOperations ) this ) . Save ( contents , userId , raiseEvents ) ;
2015-07-24 11:44:09 +02:00
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin.
/// </summary>
/// <remarks>This needs extra care and attention as its potentially a dangerous and extensive operation</remarks>
/// <param name="contentTypeId">Id of the <see cref="IContentType"/></param>
/// <param name="userId">Optional Id of the user issueing the delete operation</param>
public void DeleteContentOfType ( int contentTypeId , int userId = 0 )
{
2017-01-26 18:30:00 +01:00
//TODO: This currently this is called from the ContentTypeService but that needs to change,
2016-01-28 18:02:22 +01:00
// if we are deleting a content type, we should just delete the data and do this operation slightly differently.
// This method will recursively go lookup every content item, check if any of it's descendants are
2017-01-26 18:30:00 +01:00
// of a different type, move them to the recycle bin, then permanently delete the content items.
2016-01-28 18:02:22 +01:00
// The main problem with this is that for every content item being deleted, events are raised...
// which we need for many things like keeping caches in sync, but we can surely do this MUCH better.
2017-01-27 11:42:25 +01:00
// FIXME this is all totally entirely completely fucking WRONG
// keeping Shan's code as-is for the time being but compare to 7.5, it's all borked
2017-01-23 22:16:27 +11:00
var childList = new List < IContent > ( ) ;
var contentList = new List < IContent > ( ) ;
2013-04-23 12:49:33 -02:00
using ( new WriteLock ( Locker ) )
{
2017-01-27 14:12:53 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
{
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-23 12:49:33 -02:00
//NOTE What about content that has the contenttype as part of its composition?
var query = Query < IContent > . Builder . Where ( x = > x . ContentTypeId = = contentTypeId ) ;
2013-12-04 15:00:49 +11:00
var contents = repository . GetByQuery ( query ) . ToArray ( ) ;
2013-04-23 12:49:33 -02:00
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Deleting , this , new DeleteEventArgs < IContent > ( contents ) , "Deleting" ) )
2017-01-23 22:16:27 +11:00
{
uow . Commit ( ) ;
2013-04-23 12:49:33 -02:00
return ;
2017-01-23 22:16:27 +11:00
}
2013-04-23 12:49:33 -02:00
foreach ( var content in contents . OrderByDescending ( x = > x . ParentId ) )
{
//Look for children of current content and move that to trash before the current content is deleted
var c = content ;
var childQuery = Query < IContent > . Builder . Where ( x = > x . Path . StartsWith ( c . Path ) ) ;
var children = repository . GetByQuery ( childQuery ) ;
foreach ( var child in children )
{
2017-01-23 22:16:27 +11:00
//track children for moving to the bin
childList . Add ( child ) ;
2013-04-23 12:49:33 -02:00
}
2017-01-23 22:16:27 +11:00
//track content for deletion
contentList . Add ( content ) ;
2013-04-23 12:49:33 -02:00
}
2017-01-27 14:12:53 +01:00
// do it INSIDE the UOW because nested UOW kinda should work
// fixme - and then we probably don't need the whole mess?
// nesting UOW works, it's just that the outer one NEEDS to be flushed beforehand
// nevertheless, it would be nicer to create a global scope and inner uow
foreach ( var child in childList )
{
if ( child . ContentType . Id ! = contentTypeId )
MoveToRecycleBin ( child , userId ) ;
}
foreach ( var content in contentList )
{
//Permantly delete the content
Delete ( content , userId ) ;
}
uow . Commit ( ) ;
Audit ( AuditType . Delete ,
string . Format ( "Delete Content of Type {0} performed by user" , contentTypeId ) ,
userId , Constants . System . Root ) ;
2013-04-23 12:49:33 -02:00
}
2017-01-27 14:12:53 +01:00
2017-01-27 11:42:25 +01:00
// FIXME this is borked I don't even...
2013-04-23 12:49:33 -02:00
2017-01-23 22:16:27 +11:00
//We need to do this outside of the uow because otherwise we'll have nested uow's
//TODO: this is a problem because we are doing all of this logic in the Service when it should
//all be happening in the repository
2017-01-27 14:12:53 +01:00
//foreach (var child in childList)
//{
// if (child.ContentType.Id != contentTypeId)
// MoveToRecycleBin(child, userId);
//}
//foreach (var content in contentList)
//{
// //Permantly delete the content
// Delete(content, userId);
//}
//Audit(AuditType.Delete,
// string.Format("Delete Content of Type {0} performed by user", contentTypeId),
// userId, Constants.System.Root);
2013-01-29 12:45:42 -01:00
}
2013-04-23 12:49:33 -02:00
}
/// <summary>
2013-04-20 11:03:57 -02:00
/// Permanently deletes an <see cref="IContent"/> object as well as all of its Children.
2012-12-14 15:19:54 -01:00
/// </summary>
/// <remarks>
/// This method will also delete associated media files, child content and possibly associated domains.
/// </remarks>
/// <remarks>Please note that this method will completely remove the Content from the database</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
2013-04-23 12:49:33 -02:00
public void Delete ( IContent content , int userId = 0 )
{
2015-07-29 15:12:12 +02:00
( ( IContentServiceOperations ) this ) . Delete ( content , userId ) ;
2013-04-23 12:49:33 -02:00
}
/// <summary>
/// Permanently deletes versions from an <see cref="IContent"/> object prior to a specific date.
2013-09-18 11:49:36 +02:00
/// This method will never delete the latest version of a content item.
2013-04-23 12:49:33 -02:00
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> object to delete versions from</param>
/// <param name="versionDate">Latest version date</param>
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
public void DeleteVersions ( int id , DateTime versionDate , int userId = 0 )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-03-19 23:46:13 +06:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( DeletingVersions , this , new DeleteRevisionsEventArgs ( id , dateToRetain : versionDate ) , "DeletingVersions" ) )
2017-01-23 22:16:27 +11:00
{
2017-01-27 11:42:25 +01:00
// fixme - not consistent, shall we commit or not when cancelling?!
2017-01-23 22:16:27 +11:00
uow . Commit ( ) ;
2017-01-26 18:30:00 +01:00
return ;
2017-01-23 22:16:27 +11:00
}
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-03-19 23:46:13 +06:00
repository . DeleteVersions ( id , versionDate ) ;
uow . Commit ( ) ;
2012-12-21 04:59:51 +05:00
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( DeletedVersions , this , new DeleteRevisionsEventArgs ( id , false , dateToRetain : versionDate ) , "DeletedVersions" ) ;
2017-01-27 11:42:25 +01:00
2017-01-26 18:30:00 +01:00
Audit ( AuditType . Delete , "Delete Content by version date performed by user" , userId , Constants . System . Root ) ;
}
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Permanently deletes specific version(s) from an <see cref="IContent"/> object.
2013-09-18 11:49:36 +02:00
/// This method will never delete the latest version of a content item.
2013-04-23 12:49:33 -02:00
/// </summary>
/// <param name="id">Id of the <see cref="IContent"/> object to delete a version from</param>
/// <param name="versionId">Id of the version to delete</param>
/// <param name="deletePriorVersions">Boolean indicating whether to delete versions prior to the versionId</param>
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
public void DeleteVersion ( int id , Guid versionId , bool deletePriorVersions , int userId = 0 )
{
using ( new WriteLock ( Locker ) )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-03-19 23:46:13 +06:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( DeletingVersions , this , new DeleteRevisionsEventArgs ( id , specificVersion : versionId ) , "DeletingVersions" ) )
2017-01-26 18:30:00 +01:00
return ;
2017-01-19 17:18:06 +11:00
2017-01-26 18:30:00 +01:00
if ( deletePriorVersions )
{
var content = GetByVersion ( versionId ) ;
DeleteVersions ( id , content . UpdateDate , userId ) ;
}
2012-12-21 07:17:27 +05:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-03-19 23:46:13 +06:00
repository . DeleteVersion ( versionId ) ;
uow . Commit ( ) ;
2017-02-03 12:23:32 +11:00
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( DeletedVersions , this , new DeleteRevisionsEventArgs ( id , false , specificVersion : versionId ) , "DeletedVersions" ) ;
2012-11-14 11:20:21 -01:00
2017-01-26 18:30:00 +01:00
Audit ( AuditType . Delete , "Delete Content by version performed by user" , userId , Constants . System . Root ) ;
}
2013-04-23 12:49:33 -02:00
}
}
2012-12-11 23:19:07 +05:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Deletes an <see cref="IContent"/> object by moving it to the Recycle Bin
/// </summary>
/// <remarks>Move an item to the Recycle Bin will result in the item being unpublished</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
public void MoveToRecycleBin ( IContent content , int userId = 0 )
{
2016-02-26 14:30:32 +00:00
( ( IContentServiceOperations ) this ) . MoveToRecycleBin ( content , userId ) ;
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Moves an <see cref="IContent"/> object to a new location by changing its parent id.
/// </summary>
/// <remarks>
/// If the <see cref="IContent"/> object is already published it will be
/// published after being moved to its new location. Otherwise it'll just
/// be saved with a new parent id.
/// </remarks>
/// <param name="content">The <see cref="IContent"/> to move</param>
/// <param name="parentId">Id of the Content's new Parent</param>
/// <param name="userId">Optional Id of the User moving the Content</param>
public void Move ( IContent content , int parentId , int userId = 0 )
{
using ( new WriteLock ( Locker ) )
{
2013-03-19 23:46:13 +06:00
//This ensures that the correct method is called if this method is used to Move to recycle bin.
2015-01-04 21:02:20 +00:00
if ( parentId = = Constants . System . RecycleBinContent )
2013-03-19 23:46:13 +06:00
{
MoveToRecycleBin ( content , userId ) ;
return ;
}
2014-08-20 17:01:12 +02:00
2017-01-26 18:30:00 +01:00
// fixme - we NEED something for the events
// so even if we don't create a true unit of work (no need) we can create a scope!
// what a spectacular mess
using ( IScope scope = UowProvider . ScopeProvider . CreateScope ( ) )
2013-03-19 23:46:13 +06:00
{
2017-02-07 14:08:55 +11:00
if ( scope . Events . DispatchCancelable ( Moving , this , new MoveEventArgs < IContent > ( new MoveEventInfo < IContent > ( content , content . Path , parentId ) ) , "Moving" ) )
2017-01-26 18:30:00 +01:00
return ;
//used to track all the moved entities to be given to the event
var moveInfo = new List < MoveEventInfo < IContent > > ( ) ;
//call private method that does the recursive moving
PerformMove ( content , parentId , userId , moveInfo ) ;
2017-02-07 14:08:55 +11:00
scope . Events . Dispatch ( Moved , this , new MoveEventArgs < IContent > ( false , moveInfo . ToArray ( ) ) , "Moved" ) ;
2017-01-26 18:30:00 +01:00
scope . Complete ( ) ;
2013-03-19 23:46:13 +06:00
}
2013-01-21 14:50:58 -01:00
2015-01-19 15:12:34 +11:00
Audit ( AuditType . Move , "Move Content performed by user" , userId , content . Id ) ;
2013-04-23 12:49:33 -02:00
}
}
2012-12-11 23:19:07 +05:00
2013-01-18 10:56:14 -01:00
/// <summary>
2013-04-23 12:49:33 -02:00
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
/// </summary>
public void EmptyRecycleBin ( )
{
2013-08-14 16:12:13 +02:00
using ( new WriteLock ( Locker ) )
2013-04-23 12:49:33 -02:00
{
2014-09-10 15:07:20 +10:00
Dictionary < int , IEnumerable < Property > > entities ;
2013-08-14 16:12:13 +02:00
List < string > files ;
bool success ;
var nodeObjectType = new Guid ( Constants . ObjectTypes . Document ) ;
2012-12-11 12:03:36 +05:00
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-04-23 12:49:33 -02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-09-10 15:07:20 +10:00
//Create a dictionary of ids -> dictionary of property aliases + values
entities = repository . GetEntitiesInRecycleBin ( )
. ToDictionary (
key = > key . Id ,
val = > ( IEnumerable < Property > ) val . Properties ) ;
2012-12-21 07:42:37 +05:00
2014-09-10 15:07:20 +10:00
files = ( ( ContentRepository ) repository ) . GetFilesInRecycleBinForUploadField ( ) ;
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( EmptyingRecycleBin , this , new RecycleBinEventArgs ( nodeObjectType , entities , files ) ) )
2017-01-23 22:16:27 +11:00
{
2017-01-31 10:37:37 +01:00
uow . Commit ( ) ; // fixme not consistent! // fixme not consistent!
2013-08-14 16:12:13 +02:00
return ;
2017-01-23 22:16:27 +11:00
}
2012-12-21 07:42:37 +05:00
2014-09-10 15:07:20 +10:00
success = repository . EmptyRecycleBin ( ) ;
2013-08-14 16:12:13 +02:00
if ( success )
2015-07-02 17:20:27 +02:00
repository . DeleteMediaFiles ( files ) ;
2017-01-25 13:34:41 +01:00
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( EmptiedRecycleBin , this , new RecycleBinEventArgs ( nodeObjectType , entities , files , success ) ) ;
2017-01-25 13:34:41 +01:00
uow . Commit ( ) ;
2013-08-14 16:12:13 +02:00
}
}
2017-01-27 11:42:25 +01:00
// fixme outside the uow!
2015-01-19 15:12:34 +11:00
Audit ( AuditType . Delete , "Empty Content Recycle Bin performed by user" , 0 , Constants . System . RecycleBinContent ) ;
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2012-12-14 15:19:54 -01:00
/// <summary>
2017-01-26 18:30:00 +01:00
/// Copies an <see cref="IContent"/> object by creating a new Content object of the same type and copies all data from the current
2015-09-02 16:41:00 +02:00
/// to the new copy which is returned. Recursively copies all children.
2012-12-14 15:19:54 -01:00
/// </summary>
/// <param name="content">The <see cref="IContent"/> to copy</param>
/// <param name="parentId">Id of the Content's new Parent</param>
/// <param name="relateToOriginal">Boolean indicating whether the copy should be related to the original</param>
/// <param name="userId">Optional Id of the User copying the Content</param>
/// <returns>The newly created <see cref="IContent"/> object</returns>
2013-01-25 08:58:21 -01:00
public IContent Copy ( IContent content , int parentId , bool relateToOriginal , int userId = 0 )
2015-09-02 16:41:00 +02:00
{
return Copy ( content , parentId , relateToOriginal , true , userId ) ;
}
/// <summary>
2017-01-26 18:30:00 +01:00
/// Copies an <see cref="IContent"/> object by creating a new Content object of the same type and copies all data from the current
2015-09-02 16:41:00 +02:00
/// to the new copy which is returned.
/// </summary>
/// <param name="content">The <see cref="IContent"/> to copy</param>
/// <param name="parentId">Id of the Content's new Parent</param>
/// <param name="relateToOriginal">Boolean indicating whether the copy should be related to the original</param>
/// <param name="recursive">A value indicating whether to recursively copy children.</param>
/// <param name="userId">Optional Id of the User copying the Content</param>
/// <returns>The newly created <see cref="IContent"/> object</returns>
public IContent Copy ( IContent content , int parentId , bool relateToOriginal , bool recursive , int userId = 0 )
2012-12-27 16:08:24 -01:00
{
2014-04-23 20:19:36 +10:00
//TODO: This all needs to be managed correctly so that the logic is submitted in one
// transaction, the CRUD needs to be moved to the repo
2013-03-19 23:46:13 +06:00
using ( new WriteLock ( Locker ) )
{
2014-04-22 17:48:08 +10:00
var copy = content . DeepCloneWithResetIdentities ( ) ;
2013-03-19 23:46:13 +06:00
copy . ParentId = parentId ;
2012-12-11 23:19:07 +05:00
2013-03-19 23:46:13 +06:00
// A copy should never be set to published automatically even if the original was.
copy . ChangePublishedState ( PublishedState . Unpublished ) ;
2012-12-27 16:08:24 -01:00
2017-01-26 18:30:00 +01:00
// fixme mess!
using ( var scope = UowProvider . ScopeProvider . CreateScope ( ) )
2013-03-19 23:46:13 +06:00
{
2017-01-25 13:34:41 +01:00
2017-01-26 18:30:00 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Copying , this , new CopyEventArgs < IContent > ( content , copy , parentId ) ) )
2017-01-27 11:42:25 +01:00
{
uow . Commit ( ) ;
2017-01-26 18:30:00 +01:00
return null ;
2017-01-27 11:42:25 +01:00
}
2017-02-03 12:23:32 +11:00
2017-01-26 18:30:00 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2012-12-11 23:19:07 +05:00
2017-01-26 18:30:00 +01:00
// Update the create author and last edit author
copy . CreatorId = userId ;
copy . WriterId = userId ;
2014-07-10 13:40:22 +10:00
2017-01-26 18:30:00 +01:00
repository . AddOrUpdate ( copy ) ;
//add or update a preview
repository . AddOrUpdatePreviewXml ( copy , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
uow . Commit ( ) ;
2014-08-20 17:01:12 +02:00
2017-01-26 18:30:00 +01:00
//Special case for the associated tags
//TODO: Move this to the repository layer in a single transaction!
//don't copy tags data in tags table if the item is in the recycle bin
if ( parentId ! = Constants . System . RecycleBinContent )
2014-07-10 13:40:22 +10:00
{
2017-01-26 18:30:00 +01:00
var tags = uow . Database . Fetch < TagRelationshipDto > ( "WHERE nodeId = @Id" , new { Id = content . Id } ) ;
foreach ( var tag in tags )
{
uow . Database . Insert ( new TagRelationshipDto { NodeId = copy . Id , TagId = tag . TagId , PropertyTypeId = tag . PropertyTypeId } ) ;
}
2014-08-20 17:01:12 +02:00
}
2017-01-25 13:34:41 +01:00
2017-01-26 18:30:00 +01:00
uow . Commit ( ) ;
2013-03-19 23:46:13 +06:00
}
2015-01-19 15:12:34 +11:00
2017-01-26 18:30:00 +01:00
if ( recursive )
2015-09-02 16:41:00 +02:00
{
2017-01-26 18:30:00 +01:00
//Look for children and copy those as well
var children = GetChildren ( content . Id ) ;
foreach ( var child in children )
{
//TODO: This shouldn't recurse back to this method, it should be done in a private method
// that doesn't have a nested lock and so we can perform the entire operation in one commit.
Copy ( child , copy . Id , relateToOriginal , true , userId ) ;
}
2015-09-02 16:41:00 +02:00
}
2017-01-31 10:37:37 +01:00
scope . Events . Dispatch ( Copied , this , new CopyEventArgs < IContent > ( content , copy , false , parentId , relateToOriginal ) ) ;
2017-01-26 18:30:00 +01:00
Audit ( AuditType . Copy , "Copy Content performed by user" , content . WriterId , content . Id ) ;
2012-12-11 23:19:07 +05:00
2017-01-26 18:30:00 +01:00
scope . Complete ( ) ;
}
2012-12-11 23:19:07 +05:00
2013-03-19 23:46:13 +06:00
return copy ;
}
}
2012-12-11 23:19:07 +05:00
2014-02-17 15:26:38 +11:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Sends an <see cref="IContent"/> to Publication, which executes handlers and events for the 'Send to Publication' action.
/// </summary>
/// <param name="content">The <see cref="IContent"/> to send to publication</param>
/// <param name="userId">Optional Id of the User issueing the send to publication</param>
/// <returns>True if sending publication was succesfull otherwise false</returns>
2013-10-31 18:28:02 +11:00
public bool SendToPublication ( IContent content , int userId = 0 )
2013-04-23 12:49:33 -02:00
{
2017-01-19 17:18:06 +11:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( SendingToPublish , this , new SendToPublishEventArgs < IContent > ( content ) ) )
2017-01-19 17:18:06 +11:00
return false ;
2012-12-11 23:19:07 +05:00
2017-01-19 17:18:06 +11:00
//Save before raising event
Save ( content , userId ) ;
2012-12-11 23:19:07 +05:00
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( SentToPublish , this , new SendToPublishEventArgs < IContent > ( content , false ) ) ;
2012-12-11 23:19:07 +05:00
2017-01-19 17:18:06 +11:00
Audit ( AuditType . SendToPublish , "Send to Publish performed by user" , content . WriterId , content . Id ) ;
2015-07-29 15:12:12 +02:00
2017-01-19 17:18:06 +11:00
return true ;
}
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2013-04-23 12:49:33 -02:00
/// <summary>
/// Rollback an <see cref="IContent"/> object to a previous version.
/// This will create a new version, which is a copy of all the old data.
/// </summary>
/// <remarks>
/// The way data is stored actually only allows us to rollback on properties
/// and not data like Name and Alias of the Content.
/// </remarks>
/// <param name="id">Id of the <see cref="IContent"/>being rolled back</param>
/// <param name="versionId">Id of the version to rollback to</param>
/// <param name="userId">Optional Id of the User issueing the rollback of the Content</param>
/// <returns>The newly created <see cref="IContent"/> object</returns>
public IContent Rollback ( int id , Guid versionId , int userId = 0 )
{
2013-03-19 23:46:13 +06:00
var content = GetByVersion ( versionId ) ;
2012-11-05 14:42:21 -01:00
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-03-19 23:46:13 +06:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( RollingBack , this , new RollbackEventArgs < IContent > ( content ) ) )
2017-01-23 22:16:27 +11:00
{
uow . Commit ( ) ;
2017-01-26 18:30:00 +01:00
return content ;
2017-01-23 22:16:27 +11:00
}
2017-01-26 18:30:00 +01:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2017-01-27 11:42:25 +01:00
2013-03-19 23:46:13 +06:00
content . WriterId = userId ;
content . CreatorId = userId ;
2013-06-25 11:48:37 +02:00
content . ChangePublishedState ( PublishedState . Unpublished ) ;
2012-11-14 11:20:21 -01:00
2013-03-19 23:46:13 +06:00
repository . AddOrUpdate ( content ) ;
2014-04-29 11:11:48 +10:00
//add or update a preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-03-19 23:46:13 +06:00
uow . Commit ( ) ;
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( RolledBack , this , new RollbackEventArgs < IContent > ( content , false ) ) ;
2017-01-26 18:30:00 +01:00
Audit ( AuditType . RollBack , "Content rollback performed by user" , content . WriterId , content . Id ) ;
}
2012-10-10 12:13:23 -02:00
2013-03-19 23:46:13 +06:00
return content ;
2013-04-23 12:49:33 -02:00
}
2012-12-11 23:19:07 +05:00
2013-05-23 10:29:44 -02:00
/// <summary>
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
2013-05-29 13:18:03 -02:00
/// to the ordering of items in the passed in <see cref="IEnumerable{T}"/>.
2013-05-23 10:29:44 -02:00
/// </summary>
/// <remarks>
/// Using this method will ensure that the Published-state is maintained upon sorting
/// so the cache is updated accordingly - as needed.
/// </remarks>
/// <param name="items"></param>
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
2013-05-29 13:18:03 -02:00
public bool Sort ( IEnumerable < IContent > items , int userId = 0 , bool raiseEvents = true )
2013-05-23 10:29:44 -02:00
{
var shouldBePublished = new List < IContent > ( ) ;
var shouldBeSaved = new List < IContent > ( ) ;
2017-01-26 18:30:00 +01:00
2013-05-23 10:29:44 -02:00
using ( new WriteLock ( Locker ) )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-05-23 10:29:44 -02:00
{
2017-01-26 18:30:00 +01:00
var asArray = items . ToArray ( ) ;
if ( raiseEvents )
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Saving , this , new SaveEventArgs < IContent > ( asArray ) ) )
2017-01-23 22:16:27 +11:00
{
uow . Commit ( ) ;
2017-01-26 18:30:00 +01:00
return false ;
2017-01-23 22:16:27 +11:00
}
2017-01-26 18:30:00 +01:00
}
2017-02-03 12:23:32 +11:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-05-23 10:29:44 -02:00
int i = 0 ;
2014-04-28 17:28:40 +10:00
foreach ( var content in asArray )
2013-05-23 10:29:44 -02:00
{
2013-05-29 05:37:55 -02:00
//If the current sort order equals that of the content
//we don't need to update it, so just increment the sort order
//and continue.
if ( content . SortOrder = = i )
{
i + + ;
continue ;
}
2013-05-23 10:29:44 -02:00
content . SortOrder = i ;
content . WriterId = userId ;
i + + ;
if ( content . Published )
{
2015-07-29 15:12:12 +02:00
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
2017-01-25 16:49:44 +11:00
var published = _publishingStrategy . Publish ( uow , content , userId ) . Success ;
2013-05-23 10:29:44 -02:00
shouldBePublished . Add ( content ) ;
}
else
shouldBeSaved . Add ( content ) ;
repository . AddOrUpdate ( content ) ;
2014-04-29 11:11:48 +10:00
//add or update a preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-05-23 10:29:44 -02:00
}
2014-08-20 17:01:12 +02:00
2013-05-23 10:29:44 -02:00
foreach ( var content in shouldBePublished )
2014-08-20 17:01:12 +02:00
{
2013-05-23 10:29:44 -02:00
//Create and Save ContentXml DTO
2014-09-30 18:46:02 +10:00
repository . AddOrUpdateContentXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-05-23 10:29:44 -02:00
}
2014-04-28 17:28:40 +10:00
uow . Commit ( ) ;
2017-01-26 18:30:00 +01:00
if ( raiseEvents )
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Saved , this , new SaveEventArgs < IContent > ( asArray , false ) ) ;
2013-05-23 10:29:44 -02:00
2017-01-25 16:49:44 +11:00
if ( shouldBePublished . Any ( ) )
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
_publishingStrategy . PublishingFinalized ( uow , shouldBePublished , false ) ;
}
2017-02-03 12:23:32 +11:00
}
2015-07-29 15:12:12 +02:00
}
2017-02-03 12:23:32 +11:00
2017-01-26 18:30:00 +01:00
// fixme - out of uow?
2015-01-19 15:12:34 +11:00
Audit ( AuditType . Sort , "Sorting content performed by user" , userId , 0 ) ;
2013-05-23 10:29:44 -02:00
return true ;
}
2017-01-17 17:57:49 +11:00
/// <summary>
/// Gets paged content descendants as XML by path
/// </summary>
/// <param name="path">Path starts with</param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records the query would return without paging</param>
/// <returns>A paged enumerable of XML entries of content items</returns>
public IEnumerable < XElement > GetPagedXmlEntries ( string path , long pageIndex , int pageSize , out long totalRecords )
{
Mandate . ParameterCondition ( pageIndex > = 0 , "pageIndex" ) ;
Mandate . ParameterCondition ( pageSize > 0 , "pageSize" ) ;
var uow = UowProvider . GetUnitOfWork ( ) ;
using ( var repository = RepositoryFactory . CreateContentRepository ( uow ) )
{
2017-01-19 12:51:56 +11:00
var contents = repository . GetPagedXmlEntriesByPath ( path , pageIndex , pageSize ,
//This order by is VERY important! This allows us to figure out what is implicitly not published, see ContentRepository.BuildXmlCache and
// UmbracoContentIndexer.PerformIndexAll which uses the logic based on this sort order
2017-02-03 12:23:32 +11:00
new [ ] { "level" , "parentID" , "sortOrder" } ,
2017-01-19 12:51:56 +11:00
out totalRecords ) ;
2017-01-17 17:57:49 +11:00
return contents ;
}
}
2016-08-05 17:41:00 +02:00
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
/// <returns></returns>
public XmlDocument BuildXmlCache ( )
{
2017-01-27 14:12:53 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2016-08-05 17:41:00 +02:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2016-08-05 17:41:00 +02:00
var result = repository . BuildXmlCache ( ) ;
2017-01-25 13:34:41 +01:00
uow . Commit ( ) ;
2016-08-05 17:41:00 +02:00
return result ;
}
}
2014-10-23 18:31:08 +10:00
/// <summary>
/// Rebuilds all xml content in the cmsContentXml table for all documents
/// </summary>
/// <param name="contentTypeIds">
/// Only rebuild the xml structures for the content type ids passed in, if none then rebuilds the structures
/// for all content
/// </param>
public void RebuildXmlStructures ( params int [ ] contentTypeIds )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2014-10-23 18:31:08 +10:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2014-10-23 18:31:08 +10:00
repository . RebuildXmlStructures (
content = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , content ) ,
contentTypeIds : contentTypeIds . Length = = 0 ? null : contentTypeIds ) ;
2015-01-19 18:37:48 +11:00
2015-01-19 15:12:34 +11:00
uow . Commit ( ) ;
2014-10-23 18:31:08 +10:00
}
2015-01-19 15:12:34 +11:00
Audit ( AuditType . Publish , "ContentService.RebuildXmlStructures completed, the xml has been regenerated in the database" , 0 , Constants . System . Root ) ;
2014-10-23 18:31:08 +10:00
}
2012-12-15 11:04:03 -01:00
#region Internal Methods
2013-01-22 11:23:33 -01:00
2013-01-30 12:11:10 -01:00
/// <summary>
/// Gets a collection of <see cref="IContent"/> descendants by the first Parent.
/// </summary>
/// <param name="content"><see cref="IContent"/> item to retrieve Descendants from</param>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
internal IEnumerable < IContent > GetPublishedDescendants ( IContent content )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-01-30 12:11:10 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-04-21 09:06:22 -02:00
var query = Query < IContent > . Builder . Where ( x = > x . Id ! = content . Id & & x . Path . StartsWith ( content . Path ) & & x . Trashed = = false ) ;
2017-01-27 11:42:25 +01:00
return repository . GetByPublishedVersion ( query ) ;
2013-01-30 12:11:10 -01:00
}
}
2012-12-15 11:04:03 -01:00
#endregion
#region Private Methods
2017-01-05 10:29:03 +11:00
/// <summary>
/// Hack: This is used to fix some data if an entity's properties are invalid/corrupt
/// </summary>
/// <param name="content"></param>
private void QuickUpdate ( IContent content )
{
if ( content = = null ) throw new ArgumentNullException ( "content" ) ;
if ( content . HasIdentity = = false ) throw new InvalidOperationException ( "Cannot update an entity without an Identity" ) ;
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2017-01-05 10:29:03 +11:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
repository . AddOrUpdate ( content ) ;
2017-01-05 10:29:03 +11:00
uow . Commit ( ) ;
}
}
2015-01-19 15:12:34 +11:00
private void Audit ( AuditType type , string message , int userId , int objectId )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2015-01-19 15:12:34 +11:00
{
2017-01-25 13:34:41 +01:00
var auditRepo = RepositoryFactory . CreateAuditRepository ( uow ) ;
2015-01-19 15:12:34 +11:00
auditRepo . AddOrUpdate ( new AuditItem ( objectId , message , type , userId ) ) ;
uow . Commit ( ) ;
}
}
2016-01-14 18:25:14 +01:00
//TODO: All of this needs to be moved to the repository
2014-04-23 20:19:36 +10:00
private void PerformMove ( IContent content , int parentId , int userId , ICollection < MoveEventInfo < IContent > > moveInfo )
{
//add a tracking item to use in the Moved event
moveInfo . Add ( new MoveEventInfo < IContent > ( content , content . Path , parentId ) ) ;
content . WriterId = userId ;
2015-01-04 21:02:20 +00:00
if ( parentId = = Constants . System . Root )
2014-04-23 20:19:36 +10:00
{
2015-01-04 21:02:20 +00:00
content . Path = string . Concat ( Constants . System . Root , "," , content . Id ) ;
2014-04-23 20:19:36 +10:00
content . Level = 1 ;
}
else
{
var parent = GetById ( parentId ) ;
content . Path = string . Concat ( parent . Path , "," , content . Id ) ;
content . Level = parent . Level + 1 ;
}
//If Content is being moved away from Recycle Bin, its state should be un-trashed
if ( content . Trashed & & parentId ! = Constants . System . RecycleBinContent )
{
content . ChangeTrashedState ( false , parentId ) ;
}
else
{
content . ParentId = parentId ;
}
//If Content is published, it should be (re)published from its new location
if ( content . Published )
{
//If Content is Publishable its saved and published
//otherwise we save the content without changing the publish state, and generate new xml because the Path, Level and Parent has changed.
if ( IsPublishable ( content ) )
{
//TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine
SaveAndPublish ( content , userId ) ;
}
else
{
//TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine
Save ( content , false , userId ) ;
2017-01-26 18:30:00 +01:00
//TODO: This shouldn't be here! This needs to be part of the repository logic but in order to fix this we need to
2014-04-29 11:11:48 +10:00
// change how this method calls "Save" as it needs to save using an internal method
2015-01-19 18:37:48 +11:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2014-04-23 20:19:36 +10:00
{
2014-09-30 18:46:02 +10:00
var xml = _entitySerializer . Serialize ( this , _dataTypeService , _userService , content ) ;
2014-04-28 17:28:40 +10:00
2015-07-06 15:32:06 +02:00
var poco = new ContentXmlDto { NodeId = content . Id , Xml = xml . ToDataString ( ) } ;
2014-04-23 20:19:36 +10:00
var exists =
uow . Database . FirstOrDefault < ContentXmlDto > ( "WHERE nodeId = @Id" , new { Id = content . Id } ) ! =
null ;
int result = exists
? uow . Database . Update ( poco )
: Convert . ToInt32 ( uow . Database . Insert ( poco ) ) ;
2017-01-25 13:34:41 +01:00
uow . Commit ( ) ;
2014-04-23 20:19:36 +10:00
}
}
}
else
{
//TODO: This is raising events, probably not desirable as this costs performance for event listeners like Examine
Save ( content , userId ) ;
}
//Ensure that Path and Level is updated on children
var children = GetChildren ( content . Id ) . ToArray ( ) ;
if ( children . Any ( ) )
{
foreach ( var child in children )
{
PerformMove ( child , content . Id , userId , moveInfo ) ;
}
}
}
2014-01-09 10:33:35 +11:00
/// <summary>
/// Publishes a <see cref="IContent"/> object and all its children
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish along with its children</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
2017-01-26 18:30:00 +01:00
/// <param name="includeUnpublished">If set to true, this will also publish descendants that are completely unpublished, normally this will only publish children that have previously been published</param>
2014-01-09 10:33:35 +11:00
/// <returns>
/// A list of publish statues. If the parent document is not valid or cannot be published because it's parent(s) is not published
/// then the list will only contain one status item, otherwise it will contain status items for it and all of it's descendants that
/// are to be published.
/// </returns>
2013-10-10 09:35:23 +11:00
private IEnumerable < Attempt < PublishStatus > > PublishWithChildrenDo (
2013-02-11 20:07:23 +06:00
IContent content , int userId = 0 , bool includeUnpublished = false )
2013-01-22 11:23:33 -01:00
{
2014-01-09 10:33:35 +11:00
if ( content = = null ) throw new ArgumentNullException ( "content" ) ;
2013-02-11 03:55:58 +06:00
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2014-01-09 10:33:35 +11:00
using ( new WriteLock ( Locker ) )
{
2017-01-05 10:29:03 +11:00
//Hack: this ensures that the entity's path is valid and if not it fixes/persists it
//see: http://issues.umbraco.org/issue/U4-9336
content . EnsureValidPath ( Logger , entity = > GetById ( entity . ParentId ) , QuickUpdate ) ;
2013-03-19 23:46:13 +06:00
var result = new List < Attempt < PublishStatus > > ( ) ;
2013-02-09 04:33:56 +06:00
2013-03-19 23:46:13 +06:00
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
2015-01-04 21:02:20 +00:00
if ( content . ParentId ! = Constants . System . Root & & content . ParentId ! = Constants . System . RecycleBinContent & & IsPublishable ( content ) = = false )
2013-03-19 23:46:13 +06:00
{
2015-01-19 18:37:48 +11:00
Logger . Info < ContentService > (
2013-03-19 23:46:13 +06:00
string . Format (
"Content '{0}' with Id '{1}' could not be published because its parent or one of its ancestors is not published." ,
content . Name , content . Id ) ) ;
2015-08-04 14:52:34 +02:00
result . Add ( Attempt . Fail ( new PublishStatus ( content , PublishStatusType . FailedPathNotPublished , evtMsgs ) ) ) ;
2013-03-19 23:46:13 +06:00
return result ;
}
2013-01-22 11:23:33 -01:00
2013-03-19 23:46:13 +06:00
//Content contains invalid property values and can therefore not be published - fire event?
if ( ! content . IsValid ( ) )
{
2015-01-19 18:37:48 +11:00
Logger . Info < ContentService > (
2013-03-19 23:46:13 +06:00
string . Format ( "Content '{0}' with Id '{1}' could not be published because of invalid properties." ,
content . Name , content . Id ) ) ;
2013-08-02 16:23:32 +10:00
result . Add (
2013-09-04 17:14:06 +02:00
Attempt . Fail (
2015-08-04 14:52:34 +02:00
new PublishStatus ( content , PublishStatusType . FailedContentInvalid , evtMsgs )
2015-07-29 15:12:12 +02:00
{
InvalidProperties = ( ( ContentBase ) content ) . LastInvalidProperties
} ) ) ;
2013-03-19 23:46:13 +06:00
return result ;
}
2013-01-22 11:23:33 -01:00
2013-03-19 23:46:13 +06:00
//Consider creating a Path query instead of recursive method:
//var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(content.Path));
2013-01-22 11:23:33 -01:00
2013-03-19 23:46:13 +06:00
var updated = new List < IContent > ( ) ;
var list = new List < IContent > ( ) ;
list . Add ( content ) ; //include parent item
list . AddRange ( GetDescendants ( content ) ) ;
2013-01-22 11:23:33 -01:00
2017-01-25 16:49:44 +11:00
var internalStrategy = _publishingStrategy ;
2013-03-19 23:46:13 +06:00
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-01-22 11:23:33 -01:00
{
2017-01-27 11:42:25 +01:00
//Publish and then update the database with new status
var publishedOutcome = internalStrategy . PublishWithChildren ( uow , list , userId , includeUnpublished ) . ToArray ( ) ;
var published = publishedOutcome
. Where ( x = > x . Success | | x . Result . StatusType = = PublishStatusType . SuccessAlreadyPublished )
// ensure proper order (for events) - cannot publish a child before its parent!
. OrderBy ( x = > x . Result . ContentItem . Level )
. ThenBy ( x = > x . Result . ContentItem . SortOrder ) ;
2017-02-03 12:23:32 +11:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-06-27 10:56:45 +02:00
//NOTE The Publish with subpages-dialog was used more as a republish-type-thing, so we'll have to include PublishStatusType.SuccessAlreadyPublished
//in the updated-list, so the Published event is triggered with the expected set of pages and the xml is updated.
2015-09-08 18:38:20 +02:00
foreach ( var item in published )
2013-03-19 23:46:13 +06:00
{
item . Result . ContentItem . WriterId = userId ;
repository . AddOrUpdate ( item . Result . ContentItem ) ;
2014-04-29 11:11:48 +10:00
//add or update a preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( item . Result . ContentItem , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2014-04-29 11:11:48 +10:00
//add or update the published xml
2014-09-30 18:46:02 +10:00
repository . AddOrUpdateContentXml ( item . Result . ContentItem , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-03-19 23:46:13 +06:00
updated . Add ( item . Result . ContentItem ) ;
}
2013-01-22 11:23:33 -01:00
2013-03-19 23:46:13 +06:00
uow . Commit ( ) ;
2013-01-22 11:23:33 -01:00
2017-01-25 16:49:44 +11:00
//Save xml to db and call following method to fire event:
_publishingStrategy . PublishingFinalized ( uow , updated , false ) ;
2013-01-22 11:23:33 -01:00
2017-01-27 11:42:25 +01:00
Audit ( AuditType . Publish , "Publish with Children performed by user" , userId , content . Id ) ;
return publishedOutcome ;
}
2014-01-09 10:33:35 +11:00
}
2013-01-22 11:23:33 -01:00
}
/// <summary>
/// UnPublishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="omitCacheRefresh">Optional boolean to avoid having the cache refreshed when calling this Unpublish method. By default this method will update the cache.</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if unpublishing succeeded, otherwise False</returns>
2015-07-29 15:12:12 +02:00
private Attempt < UnPublishStatus > UnPublishDo ( IContent content , bool omitCacheRefresh = false , int userId = 0 )
2013-01-22 11:23:33 -01:00
{
2015-01-20 19:57:13 +01:00
var newest = GetById ( content . Id ) ; // ensure we have the newest version
if ( content . Version ! = newest . Version ) // but use the original object if it's already the newest version
content = newest ;
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2016-02-26 14:30:32 +00:00
2015-01-19 13:40:21 +01:00
var published = content . Published ? content : GetPublishedVersion ( content . Id ) ; // get the published version
if ( published = = null )
2013-01-22 11:23:33 -01:00
{
2015-08-04 14:52:34 +02:00
return Attempt . Succeed ( new UnPublishStatus ( content , UnPublishedStatusType . SuccessAlreadyUnPublished , evtMsgs ) ) ; // already unpublished
2015-07-29 15:12:12 +02:00
}
2016-02-26 14:30:32 +00:00
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2015-07-29 15:12:12 +02:00
{
2017-01-27 11:42:25 +01:00
var unpublished = _publishingStrategy . UnPublish ( uow , content , userId ) ;
if ( unpublished = = false )
{
uow . Commit ( ) ;
return Attempt . Fail ( new UnPublishStatus ( content , UnPublishedStatusType . FailedCancelledByEvent , evtMsgs ) ) ;
}
2017-02-03 12:23:32 +11:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-07-29 15:12:12 +02:00
content . WriterId = userId ;
repository . AddOrUpdate ( content ) ;
// is published is not newest, reset the published flag on published version
if ( published . Version ! = content . Version )
repository . ClearPublished ( published ) ;
repository . DeleteContentXml ( content ) ;
2013-01-22 11:23:33 -01:00
2015-07-29 15:12:12 +02:00
uow . Commit ( ) ;
2017-01-25 16:49:44 +11:00
//Delete xml from db? and call following method to fire event through PublishingStrategy to update cache
if ( omitCacheRefresh = = false )
_publishingStrategy . UnPublishingFinalized ( uow , content ) ;
2013-04-23 12:49:33 -02:00
}
2017-02-03 12:23:32 +11:00
2015-07-29 15:12:12 +02:00
Audit ( AuditType . UnPublish , "UnPublish performed by user" , userId , content . Id ) ;
2014-07-10 13:40:22 +10:00
2015-08-04 14:52:34 +02:00
return Attempt . Succeed ( new UnPublishStatus ( content , UnPublishedStatusType . Success , evtMsgs ) ) ;
2013-01-22 11:23:33 -01:00
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Saves and Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save and publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
2013-01-29 12:45:42 -01:00
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
2013-04-23 12:49:33 -02:00
/// <returns>True if publishing succeeded, otherwise False</returns>
2014-01-09 10:33:35 +11:00
private Attempt < PublishStatus > SaveAndPublishDo ( IContent content , int userId = 0 , bool raiseEvents = true )
2013-01-22 11:23:33 -01:00
{
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2017-01-19 17:18:06 +11:00
using ( new WriteLock ( Locker ) )
2013-01-29 12:45:42 -01:00
{
2017-01-26 18:30:00 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-02-11 03:55:58 +06:00
{
2017-01-26 18:30:00 +01:00
if ( raiseEvents )
2017-01-19 17:18:06 +11:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Saving , this , new SaveEventArgs < IContent > ( content , evtMsgs ) ) )
2017-01-26 18:30:00 +01:00
{
return Attempt . Fail ( new PublishStatus ( content , PublishStatusType . FailedCancelledByEvent , evtMsgs ) ) ;
}
2017-01-19 17:18:06 +11:00
}
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
//Has this content item previously been published? If so, we don't need to refresh the children
var previouslyPublished = content . HasIdentity & & HasPublishedVersion ( content . Id ) ; //content might not have an id
var publishStatus = new PublishStatus ( content , PublishStatusType . Success , evtMsgs ) ; //initially set to success
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
publishStatus . StatusType = CheckAndLogIsPublishable ( content ) ;
//if it is not successful, then check if the props are valid
2017-02-03 12:23:32 +11:00
if ( ( int ) publishStatus . StatusType < 10 )
2017-01-26 18:30:00 +01:00
{
//Content contains invalid property values and can therefore not be published - fire event?
publishStatus . StatusType = CheckAndLogIsValid ( content ) ;
//set the invalid properties (if there are any)
2017-02-03 12:23:32 +11:00
publishStatus . InvalidProperties = ( ( ContentBase ) content ) . LastInvalidProperties ;
2017-01-26 18:30:00 +01:00
}
//if we're still successful, then publish using the strategy
if ( publishStatus . StatusType = = PublishStatusType . Success )
{
//Publish and then update the database with new status
2017-01-27 11:42:25 +01:00
var publishResult = _publishingStrategy . Publish ( uow , content , userId ) ;
2017-01-26 18:30:00 +01:00
//set the status type to the publish result
publishStatus . StatusType = publishResult . Result . StatusType ;
}
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
//we are successfully published if our publishStatus is still Successful
bool published = publishStatus . StatusType = = PublishStatusType . Success ;
2013-01-22 11:23:33 -01:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2015-10-29 15:20:35 +00:00
if ( published = = false )
{
content . ChangePublishedState ( PublishedState . Saved ) ;
}
2013-03-19 23:46:13 +06:00
//Since this is the Save and Publish method, the content should be saved even though the publish fails or isn't allowed
2013-11-25 13:22:35 +11:00
if ( content . HasIdentity = = false )
{
content . CreatorId = userId ;
}
2013-03-19 23:46:13 +06:00
content . WriterId = userId ;
repository . AddOrUpdate ( content ) ;
2014-04-28 17:28:40 +10:00
//Generate a new preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2014-08-20 17:01:12 +02:00
2013-03-19 23:46:13 +06:00
if ( published )
{
//Content Xml
2014-09-30 18:46:02 +10:00
repository . AddOrUpdateContentXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2013-03-19 23:46:13 +06:00
}
2014-04-28 17:28:40 +10:00
uow . Commit ( ) ;
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
if ( raiseEvents )
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Saved , this , new SaveEventArgs < IContent > ( content , false , evtMsgs ) ) ;
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
//Save xml to db and call following method to fire event through PublishingStrategy to update cache
if ( published )
{
2017-01-25 16:49:44 +11:00
_publishingStrategy . PublishingFinalized ( uow , content ) ;
2017-01-26 18:30:00 +01:00
}
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
//We need to check if children and their publish state to ensure that we 'republish' content that was previously published
if ( published & & previouslyPublished = = false & & HasChildren ( content . Id ) )
{
var descendants = GetPublishedDescendants ( content ) ;
2013-01-22 11:23:33 -01:00
2017-01-25 16:49:44 +11:00
_publishingStrategy . PublishingFinalized ( uow , descendants , false ) ;
2017-01-26 18:30:00 +01:00
}
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
Audit ( AuditType . Publish , "Save and Publish performed by user" , userId , content . Id ) ;
2013-01-22 11:23:33 -01:00
2017-01-26 18:30:00 +01:00
return Attempt . If ( publishStatus . StatusType = = PublishStatusType . Success , publishStatus ) ;
}
2014-01-09 10:33:35 +11:00
}
2013-01-22 11:23:33 -01:00
}
2013-04-23 12:49:33 -02:00
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="changeState">Boolean indicating whether or not to change the Published state upon saving</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
2013-01-29 12:45:42 -01:00
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
2015-07-24 11:44:09 +02:00
private Attempt < OperationStatus > Save ( IContent content , bool changeState , int userId = 0 , bool raiseEvents = true )
2013-01-21 14:50:58 -01:00
{
2015-08-04 14:52:34 +02:00
var evtMsgs = EventMessagesFactory . Get ( ) ;
2013-04-23 12:49:33 -02:00
using ( new WriteLock ( Locker ) )
{
2017-01-25 13:34:41 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( ) )
2013-03-19 23:46:13 +06:00
{
2017-01-26 18:30:00 +01:00
if ( raiseEvents )
2017-01-19 17:18:06 +11:00
{
2017-01-31 10:37:37 +01:00
if ( uow . Events . DispatchCancelable ( Saving , this , new SaveEventArgs < IContent > ( content , evtMsgs ) ) )
2017-01-26 18:30:00 +01:00
{
return OperationStatus . Cancelled ( evtMsgs ) ;
}
2017-02-03 12:23:32 +11:00
}
2013-01-21 14:50:58 -01:00
2017-02-03 12:23:32 +11:00
if ( string . IsNullOrWhiteSpace ( content . Name ) )
{
throw new ArgumentException ( "Cannot save content with empty name." ) ;
2017-01-19 17:18:06 +11:00
}
2013-01-21 14:50:58 -01:00
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentRepository ( uow ) ;
2013-11-25 13:22:35 +11:00
if ( content . HasIdentity = = false )
{
content . CreatorId = userId ;
}
2013-03-19 23:46:13 +06:00
content . WriterId = userId ;
2013-01-21 14:50:58 -01:00
2013-04-15 07:33:26 -02:00
//Only change the publish state if the "previous" version was actually published or marked as unpublished
if ( changeState & & ( content . Published | | ( ( Content ) content ) . PublishedState = = PublishedState . Unpublished ) )
2013-03-19 23:46:13 +06:00
content . ChangePublishedState ( PublishedState . Saved ) ;
2013-01-21 14:50:58 -01:00
2013-03-19 23:46:13 +06:00
repository . AddOrUpdate ( content ) ;
2013-01-30 14:22:07 -01:00
2014-04-28 17:28:40 +10:00
//Generate a new preview
2014-09-30 18:46:02 +10:00
repository . AddOrUpdatePreviewXml ( content , c = > _entitySerializer . Serialize ( this , _dataTypeService , _userService , c ) ) ;
2014-04-28 17:28:40 +10:00
uow . Commit ( ) ;
2013-01-21 14:50:58 -01:00
2017-01-26 18:30:00 +01:00
if ( raiseEvents )
2017-01-31 10:37:37 +01:00
uow . Events . Dispatch ( Saved , this , new SaveEventArgs < IContent > ( content , false , evtMsgs ) ) ;
2013-01-21 14:50:58 -01:00
2017-01-26 18:30:00 +01:00
Audit ( AuditType . Save , "Save Content performed by user" , userId , content . Id ) ;
}
2015-07-24 11:44:09 +02:00
2016-01-27 15:22:58 +01:00
return OperationStatus . Success ( evtMsgs ) ;
2013-04-23 12:49:33 -02:00
}
2012-12-15 11:04:03 -01:00
}
2012-12-11 23:19:07 +05:00
2012-12-18 11:46:05 -01:00
/// <summary>
/// Checks if the passed in <see cref="IContent"/> can be published based on the anscestors publish state.
/// </summary>
/// <remarks>
/// Check current is only used when falling back to checking the Parent of non-saved content, as
/// non-saved content doesn't have a valid path yet.
/// </remarks>
/// <param name="content"><see cref="IContent"/> to check if anscestors are published</param>
/// <param name="checkCurrent">Boolean indicating whether the passed in content should also be checked for published versions</param>
/// <returns>True if the Content can be published, otherwise False</returns>
private bool IsPublishable ( IContent content , bool checkCurrent )
{
var ids = content . Path . Split ( ',' ) . Select ( int . Parse ) . ToList ( ) ;
foreach ( var id in ids )
{
//If Id equals that of the recycle bin we return false because nothing in the bin can be published
2015-01-04 21:02:20 +00:00
if ( id = = Constants . System . RecycleBinContent )
2012-12-18 11:46:05 -01:00
return false ;
2012-12-27 17:29:54 -01:00
//We don't check the System Root, so just continue
2015-01-04 21:02:20 +00:00
if ( id = = Constants . System . Root ) continue ;
2012-12-18 11:46:05 -01:00
//If the current id equals that of the passed in content and if current shouldn't be checked we skip it.
if ( checkCurrent = = false & & id = = content . Id ) continue ;
2012-12-27 17:29:54 -01:00
//Check if the content for the current id is published - escape the loop if we encounter content that isn't published
2013-01-10 13:55:42 -01:00
var hasPublishedVersion = HasPublishedVersion ( id ) ;
2012-12-18 11:46:05 -01:00
if ( hasPublishedVersion = = false )
return false ;
}
return true ;
}
2013-05-23 11:34:06 -02:00
private PublishStatusType CheckAndLogIsPublishable ( IContent content )
2013-05-23 10:29:44 -02:00
{
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
2015-01-04 21:02:20 +00:00
if ( content . ParentId ! = Constants . System . Root & & content . ParentId ! = Constants . System . RecycleBinContent & & IsPublishable ( content ) = = false )
2013-05-23 10:29:44 -02:00
{
2015-01-19 18:37:48 +11:00
Logger . Info < ContentService > (
2013-05-23 10:29:44 -02:00
string . Format (
"Content '{0}' with Id '{1}' could not be published because its parent is not published." ,
content . Name , content . Id ) ) ;
2013-05-23 11:34:06 -02:00
return PublishStatusType . FailedPathNotPublished ;
2013-05-23 10:29:44 -02:00
}
2015-10-29 15:21:15 +00:00
else if ( content . ExpireDate . HasValue & & content . ExpireDate . Value > DateTime . MinValue & & DateTime . Now > content . ExpireDate . Value )
{
Logger . Info < ContentService > (
string . Format (
"Content '{0}' with Id '{1}' has expired and could not be published." ,
content . Name , content . Id ) ) ;
return PublishStatusType . FailedHasExpired ;
}
else if ( content . ReleaseDate . HasValue & & content . ReleaseDate . Value > DateTime . MinValue & & content . ReleaseDate . Value > DateTime . Now )
{
Logger . Info < ContentService > (
string . Format (
"Content '{0}' with Id '{1}' is awaiting release and could not be published." ,
content . Name , content . Id ) ) ;
return PublishStatusType . FailedAwaitingRelease ;
}
2013-05-23 10:29:44 -02:00
2013-05-23 11:34:06 -02:00
return PublishStatusType . Success ;
2013-05-23 10:29:44 -02:00
}
2013-05-23 11:34:06 -02:00
private PublishStatusType CheckAndLogIsValid ( IContent content )
2013-05-23 10:29:44 -02:00
{
//Content contains invalid property values and can therefore not be published - fire event?
if ( content . IsValid ( ) = = false )
{
2015-01-19 18:37:48 +11:00
Logger . Info < ContentService > (
2013-05-23 10:29:44 -02:00
string . Format (
"Content '{0}' with Id '{1}' could not be published because of invalid properties." ,
content . Name , content . Id ) ) ;
2013-05-23 11:34:06 -02:00
return PublishStatusType . FailedContentInvalid ;
2013-05-23 10:29:44 -02:00
}
2013-05-23 11:34:06 -02:00
return PublishStatusType . Success ;
2014-08-20 17:01:12 +02:00
}
2013-05-23 10:29:44 -02:00
2013-03-20 13:51:46 -01:00
private IContentType FindContentTypeByAlias ( string contentTypeAlias )
{
2017-01-27 11:42:25 +01:00
using ( var uow = UowProvider . GetUnitOfWork ( commit : true ) )
2013-03-20 13:51:46 -01:00
{
2017-01-25 13:34:41 +01:00
var repository = RepositoryFactory . CreateContentTypeRepository ( uow ) ;
2013-03-20 13:51:46 -01:00
var query = Query < IContentType > . Builder . Where ( x = > x . Alias = = contentTypeAlias ) ;
var types = repository . GetByQuery ( query ) ;
2013-05-27 08:45:07 -02:00
if ( types . Any ( ) = = false )
2013-03-20 13:51:46 -01:00
throw new Exception (
string . Format ( "No ContentType matching the passed in Alias: '{0}' was found" ,
contentTypeAlias ) ) ;
var contentType = types . First ( ) ;
if ( contentType = = null )
throw new Exception ( string . Format ( "ContentType matching the passed in Alias: '{0}' was null" ,
contentTypeAlias ) ) ;
return contentType ;
}
}
2012-12-15 11:04:03 -01:00
#endregion
2013-04-04 07:16:33 -02:00
#region Proxy Event Handlers
/// <summary>
/// Occurs before publish.
/// </summary>
/// <remarks>Proxy to the real event on the <see cref="PublishingStrategy"/></remarks>
public static event TypedEventHandler < IPublishingStrategy , PublishEventArgs < IContent > > Publishing
{
add { PublishingStrategy . Publishing + = value ; }
remove { PublishingStrategy . Publishing - = value ; }
}
/// <summary>
/// Occurs after publish.
/// </summary>
/// <remarks>Proxy to the real event on the <see cref="PublishingStrategy"/></remarks>
public static event TypedEventHandler < IPublishingStrategy , PublishEventArgs < IContent > > Published
{
add { PublishingStrategy . Published + = value ; }
remove { PublishingStrategy . Published - = value ; }
}
/// <summary>
/// Occurs before unpublish.
/// </summary>
/// <remarks>Proxy to the real event on the <see cref="PublishingStrategy"/></remarks>
public static event TypedEventHandler < IPublishingStrategy , PublishEventArgs < IContent > > UnPublishing
{
add { PublishingStrategy . UnPublishing + = value ; }
remove { PublishingStrategy . UnPublishing - = value ; }
}
/// <summary>
/// Occurs after unpublish.
/// </summary>
/// <remarks>Proxy to the real event on the <see cref="PublishingStrategy"/></remarks>
public static event TypedEventHandler < IPublishingStrategy , PublishEventArgs < IContent > > UnPublished
{
add { PublishingStrategy . UnPublished + = value ; }
remove { PublishingStrategy . UnPublished - = value ; }
}
#endregion
2012-12-15 11:04:03 -01:00
#region Event Handlers
/// <summary>
2013-04-23 12:49:33 -02:00
/// Occurs before Delete
2017-01-26 18:30:00 +01:00
/// </summary>
2013-04-23 12:49:33 -02:00
public static event TypedEventHandler < IContentService , DeleteEventArgs < IContent > > Deleting ;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler < IContentService , DeleteEventArgs < IContent > > Deleted ;
/// <summary>
/// Occurs before Delete Versions
2017-01-26 18:30:00 +01:00
/// </summary>
2013-04-23 12:49:33 -02:00
public static event TypedEventHandler < IContentService , DeleteRevisionsEventArgs > DeletingVersions ;
/// <summary>
/// Occurs after Delete Versions
/// </summary>
public static event TypedEventHandler < IContentService , DeleteRevisionsEventArgs > DeletedVersions ;
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler < IContentService , SaveEventArgs < IContent > > Saving ;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler < IContentService , SaveEventArgs < IContent > > Saved ;
/// <summary>
/// Occurs before Create
/// </summary>
2014-02-25 01:45:41 +11:00
[Obsolete("Use the Created event instead, the Creating and Created events both offer the same functionality, Creating event has been deprecated.")]
2013-04-23 12:49:33 -02:00
public static event TypedEventHandler < IContentService , NewEventArgs < IContent > > Creating ;
/// <summary>
/// Occurs after Create
/// </summary>
2012-12-15 11:17:32 -01:00
/// <remarks>
2013-08-14 16:12:13 +02:00
/// Please note that the Content object has been created, but might not have been saved
2012-12-15 11:17:32 -01:00
/// so it does not have an identity yet (meaning no Id has been set).
/// </remarks>
2013-04-23 12:49:33 -02:00
public static event TypedEventHandler < IContentService , NewEventArgs < IContent > > Created ;
/// <summary>
/// Occurs before Copy
/// </summary>
public static event TypedEventHandler < IContentService , CopyEventArgs < IContent > > Copying ;
/// <summary>
/// Occurs after Copy
/// </summary>
public static event TypedEventHandler < IContentService , CopyEventArgs < IContent > > Copied ;
/// <summary>
/// Occurs before Content is moved to Recycle Bin
/// </summary>
public static event TypedEventHandler < IContentService , MoveEventArgs < IContent > > Trashing ;
/// <summary>
/// Occurs after Content is moved to Recycle Bin
/// </summary>
public static event TypedEventHandler < IContentService , MoveEventArgs < IContent > > Trashed ;
/// <summary>
/// Occurs before Move
/// </summary>
public static event TypedEventHandler < IContentService , MoveEventArgs < IContent > > Moving ;
/// <summary>
/// Occurs after Move
/// </summary>
public static event TypedEventHandler < IContentService , MoveEventArgs < IContent > > Moved ;
/// <summary>
/// Occurs before Rollback
/// </summary>
public static event TypedEventHandler < IContentService , RollbackEventArgs < IContent > > RollingBack ;
/// <summary>
/// Occurs after Rollback
/// </summary>
public static event TypedEventHandler < IContentService , RollbackEventArgs < IContent > > RolledBack ;
/// <summary>
/// Occurs before Send to Publish
/// </summary>
public static event TypedEventHandler < IContentService , SendToPublishEventArgs < IContent > > SendingToPublish ;
/// <summary>
/// Occurs after Send to Publish
/// </summary>
public static event TypedEventHandler < IContentService , SendToPublishEventArgs < IContent > > SentToPublish ;
2013-08-14 16:12:13 +02:00
/// <summary>
/// Occurs before the Recycle Bin is emptied
/// </summary>
public static event TypedEventHandler < IContentService , RecycleBinEventArgs > EmptyingRecycleBin ;
/// <summary>
/// Occurs after the Recycle Bin has been Emptied
/// </summary>
public static event TypedEventHandler < IContentService , RecycleBinEventArgs > EmptiedRecycleBin ;
2013-04-23 12:49:33 -02:00
#endregion
}
2012-10-03 12:51:32 -02:00
}