From 8851f622a38d219005bbc389ab590c1e449f4ee8 Mon Sep 17 00:00:00 2001 From: Benedek Farkas Date: Mon, 29 Sep 2025 20:53:30 +0200 Subject: [PATCH 1/4] Adding release package workflow (#8846) (cherry picked from commit 4268b28d95fdcc778ead9e0ba4b457ac1b3b6987) --- .../build-crowdin-translation-packages.yml | 27 +++++++++ .github/workflows/compile.yml | 8 +-- .github/workflows/release-package.yml | 39 ++++++++++++ .github/workflows/specflow.yml | 59 +++++++++++++++++++ 4 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/build-crowdin-translation-packages.yml create mode 100644 .github/workflows/release-package.yml create mode 100644 .github/workflows/specflow.yml diff --git a/.github/workflows/build-crowdin-translation-packages.yml b/.github/workflows/build-crowdin-translation-packages.yml new file mode 100644 index 000000000..de8f128e0 --- /dev/null +++ b/.github/workflows/build-crowdin-translation-packages.yml @@ -0,0 +1,27 @@ +name: Build Crowdin Translation Packages + +on: + workflow_dispatch: + schedule: + - cron: '0 6 * * *' + +jobs: + build-crowdin-translation-packages: + runs-on: ubuntu-24.04 + + steps: + - name: Orchard CMS + uses: andrii-bodnar/crowdin-request-action@ee7a2af9564d8934b5b4a8427185aaaffee0165e # v0.3.0 + with: + route: POST /projects/{projectId}/translations/builds + projectId: 46524 + env: + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }} + + - name: Orchard CMS Gallery + uses: andrii-bodnar/crowdin-request-action@ee7a2af9564d8934b5b4a8427185aaaffee0165e # v0.3.0 + with: + route: POST /projects/{projectId}/translations/builds + projectId: 63766 + env: + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }} diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index d0636920c..b15eaa0c6 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -17,13 +17,13 @@ jobs: runs-on: windows-2025 steps: - name: Clone Repository - uses: actions/checkout@v4.1.1 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Restore NuGet Packages run: nuget restore src/Orchard.sln - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 - name: Compile run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:TreatWarningsAsErrors=true -WarnAsError /p:MvcBuildViews=true @@ -69,10 +69,10 @@ jobs: runs-on: windows-2025 steps: - name: Clone Repository - uses: actions/checkout@v4.1.1 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Setup NodeJS - uses: actions/setup-node@v4.0.2 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 7 diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml new file mode 100644 index 000000000..8432969db --- /dev/null +++ b/.github/workflows/release-package.yml @@ -0,0 +1,39 @@ +name: Release Package +# Builds the release package and uploads it as an artifact. + +on: + workflow_dispatch: + +jobs: + release-package: + name: Release Package + runs-on: Windows-2025 + defaults: + run: + shell: cmd + steps: + - name: Clone Repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Restore NuGet Packages + run: nuget restore src/Orchard.sln + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 + + - name: Build Precompiled Application + run: msbuild Orchard.proj /m /v:minimal /t:Precompiled + + - name: Generate Release Package Name + id: package-name + shell: pwsh + run: | + $packageName = "Orchard-$('${{ github.ref_name }}'.Replace('/', '_'))-${{ github.sha }}" + "package-name=$packageName" >> $Env:GITHUB_OUTPUT + + - name: Upload Release Package + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ steps.package-name.outputs.package-name }} + path: .\build\Precompiled + if-no-files-found: error diff --git a/.github/workflows/specflow.yml b/.github/workflows/specflow.yml new file mode 100644 index 000000000..c36a2e5e4 --- /dev/null +++ b/.github/workflows/specflow.yml @@ -0,0 +1,59 @@ +name: SpecFlow Tests +# Compiles the solution and runs unit tests, as well the SpecFlow tests on the main development branches. + +on: + workflow_dispatch: + push: + branches: + - 1.10.x + - dev + schedule: + - cron: '0 0 * * 1' # Every Monday midnight. + +jobs: + define-matrix: + name: Define Strategy Matrix + runs-on: ubuntu-24.04 + outputs: + branches: ${{ steps.branches.outputs.branches }} + steps: + - name: Define Branches + id: branches + run: | + if [ "${{ github.event_name }}" = "schedule" ]; then + echo 'branches=["1.10.x", "dev"]' >> "$GITHUB_OUTPUT" + else + echo 'branches=["${{ github.ref_name }}"]' >> "$GITHUB_OUTPUT" + fi + + compile: + name: SpecFlow Tests + needs: define-matrix + runs-on: windows-2025 + strategy: + matrix: + branch: ${{ fromJSON(needs.define-matrix.outputs.branches) }} + fail-fast: false + defaults: + run: + shell: cmd + steps: + - name: Clone Repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ matrix.branch }} + + - name: Restore NuGet Packages + run: nuget restore src/Orchard.sln + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 + + - name: Compile + run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:MvcBuildViews=true /p:TreatWarningsAsErrors=true -WarnAsError + + - name: Test + run: msbuild Orchard.proj /m /v:minimal /t:Test + + - name: Spec + run: msbuild Orchard.proj /m /v:minimal /t:Spec From 971a97874c9268990a6cea6aa7f28173a60886d9 Mon Sep 17 00:00:00 2001 From: Benedek Farkas Date: Thu, 2 Oct 2025 19:18:19 +0200 Subject: [PATCH 2/4] Preventing Indexing AdminController and Commands from operating with unsafe index names (#8845) --- .../Commands/IndexingCommands.cs | 53 ++++++++----------- .../Controllers/AdminController.cs | 43 +++++++++------ .../Helpers/IndexingHelpers.cs | 8 +++ .../Orchard.Indexing/Orchard.Indexing.csproj | 3 +- 4 files changed, 61 insertions(+), 46 deletions(-) create mode 100644 src/Orchard.Web/Modules/Orchard.Indexing/Helpers/IndexingHelpers.cs diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Commands/IndexingCommands.cs b/src/Orchard.Web/Modules/Orchard.Indexing/Commands/IndexingCommands.cs index 1514bdc04..a89a41fb6 100644 --- a/src/Orchard.Web/Modules/Orchard.Indexing/Commands/IndexingCommands.cs +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Commands/IndexingCommands.cs @@ -4,7 +4,7 @@ using Orchard.Commands; using Orchard.ContentManagement; using Orchard.Indexing.Services; using Orchard.Tasks.Indexing; -using Orchard.Utility.Extensions; +using static Orchard.Indexing.Helpers.IndexingHelpers; namespace Orchard.Indexing.Commands { public class IndexingCommands : DefaultOrchardCommandHandler { @@ -38,27 +38,22 @@ namespace Orchard.Indexing.Commands { return; } - if (string.IsNullOrWhiteSpace(index)) { + if (!IsValidIndexName(index)) { Context.Output.WriteLine(T("Invalid index name.")); return; } - if (index.ToSafeName() != index) { - Context.Output.WriteLine(T("Invalid index name.")); + var indexProvider = _indexManager.GetSearchIndexProvider(); + if (indexProvider == null) { + Context.Output.WriteLine(T("No indexing service was found. Please enable a module like Lucene.")); } else { - var indexProvider = _indexManager.GetSearchIndexProvider(); - if(indexProvider == null) { - Context.Output.WriteLine(T("No indexing service was found. Please enable a module like Lucene.")); + if (indexProvider.Exists(index)) { + Context.Output.WriteLine(T("The specified index already exists.")); } else { - if (indexProvider.Exists(index)) { - Context.Output.WriteLine(T("The specified index already exists.")); - } - else { - _indexManager.GetSearchIndexProvider().CreateIndex(index); - Context.Output.WriteLine(T("New index has been created successfully.")); - } + _indexManager.GetSearchIndexProvider().CreateIndex(index); + Context.Output.WriteLine(T("New index has been created successfully.")); } } } @@ -66,7 +61,7 @@ namespace Orchard.Indexing.Commands { [CommandName("index update")] [CommandHelp("index update \r\n\t" + "Updates the specified index")] public void Update(string index) { - if (string.IsNullOrWhiteSpace(index)) { + if (!IsValidIndexName(index)) { Context.Output.WriteLine(T("Invalid index name.")); return; } @@ -78,7 +73,7 @@ namespace Orchard.Indexing.Commands { [CommandName("index rebuild")] [CommandHelp("index rebuild \r\n\t" + "Rebuilds the specified index")] public void Rebuild(string index) { - if (string.IsNullOrWhiteSpace(index)) { + if (!IsValidIndexName(index)) { Context.Output.WriteLine(T("Invalid index name.")); return; } @@ -91,24 +86,24 @@ namespace Orchard.Indexing.Commands { [CommandHelp("index query /Query:\r\n\t" + "Searches the specified terms in the specified index")] [OrchardSwitches("Query")] public void Search(string index) { - if (string.IsNullOrWhiteSpace(index)) { + if (!IsValidIndexName(index)) { Context.Output.WriteLine(T("Invalid index name.")); return; } - if ( !_indexManager.HasIndexProvider() ) { + if (!_indexManager.HasIndexProvider()) { Context.Output.WriteLine(T("No index available")); return; } var searchBuilder = _indexManager.GetSearchIndexProvider().CreateSearchBuilder(index); - var results = searchBuilder.Parse( new [] {"body", "title"}, Query).Search(); + var results = searchBuilder.Parse(new[] { "body", "title" }, Query).Search(); Context.Output.WriteLine("{0} result{1}\r\n-----------------\r\n", results.Count(), results.Count() > 0 ? "s" : ""); Context.Output.WriteLine("┌──────────────────────────────────────────────────────────────┬────────┐"); Context.Output.WriteLine("│ {0} │ {1,6} │", "Title" + new string(' ', 60 - "Title".Length), "Score"); Context.Output.WriteLine("├──────────────────────────────────────────────────────────────┼────────┤"); - foreach ( var searchHit in results ) { + foreach (var searchHit in results) { var contentItem = _contentManager.Get(searchHit.ContentItemId); var metadata = _contentManager.GetItemMetadata(contentItem); var title = String.IsNullOrWhiteSpace(metadata.DisplayText) ? "- no title -" : metadata.DisplayText; @@ -126,12 +121,12 @@ namespace Orchard.Indexing.Commands { [CommandHelp("index stats \r\n\t" + "Displays some statistics about the search index")] [OrchardSwitches("IndexName")] public void Stats(string index) { - if (string.IsNullOrWhiteSpace(index)) { + if (!IsValidIndexName(index)) { Context.Output.WriteLine(T("Invalid index name.")); return; } - if ( !_indexManager.HasIndexProvider() ) { + if (!_indexManager.HasIndexProvider()) { Context.Output.WriteLine(T("No index available")); return; } @@ -140,11 +135,10 @@ namespace Orchard.Indexing.Commands { } [CommandName("index refresh")] - [CommandHelp("index refresh /ContentItem: \r\n\t" + "Refreshes the index for the specifed ")] + [CommandHelp("index refresh /ContentItem: \r\n\t" + "Refreshes the index for the specified ")] [OrchardSwitches("ContentItem")] public void Refresh() { - int contentItemId; - if ( !int.TryParse(ContentItem, out contentItemId) ) { + if (!int.TryParse(ContentItem, out int contentItemId)) { Context.Output.WriteLine(T("Invalid content item id. Not an integer.")); return; } @@ -152,19 +146,18 @@ namespace Orchard.Indexing.Commands { var contentItem = _contentManager.Get(contentItemId); _indexingTaskManager.CreateUpdateIndexTask(contentItem); - Context.Output.WriteLine(T("Content Item marked for reindexing")); + Context.Output.WriteLine(T("Content Item marked for re-indexing")); } [CommandName("index delete")] - [CommandHelp("index delete /ContentItem:\r\n\t" + "Deletes the specifed from the index")] + [CommandHelp("index delete /ContentItem:\r\n\t" + "Deletes the specified from the index")] [OrchardSwitches("ContentItem")] public void Delete() { - int contentItemId; - if(!int.TryParse(ContentItem, out contentItemId)) { + if (!int.TryParse(ContentItem, out int contentItemId)) { Context.Output.WriteLine(T("Invalid content item id. Not an integer.")); return; } - + var contentItem = _contentManager.Get(contentItemId); _indexingTaskManager.CreateDeleteIndexTask(contentItem); diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Indexing/Controllers/AdminController.cs index e5ec3b4f2..ab3c25f96 100644 --- a/src/Orchard.Web/Modules/Orchard.Indexing/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Controllers/AdminController.cs @@ -2,12 +2,12 @@ using System.Linq; using System.Web.Mvc; using Orchard.Indexing.Services; +using Orchard.Indexing.ViewModels; using Orchard.Localization; using Orchard.Logging; using Orchard.Security; -using Orchard.Indexing.ViewModels; using Orchard.UI.Notify; -using Orchard.Utility.Extensions; +using static Orchard.Indexing.Helpers.IndexingHelpers; namespace Orchard.Indexing.Controllers { public class AdminController : Controller { @@ -15,7 +15,7 @@ namespace Orchard.Indexing.Controllers { private readonly IIndexManager _indexManager; public AdminController( - IIndexingService indexingService, + IIndexingService indexingService, IOrchardServices services, IIndexManager indexManager) { _indexingService = indexingService; @@ -34,23 +34,21 @@ namespace Orchard.Indexing.Controllers { IndexEntries = Enumerable.Empty(), IndexProvider = _indexManager.GetSearchIndexProvider() }; - + if (_indexManager.HasIndexProvider()) { viewModel.IndexEntries = _indexManager.GetSearchIndexProvider().List().Select(x => { try { return _indexingService.GetIndexEntry(x); } - catch(Exception e) { + catch (Exception e) { Logger.Error(e, "Index couldn't be read: " + x); - return new IndexEntry { + return new IndexEntry { IndexName = x, IndexingStatus = IndexingStatus.Unavailable }; } }); } - - // Services.Notifier.Information(T("The index might be corrupted. If you can't recover click on Rebuild.")); return View(viewModel); } @@ -59,7 +57,7 @@ namespace Orchard.Indexing.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage the search index."))) return new HttpUnauthorizedResult(); - return View("Create", String.Empty); + return View("Create"); } [HttpPost, ActionName("Create")] @@ -68,7 +66,7 @@ namespace Orchard.Indexing.Controllers { return new HttpUnauthorizedResult(); var provider = _indexManager.GetSearchIndexProvider(); - if (String.IsNullOrWhiteSpace(id) || id.ToSafeName() != id) { + if (!IsValidIndexName(id)) { Services.Notifier.Error(T("Invalid index name.")); return View("Create", id); } @@ -82,9 +80,9 @@ namespace Orchard.Indexing.Controllers { provider.CreateIndex(id); Services.Notifier.Information(T("Index named {0} created successfully", id)); } - catch(Exception e) { + catch (Exception e) { Services.Notifier.Error(T("An error occurred while creating the index: {0}", id)); - Logger.Error("An error occurred while creatign the index " + id, e); + Logger.Error("An error occurred while creating the index " + id, e); return View("Create", id); } @@ -96,7 +94,12 @@ namespace Orchard.Indexing.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage the search index."))) return new HttpUnauthorizedResult(); - _indexingService.UpdateIndex(id); + if (IsValidIndexName(id)) { + _indexingService.UpdateIndex(id); + } + else { + Services.Notifier.Error(T("Invalid index name.")); + } return RedirectToAction("Index"); } @@ -106,7 +109,12 @@ namespace Orchard.Indexing.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage the search index."))) return new HttpUnauthorizedResult(); - _indexingService.RebuildIndex(id); + if (IsValidIndexName(id)) { + _indexingService.RebuildIndex(id); + } + else { + Services.Notifier.Error(T("Invalid index name.")); + } return RedirectToAction("Index"); } @@ -116,7 +124,12 @@ namespace Orchard.Indexing.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage the search index."))) return new HttpUnauthorizedResult(); - _indexingService.DeleteIndex(id); + if (IsValidIndexName(id)) { + _indexingService.DeleteIndex(id); + } + else { + Services.Notifier.Error(T("Invalid index name.")); + } return RedirectToAction("Index"); } diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Helpers/IndexingHelpers.cs b/src/Orchard.Web/Modules/Orchard.Indexing/Helpers/IndexingHelpers.cs new file mode 100644 index 000000000..340c055e2 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Helpers/IndexingHelpers.cs @@ -0,0 +1,8 @@ +using Orchard.Utility.Extensions; + +namespace Orchard.Indexing.Helpers { + public static class IndexingHelpers { + public static bool IsValidIndexName(string name) => + !string.IsNullOrWhiteSpace(name) && name.ToSafeName() == name; + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Indexing/Orchard.Indexing.csproj b/src/Orchard.Web/Modules/Orchard.Indexing/Orchard.Indexing.csproj index 39be2faa4..c78226286 100644 --- a/src/Orchard.Web/Modules/Orchard.Indexing/Orchard.Indexing.csproj +++ b/src/Orchard.Web/Modules/Orchard.Indexing/Orchard.Indexing.csproj @@ -95,6 +95,7 @@ + @@ -180,4 +181,4 @@ - + \ No newline at end of file From 28f0bd52edb35a77fe0241314e31124505d4c0b9 Mon Sep 17 00:00:00 2001 From: Benedek Farkas Date: Sun, 5 Oct 2025 16:26:36 +0200 Subject: [PATCH 3/4] #8830: Fixing that fields attached to a Menu Item are not updated (#8844) * Navigation: Fixing that saving a menu item should not force creating a draft version * Navigation: Adding support to the AdminController for the Delete button rendered by the Contents feature * Navigation: Fixing that fields attached to a Menu Item should also be updated when creating the item * MainMenuService: Fixing that unpublished menu items could not be deleted * Adding notification when deleting a menu items --- .../Navigation/Controllers/AdminController.cs | 42 +++++++++++++------ .../Navigation/Services/MainMenuService.cs | 12 +++--- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs b/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs index 2337598da..184791a48 100644 --- a/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs +++ b/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Web.Mvc; -using System.Web.Routing; using Orchard.ContentManagement; using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.Handlers; @@ -14,6 +13,7 @@ using Orchard.Data; using Orchard.Exceptions; using Orchard.Localization; using Orchard.Logging; +using Orchard.Mvc; using Orchard.Mvc.Extensions; using Orchard.Mvc.Html; using Orchard.Security; @@ -131,11 +131,15 @@ namespace Orchard.Core.Navigation.Controllers { return RedirectToAction("Index", new { menuId }); } + [HttpPost, ActionName("Edit")] + [FormValueRequired("submit.Delete")] + public ActionResult EditDeletePOST(int id) => Delete(id); + [HttpPost] public ActionResult Delete(int id) { - MenuPart menuPart = _menuService.Get(id); int? menuId = null; + if (!_authorizer.Authorize( Permissions.ManageMenus, menuPart == null ? null : _menuService.GetMenu(menuPart.Menu.Id), @@ -152,7 +156,7 @@ namespace Orchard.Core.Navigation.Controllers { .ToList(); foreach (var menuItem in menuItems.Concat(new[] { menuPart })) { - // if the menu item is a concrete content item, don't delete it, just unreference the menu + // if the menu item is a concrete content item, don't delete it, just remove the menu reference if (!menuPart.ContentItem.TypeDefinition.Settings.ContainsKey("Stereotype") || menuPart.ContentItem.TypeDefinition.Settings["Stereotype"] != "MenuItem") { menuPart.Menu = null; @@ -162,6 +166,11 @@ namespace Orchard.Core.Navigation.Controllers { } } + _notifier.Information(T.Plural( + "The menu item '{1}' has been deleted.", + "The menu item '{1}' and its children have been deleted.", + menuItems.Count() + 1, + menuPart.MenuText)); } return RedirectToAction("Index", new { menuId }); @@ -172,7 +181,8 @@ namespace Orchard.Core.Navigation.Controllers { return new HttpUnauthorizedResult(); // create a new temporary menu item - var menuPart = _contentManager.New(id); + var contentItem = _contentManager.New(id); + var menuPart = contentItem.As(); if (menuPart == null) return HttpNotFound(); @@ -187,7 +197,7 @@ namespace Orchard.Core.Navigation.Controllers { // filter the content items for this specific menu menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu(menu)); menuPart.Menu = menu; - var model = _contentManager.BuildEditor(menuPart); + var model = _contentManager.BuildEditor(contentItem); return View(model); } @@ -206,23 +216,32 @@ namespace Orchard.Core.Navigation.Controllers { public ActionResult CreateMenuItemPost(string id, int menuId, string returnUrl) { if (!_authorizer.Authorize(Permissions.ManageMenus, _menuService.GetMenu(menuId), T("Couldn't manage the menu"))) return new HttpUnauthorizedResult(); - var menuPart = _contentManager.New(id); + + var contentItem = _contentManager.New(id); + var menuPart = contentItem.As(); + if (menuPart == null) return HttpNotFound(); + // load the menu var menu = _contentManager.Get(menuId); if (menu == null) return HttpNotFound(); + _contentManager.Create(contentItem); + menuPart.Menu = menu; - var model = _contentManager.UpdateEditor(menuPart, this); menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu(menu)); - _contentManager.Create(menuPart); + + var model = _contentManager.UpdateEditor(contentItem, this); + if (!ModelState.IsValid) { _transactionManager.Cancel(); return View(model); } - _notifier.Information(T("Your {0} has been added.", menuPart.TypeDefinition.DisplayName)); + + _notifier.Information(T("Your {0} has been added.", contentItem.TypeDefinition.DisplayName)); + return this.RedirectLocal(returnUrl, () => RedirectToAction("Index")); } @@ -308,7 +327,8 @@ namespace Orchard.Core.Navigation.Controllers { } private ActionResult EditPOST(int id, string returnUrl, Action conditionallyPublish) { - var menuPart = _contentManager.GetDraftRequired(id); + var contentItem = _contentManager.GetLatest(id); + var menuPart = contentItem.As(); if (menuPart == null) return HttpNotFound(); @@ -316,8 +336,6 @@ namespace Orchard.Core.Navigation.Controllers { if (!_authorizer.Authorize(Permissions.ManageMenus, menuPart.Menu, T("Couldn't manage the menu"))) return new HttpUnauthorizedResult(); - var contentItem = menuPart.ContentItem; - string previousRoute = null; if (contentItem.Has() && !string.IsNullOrWhiteSpace(returnUrl) diff --git a/src/Orchard.Web/Core/Navigation/Services/MainMenuService.cs b/src/Orchard.Web/Core/Navigation/Services/MainMenuService.cs index 682df695d..d314896a6 100644 --- a/src/Orchard.Web/Core/Navigation/Services/MainMenuService.cs +++ b/src/Orchard.Web/Core/Navigation/Services/MainMenuService.cs @@ -25,7 +25,7 @@ namespace Orchard.Core.Navigation.Services { } public IContent GetMenu(string menuName) { - if(string.IsNullOrWhiteSpace(menuName)) { + if (string.IsNullOrWhiteSpace(menuName)) { return null; } @@ -37,19 +37,19 @@ namespace Orchard.Core.Navigation.Services { } public IContent GetMenu(int menuId) { - return _contentManager.Get(menuId, VersionOptions.Published); + return _contentManager.Get(menuId, VersionOptions.Published); } public MenuPart Get(int menuPartId) { - return _contentManager.Get(menuPartId); + return _contentManager.Get(menuPartId, VersionOptions.Latest); } public IContent Create(string name) { - - if(string.IsNullOrWhiteSpace(name)) { + + if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(name); } - + var menu = _contentManager.Create("Menu"); menu.As().Title = name; From 1eaa9f3068a0ebdc8feb300b560683ae8dbad100 Mon Sep 17 00:00:00 2001 From: Benedek Farkas Date: Sun, 5 Oct 2025 19:53:45 +0200 Subject: [PATCH 4/4] Removing duplicate action from the Core/Navigation AdminController --- .../Core/Navigation/Controllers/AdminController.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs b/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs index b967adab3..83b8e86ae 100644 --- a/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs +++ b/src/Orchard.Web/Core/Navigation/Controllers/AdminController.cs @@ -313,10 +313,6 @@ namespace Orchard.Core.Navigation.Controllers { return RedirectToAction("Index", new { menuId = menuPart.Menu.Id }); } - [HttpPost, ActionName("Edit")] - [Mvc.FormValueRequired("submit.Delete")] - public ActionResult EditDeletePOST(int id) => Delete(id); - [HttpPost, ActionName("Edit")] [Mvc.FormValueRequired("submit.Unpublish")] public ActionResult EditUnpublishPOST(int id) => Unpublish(id);