diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/RecycleBinController.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/RecycleBinController.cs index de7d08ff7..c29626063 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/RecycleBinController.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/RecycleBinController.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Web.Mvc; using Orchard.AuditTrail.Helpers; using Orchard.AuditTrail.Services; -using Orchard.AuditTrail.Services.Models; using Orchard.AuditTrail.ViewModels; using Orchard.ContentManagement; using Orchard.Environment.Extensions; @@ -28,7 +27,12 @@ namespace Orchard.AuditTrail.Controllers private readonly IOrchardServices _services; private readonly IRecycleBin _recycleBin; - public RecycleBinController(IAuthorizer authorizer, IContentManager contentManager, INotifier notifier, IOrchardServices services, IRecycleBin recycleBin) + public RecycleBinController( + IAuthorizer authorizer, + IContentManager contentManager, + INotifier notifier, + IOrchardServices services, + IRecycleBin recycleBin) { _authorizer = authorizer; _contentManager = contentManager; @@ -42,12 +46,14 @@ namespace Orchard.AuditTrail.Controllers public Localizer T { get; set; } public ILogger Logger { get; set; } - public ActionResult Index(PagerParameters pagerParameters, AuditTrailOrderBy? orderBy = null) + public ActionResult Index(PagerParameters pagerParameters, string contentTypeName = null) { if (!_authorizer.Authorize(Permissions.ViewAuditTrail)) + { return new HttpUnauthorizedResult(); + } - var viewModel = SetupViewModel(new RecycleBinViewModel(), pagerParameters); + var viewModel = SetupViewModel(new RecycleBinViewModel(), pagerParameters, contentTypeName); return View(viewModel); } @@ -55,8 +61,11 @@ namespace Orchard.AuditTrail.Controllers public ActionResult Restore(int id, string returnUrl) { var contentItem = _contentManager.Get(id, VersionOptions.AllVersions); + if (!_authorizer.Authorize(Core.Contents.Permissions.PublishContent, contentItem)) + { return new HttpUnauthorizedResult(); + } var restoredContentItem = _recycleBin.Restore(contentItem); var restoredContentItemTitle = _contentManager.GetItemMetadata(restoredContentItem).DisplayText; @@ -104,14 +113,15 @@ namespace Orchard.AuditTrail.Controllers return RedirectToAction("Index"); } - private RecycleBinViewModel SetupViewModel(RecycleBinViewModel viewModel, PagerParameters pagerParameters) + private RecycleBinViewModel SetupViewModel(RecycleBinViewModel viewModel, PagerParameters pagerParameters, string contentTypeName = null) { var pager = new Pager(_services.WorkContext.CurrentSite, pagerParameters); - var removedContentItems = _recycleBin.List(pager.Page, pager.PageSize); - var pagershape = _services.New.Pager(pager).TotalItemCount(removedContentItems.TotalItemCount); + var removedContentItems = _recycleBin.List(pager.Page, pager.PageSize, contentTypeName); + var pagerShape = _services.New.Pager(pager).TotalItemCount(removedContentItems.TotalItemCount); + viewModel.FilterContentType = contentTypeName; viewModel.ContentItems = removedContentItems; - viewModel.Pager = pagershape; + viewModel.Pager = pagerShape; return viewModel; } @@ -160,6 +170,5 @@ namespace Orchard.AuditTrail.Controllers } } - } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IRecycleBin.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IRecycleBin.cs index 87a8c744d..33ec2b0fc 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IRecycleBin.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IRecycleBin.cs @@ -9,26 +9,27 @@ namespace Orchard.AuditTrail.Services /// /// Returns all removed content items. /// - IPageOfItems List(int page, int pageSize); + IPageOfItems List(int page, int pageSize, string contentTypeName = null); /// /// Returns all removed content items. /// - IPageOfItems List(int page, int pageSize) where T : class, IContent; + IPageOfItems List(int page, int pageSize, string contentTypeName = null) where T : class, IContent; /// /// Returns the specified list of content items from the recycle bin. /// - IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null); + IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null, string contentTypeName = null); /// /// Returns the specified list of content items from the recycle bin. /// - IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null) where T : class, IContent; + IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null, string contentTypeName = null) + where T : class, IContent; /// /// Restores the specified content item. /// ContentItem Restore(ContentItem contentItem); } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/RecycleBin.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/RecycleBin.cs index 8dd1a01e1..0f608dee8 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/RecycleBin.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/RecycleBin.cs @@ -4,7 +4,9 @@ using System.Linq; using NHibernate; using Orchard.Collections; using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData; using Orchard.Data; +using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; namespace Orchard.AuditTrail.Services @@ -14,21 +16,29 @@ namespace Orchard.AuditTrail.Services { private readonly ITransactionManager _transactionManager; private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ShellSettings _shellSettings; - public RecycleBin(ITransactionManager transactionManager, IContentManager contentManager) + public RecycleBin( + ITransactionManager transactionManager, + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + ShellSettings shellSettings) { _transactionManager = transactionManager; _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + _shellSettings = shellSettings; } - public IPageOfItems List(int page, int pageSize) + public IPageOfItems List(int page, int pageSize, string contentTypeName = null) { - return List(page, pageSize); + return List(page, pageSize, contentTypeName); } - public IPageOfItems List(int page, int pageSize) where T : class, IContent + public IPageOfItems List(int page, int pageSize, string contentTypeName = null) where T : class, IContent { - var query = GetDeletedVersionsQuery(); + var query = GetDeletedVersionsQuery(null, contentTypeName); var totalCount = query.List().Count; query.SetFirstResult((page - 1) * pageSize); @@ -44,14 +54,13 @@ namespace Orchard.AuditTrail.Services }; } - public IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null) - { - return GetMany(contentItemIds, hints); - } + public IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null, string contentTypeName = null) => + GetMany(contentItemIds, hints, contentTypeName); - public IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null) where T : class, IContent + public IEnumerable GetMany(IEnumerable contentItemIds, QueryHints hints = null, string contentTypeName = null) + where T : class, IContent { - var query = GetDeletedVersionsQuery(contentItemIds); + var query = GetDeletedVersionsQuery(contentItemIds, contentTypeName); return LoadContentItems(query, hints); } @@ -60,10 +69,10 @@ namespace Orchard.AuditTrail.Services var versions = contentItem.Record.Versions.OrderBy(x => x.Number).ToArray(); var lastVersion = versions.Last(); - if (lastVersion.Latest || lastVersion.Published) - throw new InvalidOperationException(string.Format("Cannot restore content item with ID {0} ftom the recycle bin, since that item is not deleted", contentItem.Id)); - - return _contentManager.Restore(contentItem, VersionOptions.Restore(lastVersion.Number, publish: false)); + return lastVersion.Latest || lastVersion.Published + ? throw new InvalidOperationException( + string.Format("Cannot restore content item with ID {0} from the recycle bin, since that item is not deleted.", contentItem.Id)) + : _contentManager.Restore(contentItem, VersionOptions.Restore(lastVersion.Number, publish: false)); } private IEnumerable LoadContentItems(IQuery query, QueryHints hints = null) where T : class, IContent @@ -73,22 +82,53 @@ namespace Orchard.AuditTrail.Services return _contentManager.GetManyByVersionId(versionIds, hints ?? QueryHints.Empty); } - private IQuery GetDeletedVersionsQuery(IEnumerable contentItemIds = null) + private IQuery GetDeletedVersionsQuery(IEnumerable contentItemIds = null, string contentTypeName = null) { var session = _transactionManager.GetSession(); // Select only the highest versions where both Published and Latest are false. - var select = - "select max(ContentItemVersionRecord.Id), ContentItemVersionRecord.ContentItemRecord.Id, max(ContentItemVersionRecord.Number) " + - "from Orchard.ContentManagement.Records.ContentItemVersionRecord ContentItemVersionRecord "; + var select = @" + select + max(contentItemVersionRecord.Id), + max(contentItemVersionRecord.Number), + contentItemVersionRecord.ContentItemRecord.Id + from + Orchard.ContentManagement.Records.ContentItemVersionRecord contentItemVersionRecord + left join + Orchard.Core.Common.Models.CommonPartVersionRecord commonPartVersionRecord + on + contentItemVersionRecord.Id = commonPartVersionRecord.Id"; - var filter = contentItemIds != null ? "where ContentItemVersionRecord.ContentItemRecord.Id in (:ids) " : default(string); + var filter = contentItemIds == null + ? default + : "\nwhere contentItemVersionRecord.ContentItemRecord.Id in (:ids)"; - var group = - "group by ContentItemVersionRecord.ContentItemRecord.Id " + - "having max(cast(Latest as Int32)) = 0 and max(cast(Published as Int32)) = 0 "; + // ContentTypeName is safe to use in a query directly, because it's a technical name without special characters. + if (contentTypeName != null + && _contentDefinitionManager.ListTypeDefinitions().Any(typeDefinition => typeDefinition.Name == contentTypeName)) + { + filter += $@" + {(filter == default ? "where" : "and")} + contentItemVersionRecord.ContentItemRecord.ContentType.Name = '{contentTypeName}'"; + } - var hql = string.Concat(select, filter, group); + var group = @" + group by + contentItemVersionRecord.ContentItemRecord.Id + having + max(cast(Latest as Int32)) = 0 + and max(cast(Published as Int32)) = 0"; + + var order = ""; + // SQL CE doesn't support ordering by an aggregate value. + if (!string.Equals(_shellSettings.DataProvider, "SqlCe", StringComparison.OrdinalIgnoreCase)) + { + order = @" + order by + max(commonPartVersionRecord.ModifiedUtc) desc"; + } + + var hql = string.Concat(select, filter, group, order); var query = session.CreateQuery(hql); if (contentItemIds != null) @@ -99,4 +139,4 @@ namespace Orchard.AuditTrail.Services return query; } } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/RecycleBinViewModel.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/RecycleBinViewModel.cs index e0a461210..7cb250a16 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/RecycleBinViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/RecycleBinViewModel.cs @@ -10,9 +10,11 @@ namespace Orchard.AuditTrail.ViewModels { SelectedContentItems = new List(0); } + + public string FilterContentType { get; set; } public RecycleBinCommand? RecycleBinCommand { get; set; } public IList SelectedContentItems { get; set; } public IPageOfItems ContentItems { get; set; } public dynamic Pager { get; set; } } -} \ No newline at end of file +} diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/RecycleBin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/RecycleBin/Index.cshtml index fec3c0d34..0ad17703d 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/RecycleBin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/RecycleBin/Index.cshtml @@ -1,37 +1,95 @@ -@using Orchard.AuditTrail.ViewModels +@using Orchard.AuditTrail.ViewModels @using Orchard.ContentManagement @using Orchard.Core.Common.Models @using Orchard.Localization.Services +@using Orchard.ContentManagement.MetaData +@using Orchard.Core.Contents.Settings + @model RecycleBinViewModel + @{ Style.Include("audittrail-recycle-bin.css"); Script.Require("ShapesBase"); Script.Include(("audittrail-recyclebin.js")); + Layout.Title = T("Audit Trail"); var contentItems = Model.ContentItems; var dateLocalizationServices = WorkContext.Resolve(); + + var listableContentTypeDefinitions = WorkContext.Resolve() + .ListTypeDefinitions() + .Where(definition => definition.Settings.GetModel().Listable) + .ToList(); + var selectedContentType = Request.QueryString["contentTypeName"]; + + var routeData = new RouteValueDictionary(ViewContext.RouteData.Values); + var queryString = ViewContext.HttpContext.Request.QueryString; + + if (queryString != null) + { + foreach (string key in queryString.Keys) + { + if (key != null && !routeData.ContainsKey(key)) + { + routeData[key] = queryString[key]; + } + } + } + + if (routeData.ContainsKey("id") && !HasText(routeData["id"])) + { + routeData.Remove("id"); + } } +
@Html.ValidationSummary() - @using (Html.BeginFormAntiForgeryPost()) { + + @using (Html.BeginForm("Index", "RecycleBin", FormMethod.Get)) + { +
+ + + + +
+ } + +
+ + @using (Html.BeginFormAntiForgeryPost()) + {
+
- @if (!contentItems.Any()) { + @if (!contentItems.Any()) + {

@T("There are no records to display.")

} - else { + else + { @@ -44,17 +102,18 @@ @{ var index = 0; - foreach (var contentItem in contentItems) { + foreach (var contentItem in contentItems) + { var isSelected = Model.SelectedContentItems.Where(x => x.Id == contentItem.Id && x.Selected).Select(x => x.Id).Any(); var commonPart = contentItem.As(); var removedText = commonPart != null ? dateLocalizationServices.ConvertToLocalizedString(commonPart.VersionModifiedUtc) : T("-").Text; var contentDisplayTextHtmlString = Html.ItemDisplayText(contentItem); var contentDisplayText = contentDisplayTextHtmlString != null ? contentDisplayTextHtmlString.ToString() : contentItem.ContentType; - var contentDisplayUrl = Url.Action("Detail", "Content", new {id = contentItem.Id, version = contentItem.Version, area = "Orchard.AuditTrail"}); + var contentDisplayUrl = Url.Action("Detail", "Content", new { id = contentItem.Id, version = contentItem.Version, area = "Orchard.AuditTrail" }); @@ -67,7 +126,7 @@ @Html.ActionLink(T("View Audit Trail").Text, "Index", "Admin", new { content = contentItem.Id, area = "Orchard.AuditTrail" }, null) @@ -83,4 +142,4 @@ @Display(Model.Pager) } - \ No newline at end of file +
- checked="checked"} /> + checked="checked" } /> @contentDisplayText @removedText