Skip to content

Add Message Templates management to Grand.Web.Store#694

Open
Copilot wants to merge 14 commits into
developfrom
copilot/add-message-template-controller
Open

Add Message Templates management to Grand.Web.Store#694
Copilot wants to merge 14 commits into
developfrom
copilot/add-message-template-controller

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 12, 2026

  • Explore codebase and understand patterns
  • Create MessageTemplateController in Grand.Web.Store/Controllers/
    • List, ListGlobal (POST), ListStore (POST), Create, Edit, Delete, CopyTemplate actions
    • Store-ownership enforcement on edit/delete/copy
    • Email accounts filtered to current store
    • Block duplicate template names per store on Create (fixed: uses GetAllMessageTemplates("") + explicit LimitedToStores filter, same pattern as CopyTemplate)
    • Block copy when a store-specific template with the same name already exists for the current store
    • Copy only allowed from global templates (LimitedToStores = false)
    • ListGlobal and ListStore honor command.Page / command.PageSize for server-side pagination
  • Create Grand.Web.Store/Areas/Store/Views/MessageTemplate/ views:
    • List.cshtml (two tabs: Store-specific CRUD + Global read-only with Copy button, with page size selector)
    • Create.cshtml
    • Edit.cshtml (with Save, SaveContinue, Copy, Delete buttons)
    • Partials/CreateOrUpdate.cshtml (tokens, localized editor, delay, attachment)
  • Added resource strings to DefaultLanguage.xml
  • Build verified (0 errors)
  • Code review feedback addressed

Comment thread src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs Fixed
@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot If message template exists for current store do not allow to add it again

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot If message template exists for current store do not allow to add it again

Done in commit c81b017. The Create POST action now calls GetMessageTemplateByName(model.Name, CurrentStoreId) before inserting. If a template with that name already exists for the current store, a model error is added to the Name field and the form is redisplayed — the template is not saved.

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:

Comment thread src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs Fixed
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
@KrzysztofPajak
Copy link
Copy Markdown
Member

@copilot add missing resource string

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot add missing resource string

Added in commit e720b1a. The resource string admin.content.messagetemplates.fields.name.alreadyexists with value "A message template with the same name already exists for this store." has been added to DefaultLanguage.xml. Also fixed a missing using Grand.SharedKernel that was causing a build error with GrandException.

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 If we copy message template and exist for current store do not allow to add it again

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot If we copy message template and exist for current store do not allow to add it again

Done in commit f4681db. The CopyTemplate action now checks GetMessageTemplateByName(messageTemplate.Name, CurrentStoreId) before performing the copy. If a template with the same name already exists for the current store, an error notification is shown and the user is redirected back to the list — the copy is not created.

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 now is new bug, if I try to copy template it always get a message "admin.content.messagetemplates.fields.name.alreadyexists" event if not exists!

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot now is new bug, if I try to copy template it always get a message "admin.content.messagetemplates.fields.name.alreadyexists" event if not exists!

Fixed in commit 54f4377. The root cause: GetMessageTemplateByName uses store ACL authorization which returns true for global templates (not limited to stores), so it was finding the source template itself and treating it as a duplicate.

The check now only blocks the copy if the found template has LimitedToStores = true AND contains the current store — meaning a store-specific copy already exists. Copying global templates now works correctly.

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 missing resources: admin.content.messagetemplates.tab.store, admin.content.messagetemplates.tab.global,

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot missing resources: admin.content.messagetemplates.tab.store, admin.content.messagetemplates.tab.global,

Added in commit 74c5fa1. Both resource strings have been added to DefaultLanguage.xml:

  • admin.content.messagetemplates.tab.global → "Global"
  • admin.content.messagetemplates.tab.store → "Store"

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 copy is allowed only from templates where LimitedStore = false

…Template action

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/d4201352-8e49-4a12-a9d0-e07d480710ea

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

Copilot AI commented May 13, 2026

@copilot copy is allowed only from templates where LimitedStore = false

Done in commit fb220b8. The CopyTemplate action now returns early (redirects to List) if messageTemplate.LimitedToStores = true, so copy is only permitted for global templates where LimitedToStores = false. The Copy button in the UI is already limited to the Global tab which only shows global templates.

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 missing resource string: admin.content.messagetemplates.copied, and can still copy the same Name!

…es.copied resource string

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/2421abf4-924b-47d0-b2c5-d0d54936ed31

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

Copilot AI commented May 14, 2026

@copilot missing resource string: admin.content.messagetemplates.copied, and can still copy the same Name!

Fixed in commit 5195dd8.

  1. Missing resource: admin.content.messagetemplates.copied with value "The message template has been copied." added to DefaultLanguage.xml.

  2. Duplicate name bug: The root cause was that GetMessageTemplateByName(name, storeId) uses store ACL and returns the global template first (lowest ID), so existingTemplate.LimitedToStores was always false — bypassing the check. Fixed by using GetAllMessageTemplates("") (bypasses ACL cache) and explicitly looking for a template with LimitedToStores = true && Stores.Contains(CurrentStoreId), which reliably detects an existing store-specific copy.

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 /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/ShoppingCart/OrderTotalsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/AddOrderNoteModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerMerchandiseReturnsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/ShipmentDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderPagingModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderDetails�� /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerLoyaltyPointsModel.cs (dns block)
  • ccsca2021.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/ShoppingCart/OrderTotalsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/AddOrderNoteModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerMerchandiseReturnsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/ShipmentDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderPagingModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderDetails�� /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerLoyaltyPointsModel.cs (dns block)
  • crl.certum.pl
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/ShoppingCart/OrderTotalsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/AddOrderNoteModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerMerchandiseReturnsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/ShipmentDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderPagingModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderDetails�� /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerLoyaltyPointsModel.cs (dns block)
  • subca.ocsp-certum.com
    • Triggering command: /usr/share/dotnet/dotnet dotnet build src/Web/Grand.Web.Store/Grand.Web.Store.csproj /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/ShoppingCart/OrderTotalsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/AddOrderNoteModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerMerchandiseReturnsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/ShipmentDetailsModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderPagingModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/OrderDetails�� /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/MerchandiseReturnModel.cs /home/REDACTED/work/grandnode2/grandnode2/src/Web/Grand.Web/Models/Orders/CustomerLoyaltyPointsModel.cs (dns block)

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

@KrzysztofPajak KrzysztofPajak marked this pull request as ready for review May 14, 2026 16:34
Copilot AI review requested due to automatic review settings May 14, 2026 16:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Store-area Message Templates management module so per-store staff can list, create, edit, delete, and copy message templates while only seeing/affecting templates scoped to their own store. Global templates are exposed read-only with a "Copy to my store" action.

Changes:

  • New MessageTemplateController enforcing store ownership for edit/delete/copy and exposing two grids (store-scoped, global).
  • New Razor views (List, Create, Edit, partial CreateOrUpdate) wiring kendo grids, the localized editor, tokens, attachment, and delay UI.
  • (PR description) Adds Admin.Content.MessageTemplates.* resource strings to DefaultLanguage.xml (not in the diff shown).

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs New controller for store-scoped message template CRUD plus copy-from-global.
src/Web/Grand.Web.Store/Areas/Store/Views/MessageTemplate/List.cshtml Two-tab kendo grid view: store templates (CRUD) and global templates (read-only with per-row copy form).
src/Web/Grand.Web.Store/Areas/Store/Views/MessageTemplate/Create.cshtml Wrapper view for creating a new store-specific template.
src/Web/Grand.Web.Store/Areas/Store/Views/MessageTemplate/Edit.cshtml Wrapper view for editing with Save / Save&Continue / Copy / Delete buttons.
src/Web/Grand.Web.Store/Areas/Store/Views/MessageTemplate/Partials/CreateOrUpdate.cshtml Shared form partial with localized editor, token picker, send delay, attachment fields.
Comments suppressed due to low confidence (6)

src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs:268

  • CopyTemplate's duplicate-name check is broken. GetMessageTemplateByName(name, storeId) filters results through IAclService.Authorize, which returns true for any template where LimitedToStores == false (see AclService.Authorize). The global template being copied therefore matches its own name lookup, so existing is never null and every copy attempt fails with "Admin.Content.MessageTemplates.Fields.Name.AlreadyExists". To detect only store-specific duplicates for the current store (as the PR description states), iterate GetAllMessageTemplates("") and look for a template where LimitedToStores == true && Stores.Contains(CurrentStoreId) && Name == messageTemplate.Name (or otherwise exclude global matches).
        // Prevent duplicate: check if a store-specific template with the same name already exists for the current store
        var existing = await messageTemplateService.GetMessageTemplateByName(messageTemplate.Name, CurrentStoreId);
        if (existing != null)
        {
            Error(translationService.GetResource("Admin.Content.MessageTemplates.Fields.Name.AlreadyExists"));
            return RedirectToAction("List");
        }

src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs:252

  • CopyTemplate is gated by PermissionActionName.Edit, but the action actually creates a new entity (it inserts a copy via CopyMessageTemplate and only then updates the store assignment). For consistency with other create flows in this controller and the rest of the Store area, consider gating it with PermissionActionName.Create instead, so that a user with view/edit but not create permission cannot spawn new templates.
    [PermissionAuthorizeAction(PermissionActionName.Edit)]
    [HttpPost]
    public async Task<IActionResult> CopyTemplate(MessageTemplateModel model)

src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs:279

  • CopyMessageTemplate copies Stores and LimitedToStores from the source. Because the source is always a global template (LimitedToStores == false), the inserted copy is briefly visible globally to all stores between InsertMessageTemplate and the subsequent UpdateMessageTemplate that re-scopes it to the current store. Setting LimitedToStores = true and Stores = [CurrentStoreId] on the in-memory entity before persisting (or doing the assignment inside a single insert) would avoid the temporary cross-store exposure and remove a window where another store could load the new template via cache.
        try
        {
            var newMessageTemplate = await messageTemplateService.CopyMessageTemplate(messageTemplate);
            // Assign copy to the current store
            newMessageTemplate.LimitedToStores = true;
            newMessageTemplate.Stores = [CurrentStoreId];
            await messageTemplateService.UpdateMessageTemplate(newMessageTemplate);

            Success(translationService.GetResource("Admin.Content.MessageTemplates.Copied"));
            return RedirectToAction("Edit", new { id = newMessageTemplate.Id });

src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs:196

  • messageTemplate.Stores = [CurrentStoreId]; unconditionally collapses the assigned-store collection to a single entry. If a store-specific template was ever assigned to multiple stores (e.g. a future admin enhancement), an edit by a staff user would silently strip all other stores from it. Consider preserving any other entries and only ensuring CurrentStoreId is included, or at minimum documenting the single-store assumption.
            // Keep it assigned to the current store
            messageTemplate.LimitedToStores = true;
            messageTemplate.Stores = [CurrentStoreId];

src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs:256

  • When the staff user uses the "Copy" button on the Edit page, this submits the standard Edit <form> with formaction=CopyTemplate. The form does not include the source template's Name field as a hidden input when editing (line 65 only emits the hidden Name because the template already has an Id), but the Body field plus a number of other model properties WILL be submitted unfiltered to CopyTemplate(MessageTemplateModel model). The action only reads model.Id so this is harmless today, but it means the Copy button effectively bypasses normal model validation; if a future change starts using fields off model here, that data would come from a partially-edited form rather than the original entity. Worth either narrowing the action's parameter to just string id or documenting the intent.
    public async Task<IActionResult> CopyTemplate(MessageTemplateModel model)
    {
        var messageTemplate = await messageTemplateService.GetMessageTemplateById(model.Id);
        if (messageTemplate == null)
            return RedirectToAction("List");

src/Web/Grand.Web.Store/Areas/Store/Views/MessageTemplate/Partials/CreateOrUpdate.cshtml:213

  • The kendo column template builds a regex from the user's search input using new RegExp(searchVal, "i") without escaping. If a token name (or, more likely, a typed search character) contains a regex metacharacter such as (, [, *, etc., this will throw a SyntaxError and break the search behavior on every subsequent keystroke. Escape the input before constructing the regex (e.g. searchVal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) or use indexOf/includes instead.
    function SearchTokens(e) {
        var searchVal = $(e.target).val();
        var TokensPnl = $(e.target).closest(".form-group").find(".AllowedTokens");
        $(TokensPnl).find(".btn").show()
            .each(function (i, c) {
                var Token = $(c).text();
                if (Token.search(new RegExp(searchVal, "i")) == -1) {
                    $(c).hide();
                }
            });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +100 to +109
var existing = await messageTemplateService.GetMessageTemplateByName(model.Name, CurrentStoreId);
if (existing != null)
{
ModelState.AddModelError("Name", translationService.GetResource("Admin.Content.MessageTemplates.Fields.Name.AlreadyExists"));
model.HasAttachedDownload = !string.IsNullOrEmpty(model.AttachedDownloadId);
model.AllowedTokens = messageTokenProvider.GetListOfAllowedTokens();
foreach (var ea in await emailAccountService.GetAllEmailAccounts(CurrentStoreId))
model.AvailableEmailAccounts.Add(ea.ToModel());
return View(model);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot any suggestion?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 2668755. The Create duplicate check now uses GetAllMessageTemplates("") and filters explicitly for LimitedToStores = true && Stores.Contains(CurrentStoreId) — the same pattern already in CopyTemplate. This means a store user can create a template named "OrderPlaced.CustomerNotification" even though a global template with that name exists; only an existing store-specific copy of that name blocks creation.

Comment on lines +60 to +75
public async Task<IActionResult> ListStore(DataSourceRequest command)
{
var allTemplates = await messageTemplateService.GetAllMessageTemplates(CurrentStoreId);
var storeTemplates = allTemplates
.Where(t => t.LimitedToStores && t.Stores.Contains(CurrentStoreId))
.ToList();

var items = storeTemplates.Select(x => x.ToModel()).ToList();

var gridModel = new DataSourceResult {
Data = items,
Total = items.Count
};

return Json(gridModel);
}
Comment on lines +41 to +56
public async Task<IActionResult> ListGlobal(DataSourceRequest command)
{
var allTemplates = await messageTemplateService.GetAllMessageTemplates("");
var globalTemplates = allTemplates
.Where(t => !t.LimitedToStores)
.ToList();

var items = globalTemplates.Select(x => x.ToModel()).ToList();

var gridModel = new DataSourceResult {
Data = items,
Total = items.Count
};

return Json(gridModel);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Add pagination

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 3cd28d8. Both ListGlobal and ListStore now apply Skip((command.Page - 1) * command.PageSize).Take(command.PageSize) before projecting to models and return the full count in Total. The List.cshtml view now injects AdminAreaSettings and sets pageSize: @(adminAreaSettings.DefaultGridPageSize) and pageSizes: [@(adminAreaSettings.GridPageSizes)] on both grids, matching the pattern used by other Store-area list views.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@KrzysztofPajak KrzysztofPajak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 14, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

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:

…ecific templates with same name as global templates

Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/e7f3eee3-ad46-44a1-834c-44b0a06d6341

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

Copilot AI commented May 14, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

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.

3 participants