Skip to content

feat: Pages (Topics) CRUD management in Grand.Web.Store#693

Draft
Copilot wants to merge 5 commits into
developfrom
copilot/implement-page-management-ui
Draft

feat: Pages (Topics) CRUD management in Grand.Web.Store#693
Copilot wants to merge 5 commits into
developfrom
copilot/implement-page-management-ui

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 10, 2026

  • Create PageController.cs in Grand.Web.Store/Controllers/
  • Create views in Grand.Web.Store/Areas/Store/Views/Page/
  • Add @using Grand.Web.AdminShared.Models.Pages to _ViewImports.cshtml
  • Add Copy button & action on Edit page (for multistore/no-store topics)
  • Add EN translations for new keys to DefaultLanguage.xml
    • admin.common.copy → "Copy"
    • admin.content.pages.copy.duplicatesystemname → duplicate SystemName error message
    • admin.content.pages.permissions → multistore warning message
  • Fix ArgumentNullException on Copy: null-safe seoSettings.ReservedEntityUrlSlugs in SeNameService.ValidateSeName; add regression test

Copilot AI and others added 2 commits May 10, 2026 18:04
- Add PageController with CRUD operations, filtering by store, and
  automatic storeId assignment on create/edit
- Add List, Create, Edit views with Partials (TabInfo, TabSeo)
- Add Grand.Web.AdminShared.Models.Pages namespace to _ViewImports.cshtml"

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/e126ff73-2407-4da7-ae12-fd982e28a981

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
- Remove unused NoAccessToPageMessage constant
- Simplify redundant LimitedToStores condition in Edit GET
- Remove unnecessary ModelState check in Delete action

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/e126ff73-2407-4da7-ae12-fd982e28a981

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot Grand.Web.Store - If the topic is multistore or without set as a store, Add new button on the form EDIT with the name Copy, and add action copy current topic to the new one for the current store (but only if this topic name does not exists in the current store)

When a topic is multistore (LimitedToStores + multiple stores) or has no
store restriction, a "Copy" button is shown on the Edit page. Clicking
it creates a store-specific copy (all fields + locales preserved) for
the current store, guarded by a duplicate SystemName check. Redirects
to the new page's Edit view on success.

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/623ad074-c18a-43f8-9a53-00b7f6df747c

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 11, 2026

@copilot Grand.Web.Store - If the topic is multistore or without set as a store, Add new button on the form EDIT with the name Copy, and add action copy current topic to the new one for the current st...

Done in commit 7effd7c.

Copy button behaviour:

  • The Copy button appears on the Edit form only when the topic is multistore (LimitedToStores + Stores.Count > 1) or has no store restriction (!LimitedToStores).
  • Clicking it POSTs to the new Copy action which:
    1. Refuses to copy a topic that is already single-store restricted.
    2. Checks whether a page with the same SystemName already exists for the current store (excluding the original). Shows an error and stays on the edit page if so.
    3. Creates a full copy — all fields and localized content preserved — limited exclusively to the current store (LimitedToStores = true, Stores = [storeId]).
    4. Redirects the store manager to the new page's Edit view.

The button uses the HTML5 form= attribute to reference a small standalone form rendered outside the main edit form, keeping the markup valid.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • ccsca2021.crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)
  • ccsca2021.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)
  • crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)
  • subca.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot please add new translation to default language EN

@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot After click Copy I've got error
ArgumentNullException: Value cannot be null. (Parameter 'source')
System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
System.Linq.Enumerable.Contains(IEnumerable source, TSource value, IEqualityComparer comparer)
Grand.Business.Common.Services.Seo.SeNameService.ValidateSeName(T entity, string seName, string name, bool ensureNotEmpty) in SeNameService.cs
+
var reserved2 = seoSettings.ReservedEntityUrlSlugs.Contains(tempSeName, StringComparer.OrdinalIgnoreCase);
Grand.Web.AdminShared.Services.PageViewModelService.InsertPageModel(PageModel model) in PageViewModelService.cs
+
page.SeName = await _seNameService.ValidateSeName(page, model.SeName, page.Title ?? page.SystemName, true);
Grand.Web.Store.Controllers.PageController.Copy(string id) in PageController.cs
+
var newPage = await _pageViewModelService.InsertPageModel(model);

- admin.common.copy: Copy
- admin.content.pages.copy.duplicatesystemname: duplicate SystemName error
- admin.content.pages.permissions: multistore warning
- Update PageController to use translation resource instead of hardcoded string

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/e594e525-bd03-400b-9b1f-f634b62e6301

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 11, 2026

@copilot please add new translation to default language EN

Done in commit 7f0366b. Added 3 new entries to DefaultLanguage.xml (all in alphabetical order):

Key Value
admin.common.copy Copy
admin.content.pages.copy.duplicatesystemname A page with the same system name already exists for this store.
admin.content.pages.permissions Warning: This page is shared across multiple stores. Changes may affect other stores.

The hardcoded error string in PageController.Copy was also replaced with _translationService.GetResource("Admin.Content.Pages.Copy.DuplicateSystemName").

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • ccsca2021.crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj 53 -j ACCEPT administration/roxy_fileman/lang/es.json (dns block)
  • ccsca2021.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj 53 -j ACCEPT administration/roxy_fileman/lang/es.json (dns block)
  • crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj 53 -j ACCEPT administration/roxy_fileman/lang/es.json (dns block)
  • subca.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj 53 -j ACCEPT administration/roxy_fileman/lang/es.json (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

The property can be null when loaded from MongoDB in older installations.
Use null-conditional operator to avoid ArgumentNullException when Copy
(or any page insert) is triggered.

Also adds a regression test for the null-settings case.

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/e444e392-5111-4a1b-a6a1-297c8f3f5c98

Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 11, 2026

@copilot After click Copy I've got error
ArgumentNullException: Value cannot be null. (Parameter 'source')
System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
...

Fixed in commit c0dbedf.

seoSettings.ReservedEntityUrlSlugs can be null when the property was stored as null in MongoDB (older installation). The Copy action copied the original page's SeName verbatim — since that SeName is already registered as a slug for another entity, ValidateSeName entered its deduplication loop and hit the null list before any admin-area path had done so.

The fix is a one-character null-conditional in SeNameService.ValidateSeName:

// before
var reserved2 = seoSettings.ReservedEntityUrlSlugs.Contains(tempSeName, ...);
// after
var reserved2 = seoSettings.ReservedEntityUrlSlugs?.Contains(tempSeName, ...) ?? false;

A regression test covering the null-settings case was added to SeNameServiceTests.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • ccsca2021.crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)
  • ccsca2021.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)
  • crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)
  • subca.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants