Incremental work on Dashboards.

This commit is contained in:
Sipke Schoorstra
2015-05-09 01:12:17 +02:00
parent 55edf7ccfd
commit 865f583be2
14 changed files with 378 additions and 86 deletions

View File

@@ -0,0 +1,19 @@
using Orchard.UI.Navigation;
namespace Orchard.Dashboards {
public class AdminMenu : Component, INavigationProvider {
public string MenuName { get { return "admin"; } }
public void GetNavigation(NavigationBuilder builder) {
builder.Add(T("Settings"), settings => settings
.Add(T("Dashboards"), "16", dashboard => dashboard
.Add(T("Settings"), "5", dashboardSettings => dashboardSettings
.Action("Index", "Settings", new { area = "Orchard.Dashboards" })
.LocalNav())
.Add(T("Manage Dashboards"), "6", manageDashboards => manageDashboards
.Action("List", "Dashboard", new { area = "Orchard.Dashboards" })
.LocalNav())));
}
}
}

View File

@@ -1,17 +0,0 @@
using System.Web.Mvc;
using Orchard.Dashboards.Services;
using Orchard.Mvc;
namespace Orchard.Dashboards.Controllers {
public class AdminController : Controller {
private readonly IDashboardService _dashboardService;
public AdminController(IDashboardService dashboardService) {
_dashboardService = dashboardService;
}
public ActionResult Index() {
var shape = _dashboardService.GetDashboardShape();
return new ShapeResult(this, shape);
}
}
}

View File

@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.Core.Contents.ViewModels;
using Orchard.Dashboards.Services;
using Orchard.Localization;
using Orchard.Localization.Services;
using Orchard.Mvc;
using Orchard.UI.Admin;
using Orchard.UI.Navigation;
using Orchard.UI.Notify;
namespace Orchard.Dashboards.Controllers {
[Admin]
public class DashboardController : Controller {
private readonly IDashboardService _dashboardService;
private readonly IOrchardServices _services;
private readonly ICultureFilter _cultureFilter;
private readonly ICultureManager _cultureManager;
public DashboardController(IDashboardService dashboardService, IOrchardServices services, ICultureFilter cultureFilter, ICultureManager cultureManager) {
_dashboardService = dashboardService;
_services = services;
_cultureFilter = cultureFilter;
_cultureManager = cultureManager;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public ActionResult Display() {
var shape = _dashboardService.GetDashboardShape();
return new ShapeResult(this, shape);
}
public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) {
var pager = new Pager(_services.WorkContext.CurrentSite, pagerParameters);
VersionOptions versionOptions;
switch (model.Options.ContentsStatus) {
case ContentsStatus.Published:
versionOptions = VersionOptions.Published;
break;
case ContentsStatus.Draft:
versionOptions = VersionOptions.Draft;
break;
case ContentsStatus.AllVersions:
versionOptions = VersionOptions.AllVersions;
break;
default:
versionOptions = VersionOptions.Latest;
break;
}
var query = _services.ContentManager.Query(versionOptions, "Dashboard");
switch (model.Options.OrderBy) {
case ContentsOrder.Modified:
query = query.OrderByDescending<CommonPartRecord>(cr => cr.ModifiedUtc);
break;
case ContentsOrder.Published:
query = query.OrderByDescending<CommonPartRecord>(cr => cr.PublishedUtc);
break;
case ContentsOrder.Created:
query = query.OrderByDescending<CommonPartRecord>(cr => cr.CreatedUtc);
break;
}
if (!String.IsNullOrWhiteSpace(model.Options.SelectedCulture)) {
query = _cultureFilter.FilterCulture(query, model.Options.SelectedCulture);
}
model.Options.Cultures = _cultureManager.ListCultures();
var maxPagedCount = _services.WorkContext.CurrentSite.MaxPagedCount;
if (maxPagedCount > 0 && pager.PageSize > maxPagedCount)
pager.PageSize = maxPagedCount;
var pagerShape = _services.New.Pager(pager).TotalItemCount(maxPagedCount > 0 ? maxPagedCount : query.Count());
var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList();
var list = _services.New.List();
list.AddRange(pageOfContentItems.Select(ci => _services.ContentManager.BuildDisplay(ci, "SummaryAdmin")));
var viewModel = _services.New.ViewModel()
.ContentItems(list)
.Pager(pagerShape)
.Options(model.Options)
.TypeDisplayName(model.TypeDisplayName ?? "");
return View(viewModel);
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Filter")]
public ActionResult ListFilterPOST(ContentOptions options) {
var routeValues = ControllerContext.RouteData.Values;
if (options != null) {
routeValues["Options.SelectedCulture"] = options.SelectedCulture; //todo: don't hard-code the key
routeValues["Options.OrderBy"] = options.OrderBy; //todo: don't hard-code the key
routeValues["Options.ContentsStatus"] = options.ContentsStatus; //todo: don't hard-code the key
}
return RedirectToAction("List", routeValues);
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.BulkEdit")]
public ActionResult ListPOST(ContentOptions options, IEnumerable<int> itemIds, string returnUrl) {
if (itemIds != null) {
var checkedContentItems = _services.ContentManager.GetMany<ContentItem>(itemIds, VersionOptions.Latest, QueryHints.Empty);
switch (options.BulkAction) {
case ContentsBulkAction.None:
break;
case ContentsBulkAction.PublishNow:
foreach (var item in checkedContentItems) {
if (!_services.Authorizer.Authorize(Core.Contents.Permissions.PublishContent, item, T("Couldn't publish selected content."))) {
_services.TransactionManager.Cancel();
return new HttpUnauthorizedResult();
}
_services.ContentManager.Publish(item);
}
_services.Notifier.Information(T("Content successfully published."));
break;
case ContentsBulkAction.Unpublish:
foreach (var item in checkedContentItems) {
if (!_services.Authorizer.Authorize(Core.Contents.Permissions.PublishContent, item, T("Couldn't unpublish selected content."))) {
_services.TransactionManager.Cancel();
return new HttpUnauthorizedResult();
}
_services.ContentManager.Unpublish(item);
}
_services.Notifier.Information(T("Content successfully unpublished."));
break;
case ContentsBulkAction.Remove:
foreach (var item in checkedContentItems) {
if (!_services.Authorizer.Authorize(Core.Contents.Permissions.DeleteContent, item, T("Couldn't remove selected content."))) {
_services.TransactionManager.Cancel();
return new HttpUnauthorizedResult();
}
_services.ContentManager.Remove(item);
}
_services.Notifier.Information(T("Content successfully removed."));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return RedirectToAction("List");
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.Dashboards.Models;
using Orchard.Dashboards.ViewModels;
using Orchard.Localization;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
namespace Orchard.Dashboards.Controllers {
[Admin]
public class SettingsController : Controller {
private readonly IOrchardServices _services;
public SettingsController(IOrchardServices services) {
_services = services;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public ActionResult Index() {
var settings = _services.WorkContext.CurrentSite.As<DashboardSiteSettingsPart>();
var viewModel = new DashboardSiteSettingsViewModel {
SelectedDashboardId = settings.DefaultDashboardId.ToString(),
SelectedDashboard = settings.DefaultDashboardId != null ? _services.ContentManager.Get(settings.DefaultDashboardId.Value) : default(ContentItem)
};
return View(viewModel);
}
[HttpPost]
public ActionResult Index(DashboardSiteSettingsViewModel viewModel) {
var settings = _services.WorkContext.CurrentSite.As<DashboardSiteSettingsPart>();
if (!ModelState.IsValid) {
viewModel.SelectedDashboard = settings.DefaultDashboardId != null ? _services.ContentManager.Get(settings.DefaultDashboardId.Value) : default(ContentItem);
return View(viewModel);
}
settings.DefaultDashboardId = ParseContentId(viewModel.SelectedDashboardId);
_services.Notifier.Information(T("Dashboard settings updated."));
return RedirectToAction("Index");
}
private static int? ParseContentId(string value) {
if (String.IsNullOrWhiteSpace(value))
return null;
var items = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (!items.Any())
return null;
return XmlHelper.Parse<int>(items.First());
}
}
}

View File

@@ -1,48 +1,7 @@
using System;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Drivers;
using Orchard.Dashboards.Models;
using Orchard.Dashboards.ViewModels;
namespace Orchard.Dashboards.Drivers {
public class DashboardSiteSettingsPartDriver : ContentPartDriver<DashboardSiteSettingsPart> {
private readonly IContentManager _contentManager;
public DashboardSiteSettingsPartDriver(IContentManager contentManager) {
_contentManager = contentManager;
}
protected override DriverResult Editor(DashboardSiteSettingsPart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(DashboardSiteSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
return ContentShape("Parts_DashboardSettings", () => {
var viewModel = new DashboardSiteSettingsViewModel {
SelectedDashboardId = part.DefaultDashboardId.ToString()
};
if (updater != null) {
if (updater.TryUpdateModel(viewModel, Prefix, null, new[] { "SelectedDashboard" })) {
part.DefaultDashboardId = ParseContentId(viewModel.SelectedDashboardId);
}
}
viewModel.SelectedDashboard = part.DefaultDashboardId != null ? _contentManager.Get(part.DefaultDashboardId.Value) : default(ContentItem);
return shapeHelper.EditorTemplate(TemplateName: "Parts.DashboardSettings", Model: viewModel, Prefix: Prefix);
}).OnGroup("Dashboard");
}
private int? ParseContentId(string value) {
if (String.IsNullOrWhiteSpace(value))
return null;
var items = value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
if (!items.Any())
return null;
return XmlHelper.Parse<int>(items.First());
}
}
}

View File

@@ -1,17 +1,10 @@
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Handlers;
using Orchard.Dashboards.Models;
using Orchard.Localization;
namespace Orchard.Dashboards.Handlers {
public class DashboardSiteSettingsPartHandler : ContentHandler {
public DashboardSiteSettingsPartHandler() {
Filters.Add(new ActivatingFilter<DashboardSiteSettingsPart>("Site"));
OnGetContentItemMetadata<DashboardSiteSettingsPart>((context, part) => context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Dashboard"))));
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
}
}

View File

@@ -76,12 +76,16 @@
<Content Include="Styles\Web.config" />
<Content Include="Properties\AssemblyInfo.cs" />
<Content Include="Module.txt" />
<Content Include="Views\EditorTemplates\Parts.DashboardSettings.cshtml" />
<Content Include="Views\StaticDashboard.cshtml" />
<Content Include="Views\Content.Dashboard.cshtml" />
<Content Include="Views\Elements\Grid.Dashboard.cshtml" />
<Content Include="Views\Elements\Column.Dashboard.cshtml" />
<Content Include="Views\Parts\Layout.Dashboard.cshtml" />
<Content Include="Views\Settings\Index.cshtml" />
<Content Include="Views\Dashboard\List.cshtml">
<SubType>
</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
@@ -101,10 +105,13 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\DashboardController.cs" />
<Compile Include="Controllers\SettingsController.cs" />
<Compile Include="Drivers\DashboardSiteSettingsPartDriver.cs" />
<Compile Include="Handlers\DashboardSiteSettingsPartHandler.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Services\IDashboardService.cs" />
<Compile Include="Shapes\LayoutPartShape.cs" />
<Compile Include="ViewModels\DashboardSiteSettingsViewModel.cs" />

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Orchard.Environment.Extensions.Models;
using Orchard.Security.Permissions;
namespace Orchard.Dashboards {
public class Permissions : IPermissionProvider {
public static readonly Permission ManageDashboards = new Permission { Description = "Manage dashboards", Name = "ManageDashboards" };
public static readonly Permission ManageOwnDashboard = new Permission { Description = "Manage your own dashboard", Name = "ManageOwnDashboard", ImpliedBy = new[] { ManageDashboards } };
public virtual Feature Feature { get; set; }
public IEnumerable<Permission> GetPermissions() {
yield return ManageDashboards;
yield return ManageOwnDashboard;
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
yield return new PermissionStereotype {
Name = "Administrator",
Permissions = new[] {
ManageDashboards
}
};
}
}
}

View File

@@ -1,5 +1,4 @@
<Placement>
<Place Parts_DashboardSettings="Content:0" />
<Match DisplayType="Dashboard">
<Place
Parts_Common_Body="Content:0"

View File

@@ -17,7 +17,22 @@ namespace Orchard.Dashboards {
"Admin",
new RouteValueDictionary {
{"area", "Orchard.Dashboards"},
{"controller", "Admin"},
{"controller", "Dashboard"},
{"action", "Display"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "Orchard.Dashboards"}
},
new MvcRouteHandler())
};
yield return new RouteDescriptor {
Route = new Route(
"Admin/Dashboards/Settings",
new RouteValueDictionary {
{"area", "Orchard.Dashboards"},
{"controller", "Settings"},
{"action", "Index"}
},
new RouteValueDictionary(),
@@ -26,6 +41,21 @@ namespace Orchard.Dashboards {
},
new MvcRouteHandler())
};
yield return new RouteDescriptor {
Route = new Route(
"Admin/Dashboards/List",
new RouteValueDictionary {
{"area", "Orchard.Dashboards"},
{"controller", "Dashboard"},
{"action", "List"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "Orchard.Dashboards"}
},
new MvcRouteHandler())
};
}
}
}

View File

@@ -8,7 +8,7 @@
var contentItem = (ContentItem)Model.ContentItem;
var title = Html.ItemDisplayText(contentItem);
Layout.Title = title;
Layout.Title = HttpUtility.HtmlDecode(title.ToString());
}
<article class="content-item dashboard">
@Display(Model.Content)

View File

@@ -0,0 +1,55 @@
@using Orchard.Core.Contents.ViewModels;
@{
var typeDisplayName = Model.TypeDisplayName;
var pageTitle = T("Manage Dashboards");
var createLinkText = T("Create New Dashboard");
var cultures = (IEnumerable<string>)Model.Options.Cultures;
Layout.Title = pageTitle;
}
<div class="manage">
@Html.ActionLink(createLinkText.Text, "Create", "Admin", new { area = "Contents", id = "Dashboard", returnUrl = Url.Action("List", "Dashboard", new { area = "Orchard.Dashboards" }) }, new { @class = "button primaryAction" })
</div>
@using (Html.BeginFormAntiForgeryPost()) {
<fieldset class="bulk-actions">
<label for="publishActions">@T("Actions:")</label>
<select id="publishActions" name="Options.BulkAction">
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.None, T("Choose action...").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.PublishNow, T("Publish Now").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.Unpublish, T("Unpublish").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.Remove, T("Delete").ToString())
</select>
<button type="submit" name="submit.BulkEdit" value="yes">@T("Apply")</button>
</fieldset>
<fieldset class="bulk-actions">
@if (cultures.Count() > 1) {
<label for="filterCultures" class="bulk-culture">@T("Culture")</label>
<select id="filterCultures" name="Options.SelectedCulture">
@Html.SelectOption((string)Model.Options.SelectedCulture, "", T("any (show all)").ToString())
@foreach (string culture in cultures) {
@Html.SelectOption((string)Model.Options.SelectedCulture, culture, System.Globalization.CultureInfo.GetCultureInfo(culture).DisplayName)
}
</select>
}
<label for="orderResults" class="bulk-order">@T("Ordered by")</label>
<select id="orderResults" name="Options.OrderBy">
@Html.SelectOption((ContentsOrder)Model.Options.OrderBy, ContentsOrder.Created, T("recently created").ToString())
@Html.SelectOption((ContentsOrder)Model.Options.OrderBy, ContentsOrder.Modified, T("recently modified").ToString())
@Html.SelectOption((ContentsOrder)Model.Options.OrderBy, ContentsOrder.Published, T("recently published").ToString())
</select>
<label for="contentResults" class="bulk-order">@T("Filter by")</label>
<select id="contentResults" name="Options.ContentsStatus">
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Latest, T("latest").ToString())
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Published, T("published").ToString())
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.Draft, T("unpublished").ToString())
@Html.SelectOption((ContentsStatus)Model.Options.ContentsStatus, ContentsStatus.AllVersions, T("all versions").ToString())
</select>
<button type="submit" name="submit.Filter" value="yes please">@T("Apply")</button>
</fieldset>
<fieldset class="contentItems bulk-items">
@Display(Model.ContentItems)
</fieldset>
@Display(Model.Pager)
}

View File

@@ -1,14 +0,0 @@
@using Orchard.ContentManagement
@model Orchard.Dashboards.ViewModels.DashboardSiteSettingsViewModel
@{
Layout.Title = T("Dashboard Settings").ToString();
}
<section>
<div class="form-group">
@Display.ContentPicker_Edit(
DisplayName: T("Default Dashboard").Text,
SelectedItemsFieldName: Html.FieldNameFor(m => m.SelectedDashboardId),
ContentItems: Model.SelectedDashboard != null ? new[] {Model.SelectedDashboard} : Enumerable.Empty<ContentItem>(),
Hint: T("Select the content item to use as the dashboard.").Text)
</div>
</section>

View File

@@ -0,0 +1,18 @@
@using Orchard.ContentManagement
@model Orchard.Dashboards.ViewModels.DashboardSiteSettingsViewModel
@{
Layout.Title = T("Dashboard Settings").ToString();
}
@using (Html.BeginFormAntiForgeryPost()) {
<section>
<div class="form-group">
@Display.ContentPicker_Edit(
DisplayName: T("Default Dashboard").Text,
SelectedItemsFieldName: Html.FieldNameFor(m => m.SelectedDashboardId),
ContentItems: Model.SelectedDashboard != null ? new[] {Model.SelectedDashboard} : Enumerable.Empty<ContentItem>(),
Hint: T("Select the content item to use as the dashboard.").Text,
Types: new[]{ "Dashboard" })
</div>
</section>
<button>@T("Save")</button>
}