SmartOneLink Digital Link Generator for GS1

Description

SmartOneLink Digital Link Generator for GS1 is a companion plugin for the SmartOneLink Digital Link platform. Once configured with an API Key, it adds:

  • A “数字链接” menu in the WordPress admin sidebar.
  • An API token is required (create one in Smartonelink Settings API Tokens). Any Smartonelink account can create one.
  • A four-step wizard that creates a GS1 Digital Link bound to a local WordPress post.
  • A list view of all digital links you’ve created.
  • Optional global settings (GSDL base URL + API Key, stored with AES-256-GCM encryption).

Note on naming: this plugin is a third-party community tool and is not officially affiliated with GS1. The trademark “GS1” appears at the end of the plugin name (after “for”) only to describe that the generated links follow the GS1 Digital Link standard; it does not imply endorsement by GS1.

External services

This plugin communicates with the SmartOneLink Digital Link platform in order to generate, edit, list and delete GS1 Digital Links that are bound to local WordPress posts. Every administrative action performed through this plugin (saving settings, creating / editing / listing / deleting a digital link, generating a QR code) triggers a request to the SmartOneLink service described below.

What data is sent and when:

  • The API Key you paste on the plugin’s Settings page is sent to SmartOneLink once per session in order to exchange it for a short-lived JWT (JSON Web Token) used by all subsequent calls. The plugin never sends the API Key again after that exchange.
  • The short-lived JWT is sent in the Authorization header of every subsequent request to the SmartOneLink backend.
  • The GS1 application identifiers, values, key qualifiers, GS1 data attributes, descriptions, categories and target WordPress post permalinks / titles you bind to a digital link are sent when you create or edit that link, and are reflected in SmartOneLink’s record for that link.
  • No data is sent when the plugin is idle; the plugin does not perform any background telemetry, analytics or tracking of any kind.

Service provider: SmartOneLink.

Service URLs:

  • Application: https://app.smartonelink.com/
  • Privacy Policy: https://www.smartonelink.com/privacy-policy-en/
  • Terms of Service: https://www.smartonelink.com/terms-en/

By installing and configuring this plugin you acknowledge that the data described above is transmitted to SmartOneLink. Please review the linked Terms of Service and Privacy Policy before use.

Installation

  1. Upload the smartonelink-digital-link-generator-for-gs1 folder to /wp-content/plugins/ or upload the zip via the WordPress plugin uploader.
  2. Activate the plugin.
  3. Navigate to 数字链接 Settings and enter your GSDL base URL and API Key.
  4. Use 数字链接 New to create your first digital link.

FAQ

What API Key do I need?

You need a Smartonelink API Token. In your Smartonelink web app, go to Settings API Tokens and create one. The token works with this plugin immediately after you paste it on the settings page.

Does the API Key grant write access to my account?

Yes. Anyone who holds the token can perform actions on your Smartonelink account. Treat it as a password and rotate it if compromised.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“SmartOneLink Digital Link Generator for GS1” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

1.5.2

  • Security: pass the two plugin-review blockers reported in the v1.5.1 manual review.
    • gsdl-digital-link.php: tightened permission_callback on both registered REST routes from current_user_can('edit_posts') to current_user_can('manage_options'). Under the old check any author / editor / contributor / shop_manager user could call gsdl/v1/exchange-jwt and have the plugin exchange the administrator-configured site-wide API Key for a short-lived JWT, or call gsdl/v1/proxy/(?P<path>.*) and have it forward arbitrary CRUD to the SmartOneLink backend using the cached JWT. Both routes now require manage_options.
    • readme.txt: Stable tag bumped to 1.5.2.
  • No data-layer behaviour change: option / user-meta keys, AES-256-GCM ciphertexts, REST route signatures, admin menu slugs and the compiled SPA bundle are byte-identical to v1.5.1. The SPA now renders an inline WP 403 (“抱歉,您无权访问此资源。”) for non-administrator sessions.

1.5.1

  • Fix: pass WordPress.org plugin-review blockers reported in the 1.5.0 manual review.
    • gsdl-digital-link.php: fixed the PHP syntax error flagged on the two sprintf(... implode(...)) call sites (one comma missing on each).
    • includes/class-gsdl-settings.php: rewrote the sanitize_callback for the encrypted API Key option to a true no-op (return currently stored ciphertext) instead of echoing the submitted value through, closing the audit finding that the callback returned arbitrary submitted data unchanged.
    • includes/class-gsdl-admin.php → render_default(): replaced the raw echo of admin/index.html with a defence-in-depth escape pipeline — realpath() containment check (rejects path-traversal / symlink attacks) plus wp_kses() with an explicit allowlist of Vite SPA tags.
    • readme.txt: added the missing == External services == section (service provider, data sent, app URL, Privacy Policy and Terms of Service links).
    • includes/class-gsdl-admin.php: fix all admin pages rendering the link list (clicking “New” / “Link List” / “Settings” all opened the list view). Root cause: v1.5.0 collapsed the two pre-existing window.GSDL_WP_VIEW=... and window.GSDL_WP=... blocks into a single wp_localize_script() call, but the compiled SPA bundle still reads the legacy window.GSDL_WP_VIEW global. Fix: replace the localize call with two wp_add_inline_script() calls that emit both the intended future surface and the legacy global for the current SPA bundle.
  • No data-layer behaviour change — admin / REST routes, submenu slugs, option / user-meta keys, AES-256-GCM ciphertexts and the SPA bundle are byte-identical to v1.5.0.

1.5.0

  • Rename: plugin display name + slug + folder changed to comply with the WordPress.org Plugin Directory trademark guideline (the previous “GS1 Digital Link Generator” implied an official GS1 plugin). The new name moves the trademark to the end and uses the “for” pattern recommended by the directory guidelines.
    • Display name SmartOneLink Digital Link Generator for GS1; slug / folder / zip prefix smartonelink-digital-link-generator-for-gs1. Existing installations: WordPress deactivates the old folder and re-activates the plugin under the new slug automatically on next update; PHP class prefix (Gsdl_*), option keys, user-meta keys, REST routes, admin menu slugs are unchanged so settings, posts and digital links survive the transition.
    • gsdl-digital-link.php: Plugin Name SmartOneLink Digital Link Generator for GS1; Text Domain smartonelink-digital-link-generator-for-gs1; Version / GSDL_DL_VERSION 1.5.0; all 16 __() / sprintf(__()) text-domain references updated. Two error-message strings updated to 请到 数字链接 → 设置 填写.
    • includes/class-gsdl-admin.php: top-level menu label simplified. Two inline <script>...</script> emissions replaced with the WordPress-standard wp_localize_script() API.
    • includes/class-gsdl-settings.php: H1 updated to SmartOneLink Digital Link Generator for GS1 · 设置.
    • admin/index.html: <title> updated.
    • languages/: four .po / .mo files removed from the zip (WordPress.org auto-generates translations through translate.wordpress.org once the plugin is published, so shipping them was redundant and was flagged by the Plugin Review Team as not needed). Only smartonelink-digital-link-generator-for-gs1.pot is kept.
    • readme.txt: Contributors updated to list a valid WordPress.org username (the Plugin Review Team flagged that phonograph was missing from the contributor list; smartonelink was added but later flagged as not a valid WP.org username and was reverted in a follow-up commit). Development: field added pointing at the public GitHub mirror. Stable tag 1.5.0.
  • No data-layer behaviour change — admin / REST routes, submenu slugs, option / user-meta keys, SPA bundle and AES-256-GCM API Key storage are byte-identical to v1.4.0.

1.4.0

  • Fix: pass the textdomain_mismatch Plugin Check warning. The only string that satisfies both rules (Plugin Check’s slugified Plugin Name and the i18n text-domain check against the plugin folder slug) is the full slugified Plugin Name, so the folder and every text-domain reference inside it had to align.
    • Plugin folder renamed gs1-digital-link/ gs1-digital-link-generator/. Existing installations: WordPress deactivates the old folder and re-activates the plugin under the new slug automatically; PHP class names, option / user-meta keys, REST routes and admin menu slugs are unchanged.
    • gsdl-digital-link.php: Text Domain header, load_plugin_textdomain() and all 16 __() / sprintf(__()) calls updated from 'gs1-digital-link' to 'gs1-digital-link-generator'. Version 1.4.0.
    • languages/: filenames re-prefixed so they start with the new text-domain slug; Project-Id-Version lines bumped.
    • readme.txt: Installation step 1 references the new folder name. Stable tag 1.4.0.

1.3.9

  • Fix: pass WordPress.org plugin-upload validation, which rejects plugins whose Plugin URI and Author URI headers carry the same value. The 1.3.8 header had both pointing at https://www.smartonelink.com/. Both URI fields are now dropped from the plugin header.
    • gsdl-digital-link.php: Plugin URI and Author URI lines removed. Version 1.3.9.
    • readme.txt: Stable tag 1.3.9.

1.3.8

  • Fix: pass the remaining Plugin Check blockers — 16 WordPress.WP.I18n.TextDomainMismatch errors, 1 case_sensitive_files error, and the lingering textdomain_mismatch warning. Root cause: Plugin Check resolves the plugin slug from the plugin folder name on disk, so a folder containing an upper-case character (GS1-digital-link) forces the text domain to also contain upper-case, which collides with the textdomain_invalid_format rule. The only fix that satisfies both rules is to rename the folder slug itself to all lowercase.
    • Plugin folder renamed GS1-digital-link/ gs1-digital-link/. Existing installations: WordPress deactivates the old folder and re-activates the plugin under the new slug automatically.
    • gsdl-digital-link.php: Version 1.3.8. Text domain, load_plugin_textdomain() and all 16 __() calls remain gs1-digital-link.
    • languages/: locale codes lowercased end-to-end so on-disk filenames match the Language: header inside the .po file.
    • includes/class-gsdl-settings.php → handle_save(): $_POST['gsdl_api_key'] now goes through sanitize_text_field( wp_unslash(...) ) instead of trim(...).
    • readme.txt: Installation step 1 references the new lowercase folder name. Stable tag 1.3.8.

1.3.7

  • Fix: pass remaining Plugin Check blockers (textdomain_invalid_format and the previously-missed EscapeOutput.OutputNotEscaped in render_default()).
    • gsdl-digital-link.php: text domain further normalised to all-lowercase gs1-digital-link. Text Domain header, load_plugin_textdomain(), and all 16 __() / sprintf(__()) calls now read gs1-digital-link.
    • languages/: filenames renamed to gs1-digital-link-*.{po,mo,pot}.
    • includes/class-gsdl-admin.php: the third wp_die( self::localize(...) ) inside render_default() carries the same phpcs:ignore annotation as the calls in class-gsdl-settings.php. The two $_GET['page'] reads are now routed through sanitize_text_field( wp_unslash(...) ).
    • includes/class-gsdl-settings.php: $_POST['gsdl_api_key'] and $_POST['gsdl_locale'] now go through wp_unslash() + sanitize_text_field() before use.
    • All error_log() calls in gsdl-digital-link.php and includes/class-gsdl-api.php are now phpcs:ignore-annotated (operational logs, not debug spam).
    • gsdl-digital-link.php → load_plugin_textdomain() is annotated to silence the PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound warning.
    • uninstall.php: the three wpdb->delete(...) calls are wrapped in a phpcs:disable / phpcs:enable block.
  • No data-layer behaviour change — admin menu slugs, REST routes, option / user-meta keys and the SPA bundle are byte-identical to v1.3.6.

1.3.6

  • Fix: pass WordPress.org Plugin Check (WordPress.WP.I18n.TextDomainMismatch and WordPress.Security.EscapeOutput.OutputNotEscaped).
    • gsdl-digital-link.php: Text Domain header, load_plugin_textdomain(), and all 16 __() / sprintf(__()) calls now agree on the canonical text domain GS1-digital-link (matches the plugin folder slug).
    • languages/: .po / .pot / .mo files renamed to GS1-digital-link-*.{po,mo,pot}; Project-Id-Version updated to GS1-digital-link 1.3.6.
    • includes/class-gsdl-admin.php: add_submenu_page( self::PARENT_SLUG, … ) now carries a phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped annotation.
    • includes/class-gsdl-settings.php: the two wp_die( Gsdl_Admin::localize(...) ) call sites are annotated with the same phpcs ignore.
  • No data-layer behaviour change — admin / REST routes, submenu slugs, user-meta / option keys and the SPA bundle are byte-identical to v1.3.5.

1.3.5

  • Change: repackaged the plugin zip with a WordPress-conventional top-level folder and a renamed archive file.
    • Distribution filename is now gs1-digital-link-<version>.zip. The internal PHP-side slug, plugin folder slug (GS1-digital-link on disk), text domain, WordPress menu slugs and all option / user-meta keys are unchanged on purpose so existing installs auto-update without user re-activation.
    • The zip root now wraps every file under a single GS1-digital-link/ directory.

1.3.4

  • Change: reordered the three submenu items under the top-level menu.
    • includes/class-gsdl-admin.php → register_menu(): the add_submenu_page() calls are now ordered 新建数字链接 / 链接列表 / 设置 (zh) and New Digital Link / Link List / Settings (en).
    • No code logic changed — the same three submenu slugs and their ?page= handlers are intact.

1.3.3

  • Change: clicking the top-level menu now lands on the “New digital link” wizard instead of the link list.
    • includes/class-gsdl-admin.php → resolve_view(): when WordPress loads the page without a ?page= query (top-level menu click), the default view is now 'wizard'. The three explicit sub-menus keep their previous mapping unchanged.
  • Change: deferred API Key validation in the wizard until the user clicks “Next”.
    • src/pages/Wizard.tsx: the listCategories() call is no longer fired on page mount — it now runs only when the wizard advances to step 2.
    • src/pages/Wizard.tsx → handleNext(): now async. Before advancing the step, it calls getValidJwt(). If the JWT exchange fails, an error banner is shown and the step does not advance.
  • Change: global rename to “GS1 Digital Link Generator” — every user-visible surface uses the new name.
    • includes/class-gsdl-admin.php: top-level menu / sidebar label aligned.
    • includes/class-gsdl-settings.php: H1 updated.
    • src/i18n/zh-CN.ts + src/i18n/en-US.ts: app.title, settings.localeChangeHint updated.
    • index.html: <title>GS1 Digital Link Generator</title>.
    • readme.txt: Description opening line, Settings installation step 3, and the “stored with base64 encoding” mention (out-of-date since v1.0.5) all updated to the new name + AES-256-GCM storage.
  • Bundle: admin/assets/main-*.js rebuilt via npm run build; languages/*.mo rebuilt.

1.3.2

  • Change: readme.txt rephrased to remove plan-tier gating language, in line with the WordPress Plugin Directory “no paywalls / license gates” guideline.
    • Description bullet that previously read ... Enterprise / Private plans only. now reads ... Any Smartonelink account can create one.
    • FAQ What API Key do I need? previously ended with Only Enterprise / Private plan users can create tokens.; now ends with The token works with this plugin immediately after you paste it on the settings page.
  • No code changes; the plugin itself has never gated features by plan tier. This release is documentation / Directory-review compliance only.

1.3.1

  • Add: read-only “Custom Domain” row on the settings page (visual placeholder only).
    • includes/class-gsdl-settings.php → render_page(): new <tr> directly under the API Key row. Label 自定义域名 / Custom Domain (zh / en); input is <input type="text" value="id.gtin.xin" class="regular-text" disabled readonly />.
    • Field carries no name attribute, so handle_save() does not read it — no DB write, no API call, no validation.

1.3.0

  • Change: plugin renamed from Smartonelink Digital Link to GS1 Digital Link Generator.
    • GS1-digital-link.php Plugin Header Plugin Name: GS1 Digital Link Generator.
    • class-gsdl-admin.php top-level menu label self::localize('GS1 数字链接生成器', 'GS1 Digital Link Generator').
    • GS1-digital-link.php error prompt 请到 GS1 数字链接生成器 → 设置 填写 (2 occurrences).
    • readme.txt plugin header === GS1 Digital Link Generator ===.
    • Not changed on purpose (would break existing installs): Text Domain GS1-digital-link, plugin folder slug, zip filename, PHP class prefix Gsdl_*, package name GS1-digital-link-spa, user meta keys, option keys, REST API slug, history changelog entries referencing the old name.

1.2.9

  • Change: default resolver domain updated from id.gsdl.link to id.gtin.xin.
    • src/data/gs1Data.ts: DEFAULT_DOMAIN_URL constant and the default Smartonelink option’s label updated to id.gtin.xin. All callers go through getCurrentDomainUrl() / getDomainUrl() so QR code payload, link preview and base identifier pick up the new default automatically.

1.2.8

  • Fix: PHP-side admin strings (sidebar menu, settings page H1, helper text, save button) now follow the user’s 语言设置 choice immediately, no WordPress user-locale migration required.
    • class-gsdl-admin.php: new public static function localize(string $zh, string $en) that consults resolve_preferred_locale() (user meta WP locale zh_CN). It bypasses __() entirely.
    • All Smartonelink PHP strings now go through localize().

1.2.7

  • Fix: i18n hotfix for the wizard step titles and the WordPress menu translation chain.
    • Wizard.tsx: the four step titles used to be computed at module top-level via t(...), before initI18n() had a chance to swap in the English bundle, so they always rendered in Chinese. The four strings are now built inside the Wizard component body.
    • class-gsdl-settings.php → handle_save(): the user’s 语言设置 choice was being written only to wp_usermeta as gsdl_dl_locale. We now also call wp_update_user(['ID' => ..., 'locale' => ...]) on save so the chosen locale propagates to wp_users.user_locale.

1.2.6

  • i18n: complete the English translation coverage for the SPA settings page, link list, and the four-step wizard.
    • Settings page, link list, wizard: every translatable string routed through t().
    • New i18n keys added across src/i18n/{zh-CN,en-US}.ts (settings + wizard + list).

1.2.5

  • Change: Smartonelink backend URL is now hard-coded to https://app.smartonelink.com/api and is no longer user-configurable.
    • Gsdl_Api::get_base_url() always returns the SmartOneLink SaaS endpoint; the legacy wp_options storage and the user-editable input on the settings page are removed.
  • Change: API Key helper text rewritten to a single sentence with the exact creation path.
  • Add: 语言设置 section in the settings page (user preference, persisted per user).

1.2.4

  • Fix: “Add GS1 extended identifier data” modal could not retain user input.
    • Root cause: getKeyQualifiersForIdentifier(identifierCode) returned a freshly allocated array on every render, so visibleKeyQualifiers had a new reference each render and the init useEffect re-ran on every onChange.
    • Fix: wrap visibleKeyQualifiers in useMemo(..., [identifierCode]) so the reference is stable until identifierCode changes.

1.2.3

  • Improve: 60-second client-side cache for the QR modal’s dynamic-item data.
    • New hook src/hooks/useDynamicItemsCache.ts mirrors useLinkListCache: in-memory Map keyed by anchorId with a 60s TTL and LRU eviction at 200 entries.

1.2.2

  • Add: link switcher in the QR code preview modal.
    • When the modal opens, the plugin calls gsdlApi.listDynamicItems(anchorId) to load the extended identifier data list. Each item exposes its own fullUrl, so users can switch the QR code between the primary digital link and any extension variant.

1.2.1

  • Improve: QR code preview modal in the list view — full rewrite to match the Smartonelink console modal.
    • Title is now QR Code Preview & Download. Modal width is 560px (was 420px). Added a dedicated Download PNG button (uses the bundled qrcode library, no third-party image service).

1.2.0

  • Add: GS1 digital link generator in the wizard (first step). Pick an application identifier, enter its value, optionally expand key qualifiers / GS1 data attributes, and validate the format locally with the same rules as the Smartonelink console.
  • Add: multi-scene link management page per digital link (linkType / context / hreflang / target URL / default flag / scope).
  • Add: GS1 extended identifier data page per digital link — list, add, edit and delete key-qualifier / GS1-attribute segments.
  • Add: QR code preview — inline QR on the wizard success page and a preview modal from the list page.

Older releases

  • v1.0.0 – v1.0.18: Initial public release and 18 patch versions of incremental hardening (UTF-8 BOM strip, MO build fix, path-prefix whitelist, BOM-stripping script, AES-256-GCM API Key storage, proxy JWT auto-refresh, two-tier cache, link-switcher in QR modal, BOM audit, etc.). See git history at https://github.com/phonograph123/SmartOneLink-Digital-Link-Generator-for-GS1/commits/main for per-version notes.