using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Web.Mvc; using System.Web.Routing; using Orchard.ContentManagement; using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Models; using Orchard.Core.Common.Models; using Orchard.Core.Containers.Models; using Orchard.Core.Contents.Settings; using Orchard.Core.Contents.ViewModels; using Orchard.Core.Routable.Models; using Orchard.Data; using Orchard.DisplayManagement; using Orchard.Localization; using Orchard.Logging; using Orchard.Mvc.Extensions; using Orchard.Mvc.Html; using Orchard.UI.Navigation; using Orchard.UI.Notify; using Orchard.Settings; namespace Orchard.Core.Contents.Controllers { [ValidateInput(false)] public class AdminController : Controller, IUpdateModel { private readonly IContentManager _contentManager; private readonly IContentDefinitionManager _contentDefinitionManager; private readonly ITransactionManager _transactionManager; private readonly ISiteService _siteService; public AdminController( IOrchardServices orchardServices, IContentManager contentManager, IContentDefinitionManager contentDefinitionManager, ITransactionManager transactionManager, ISiteService siteService, IShapeFactory shapeFactory) { Services = orchardServices; _contentManager = contentManager; _contentDefinitionManager = contentDefinitionManager; _transactionManager = transactionManager; _siteService = siteService; T = NullLocalizer.Instance; Logger = NullLogger.Instance; Shape = shapeFactory; } dynamic Shape { get; set; } public IOrchardServices Services { get; private set; } public Localizer T { get; set; } public ILogger Logger { get; set; } public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) { Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); var query = _contentManager.Query(VersionOptions.Latest, GetCreatableTypes(false).Select(ctd => ctd.Name).ToArray()); if (!string.IsNullOrEmpty(model.TypeName)) { var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.TypeName); if (contentTypeDefinition == null) return HttpNotFound(); model.TypeDisplayName = !string.IsNullOrWhiteSpace(contentTypeDefinition.DisplayName) ? contentTypeDefinition.DisplayName : contentTypeDefinition.Name; query = query.ForType(model.TypeName); } switch (model.Options.OrderBy) { case ContentsOrder.Modified: //query = query.OrderByDescending(ci => ci.ContentItemRecord.Versions.Single(civr => civr.Latest).Id); query = query.OrderByDescending(cr => cr.ModifiedUtc); break; case ContentsOrder.Published: query = query.OrderByDescending(cr => cr.PublishedUtc); break; case ContentsOrder.Created: //query = query.OrderByDescending(ci => ci.Id); query = query.OrderByDescending(cr => cr.CreatedUtc); break; } model.Options.SelectedFilter = model.TypeName; model.Options.FilterOptions = GetCreatableTypes(false) .Select(ctd => new KeyValuePair(ctd.Name, ctd.DisplayName)) .ToList().OrderBy(kvp => kvp.Value); var pagerShape = Shape.Pager(pager).TotalItemCount(query.Count()); var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList(); var list = Shape.List(); list.AddRange(pageOfContentItems.Select(ci => _contentManager.BuildDisplay(ci, "SummaryAdmin"))); dynamic viewModel = Shape.ViewModel() .ContentItems(list) .Pager(pagerShape) .Options(model.Options) .TypeDisplayName(model.TypeDisplayName ?? ""); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View((object)viewModel); } private IEnumerable GetCreatableTypes(bool andContainable) { return _contentDefinitionManager.ListTypeDefinitions().Where(ctd => ctd.Settings.GetModel().Creatable && (!andContainable || ctd.Parts.Any(p => p.PartDefinition.Name == "ContainablePart")) ); } [HttpPost, ActionName("List")] [FormValueRequired("submit.Filter")] public ActionResult ListFilterPOST(ContentOptions options) { var routeValues = ControllerContext.RouteData.Values; if (options != null) { routeValues["Options.OrderBy"] = options.OrderBy; //todo: don't hard-code the key if (GetCreatableTypes(false).Any(ctd => string.Equals(ctd.Name, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) { routeValues["id"] = options.SelectedFilter; } else { routeValues.Remove("id"); } } return RedirectToAction("List", routeValues); } [HttpPost, ActionName("List")] [FormValueRequired("submit.BulkEdit")] public ActionResult ListPOST(ContentOptions options, IEnumerable itemIds, string returnUrl) { try { if (itemIds != null) { switch (options.BulkAction) { case ContentsBulkAction.None: break; case ContentsBulkAction.PublishNow: foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { if (!Services.Authorizer.Authorize(Permissions.PublishContent, item, T("Couldn't publish selected content."))) { _transactionManager.Cancel(); return new HttpUnauthorizedResult(); } _contentManager.Publish(item); } Services.Notifier.Information(T("Content successfully published.")); break; case ContentsBulkAction.Unpublish: foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { if (!Services.Authorizer.Authorize(Permissions.PublishContent, item, T("Couldn't unpublish selected content."))) { _transactionManager.Cancel(); return new HttpUnauthorizedResult(); } _contentManager.Unpublish(item); } Services.Notifier.Information(T("Content successfully unpublished.")); break; case ContentsBulkAction.Remove: foreach (var item in itemIds.Select(itemId => _contentManager.GetLatest(itemId))) { if (!Services.Authorizer.Authorize(Permissions.DeleteContent, item, T("Couldn't remove selected content."))) { _transactionManager.Cancel(); return new HttpUnauthorizedResult(); } _contentManager.Remove(item); } Services.Notifier.Information(T("Content successfully removed.")); break; default: throw new ArgumentOutOfRangeException(); } } } catch { _transactionManager.Cancel(); } return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); } ActionResult CreatableTypeList(int? containerId) { dynamic viewModel = Shape.ViewModel(ContentTypes: GetCreatableTypes(containerId.HasValue), ContainerId: containerId); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View("CreatableTypeList", (object)viewModel); } public ActionResult Create(string id, int? containerId) { if (string.IsNullOrEmpty(id)) return CreatableTypeList(containerId); var contentItem = _contentManager.New(id); if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Cannot create content"))) return new HttpUnauthorizedResult(); if (containerId.HasValue && contentItem.Is()) { var common = contentItem.As(); if (common != null) { common.Container = _contentManager.Get(containerId.Value); } } dynamic model = _contentManager.BuildEditor(contentItem); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View((object)model); } [HttpPost, ActionName("Create")] [FormValueRequired("submit.Save")] public ActionResult CreatePOST(string id, string returnUrl) { return CreatePOST(id, returnUrl, contentItem => { if (!contentItem.Has() && !contentItem.TypeDefinition.Settings.GetModel().Draftable) _contentManager.Publish(contentItem); }); } [HttpPost, ActionName("Create")] [FormValueRequired("submit.Publish")] public ActionResult CreateAndPublishPOST(string id, string returnUrl) { if (!Services.Authorizer.Authorize(Permissions.PublishOwnContent, T("Couldn't create content"))) return new HttpUnauthorizedResult(); return CreatePOST(id, returnUrl, contentItem => _contentManager.Publish(contentItem)); } private ActionResult CreatePOST(string id, string returnUrl, Action conditionallyPublish) { var contentItem = _contentManager.New(id); if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Couldn't create content"))) return new HttpUnauthorizedResult(); _contentManager.Create(contentItem, VersionOptions.Draft); dynamic model = _contentManager.UpdateEditor(contentItem, this); if (!ModelState.IsValid) { _transactionManager.Cancel(); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View((object)model); } conditionallyPublish(contentItem); Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("Your content has been created.") : T("Your {0} has been created.", contentItem.TypeDefinition.DisplayName)); if (!string.IsNullOrEmpty(returnUrl)) { return this.RedirectLocal(returnUrl); } var adminRouteValues = _contentManager.GetItemMetadata(contentItem).AdminRouteValues; return RedirectToRoute(adminRouteValues); } public ActionResult Edit(int id) { var contentItem = _contentManager.Get(id, VersionOptions.Latest); if (contentItem == null) return HttpNotFound(); if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Cannot edit content"))) return new HttpUnauthorizedResult(); dynamic model = _contentManager.BuildEditor(contentItem); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View((object)model); } [HttpPost, ActionName("Edit")] [FormValueRequired("submit.Save")] public ActionResult EditPOST(int id, string returnUrl) { return EditPOST(id, returnUrl, contentItem => { if (!contentItem.Has() && !contentItem.TypeDefinition.Settings.GetModel().Draftable) _contentManager.Publish(contentItem); }); } [HttpPost, ActionName("Edit")] [FormValueRequired("submit.Publish")] public ActionResult EditAndPublishPOST(int id, string returnUrl) { var content = _contentManager.Get(id, VersionOptions.DraftRequired); if (content == null) return HttpNotFound(); if (!Services.Authorizer.Authorize(Permissions.PublishContent, content, T("Couldn't publish content"))) return new HttpUnauthorizedResult(); return EditPOST(id, returnUrl, contentItem => _contentManager.Publish(contentItem)); } private ActionResult EditPOST(int id, string returnUrl, Action conditionallyPublish) { var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired); if (contentItem == null) return HttpNotFound(); if (!Services.Authorizer.Authorize(Permissions.EditContent, contentItem, T("Couldn't edit content"))) return new HttpUnauthorizedResult(); // store the previous route in case a back redirection is requested string previousRoute = null; if(contentItem.Has() &&!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl) // only if the original returnUrl is the content itself && String.Equals(returnUrl, Url.ItemDisplayUrl(contentItem), StringComparison.OrdinalIgnoreCase) ) { previousRoute = contentItem.As().Path; } dynamic model = _contentManager.UpdateEditor(contentItem, this); if (!ModelState.IsValid) { _transactionManager.Cancel(); // Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation. return View("Edit", (object)model); } conditionallyPublish(contentItem); // did the route change ? if (!string.IsNullOrWhiteSpace(returnUrl) && previousRoute != null && !String.Equals(contentItem.As().Path, previousRoute, StringComparison.OrdinalIgnoreCase)) { returnUrl = Url.ItemDisplayUrl(contentItem); } Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("Your content has been saved.") : T("Your {0} has been saved.", contentItem.TypeDefinition.DisplayName)); return this.RedirectLocal(returnUrl, () => RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } })); } public ActionResult Remove(int id, string returnUrl) { var contentItem = _contentManager.Get(id, VersionOptions.Latest); if (!Services.Authorizer.Authorize(Permissions.DeleteContent, contentItem, T("Couldn't remove content"))) return new HttpUnauthorizedResult(); if (contentItem != null) { _contentManager.Remove(contentItem); Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("That content has been removed.") : T("That {0} has been removed.", contentItem.TypeDefinition.DisplayName)); } return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); } [HttpPost] public ActionResult Publish(int id, string returnUrl) { var contentItem = _contentManager.GetLatest(id); if (contentItem == null) return HttpNotFound(); if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("Couldn't publish content"))) return new HttpUnauthorizedResult(); _contentManager.Publish(contentItem); Services.ContentManager.Flush(); Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("That content has been published.") : T("That {0} has been published.", contentItem.TypeDefinition.DisplayName)); return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); } [HttpPost] public ActionResult Unpublish(int id, string returnUrl) { var contentItem = _contentManager.GetLatest(id); if (contentItem == null) return HttpNotFound(); if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("Couldn't unpublish content"))) return new HttpUnauthorizedResult(); _contentManager.Unpublish(contentItem); Services.ContentManager.Flush(); Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName) ? T("That content has been unpublished.") : T("That {0} has been unpublished.", contentItem.TypeDefinition.DisplayName)); return this.RedirectLocal(returnUrl, () => RedirectToAction("List")); } bool IUpdateModel.TryUpdateModel(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { return TryUpdateModel(model, prefix, includeProperties, excludeProperties); } void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) { ModelState.AddModelError(key, errorMessage.ToString()); } } public class FormValueRequiredAttribute : ActionMethodSelectorAttribute { private readonly string _submitButtonName; public FormValueRequiredAttribute(string submitButtonName) { _submitButtonName = submitButtonName; } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { var value = controllerContext.HttpContext.Request.Form[_submitButtonName]; return !string.IsNullOrEmpty(value); } } }