Skip to content

fix: render styled 405 Method Not Allowed page and add translations for all locales - #417

Merged
navneetkumar-pim-webkul merged 1 commit into
unopim:2.1from
dripar-webkul:fix/method-not-allowed-405-2.1
May 26, 2026
Merged

navneetkumar-pim-webkul merged 1 commit into
unopim:2.1from
dripar-webkul:fix/method-not-allowed-405-2.1

Conversation

@dripar-webkul

Copy link
Copy Markdown
Collaborator

Backport of #414 to the 2.1 branch.

  • Render styled 405 error page and preserve Allow header for unsupported HTTP methods
  • Add 405 Method Not Allowed translations for all locales

@navneetkumar-pim-webkul
navneetkumar-pim-webkul merged commit ecaa831 into unopim:2.1 May 26, 2026
10 checks passed
navneetkumar-pim-webkul added a commit that referenced this pull request May 26, 2026
Date bumped to 2026-05-26 (actual release day). Add the four bullet
groups that were folded into the v2.1.1 line after the initial security
tag — installer fixes (#419, #420), styled 405 page (#417), pgsql
demo seeder (#418), MagicAI chat-latest temperature (#416), and the
multi-arch Docker publish (#426). Keeps the two original security
bullets at the top.
navneetkumar-pim-webkul added a commit that referenced this pull request Jun 26, 2026
…ixes) (#525)

* Fix: enforce ACL on remaining write-verb admin routes

Audit of the Bouncer middleware route-name ACL check revealed the
same bypass pattern as the roles fix (commit 960b12b) across many
admin sections: only the GET form routes (.create / .edit) were
declared in acl.php, while the actual POST .store and PUT .update
sibling routes were unmapped, so low-privileged admins could submit
forms they could not legitimately view.

Map the missing write-verb routes to the existing permission keys
following the established duplicate-key pattern. Sections covered:

- catalog.products.update
- catalog.categories.store / update
- catalog.category_fields.store / update
- catalog.attributes.store / update
- catalog.attributes.options.store / update / delete / update_sort
- catalog.attribute.groups.store / update
- catalog.families.store / update
- settings.channels.store / update
- settings.currencies.store / update
- settings.locales.update
- settings.data_transfer.imports.store / update / cancel / pause / resume / upload_images_zip
- settings.data_transfer.exports.store / update

Attribute option sub-routes are gated by catalog.attributes.edit
(managing options is part of attribute edit). Import lifecycle
actions (cancel/pause/resume) are gated by data_transfer.imports.execute.

Add Pest regression tests covering each newly mapped route.

Out of scope (separate ticket): configuration.integrations.*,
configuration.store, history.version.delete/restore,
magic_ai.platform.* / prompt.* / system_prompt.*,
catalog.products.bulk-edit.*, catalog.products.check-variant,
catalog.families.completeness.update. These need new permission keys
or product-owner input.

* chore(release): v2.1.1 — ACL write-route bypass patch

Adds CHANGELOG entry for the security fix landed in 5f84257.

* Fix: harden against Host / X-Forwarded-Host header poisoning

Laravel's url(), asset(), and Vite helpers resolve against the request
Host header by default. Combined with trustProxies(at: '*'), this lets
any client cause UnoPim to render asset URLs (admin <meta base-url>,
Vite <script src>, TinyMCE document_base_url) pointing at an attacker
origin, yielding full frontend takeover.

Layered fix:

1. Pin URL::forceRootUrl + URL::forceScheme to config('app.url') in
   CoreServiceProvider::boot so every url()/asset()/Vite call resolves
   against APP_URL regardless of any Host / X-Forwarded-Host value.

2. Replace url()->to('/') and asset('/') in the admin layout, history
   layout, anonymous layout, and TinyMCE component with
   rtrim(config('app.url'), '/') so the rendered output never reads
   from the request even if a future change drops layer 1.

3. Restrict trustProxies from '*' to env('TRUSTED_PROXIES','127.0.0.1')
   so X-Forwarded-* headers from arbitrary clients are ignored.

4. Enable trustHosts(at: ...) seeded from APP_URL + TRUSTED_HOSTS so
   Symfony returns 400 in non-local environments when the Host header
   is unrecognised.

Documents TRUSTED_PROXIES and TRUSTED_HOSTS in .env.example. Adds a
Pest regression suite that spoofs Host and X-Forwarded-Host headers and
asserts the rendered base-url meta + asset()/url() output stays pinned
to APP_URL.

* fix: cast booleans + bypass FK + filter sequences in demo extras seeder for pgsql (#418)

* fix: render styled 405 Method Not Allowed page and add translations for all locales (#417)

* fix: reject special characters in DB_DATABASE to prevent reinstall failure (#419)

* fix(installer): trim DB/Elasticsearch inputs and apply transform: trim(...) across installer prompts (#420)

* Update code for chat-latest model temperature not supported (#416)

* fix(installer): preserve user locales/currencies during demo seed  (#401)

* fix(installer): preserve user-selected locales/currencies during demo seeding

* refactor: optimize installer demo seed locale/currency handling

* fix(installer): redirect to install.php when vendor/autoload.php is missing (#400)

* Fix: restored the ui dropdown, and removed the hyphen from placeholder text (#399)

* fix(admin): hide product edit More button when no actions are available (#397)

* Fix: fixed the hide/unhide more button on the product edit page

* Fix: added translation for more text and fixed the test script

* Fix: fixed the dynamic column on the product page (#396)

* fix(webhook): validate URL on save + rename menu to "Webhook" (#398)

* fix(webhook): validate URL with test probe before save and rename menu to singular

* fix(webhook): probe URL before save, rename menu to singular, fix flaky e2e

* fix(webhook): make logs status filter driver-agnostic and per-code

* Add Gravatar fallback for admin avatars (#357)

* Add gravatar-based admin avatar fallback with local proxy

* Fix Blade conflict for Vue image error handler

* fix: address gravatar PR review feedback

* feat(admin): ACL-aware login redirect, datagrid copy/share-edit actions, conditional row actions

- SessionController: redirect post-login to first menu item the admin actually has ACL on (route-level key via app('acl')->roles), with fallback scan of acl config and logout when nothing is accessible
- AdminServiceProvider: expose adminLandingUrl (first authorized menu URL) to views; header logo now uses it instead of hardcoded dashboard route
- Datagrid: support new 'copy' and 'edit-share' action methods, plus per-row action 'condition' callback on Action/DataGrid to hide actions per record
- Datagrid: listen for share-link-changed emitter event to refetch; cleanup on beforeUnmount
- Header: revert avatar to admin->image / image_url path
- Lang: add link-copied / copy-failed strings, prefix select placeholders with "--", drop unused 'more' key

* fix(i18n): add link-copied / copy-failed and drop 'more' orphan in 32 locales

Propagates the en_US additions (admin.components.datagrid.index.link-copied
and copy-failed) and the removal of the unused datagrid 'more' label to all
33 supported locales, restoring Translation Integrity Audit pass.

* test(user): allow null intended URL in open-redirect tests

SessionController no longer stores url.intended when the referer is from a
foreign host (it skips the put instead of defaulting to the dashboard).
A null intended URL is equally safe — login then falls back to the
ACL-aware landing URL — so the test should only enforce that nothing
attacker-controlled is persisted, not that an internal URL is.

* test(e2e): update Hindi-translate test to match current modal markup

The translate modal (translate-action.blade.php) renders step indicators
as numbered circles with "Select Source" / "Select Target" labels — the
literal "Step 1" text was removed in #372 but this assertion was never
updated, causing the test to time out on every push-event run on master.

Switch the visibility check to the "Select Source" label which is the
actual user-visible text when the modal opens.

* Update chat-latest model for temperature not supported

* fix(admin): strip HTML from datagrid cell title tooltip (#403)

* Add Playwright REST API test suite and CI improvements (#413)

* test(api): add Playwright REST API test suite (211 tests)

* fix(test): make API tests run in CI without pre-seeded creds

- oauth.spec.js: lazy-refresh creds after apiToken bootstrap fixture
  (previously credsReady was frozen at file load → always skipped)
- auth-helper.js: fall back ADMIN_EMAIL to ADMIN_USERNAME (the env var
  name used by the existing playwright_test.yml workflow)

* fix(test): make API tests run in CI without pre-seeded creds

* Increase shard count for Playwright tests to 8

* Update Playwright test shards to 10 total

* Reduce Playwright test shards from 10 to 8

* Clean up comments in OAuth test file

Removed comments explaining performance considerations for OAuth tests.

* Address Copilot review comments on API test suite

- schema-validator: drop unsupported keywords from fallback comment;
  cache compiled Ajv validators via WeakMap to avoid per-call recompile
- config.js / .env.example: correct .api-config.json path to
  tests/e2e-pw/ (matches actual resolution)
- utils/api-config.js: add 'use strict' for consistency with siblings
- tests/api/README.md: align parallelism docs with the real
  playwright.config (fullyParallel: false, dynamic worker count)
- playwright.config.js: drop hardcoded "4 shards" from inline comment

* Address Copilot review: clarify validation-error helper and reap category-fields

- expectValidationError: update docstring to match the restricted
  [400, 422, 500] allowlist instead of claiming "any 4xx/5xx", so the
  comment no longer overstates what the helper accepts.
- media-upload spec: track the category-field created in 15.6 and delete
  it in afterAll so repeated runs don't leak cf_media_* rows.

---------

Co-authored-by: kunal kumar <[email protected]>

* Update AiApiClient.php

* Update regex to include 'chat-latest' model

* version2.1

* Update AgentRunner.php

* Update AiApiClient.php

* Update regex to include 'chat-latest' model

* Updated

* Updated chat-latest model for not support temperature

* Updated

* Update AgentRunner.php

* Update AiApiClient.php

* Update LaravelAiAdapter.php

---------

Co-authored-by: Dripar gupta <[email protected]>
Co-authored-by: Prince Kumar Sahni <[email protected]>
Co-authored-by: Johannes Rudolph <[email protected]>
Co-authored-by: Navneet Kumar <[email protected]>
Co-authored-by: Kunal kumar <[email protected]>
Co-authored-by: kunal kumar <[email protected]>

* docs(security): formalise rolling support policy; mark 1.0 EOL

Update SECURITY.md to reflect the 4-branch reality:
- 2.1.x active (current stable)
- 2.0.x security-only until ~90 days after 2.2 ships
- 1.0.x End of Life — v1.0.1 is the final release
- 0.x lines remain EOL

Adds rationale for 1.0 EOL (Laravel 10 EOL Aug 2026, pre-v11 bootstrap,
per-branch backport cost), clarifies the patched-release flow, and
points reporters at GitHub Security Advisories alongside the email
channel.

* docs(security): adopt Laravel-style support matrix (sync from master)

* Fix/docker multi arch publish (#426)

* ci(docker): resolve version with semver validation and :latest gating

* ci(docker): add QEMU setup and per-image tag computation

* ci(docker): build and push multi-arch (amd64+arm64) web image

* ci(docker): build and push multi-arch (amd64+arm64) queue worker image

* ci(docker): write multi-arch publish summary to GITHUB_STEP_SUMMARY

* ci(docker): gate :minor floating tag, fix MINOR for pre-releases, harden tag input

Review feedback addressed:

- MINOR was derived from VERSION via ${VERSION%.*}, which on tags like
  v2.1.0-beta.2 produced '2.1.0-beta' (last .suffix stripped). Now strip
  the -suffix first via ${VERSION%%-*} so MINOR is always the bare X.Y.
- :minor floating tag was always pushed, so a pre-release like v2.1.0-rc1
  would overwrite the stable :2.1 floating tag — the exact class of bug
  this PR is fixing for :latest. Gate :minor behind is_prerelease == false
  to match :latest gating.
- Move release/input tag from inline \${{ }} into env to avoid shell
  injection via a tag name containing shell metacharacters. Required
  contents:write to exploit, but cheap to harden.

Manual trace verified for v2.1.0, v2.1.0-rc1, v2.1.0-beta.2,
v2.1.0-alpha.1-build.5, v10.20.30.

* ci(docker): smoke-test multi-arch manifests + add queue HEALTHCHECK

Two best-practice additions on top of the multi-arch publish:

1. Smoke-test step after both builds:
   - docker buildx imagetools inspect verifies manifest list shape
     contains both linux/amd64 and linux/arm64 (regex match on raw
     manifest JSON, no false positives).
   - docker run --platform linux/amd64|arm64 boots the image and runs
     'php -v' as a minimal liveness check. Catches QEMU-segfault class
     regressions before users hit them.
   - set -euo pipefail + ::error:: annotations + ::group:: log folding.
   - Tag value passed via env (VERSION) not inline templating, so shell
     metacharacters in a tag cannot inject.

2. HEALTHCHECK in q.Dockerfile:
   - pgrep -f matches either 'artisan queue:work' or 'artisan
     schedule:work' (the same image runs both via overridden entrypoint
     for the scheduler service).
   - Bracket-trick pattern '[a]rtisan ...' keeps pgrep from matching
     its own command line (verified locally).
   - 60s start period gives Laravel boot + initial DB ping time to
     complete before the first probe.

* fix(docker): smoke-test entrypoint override, procps for HEALTHCHECK, workflow concurrency

Fixes the two bugs the previous commit introduced and hardens the
workflow with industry-standard concurrency + timeout controls.

1. Smoke test now overrides ENTRYPOINT:
   web-entrypoint.sh and q-entrypoint.sh require a live database and
   either run migrations or wait for the install lock file. Passing
   'php -v' as CMD reaches the entrypoint, not the PHP binary, so the
   container always exited non-zero before the smoke check could run.
   `--entrypoint php` bypasses the runtime entrypoint and exercises
   only the PHP runtime — sufficient to catch QEMU-segfault / glibc
   regressions, which is the bug class this smoke test exists for.

2. Wrap each docker run in `timeout 90s`:
   prevents a QEMU stall from hanging the whole job until the workflow
   timeout fires.

3. Install procps in q.Dockerfile:
   the HEALTHCHECK uses pgrep, which lives in procps. The php:8.3-cli
   base (debian-slim) does not include procps, so the previous commit
   would have made every queue container report unhealthy on first
   probe.

4. Workflow-level concurrency + per-job timeout:
   - concurrency.group keyed on the release / dispatch tag prevents
     two simultaneous publishes from racing on the same registry tag.
   - timeout-minutes: 90 caps the job below the default 360-minute
     ceiling so a stuck arm64 build fails predictably.

* fix(docker): widen queue HEALTHCHECK start_period to cover install lock wait

q-entrypoint.sh waits up to 300s (SETUP_WAIT_TIMEOUT) for the application
server to finish first-time setup and write storage/unopim.lock before
launching `php artisan queue:work`. With start_period=60s the container
would be marked unhealthy after the first ~150s while still legitimately
waiting for setup. Widen start_period to 360s (300s wait + 60s margin)
so the orchestrator only flags genuine post-startup failures.

Also document the push-then-smoke trade-off in the workflow: multi-arch
builds cannot use load:true (local docker holds only one arch), so the
smoke test runs after push. A failure means the bad image is already
public and requires manual rollback.

* fix(ci): allow hyphens in semver pre-release identifiers (#426 review)

Copilot review on PR #426 flagged the previous regex rejected valid
SemVer pre-release identifiers containing hyphens. Per SemVer 2.0.0 §9,
identifiers comprise ASCII alphanumerics + hyphens (separated by dots),
so tags like v2.1.0-rc-1 and v2.1.0-alpha-build.5 are legitimate
release shapes.

Old pattern: '(-[A-Za-z0-9.]+)?'
New pattern: '(-[0-9A-Za-z.-]+)?'

Trace verified:
  PASS: v2.1.0, v2.1.0-rc1, v2.1.0-beta.2, v2.1.0-rc-1,
        v2.1.0-alpha-build.5, v2.1.0-x-y-z, v10.20.30
  REJECT: vfoo, v2.1, v2.1.0- (trailing hyphen with no suffix)

* fix(ci/docker): address Copilot PR #427 review feedback

Four valid findings, all fixed:

1. HEALTHCHECK ignores operator-overridable SETUP_WAIT_TIMEOUT:
   The previous 360s start_period assumed the default 300s wait. If an
   operator raises SETUP_WAIT_TIMEOUT, the container would be marked
   unhealthy while still legitimately waiting for the install lock,
   triggering restart loops in some orchestrators.

   Rewrite the check so the lock file's presence is part of the
   condition: no lock yet → report healthy (worker still waiting for
   web/fpm setup); lock present → require an active queue:work or
   schedule:work process. start_period drops back to 60s since the
   probe now self-adjusts.

2. workflow_dispatch can build the wrong code:
   For release events github.sha already equals the tag commit, so the
   initial checkout is correct. For workflow_dispatch the initial
   checkout is the dispatched-from ref (master/2.1/whatever), which
   can differ from the `tag` input the operator typed. A `v2.0.5`
   dispatch from master would otherwise publish master's code tagged
   as 2.0.5.

   Add a `Re-checkout tag (workflow_dispatch)` step that runs only on
   dispatch events. It fetches the tag and checks it out in detached
   HEAD before any of the tag-compute or build steps see the
   workspace.

3. + 4. :minor gating comment misleading (web + queue tag steps):
   The code intentionally gates :minor on `is_prerelease=false` only,
   not on `on_default_branch`, so a stable hotfix on a side branch
   (v2.0.5 on the 2.0 line) updates :2.0 without touching :latest.
   The previous comment said "default-branch line", which implied a
   stricter gate than the code actually enforces.

   Rewrite both comments to explain the intent: :minor tracks the
   latest stable on its own release line; only :latest is tied to
   the default branch.

* docs(security): split Supported vs End of Life sections

* docs(changelog): expand v2.1.1 release notes

Date bumped to 2026-05-26 (actual release day). Add the four bullet
groups that were folded into the v2.1.1 line after the initial security
tag — installer fixes (#419, #420), styled 405 page (#417), pgsql
demo seeder (#418), MagicAI chat-latest temperature (#416), and the
multi-arch Docker publish (#426). Keeps the two original security
bullets at the top.

* ci: retrigger workflows after GitHub setup-php fetch glitch

* ci: pin shivammathur/setup-php to v2.37.1

GitHub Actions runner is caching a stale v2 tag resolution
(SHA 7c071dfe...) that no longer exists on github.com/shivammathur/setup-php,
so every job using setup-php@v2 fails at 'Set up job' with:

  An action could not be found at the URI
  'https://codeload.github.com/shivammathur/setup-php/tar.gz/7c071dfe...'

Pinning to the latest released tag v2.37.1 (current actual HEAD of v2)
bypasses the stale cache and unblocks Linting / Pest / Playwright /
Translation / Pest-PgSQL workflows until GitHub refreshes the action
resolver.

* ci: correct setup-php tag (drop v prefix)

shivammathur/setup-php releases as '2.37.1' not 'v2.37.1'. Fix the
pin so the action resolver can find it.

* Update CHANGELOG.md

* docs(security): drop GitHub Security Advisories channel (sync from master)

Private vulnerability reporting is disabled on the repository so the
GHSA 'Report a vulnerability' button does not exist. Keep email
([email protected]) as the single channel.

* fix(webhook): validate URL on save, rename menu to Webhook, and harden logs status filter (#448)

* chore(webhook): harden webhook URL validation and dispatch [v2.1 backport] (#450)

* chore(release): v2.1.2 — harden webhook URL handling (#455)

* [2.1] Harden installer: seal /install routes and endpoints once installed (#460)

* fix: seal installer to prevent pre-auth admin takeover via AJAX bypass

* fix(installer): seal installer on Docker setup to close pre-auth admin takeover

* [Backport 2.1] fix(installer): remove hardcoded admin default credentials & make seeders idempotent (#462)

* fix(installer): remove hardcoded admin default credentials and make seeders idempotent (backport #457)

* ci(e2e): pass installer admin creds to seeder and read them in login util so Playwright login matches the seeded admin

* Enforce ACL permission checks on state-changing admin routes (2.1 backport) (#468)

* chore(release): v2.1.2 — harden webhook URL handling

* Enforce ACL permission checks on state-changing admin routes

* feat: add debug-only APP_URL mismatch guard (AppUrlGuard) (#465)

* chore(release): v2.1.3 — installer & ACL security hardening, AppUrlGuard (#473)

* fix(ci): remove empty ${{ }} expression breaking Docker Publish workflow (#471)

An empty "${{ }}" inside the Resolve-Version run-block comment is parsed
by GitHub Actions as an expression interpolation. Empty braces fail
validation ("An expression was expected", L50), invalidating the whole
workflow at startup. As a result every release: published event on the
2.1 line silently failed to dispatch, so no Docker image was built or
pushed (e.g. v2.1.2 never reached Docker Hub) and the demo never updated.

Reword the comment to plain text, matching the already-fixed master.
Backports the master fix to the 2.1 default branch so v2.1.3+ releases
auto-publish.

* docs(agents): update AI agent instructions from Laravel 11 → Laravel 12 / PHP 8.3 (#466)

The project's composer.json requires laravel/framework ^12.0 and
php ^8.3, but six agent-instruction files (read by Copilot, Kilo
Code, Claude, Cursor, and Codex at session start) still describe
the project as 'Laravel 11' and '.github/copilot-instructions.md'
still requires 'PHP 8.1+'. This causes AI agents to apply Laravel
11 idioms and target the wrong PHP version when generating code.

Files updated:
- AGENTS.md: Laravel 11 → Laravel 12
- code-generation-instructions.md (root + .github/): Laravel 11 → Laravel 12
- code-review-instructions.md     (root + .github/): Laravel 11 → Laravel 12
- .github/copilot-instructions.md:
    * Laravel 11 → Laravel 12, PHP 8.1+ → PHP 8.3+
    * Section 1 was actively misleading: it told agents to hardcode
      the 'wk_' table prefix, which contradicts AGENTS.md
      ('Never hardcode wk_ in table names — it causes wk_wk_ double
      prefix issues') and the actual production code
      (Channel.php → 'channels', CoreConfig.php → 'core_config').
      The section has been rewritten to point at the real
      unprefixed-name convention, with corrected examples.

Co-authored-by: dashitongzhi <[email protected]>

* Clarify DB table prefix guidance to avoid Laravel double-prefixing (#470)

* Initial plan

* Clarify DB prefix behavior in Copilot instructions

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Feat/help and installer 2.1 (#474)

* docs: add design spec for Help & Resources menu section

(cherry picked from commit 3c6bc06)

* docs: add implementation plan for Help & Resources menu section

(cherry picked from commit 0847184)

* feat(admin): add reusable global card component

(cherry picked from commit 3e065bd)

* refactor(admin): harden card rel attr + document trusted icon output

(cherry picked from commit 470fe10)

* feat(admin): add help config with sections and cta

(cherry picked from commit 5f71a7d)

* feat(admin): add en_US translations for help section

(cherry picked from commit 4166d0a)

* feat(admin): add help route and controller

(cherry picked from commit 3bec3ad)

* feat(admin): add help page view using card component

(cherry picked from commit a0b710c)

* feat(admin): add help sidebar menu and acl entries

(cherry picked from commit b474fb8)

* i18n(admin): translate help section into all supported locales

(cherry picked from commit cd908fe)

* docs(installer): spec for optional packages + cloud hosting banner

(cherry picked from commit 43d0c8b462d96ba272a4542ac47f59438658c3c3)

* feat(installer): optional add-on packages + cloud hosting banner

Add a multiselect (and --with-packages CSV option) to unopim:install so
operators can pull in official add-ons (DAM, Shopify, Bagisto connectors).
Each is installed after core via composer require + the package's own
artisan installer in a fresh process, with optimize:clear and queue:restart
around it, pinned to the resolved DB connection. Always renders a UnoPim
cloud-hosting promo banner at the end.

* feat(admin): finalize Help & Resources page and card component

Source refinements from 99529f2 (card.blade.php + help/index.blade.php),
without the master-only cleanup deletions / rebuilt assets.

* feat(admin): add dismissible cloud-hosting & version-upgrade promo banners

Carousel promo bar injected at the top of every admin page via the
content.before view event. Cloud-hosting slide always shows; the upgrade
slide appears only when the running version is behind the latest release
(remote Packagist check, cached, fail-silent). Each slide has its own
per-user 'Don't show again' persisted in DB; the upgrade dismissal is
version-scoped so a newer release re-shows it.

Reusable <x-admin::promo-bar> anonymous component + inline v-promo-bar Vue
(auto-rotate, dots, axios dismiss). Strings translated across all 33 locales.

(cherry picked from commit 064617fd9927e614e769d3ba643fb7dd7e4cdfc8)

* fix(admin): correct help service URLs, pin Help menu last, offset layout for promo bar

- Point Services cards at live pages (support-maintenance-services,
  pim-connector-development) and API docs at devdocs.unopim.com/2.1/api.
- Document the Help menu sentinel sort (99999) that keeps it pinned last.
- Offset fixed header/sidebar via :has(#unopim-promo-bar) so the promo bar
  no longer cuts the left menu; make the bar sticky at top.

* build(admin): recompile theme assets with promo bar + help page styles

Rebuild app.css from 2.1 source so the promo-bar/help Tailwind arbitrary
classes (z-[10050], text-[13.5px], max-[820px], h-[30px], etc.) are present.
JS unchanged in spirit — promo bar is registered inline in its blade.

* cleanup extra files

* fix(installer): ask optional-package selection before demo seeding

Demo-data seeding runs Artisan::call() internally, which flips the console
input to non-interactive. Resolving the package multiselect afterwards
silently skipped it when the user chose to seed sample products. Resolve the
selection up front while still interactive; install the packages at the end.

* feat(installer): non-interactive flags --modules and --sampledata

Add --modules (alias for --with-packages) and --sampledata=yes|no (alias for
--with-demo-data) so a fully scripted install needs no prompts. The
interactive 'sample products?' question now yields to either demo-data flag,
so passing one never blocks or double-seeds.

* revert(installer): drop duplicate --modules/--sampledata aliases

Redundant with the existing --with-packages and --with-demo-data flags. Keep
the real fixes: resolve the package selection up front, and skip the
interactive 'sample products?' prompt when --with-demo-data is set or the run
is non-interactive.

* fix(installer): gate prompts on a real TTY, not isInteractive()

GitHub Actions reports input->isInteractive() == true with no STDIN attached,
so the up-front package multiselect prompted and aborted on EOF, failing the
install step (and thus every Pest/Playwright job). Gate the multiselect and
the 'sample products?' prompt on stream_isatty(STDIN) via hasInteractiveTerminal();
headless/CI installs skip them, real terminals still prompt.

* feat(installer): revamp web installer + CLI add-ons, docs, translations

Web installer: admin-themed UI (cloud top bar, vertical stepper, card panels,
language switcher), config-first/install-last flow, live SSE terminal that
streams database → migrate → seed → admin → optional sample data → optional
add-on packages (DAM/Shopify/Bagisto, installed server-side where shell exists).
Adds Elasticsearch setup, DB auto-create (MySQL + pgsql), runtime .env reload,
and shared/FTP-only hosting safeguards. UI polish: requirement/DB grids, bounded
allowed-locale/currency lists, outline Back buttons, browser-origin URL, UTC tz.

Fixes web-context command registration for demo seeding and persists add-on/
sample selections to a transient state file (session was unreliable across the
install). Pins the Help menu/ACL entry last. Adds the new installer keys to all
33 locales, a v2.1.4 changelog entry, and a Cloud Hosting section in the README.

* fix(admin): render flash messages above the promo banner

The promo banner (z-[10050]) overlapped the flash-message stack (z-[10003]),
covering the 'Close' button and breaking catalog E2E specs that dismiss flashes.
Raise the flash group to z-[10060] so notifications sit above the banner.

* fix(admin): keep promo banner in flow so it never overlays UI

* fix(admin): address PR review — guard version JSON, atomic dismissal, bind promo timer

- VersionCheck: bail to fallback when Packagist JSON does not decode to an array
  (avoids array-offset-on-null warnings).
- AdminPromoDismissalRepository: use firstOrCreate so the unique-index dismissal
  write stays idempotent under concurrent requests.
- promo-bar: bind the rotation interval via an arrow fn so `this` is preserved.

* fix(installer): address PR review — server-side promo version, safe .env writes, driver-based db create

- HelpController: derive dismissal version server-side instead of trusting client
- EnvironmentManager: preg_replace_callback so $ in passwords is not a backreference
- DatabaseManager: branch on db driver, not connection name, for pgsql create

* refactor(admin): use Laravel HTTP client instead of curl in VersionCheck

* chore(core): bump version to 2.1.4

* Chore(deps): Bump symfony/polyfill-intl-idn from 1.33.0 to 1.38.1 (#481)

Bumps [symfony/polyfill-intl-idn](https://github.com/symfony/polyfill-intl-idn) from 1.33.0 to 1.38.1.
- [Release notes](https://github.com/symfony/polyfill-intl-idn/releases)
- [Commits](symfony/polyfill-intl-idn@v1.33.0...v1.38.1)

---
updated-dependencies:
- dependency-name: symfony/polyfill-intl-idn
  dependency-version: 1.38.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Chore(deps): Bump symfony/http-kernel from 7.4.7 to 7.4.13 (#482)

Bumps [symfony/http-kernel](https://github.com/symfony/http-kernel) from 7.4.7 to 7.4.13.
- [Release notes](https://github.com/symfony/http-kernel/releases)
- [Changelog](https://github.com/symfony/http-kernel/blob/8.2/CHANGELOG.md)
- [Commits](symfony/http-kernel@v7.4.7...v7.4.13)

---
updated-dependencies:
- dependency-name: symfony/http-kernel
  dependency-version: 7.4.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Chore(deps): Bump phpoffice/phpspreadsheet from 1.30.4 to 1.30.5 (#487)

Bumps [phpoffice/phpspreadsheet](https://github.com/PHPOffice/PhpSpreadsheet) from 1.30.4 to 1.30.5.
- [Release notes](https://github.com/PHPOffice/PhpSpreadsheet/releases)
- [Changelog](https://github.com/PHPOffice/PhpSpreadsheet/blob/1.30.5/CHANGELOG.md)
- [Commits](PHPOffice/PhpSpreadsheet@1.30.4...1.30.5)

---
updated-dependencies:
- dependency-name: phpoffice/phpspreadsheet
  dependency-version: 1.30.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Chore(deps): Bump symfony/mailer from 7.4.6 to 7.4.12 (#485)

Bumps [symfony/mailer](https://github.com/symfony/mailer) from 7.4.6 to 7.4.12.
- [Release notes](https://github.com/symfony/mailer/releases)
- [Changelog](https://github.com/symfony/mailer/blob/8.2/CHANGELOG.md)
- [Commits](symfony/mailer@v7.4.6...v7.4.12)

---
updated-dependencies:
- dependency-name: symfony/mailer
  dependency-version: 7.4.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: make promo banner sticky to remove empty gap on scroll (#480)

* fix: make promo banner sticky to remove empty gap on scroll

Promo banner used position:relative, so it scrolled out of view while
the layout already offsets the header by 3rem (its height). When the
banner scrolled away the sticky header stayed pinned at top:48px,
leaving a 48px empty gap above it.

Make the banner sticky (top-0, z above header) so it stays pinned and
the header sits flush below it.

* fix: drop promo bar z-index to avoid modal overlay collision

Addresses review feedback: z-[10002] tied modal overlays (confirm/history/
bulk-edit all use z-[10002]) and, being rendered later in the DOM, painted
above them — leaving the banner visible/clickable over an open modal.

The banner only needs to stay above scrolling page content (which it does
via position:sticky alone, as a positioned element) and below modal
overlays. Removing the explicit z-index achieves both. Also avoids relying
on a new arbitrary Tailwind value (z-[10000]) that the committed build CSS
does not contain (CI does not rebuild assets).

* fix: give promo bar z-[9999] to sit above transform-stacked charts

Dashboard chart SVGs use CSS transform, so each creates its own stacking
context. With the banner at z-index:auto, those contexts painted at the
same level and (being later in the DOM) bled over the sticky banner on
scroll — e.g. completeness donut charts appeared inside the banner.

A positive z-index is required to beat transform stacking contexts.
z-[9999] sits above all page content (<=1000) while staying below the
header/modal/drawer overlays (10001/10002), so modals and drawers still
cover the banner correctly. z-[9999] already exists in the committed
build CSS (CI does not rebuild assets), so no asset rebuild is needed.

* fix(admin): validate TinyMCE uploads to image allowlist and store with randomised name (#476)

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* [Security] Fix ORDER BY SQL injection via sort order in Product DataGrid  (#488)

* fix(datagrid): allowlist product sort direction to prevent ORDER BY SQL injection

* refactor: optimize code

---------

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Chore(deps): Bump symfony/routing from 7.4.6 to 7.4.13 (#483)

Bumps [symfony/routing](https://github.com/symfony/routing) from 7.4.6 to 7.4.13.
- [Release notes](https://github.com/symfony/routing/releases)
- [Changelog](https://github.com/symfony/routing/blob/8.2/CHANGELOG.md)
- [Commits](symfony/routing@v7.4.6...v7.4.13)

---
updated-dependencies:
- dependency-name: symfony/routing
  dependency-version: 7.4.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Chore(deps-dev): Bump symfony/yaml from 7.4.6 to 7.4.13 (#486)

Bumps [symfony/yaml](https://github.com/symfony/yaml) from 7.4.6 to 7.4.13.
- [Release notes](https://github.com/symfony/yaml/releases)
- [Changelog](https://github.com/symfony/yaml/blob/8.2/CHANGELOG.md)
- [Commits](symfony/yaml@v7.4.6...v7.4.13)

---
updated-dependencies:
- dependency-name: symfony/yaml
  dependency-version: 7.4.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* fix(api): close fail-open scope bypass on configurable-product write routes (#477)

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Update Cloud Hosting section in README

Removed and re-added the Cloud Hosting section with an image link for better visibility.

* Fix MagicAI Platform authorization bypass: update & set-default reachable without permission (#479)

* fix(acl): require platform-edit permission for MagicAI platform update and set-default

* packages/Webkul/refactor: optimize code

* Fix stored XSS via channel name breaking out of HTML attribute (family completeness) (#489)

* fix(xss): escape channel options on family completeness page

* test(e2e): log in over HTTP in global-setup so storageState is authenticated

* refactor: optimize code

* Chore(deps): Bump guzzlehttp/psr7 from 2.9.0 to 2.11.0 (#496)

Bumps [guzzlehttp/psr7](https://github.com/guzzle/psr7) from 2.9.0 to 2.11.0.
- [Release notes](https://github.com/guzzle/psr7/releases)
- [Changelog](https://github.com/guzzle/psr7/blob/2.11/CHANGELOG.md)
- [Commits](guzzle/psr7@2.9.0...2.11.0)

---
updated-dependencies:
- dependency-name: guzzlehttp/psr7
  dependency-version: 2.11.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix authorization bypass on MagicAI prompt/system-prompt and AiAgent generate routes [2.1] (#494)

* [Security] Fix authorization bypass on MagicAI prompt/system-prompt and AiAgent generate routes

* refactor(acl): enforce ai-agent.generate.process via acl map instead of inline middleware

---------

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* fix:Login lockout shows 500 Internal Server Error page instead of 429 Too Many Requests when APP_DEBUG=false (#499)

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Validate and sanitize attribute swatch image uploads [2.1] (#495)

* fix(upload): sanitize & validate attribute swatch image uploads via FileStorer

* refactor: optimize code

---------

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* feat(installer): add type-to-search locale & currency prompts to CLI installer (#491)

Co-authored-by: Navneet Kumar - Webkul <[email protected]>
Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* chore: v2.1.5 patch cleanup — upgrade-script fixes, skills extraction, dead-file removal (#493)

* chore(github): overhaul issue templates, PR template, contributing guide

- single bug-report form with required environment checklist and
  duplicate-search confirmation; remove legacy markdown duplicate
- fix label case and add title prefixes so labels auto-apply
- issue chooser: add Discussions and documentation links
- CONTRIBUTING: fix dead template links, document conventional
  commits, branch targets, security policy and local CI checks
- PR template: fix malformed HTML comments, actionable checklist

* chore: move AI agent skills to unopim/agent-skills repository

Skills now live in https://github.com/unopim/agent-skills and install
via 'npx skills add unopim/agent-skills'. Remove in-repo skill copies,
agent symlink dirs, the skills-consistency workflow and validator, and
the connector instruction files (moved to the skills repo). Ignore
agent directories so local installs never get committed.

* fix(installer): upgrade script and web-installer composer handling

upgrade.sh:
- backup now includes storage/ user data (media); previously a restore
  lost all uploads. Excludes only deps, caches, logs, debugbar
- copy step includes dotfiles (.env.example, .gitignore were dropped)
- anchored .env parsing; passwords with '=' or quotes no longer break
  the dump; credentials passed via MYSQL_PWD/PGPASSWORD env vars
- PostgreSQL support via pg_dump based on DB_CONNECTION
- rm -f the temp dump so a failed dump cannot abort the run (set -e)
- drop dead UPGRADE_TO_VERSION stub

install.php: COMPOSER_HOME now points to a writable directory
(storage/composer) instead of the phar file path.

InstallerController: probe bundled bin/composer/composer.phar in
resolveComposerBinary(); paths extracted to composerProbePaths() with
tests. Stabilise installer tests with DatabaseTransactions so runs no
longer pollute the dev database.

Docs: consolidate UPGRADE.md (remove stale per-release impact lists and
dead link), drop superseded per-version upgrade guides, fix README
upgrade link, add pg_dump backup instructions.

* chore: remove dead files, update bundled composer, add favicon

- remove unused leftovers: public/forge, patches.lock.json (plugin not
  installed), config/horizon.php and config/sitemap.php (packages not
  installed), broken bin/codecept symlink, placeholder DataGrid test
  and its now-empty phpunit suite entries
- update bundled composer.phar 2.8.3 -> 2.10.1
- add public/favicon.ico (browsers request it unconditionally)
- changelog: draft v2.1.5 entry

---------

Co-authored-by: Dripar gupta <[email protected]>

* docs(changelog): add v2.1.x security fixes (#492)

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Update CHANGELOG.md (#506)

* chore: bump version to 2.1.5

- Core::VERSION 2.1.4 -> 2.1.5
- Remove unused version field from private package.json

* Fixed #502 - Accept uppercase image extensions (.JPG/.JPEG) (#511)

* Fix: Index audits, skip empty translation audits, and fix history preview (#509)

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Fixed   Confirm before demo-data seeding overwrites existing data[2.1] (#503)

* fixed: Confirm before demo-data seeding overwrites existing data

* Fixed:Confirm and guard demo-data seeding to prevent data loss

* refactor: shorten code comments

---------

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Chore(deps): Bump laravel/framework from 12.55.1 to 12.61.1 (#517)

Bumps [laravel/framework](https://github.com/laravel/framework) from 12.55.1 to 12.61.1.
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/13.x/CHANGELOG.md)
- [Commits](laravel/framework@v12.55.1...v12.61.1)

---
updated-dependencies:
- dependency-name: laravel/framework
  dependency-version: 12.61.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Chore(deps): Bump phpseclib/phpseclib from 3.0.52 to 3.0.55 (#516)

Bumps [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib) from 3.0.52 to 3.0.55.
- [Release notes](https://github.com/phpseclib/phpseclib/releases)
- [Changelog](https://github.com/phpseclib/phpseclib/blob/master/CHANGELOG.md)
- [Commits](phpseclib/phpseclib@3.0.52...3.0.55)

---
updated-dependencies:
- dependency-name: phpseclib/phpseclib
  dependency-version: 3.0.55
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* Preselect application locale and currency from .env during reinstallation (#514)

* Feat: added the default selection value on installer cli with search

* Chore: class name rename

---------

Co-authored-by: Navneet Kumar - Webkul <[email protected]>

* fix(i18n): add 3 missing translation keys across all 32 locales

Resolve pre-existing translation parity gaps where en_US carried keys the
other locales lacked (surfaced during the 2.1 convergence; present on master
independently of it):

- Admin:     settings.data-transfer.exports.create.search-categories
- Installer: seeders.core.currencies.CAD
- Installer: installer.index.environment-configuration.dollar

Added with natural per-language translations (not English copies) to all 32
non-English locales. en_US untouched; en_AU/en_GB/en_NZ use English text.

unopim:translations:check: 258/258 locales pass. Pint clean.

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: Dripar gupta <[email protected]>
Co-authored-by: Pravat Senapati <[email protected]>
Co-authored-by: Prince Kumar Sahni <[email protected]>
Co-authored-by: Johannes Rudolph <[email protected]>
Co-authored-by: Kunal kumar <[email protected]>
Co-authored-by: kunal kumar <[email protected]>
Co-authored-by: Kral <[email protected]>
Co-authored-by: dashitongzhi <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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