Restoapp logo

RestoApp Marketplace

blocking-popup

Teaser:

Last Published: 7/12/2026

Author: webresto

Category:

Tags: popup, modal, ads, sails, hook

Free Module

Available Versions

  • 1.1.6 (Latest)
  • 1.1.5
  • 1.1.4
  • 1.1.3
  • 1.1.2
  • 1.0.1
  • 1.0.0 [staging]

Description

Blocking Popup (реклама в лоб)

A Sails hook that shows a blocking full-screen popup over the site and lets admins configure several of them, ranked between each other. Only one popup is shown per visit — the active one with the lowest priority number wins.

The creative is a single image; everything else is behaviour settings. The admin editor uses Adminizer’s shared media manager, so an image can be selected from the existing library or uploaded without leaving the popup form. Per popup you configure:

  • Image (referenced by URL) and an alt tag (the whole creative).
  • Click-through URL — the image navigates here when clicked (optional).
  • Priority / ordering (sortOrder) — rank popups; the lowest active one shows.
  • Show delay — how many seconds after page load the popup appears.
  • Closable or not — whether the visitor may dismiss it.
  • Close countdown — seconds before the close button becomes active (or none).
  • Overlay — dimming backdrop on/off, and whether clicking it closes the popup.
  • Positioncenter, top, bottom, or any corner.
  • Cooldown — after dismissal, don’t show again for N hours (per browser).
  • Work time schedule — standard workTime editor; a popup only shows within its schedule.

How it works

Admin module  ──►  BlockingPopup rows  ──►  GET /blocking-popups  ──►  dist/module.js widget
 (React/Inertia)     (DB, workTime)          (server picks winner)      (renders the modal)

The server evaluates each popup’s workTime (in the configured timezone) and returns the single popup to show. The client widget handles delay, position, overlay, close/countdown and the localStorage cooldown.

Client integration

Automatic (admin-frontend snippet)

The module registers itself as an admin-frontend snippet, so on any frontend built through admin-frontend the widget <script> is injected automatically (at the bottom of <body>). The operator sees it in the Snippets list as blocking-popup and can enable/disable it there — no manual work needed. See Snippet registration below.

Manual

To add the widget yourself, include the served script on any page. It auto-runs on load:

<script src="/blocking-popups/widget.js" defer></script>

Optional configuration — set a global before the script tag:

<script>
  window.BLOCKING_POPUP = {
    endpoint: "/blocking-popups", // REST endpoint (default)
    auto: true,                   // auto-run on load (default)
    zIndex: 2147483000            // base z-index (default)
  };
</script>
<script src="/blocking-popups/widget.js" defer></script>

To trigger manually instead of on load, set auto: false and call:

window.BlockingPopupWidget.run();

Silent mode

The widget fails safe. If the REST backend is down, unreachable, times out (8s), returns a non-2xx status, or sends malformed JSON, nothing is shown and nothing is thrown — the widget only writes a single informational line to the console (console.info, never console.error):

[blocking-popup] REST endpoint unavailable, no popup shown (network error)

The host page is never affected.

REST API

  • GET /blocking-popups{ ad: PublicPopup | null } — the single popup to show this visit.
  • GET /blocking-popups?all=true{ ads: PublicPopup[] } — the full ordered queue (for custom clients).
  • GET /blocking-popups/widget.js → the client widget script (application/javascript).

Data responses are sent with Cache-Control: no-store; the widget script with max-age=3600.

Snippet registration

On initialize the module subscribes to the core emitter event admin-frontend:collect-snippets (subscriber id blocking-popup) and returns a single ProvidedSnippet:

Field Value
module blocking-popup
key widget
placement bodyBottom
content <script src="/blocking-popups/widget.js" defer>
enabledByDefault true

admin-frontend polls this on every snippet listing and every recipe build, so the operator can toggle it on/off, and it drops out of the build automatically if the module stops answering (marked Unavailable). See src/lib/registerSnippet.ts.

Configuration

config/blocking-popup.js (see config.example.js):

module.exports["blocking-popup"] = {
  // Timezone used to evaluate each popup's workTime schedule.
  timezone: "Europe/Moscow",
  // Adminizer's registered default media manager used for popup images.
  mediaManager: {
    id: "default",
    group: "blocking-popups",
    accept: ["image/jpeg", "image/png"],
  },
};

Admin panel

A custom React / Inertia admin module (under the Marketing section), built with Vite exactly like the webresto core modules and served like admin-frontend. It lists popups as cards, edits every field inline, and lets you toggle isActive or delete without leaving the page. The image picker reuses the configured Adminizer media manager (default, group blocking-popups); selection and multipart upload are sent through Adminizer’s standard media-manager routes and access right. The selected public URL is stored as { url } JSON (kept compatible with the client widget); the workTime schedule is an optional JSON array (a malformed schedule fails open, so the popup still shows).

  • Page route: ${routePrefix}/blocking-popups-manager
  • JSON API: ${routePrefix}/blocking-popups-manager/{list,upsert,toggle,delete}
  • Bundle served at /blocking-popups-admin-assets/BlockingPopupsManager.js

Access rights

Following the core convention (“separate view/edit rights for modules”), three tokens are registered in the Marketing department:

Token Grants
blocking-popups-manager Legacy full access (view + manage)
blocking-popups-view Read-only: open the page and list popups
blocking-popups-manage Create / edit / toggle / delete popups

The navbar link and page are gated by blocking-popups-view; every write endpoint re-checks blocking-popups-manage server-side, and the UI hides write controls for view-only users. blocking-popups-view is added to the default Marketer group.

Build

npm install
npm run build            # tsc → verify:build → browserify src/client/widget.js → dist/module.js
npm run build:adminizer  # vite build → assets/blocking-popups-admin/BlockingPopupsManager.js

dist/module.js is the ready-to-serve client widget bundle. assets/blocking-popups-admin/ holds the admin module bundle served to the admin panel.

Changelog

Changelog

1.1.2 (2026-07-06)

  • Fixed the admin bundle being shadowed by the site frontend: the static route /blocking-popups-admin-assets is now mounted at hook-initialize time via src/hook/bindAssets.ts (same as core’s hook/bindAssets). Previously it was mounted inside the Adminpanel:afterHook:loaded handler — middleware added at that point lands after the app’s frontendRoutes catch-all (config/http.js), so the bundle URL returned the site index.html instead of JS.

1.1.0

  • Replaced the plain model-CRUD admin page with a React/Inertia admin module (Blocking Popups under the Marketing section), built with Vite the same way webresto core builds its modules and admin-frontend serves its bundle.
  • Added separated access-rights tokens following the core convention (“separate view/edit rights for modules”):
    • blocking-popups-manager — legacy full access
    • blocking-popups-view — read-only (opens the page, lists popups)
    • blocking-popups-manage — create / edit / toggle / delete Every write endpoint re-checks the manage permission server-side; the UI hides all write controls for view-only users.
  • The blocking-popups-view token is added to the default Marketer group.

1.0.1

  • Blocking full-screen popup ads with model-based admin panel and standalone client widget.