From ede63d04fe043b0a6a477a7a55cd01ccfe5840c8 Mon Sep 17 00:00:00 2001 From: Drew Brasher Date: Wed, 27 Dec 2023 09:53:33 -0600 Subject: [PATCH 1/6] Added a new module called IssueTracker and selected to add all files required for a part named Issue. --- OrchardCoreContrib.Modules.sln | 9 ++- .../Controllers/HomeController.cs | 15 +++++ .../Drivers/IssuePartDisplayDriver.cs | 56 +++++++++++++++++++ .../Handlers/IssuePartHandler.cs | 14 +++++ .../Manifest.cs | 11 ++++ .../Migrations.cs | 23 ++++++++ .../Models/IssuePart.cs | 7 +++ .../OrchardCoreContrib.IssueTracker.csproj | 21 +++++++ .../Settings/IssuePartSettings.cs | 6 ++ .../IssuePartSettingsDisplayDriver.cs | 44 +++++++++++++++ .../Settings/IssuePartSettingsViewModel.cs | 10 ++++ .../Startup.cs | 44 +++++++++++++++ .../ViewModels/IssuePartViewModel.cs | 21 +++++++ .../Views/Home/Index.cshtml | 1 + .../Views/IssuePart.Edit.cshtml | 8 +++ .../Views/IssuePart.liquid | 3 + .../Views/IssuePartSettings.Edit.cshtml | 9 +++ .../Views/IssuePart_Summary.liquid | 2 + .../Views/_ViewImports.cshtml | 10 ++++ 19 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 src/OrchardCoreContrib.IssueTracker/Controllers/HomeController.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Manifest.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Migrations.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj create mode 100644 src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Startup.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs create mode 100644 src/OrchardCoreContrib.IssueTracker/Views/Home/Index.cshtml create mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml create mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid create mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml create mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid create mode 100644 src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml diff --git a/OrchardCoreContrib.Modules.sln b/OrchardCoreContrib.Modules.sln index 7d135735..71a69844 100644 --- a/OrchardCoreContrib.Modules.sln +++ b/OrchardCoreContrib.Modules.sln @@ -54,7 +54,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCoreContrib.System", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCoreContrib.Themes.Admin", "src\OrchardCoreContrib.Themes.Admin\OrchardCoreContrib.Themes.Admin.csproj", "{EF11652A-B9A2-4E5E-897E-16F26C4D6946}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCoreContrib.Liquid", "src\OrchardCoreContrib.Liquid\OrchardCoreContrib.Liquid.csproj", "{8C49A05B-5D69-4222-9245-310E412E237E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCoreContrib.Liquid", "src\OrchardCoreContrib.Liquid\OrchardCoreContrib.Liquid.csproj", "{8C49A05B-5D69-4222-9245-310E412E237E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCoreContrib.IssueTracker", "src\OrchardCoreContrib.IssueTracker\OrchardCoreContrib.IssueTracker.csproj", "{44D8A9E0-5CC5-48C8-8507-E5622166682D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -150,6 +152,10 @@ Global {8C49A05B-5D69-4222-9245-310E412E237E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C49A05B-5D69-4222-9245-310E412E237E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C49A05B-5D69-4222-9245-310E412E237E}.Release|Any CPU.Build.0 = Release|Any CPU + {44D8A9E0-5CC5-48C8-8507-E5622166682D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44D8A9E0-5CC5-48C8-8507-E5622166682D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44D8A9E0-5CC5-48C8-8507-E5622166682D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44D8A9E0-5CC5-48C8-8507-E5622166682D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -177,6 +183,7 @@ Global {F675A009-CD42-495F-B866-FF917B3D4BDA} = {C80A325F-F4C4-4C7B-A3CF-FB77CD8C9949} {EF11652A-B9A2-4E5E-897E-16F26C4D6946} = {C80A325F-F4C4-4C7B-A3CF-FB77CD8C9949} {8C49A05B-5D69-4222-9245-310E412E237E} = {C80A325F-F4C4-4C7B-A3CF-FB77CD8C9949} + {44D8A9E0-5CC5-48C8-8507-E5622166682D} = {C80A325F-F4C4-4C7B-A3CF-FB77CD8C9949} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48F73B05-7D3D-4ACF-81AE-A98B2B4EFDB2} diff --git a/src/OrchardCoreContrib.IssueTracker/Controllers/HomeController.cs b/src/OrchardCoreContrib.IssueTracker/Controllers/HomeController.cs new file mode 100644 index 00000000..97df07c3 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Controllers/HomeController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OrchardCoreContrib.IssueTracker.Controllers; +public class HomeController : Controller +{ + public ActionResult Index() + { + return View(); + } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs b/src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs new file mode 100644 index 00000000..5f799c61 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs @@ -0,0 +1,56 @@ +using OrchardCore.ContentManagement.Display.ContentDisplay; +using OrchardCore.ContentManagement.Display.Models; +using OrchardCore.ContentManagement.Metadata; +using OrchardCore.DisplayManagement.ModelBinding; +using OrchardCore.DisplayManagement.Views; +using OrchardCoreContrib.IssueTracker.Models; +using OrchardCoreContrib.IssueTracker.Settings; +using OrchardCoreContrib.IssueTracker.ViewModels; +using System.Threading.Tasks; + +namespace OrchardCoreContrib.IssueTracker.Drivers; +public class IssuePartDisplayDriver : ContentPartDisplayDriver +{ + private readonly IContentDefinitionManager _contentDefinitionManager; + + public IssuePartDisplayDriver(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public override IDisplayResult Display(IssuePart part, BuildPartDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), m => BuildViewModel(m, part, context)) + .Location("Detail", "Content:10") + .Location("Summary", "Content:10") + ; + } + + public override IDisplayResult Edit(IssuePart part, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => + { + model.Show = part.Show; + model.ContentItem = part.ContentItem; + model.IssuePart = part; + }); + } + + public override async Task UpdateAsync(IssuePart model, IUpdateModel updater) + { + await updater.TryUpdateModelAsync(model, Prefix, t => t.Show); + + return Edit(model); + } + + private static void BuildViewModel(IssuePartViewModel model, IssuePart part, BuildPartDisplayContext context) + { + var settings = context.TypePartDefinition.GetSettings(); + + model.ContentItem = part.ContentItem; + model.MySetting = settings.MySetting; + model.Show = part.Show; + model.IssuePart = part; + model.Settings = settings; + } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs b/src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs new file mode 100644 index 00000000..d4d24636 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs @@ -0,0 +1,14 @@ +using OrchardCore.ContentManagement.Handlers; +using OrchardCoreContrib.IssueTracker.Models; +using System.Threading.Tasks; + +namespace OrchardCoreContrib.IssueTracker.Handlers; +public class IssuePartHandler : ContentPartHandler +{ + public override Task InitializingAsync(InitializingContentContext context, IssuePart part) + { + part.Show = true; + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/OrchardCoreContrib.IssueTracker/Manifest.cs b/src/OrchardCoreContrib.IssueTracker/Manifest.cs new file mode 100644 index 00000000..86f50a16 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Manifest.cs @@ -0,0 +1,11 @@ +using OrchardCore.Modules.Manifest; + +[assembly: Module( + Name = "OrchardCoreContrib.IssueTracker", + Author = "The Orchard Core Team", + Website = "https://orchardcore.net", + Version = "0.0.1", + Description = "OrchardCoreContrib.IssueTracker", + Dependencies = new[] { "OrchardCore.Contents" }, + Category = "Content Management" +)] diff --git a/src/OrchardCoreContrib.IssueTracker/Migrations.cs b/src/OrchardCoreContrib.IssueTracker/Migrations.cs new file mode 100644 index 00000000..3957be2b --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Migrations.cs @@ -0,0 +1,23 @@ +using OrchardCore.ContentManagement.Metadata; +using OrchardCore.ContentManagement.Metadata.Settings; +using OrchardCore.Data.Migration; + +namespace OrchardCoreContrib.IssueTracker; +public class Migrations : DataMigration +{ + IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public int Create() + { + _contentDefinitionManager.AlterPartDefinition("IssuePart", builder => builder + .Attachable() + .WithDescription("Provides a Issue part for your content item.")); + + return 1; + } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs b/src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs new file mode 100644 index 00000000..ca28bf52 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs @@ -0,0 +1,7 @@ +using OrchardCore.ContentManagement; + +namespace OrchardCoreContrib.IssueTracker.Models; +public class IssuePart : ContentPart +{ + public bool Show { get; set; } +} diff --git a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj new file mode 100644 index 00000000..a3484aea --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj @@ -0,0 +1,21 @@ + + + + net7.0 + true + enable + enable + + + + + + + + + + + + + + diff --git a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs new file mode 100644 index 00000000..8ce57f7a --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs @@ -0,0 +1,6 @@ +namespace OrchardCoreContrib.IssueTracker.Settings; + +public class IssuePartSettings +{ + public string MySetting { get; set; } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs new file mode 100644 index 00000000..9356d03e --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs @@ -0,0 +1,44 @@ +using OrchardCore.ContentManagement.Metadata.Models; +using OrchardCore.ContentTypes.Editors; +using OrchardCore.DisplayManagement.ModelBinding; +using OrchardCore.DisplayManagement.Views; +using OrchardCoreContrib.IssueTracker.Models; +using System; +using System.Threading.Tasks; + +namespace OrchardCoreContrib.IssueTracker.Settings; +public class IssuePartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver +{ + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater) + { + if(!String.Equals(nameof(IssuePart), contentTypePartDefinition.PartDefinition.Name)) + { + return null; + } + + return Initialize("IssuePartSettings_Edit", model => + { + var settings = contentTypePartDefinition.GetSettings(); + + model.MySetting = settings.MySetting; + model.IssuePartSettings = settings; + }).Location("Content"); + } + + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + if(!String.Equals(nameof(IssuePart), contentTypePartDefinition.PartDefinition.Name)) + { + return null; + } + + var model = new IssuePartSettingsViewModel(); + + if(await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.MySetting)) + { + context.Builder.WithSettings(new IssuePartSettings { MySetting = model.MySetting }); + } + + return Edit(contentTypePartDefinition, context.Updater); + } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs new file mode 100644 index 00000000..d734f597 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace OrchardCoreContrib.IssueTracker.Settings; +public class IssuePartSettingsViewModel +{ + public string MySetting { get; set; } + + [BindNever] + public IssuePartSettings IssuePartSettings { get; set; } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Startup.cs b/src/OrchardCoreContrib.IssueTracker/Startup.cs new file mode 100644 index 00000000..6f756c5e --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Startup.cs @@ -0,0 +1,44 @@ +using Fluid; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.ContentManagement; +using OrchardCore.ContentManagement.Display.ContentDisplay; +using OrchardCore.ContentManagement.Handlers; +using OrchardCore.ContentTypes.Editors; +using OrchardCore.Data.Migration; +using OrchardCore.Modules; +using OrchardCoreContrib.IssueTracker.Drivers; +using OrchardCoreContrib.IssueTracker.Handlers; +using OrchardCoreContrib.IssueTracker.Models; +using OrchardCoreContrib.IssueTracker.Settings; +using OrchardCoreContrib.IssueTracker.ViewModels; + +namespace OrchardCoreContrib.IssueTracker; +public class Startup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(o => + { + o.MemberAccessStrategy.Register(); + }); + + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddDataMigration(); + } + + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "Home", + areaName: "OrchardCoreContrib.IssueTracker", + pattern: "Home/Index", + defaults: new { controller = "Home", action = "Index" } + ); + } +} diff --git a/src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs b/src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs new file mode 100644 index 00000000..4cef2209 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using OrchardCore.ContentManagement; +using OrchardCoreContrib.IssueTracker.Models; +using OrchardCoreContrib.IssueTracker.Settings; + +namespace OrchardCoreContrib.IssueTracker.ViewModels; +public class IssuePartViewModel +{ + public string MySetting { get; set; } + + public bool Show { get; set; } + + [BindNever] + public ContentItem ContentItem { get; set; } + + [BindNever] + public IssuePart IssuePart { get; set; } + + [BindNever] + public IssuePartSettings Settings { get; set; } +} diff --git a/src/OrchardCoreContrib.IssueTracker/Views/Home/Index.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/Home/Index.cshtml new file mode 100644 index 00000000..abab7be9 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Views/Home/Index.cshtml @@ -0,0 +1 @@ +Template \ No newline at end of file diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml new file mode 100644 index 00000000..fa96963c --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml @@ -0,0 +1,8 @@ +@model IssuePartViewModel + +
+
+ + +
+
diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid new file mode 100644 index 00000000..3150ee42 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid @@ -0,0 +1,3 @@ +{% if Model.Show %} +{{ Model.MySetting }} {{ Model.ContentItem.ContentItemId }} +{% endif %} diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml new file mode 100644 index 00000000..61c3977d --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml @@ -0,0 +1,9 @@ +@model OrchardCoreContrib.IssueTracker.Settings.IssuePartSettingsViewModel + +
+
+ + +
+ @T["The setting."] +
diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid new file mode 100644 index 00000000..ba4360e8 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid @@ -0,0 +1,2 @@ +{{ Model.MySetting }} +{{ Model.ContentItem.ContentItemId }} diff --git a/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml new file mode 100644 index 00000000..75168dc9 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml @@ -0,0 +1,10 @@ +@* + For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 + +*@ + +@inherits OrchardCore.DisplayManagement.Razor.RazorPage +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, OrchardCore.DisplayManagement +@addTagHelper *, OrchardCore.ResourceManagement +@using OrchardCoreContrib.IssueTracker.ViewModels From 300afbd0de455b267919d8c655cca2708b0aac8c Mon Sep 17 00:00:00 2001 From: Drew Brasher Date: Wed, 27 Dec 2023 13:02:26 -0600 Subject: [PATCH 2/6] Changed target frameworks and OrchardCore version tomatch the other modules. Added a reference to the web project. --- .../OrchardCoreContrib.IssueTracker.csproj | 10 +++++----- .../OrchardCoreContrib.Modules.Web.csproj | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj index a3484aea..4ea76354 100644 --- a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj +++ b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj @@ -1,7 +1,7 @@ - net7.0 + net6.0;net7.0 true enable enable @@ -12,10 +12,10 @@ - - - - + + + + diff --git a/src/OrchardCoreContrib.Modules.Web/OrchardCoreContrib.Modules.Web.csproj b/src/OrchardCoreContrib.Modules.Web/OrchardCoreContrib.Modules.Web.csproj index c13e5deb..8fbcc60d 100644 --- a/src/OrchardCoreContrib.Modules.Web/OrchardCoreContrib.Modules.Web.csproj +++ b/src/OrchardCoreContrib.Modules.Web/OrchardCoreContrib.Modules.Web.csproj @@ -26,6 +26,7 @@ + From 98510627b2301849a3b03fe287e31f19c83594a1 Mon Sep 17 00:00:00 2001 From: Drew Brasher Date: Thu, 28 Dec 2023 10:15:41 -0600 Subject: [PATCH 3/6] Created a readme with the start of the module requirements. --- src/OrchardCoreContrib.IssueTracker/readme.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/OrchardCoreContrib.IssueTracker/readme.md diff --git a/src/OrchardCoreContrib.IssueTracker/readme.md b/src/OrchardCoreContrib.IssueTracker/readme.md new file mode 100644 index 00000000..4975c721 --- /dev/null +++ b/src/OrchardCoreContrib.IssueTracker/readme.md @@ -0,0 +1,43 @@ +# Issue Tracker Module + +## Definitions +**Issue** - Used for reporting problems, requesting new features, or any request for something to be done. + +**Project** - A set of related tasks that need to be completed. + +**Milestone** - A date when something should happen such the date something is due to be completed. + +**Member** - A user who is responsible for some part of an issue or project. + +## Features +### Common to both an Issue and a Project +- [ ] Title +- [ ] Body (html or markdown) +- [ ] Requestor contact information +- [ ] Members (Assign to one or more users who are responsible for the task) +- [ ] Attach files +- [ ] Category +- [ ] Different groups of people should be linked to different categories. For example, IT would be responsible for the category "Computer" and Application Development would be responsible for the "Website" category +- [ ] Status (New, In Progress, completed, cancelled, etc) +- [ ] Priority +- [ ] Comments +- [ ] Milestones + +### Issue +- [ ] A user should be able to submit an Issue without having access to the admin side +- [ ] Users can view all Issues they have submitted + +### Project +- [ ] Multiple Issues can be associated with a project + +### Milestone +- [ ] Title +- [ ] Body (html or markdown) +- [ ] Date + +### Member +- [ ] User + +### Workflows +- [ ] Users who submitted or are assigned to should be notified when a change is made or comment added +- [ ] Users should be notified when new content is added with a Category they are responsible for From 3afc48ea527cee96130643b35822a4bf2be433e4 Mon Sep 17 00:00:00 2001 From: Drew Brasher Date: Wed, 3 Apr 2024 08:40:52 -0500 Subject: [PATCH 4/6] Removed IssuePart --- .../Drivers/IssuePartDisplayDriver.cs | 56 ------------------- .../Handlers/IssuePartHandler.cs | 14 ----- .../Migrations.cs | 3 - .../Models/IssuePart.cs | 7 --- .../OrchardCoreContrib.IssueTracker.csproj | 6 ++ .../Settings/IssuePartSettings.cs | 6 -- .../IssuePartSettingsDisplayDriver.cs | 44 --------------- .../Settings/IssuePartSettingsViewModel.cs | 10 ---- .../Startup.cs | 15 ----- .../ViewModels/IssuePartViewModel.cs | 21 ------- .../Views/IssuePart.Edit.cshtml | 8 --- .../Views/IssuePart.liquid | 3 - .../Views/IssuePartSettings.Edit.cshtml | 9 --- .../Views/IssuePart_Summary.liquid | 2 - .../Views/_ViewImports.cshtml | 1 - 15 files changed, 6 insertions(+), 199 deletions(-) delete mode 100644 src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs delete mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml delete mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid delete mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml delete mode 100644 src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid diff --git a/src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs b/src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs deleted file mode 100644 index 5f799c61..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Drivers/IssuePartDisplayDriver.cs +++ /dev/null @@ -1,56 +0,0 @@ -using OrchardCore.ContentManagement.Display.ContentDisplay; -using OrchardCore.ContentManagement.Display.Models; -using OrchardCore.ContentManagement.Metadata; -using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Views; -using OrchardCoreContrib.IssueTracker.Models; -using OrchardCoreContrib.IssueTracker.Settings; -using OrchardCoreContrib.IssueTracker.ViewModels; -using System.Threading.Tasks; - -namespace OrchardCoreContrib.IssueTracker.Drivers; -public class IssuePartDisplayDriver : ContentPartDisplayDriver -{ - private readonly IContentDefinitionManager _contentDefinitionManager; - - public IssuePartDisplayDriver(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } - - public override IDisplayResult Display(IssuePart part, BuildPartDisplayContext context) - { - return Initialize(GetDisplayShapeType(context), m => BuildViewModel(m, part, context)) - .Location("Detail", "Content:10") - .Location("Summary", "Content:10") - ; - } - - public override IDisplayResult Edit(IssuePart part, BuildPartEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => - { - model.Show = part.Show; - model.ContentItem = part.ContentItem; - model.IssuePart = part; - }); - } - - public override async Task UpdateAsync(IssuePart model, IUpdateModel updater) - { - await updater.TryUpdateModelAsync(model, Prefix, t => t.Show); - - return Edit(model); - } - - private static void BuildViewModel(IssuePartViewModel model, IssuePart part, BuildPartDisplayContext context) - { - var settings = context.TypePartDefinition.GetSettings(); - - model.ContentItem = part.ContentItem; - model.MySetting = settings.MySetting; - model.Show = part.Show; - model.IssuePart = part; - model.Settings = settings; - } -} diff --git a/src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs b/src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs deleted file mode 100644 index d4d24636..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Handlers/IssuePartHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using OrchardCore.ContentManagement.Handlers; -using OrchardCoreContrib.IssueTracker.Models; -using System.Threading.Tasks; - -namespace OrchardCoreContrib.IssueTracker.Handlers; -public class IssuePartHandler : ContentPartHandler -{ - public override Task InitializingAsync(InitializingContentContext context, IssuePart part) - { - part.Show = true; - - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/src/OrchardCoreContrib.IssueTracker/Migrations.cs b/src/OrchardCoreContrib.IssueTracker/Migrations.cs index 3957be2b..571c32fa 100644 --- a/src/OrchardCoreContrib.IssueTracker/Migrations.cs +++ b/src/OrchardCoreContrib.IssueTracker/Migrations.cs @@ -14,9 +14,6 @@ public Migrations(IContentDefinitionManager contentDefinitionManager) public int Create() { - _contentDefinitionManager.AlterPartDefinition("IssuePart", builder => builder - .Attachable() - .WithDescription("Provides a Issue part for your content item.")); return 1; } diff --git a/src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs b/src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs deleted file mode 100644 index ca28bf52..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Models/IssuePart.cs +++ /dev/null @@ -1,7 +0,0 @@ -using OrchardCore.ContentManagement; - -namespace OrchardCoreContrib.IssueTracker.Models; -public class IssuePart : ContentPart -{ - public bool Show { get; set; } -} diff --git a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj index 4ea76354..1490d696 100644 --- a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj +++ b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj @@ -18,4 +18,10 @@ + + + + + + diff --git a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs deleted file mode 100644 index 8ce57f7a..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettings.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OrchardCoreContrib.IssueTracker.Settings; - -public class IssuePartSettings -{ - public string MySetting { get; set; } -} diff --git a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs deleted file mode 100644 index 9356d03e..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsDisplayDriver.cs +++ /dev/null @@ -1,44 +0,0 @@ -using OrchardCore.ContentManagement.Metadata.Models; -using OrchardCore.ContentTypes.Editors; -using OrchardCore.DisplayManagement.ModelBinding; -using OrchardCore.DisplayManagement.Views; -using OrchardCoreContrib.IssueTracker.Models; -using System; -using System.Threading.Tasks; - -namespace OrchardCoreContrib.IssueTracker.Settings; -public class IssuePartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver -{ - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater) - { - if(!String.Equals(nameof(IssuePart), contentTypePartDefinition.PartDefinition.Name)) - { - return null; - } - - return Initialize("IssuePartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); - - model.MySetting = settings.MySetting; - model.IssuePartSettings = settings; - }).Location("Content"); - } - - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - if(!String.Equals(nameof(IssuePart), contentTypePartDefinition.PartDefinition.Name)) - { - return null; - } - - var model = new IssuePartSettingsViewModel(); - - if(await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.MySetting)) - { - context.Builder.WithSettings(new IssuePartSettings { MySetting = model.MySetting }); - } - - return Edit(contentTypePartDefinition, context.Updater); - } -} diff --git a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs b/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs deleted file mode 100644 index d734f597..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Settings/IssuePartSettingsViewModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding; - -namespace OrchardCoreContrib.IssueTracker.Settings; -public class IssuePartSettingsViewModel -{ - public string MySetting { get; set; } - - [BindNever] - public IssuePartSettings IssuePartSettings { get; set; } -} diff --git a/src/OrchardCoreContrib.IssueTracker/Startup.cs b/src/OrchardCoreContrib.IssueTracker/Startup.cs index 6f756c5e..597b2a50 100644 --- a/src/OrchardCoreContrib.IssueTracker/Startup.cs +++ b/src/OrchardCoreContrib.IssueTracker/Startup.cs @@ -8,27 +8,12 @@ using OrchardCore.ContentTypes.Editors; using OrchardCore.Data.Migration; using OrchardCore.Modules; -using OrchardCoreContrib.IssueTracker.Drivers; -using OrchardCoreContrib.IssueTracker.Handlers; -using OrchardCoreContrib.IssueTracker.Models; -using OrchardCoreContrib.IssueTracker.Settings; -using OrchardCoreContrib.IssueTracker.ViewModels; namespace OrchardCoreContrib.IssueTracker; public class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }); - - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); services.AddDataMigration(); } diff --git a/src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs b/src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs deleted file mode 100644 index 4cef2209..00000000 --- a/src/OrchardCoreContrib.IssueTracker/ViewModels/IssuePartViewModel.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ModelBinding; -using OrchardCore.ContentManagement; -using OrchardCoreContrib.IssueTracker.Models; -using OrchardCoreContrib.IssueTracker.Settings; - -namespace OrchardCoreContrib.IssueTracker.ViewModels; -public class IssuePartViewModel -{ - public string MySetting { get; set; } - - public bool Show { get; set; } - - [BindNever] - public ContentItem ContentItem { get; set; } - - [BindNever] - public IssuePart IssuePart { get; set; } - - [BindNever] - public IssuePartSettings Settings { get; set; } -} diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml deleted file mode 100644 index fa96963c..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.Edit.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@model IssuePartViewModel - -
-
- - -
-
diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid deleted file mode 100644 index 3150ee42..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart.liquid +++ /dev/null @@ -1,3 +0,0 @@ -{% if Model.Show %} -{{ Model.MySetting }} {{ Model.ContentItem.ContentItemId }} -{% endif %} diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml deleted file mode 100644 index 61c3977d..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Views/IssuePartSettings.Edit.cshtml +++ /dev/null @@ -1,9 +0,0 @@ -@model OrchardCoreContrib.IssueTracker.Settings.IssuePartSettingsViewModel - -
-
- - -
- @T["The setting."] -
diff --git a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid b/src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid deleted file mode 100644 index ba4360e8..00000000 --- a/src/OrchardCoreContrib.IssueTracker/Views/IssuePart_Summary.liquid +++ /dev/null @@ -1,2 +0,0 @@ -{{ Model.MySetting }} -{{ Model.ContentItem.ContentItemId }} diff --git a/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml b/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml index 75168dc9..4769da1c 100644 --- a/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml +++ b/src/OrchardCoreContrib.IssueTracker/Views/_ViewImports.cshtml @@ -7,4 +7,3 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, OrchardCore.DisplayManagement @addTagHelper *, OrchardCore.ResourceManagement -@using OrchardCoreContrib.IssueTracker.ViewModels From 21762e9f21b1aa6a810e41f2ab4da3244fba7a4d Mon Sep 17 00:00:00 2001 From: Drew Brasher Date: Thu, 4 Apr 2024 13:35:56 -0500 Subject: [PATCH 5/6] Added Issue, IssuePriority, and IssueStatus content types. --- .../Manifest.cs | 10 +- .../Migrations.cs | 153 ++++++++++++++++++ .../OrchardCoreContrib.IssueTracker.csproj | 5 +- 3 files changed, 162 insertions(+), 6 deletions(-) diff --git a/src/OrchardCoreContrib.IssueTracker/Manifest.cs b/src/OrchardCoreContrib.IssueTracker/Manifest.cs index 86f50a16..64b6266a 100644 --- a/src/OrchardCoreContrib.IssueTracker/Manifest.cs +++ b/src/OrchardCoreContrib.IssueTracker/Manifest.cs @@ -1,11 +1,11 @@ using OrchardCore.Modules.Manifest; [assembly: Module( - Name = "OrchardCoreContrib.IssueTracker", - Author = "The Orchard Core Team", - Website = "https://orchardcore.net", + Name = "Issue Tracker", + Author = "The Orchard Core Community", + Website = "https://github.com/OrchardCoreContrib", Version = "0.0.1", - Description = "OrchardCoreContrib.IssueTracker", - Dependencies = new[] { "OrchardCore.Contents" }, + Description = "Adds Issue Tracker Features ", + Dependencies = new[] { "OrchardCore.Contents", "OrchardCore.Taxonomies", "OrchardCore.Media" }, Category = "Content Management" )] diff --git a/src/OrchardCoreContrib.IssueTracker/Migrations.cs b/src/OrchardCoreContrib.IssueTracker/Migrations.cs index 571c32fa..f64a131c 100644 --- a/src/OrchardCoreContrib.IssueTracker/Migrations.cs +++ b/src/OrchardCoreContrib.IssueTracker/Migrations.cs @@ -1,6 +1,10 @@ +using OrchardCore.ContentFields.Settings; +using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata; using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; +using OrchardCore.Media.Settings; +using OrchardCore.Taxonomies.Settings; namespace OrchardCoreContrib.IssueTracker; public class Migrations : DataMigration @@ -14,7 +18,156 @@ public Migrations(IContentDefinitionManager contentDefinitionManager) public int Create() { + // Issue Priority + _contentDefinitionManager.AlterTypeDefinition("IssuePriority", type => type + .DisplayedAs("Issue Priority") + .WithPart("TitlePart", part => part + .WithPosition("0") + ) + .WithPart("IssuePriority", part => part + .WithPosition("1") + ) + .WithPart("AutoroutePart", part => part + .WithPosition("2") + ) + ); + // Issue Status + _contentDefinitionManager.AlterTypeDefinition("IssueStatus", type => type + .DisplayedAs("Issue Status") + .WithPart("TitlePart", part => part + .WithPosition("0") + ) + .WithPart("IssueStatus", part => part + .WithPosition("1") + ) + .WithPart("AutoroutePart", part => part + .WithPosition("2") + ) + ); + + // Issue Category + _contentDefinitionManager.AlterTypeDefinition("IssueCategory", type => type + .DisplayedAs("Issue Category") + .WithPart("TitlePart", part => part + .WithPosition("0") + ) + .WithPart("IssueCategory", part => part + .WithPosition("1") + ) + .WithPart("AutoroutePart", part => part + .WithPosition("2") + ) + ); + + _contentDefinitionManager.AlterPartDefinition("IssueCategory", part => part + .WithField("ResponsibleUsers", field => field + .OfType("UserPickerField") + .WithDisplayName("Responsible Users") + .WithPosition("0") + .WithSettings(new UserPickerFieldSettings + { + Required = true, + Multiple = true, + DisplayAllUsers = false, + DisplayedRoles = new[] + { + "Administrator", + }, + }) + ) + ); + + // Issue + _contentDefinitionManager.AlterTypeDefinition("Issue", type => type + .DisplayedAs("Issue") + .Creatable() + .Listable() + .Securable() + .WithPart("Issue", part => part + .WithPosition("2") + ) + .WithPart("AutoroutePart", part => part + .WithPosition("3") + ) + .WithPart("TitlePart", part => part + .WithPosition("0") + ) + .WithPart("ContactInformationPart", part => part + .WithPosition("1") + ) + ); + + _contentDefinitionManager.AlterPartDefinition("Issue", part => part + .WithField("Description", field => field + .OfType("MarkdownField") + .WithDisplayName("Description") + .WithEditor("Wysiwyg") + .WithPosition("0") + ) + .WithField("Files", field => field + .OfType("MediaField") + .WithDisplayName("Files") + .WithEditor("Attached") + .WithPosition("4") + ) + .WithField("Category", field => field + .OfType("TaxonomyField") + .WithDisplayName("Category") + .WithPosition("1") + .WithSettings(new TaxonomyFieldSettings + { + Required = true, + TaxonomyContentItemId = "4gj5zq9wf1zaet99mqab9gvw4g", + Unique = true, + }) + ) + .WithField("Assignees", field => field + .OfType("UserPickerField") + .WithDisplayName("Assignees") + .WithPosition("2") + .WithSettings(new UserPickerFieldSettings + { + Multiple = true, + DisplayAllUsers = false, + DisplayedRoles = new[] + { + "Administrator", + }, + }) + ) + .WithField("Priority", field => field + .OfType("TaxonomyField") + .WithDisplayName("Priority") + .WithPosition("3") + .WithSettings(new TaxonomyFieldSettings + { + TaxonomyContentItemId = "4mwtqz7n7dqyr1hxnr7hcte148", + }) + ) + ); + + _contentDefinitionManager.AlterPartDefinition("ContactInformationPart", part => part + .Attachable() + .WithDescription("Contact Information") + .WithField("Name", field => field + .OfType("TextField") + .WithDisplayName("Name") + .WithPosition("0") + ) + .WithField("Email", field => field + .OfType("TextField") + .WithDisplayName("Email") + .WithEditor("Email") + .WithPosition("2") + ) + .WithField("Phone", field => field + .OfType("TextField") + .WithDisplayName("Phone") + .WithEditor("Tel") + .WithPosition("1") + ) + ); return 1; } } diff --git a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj index 1490d696..98e575ed 100644 --- a/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj +++ b/src/OrchardCoreContrib.IssueTracker/OrchardCoreContrib.IssueTracker.csproj @@ -1,4 +1,4 @@ - + net6.0;net7.0 @@ -16,6 +16,9 @@ + + + From 14a94f2c63cd00fba7903f34d9c3a08eab2666a1 Mon Sep 17 00:00:00 2001 From: Drew Brasher Date: Mon, 29 Apr 2024 08:32:46 -0500 Subject: [PATCH 6/6] Updated IssueTracker Module Description --- src/OrchardCoreContrib.IssueTracker/Manifest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCoreContrib.IssueTracker/Manifest.cs b/src/OrchardCoreContrib.IssueTracker/Manifest.cs index 64b6266a..827428a6 100644 --- a/src/OrchardCoreContrib.IssueTracker/Manifest.cs +++ b/src/OrchardCoreContrib.IssueTracker/Manifest.cs @@ -5,7 +5,7 @@ Author = "The Orchard Core Community", Website = "https://github.com/OrchardCoreContrib", Version = "0.0.1", - Description = "Adds Issue Tracker Features ", + Description = "Provides a set of features and services to manage and track issues.", Dependencies = new[] { "OrchardCore.Contents", "OrchardCore.Taxonomies", "OrchardCore.Media" }, Category = "Content Management" )]