diff --git a/.distignore b/.distignore new file mode 100644 index 0000000..8db4020 --- /dev/null +++ b/.distignore @@ -0,0 +1,18 @@ +.git +.github +.gitignore +.distignore +.wordpress-org +.slimm +.phpunit.cache +tests +bin +phpunit.xml +composer.lock +build +*.zip +.DS_Store +languages/*.po +languages/*.mo +languages/*.l10n.php +*.l10n.php diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 28adffc..be3472b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,8 @@ name: Release # Build a distributable plugin zip and attach it to the GitHub release whenever -# a version tag is pushed. The Datalumo plugin checks for updates against these -# release assets (see src/Support/UpdateChecker.php), so the zip must contain the -# Composer-built vendor/ directory — GitHub's auto-generated source zip does not. +# a version tag is pushed. The zip must contain the Composer-built vendor/ +# directory — GitHub's auto-generated source archive does not. on: push: tags: @@ -52,6 +51,8 @@ jobs: --exclude '.git' \ --exclude '.github' \ --exclude '.gitignore' \ + --exclude '.distignore' \ + --exclude '.wordpress-org' \ --exclude '.slimm' \ --exclude 'build' \ --exclude '*.zip' \ @@ -59,7 +60,12 @@ jobs: --exclude 'bin' \ --exclude 'phpunit.xml' \ --exclude '.phpunit.cache' \ - --exclude 'composer.lock' + --exclude '.DS_Store' \ + --exclude 'composer.lock' \ + --exclude 'languages/*.po' \ + --exclude 'languages/*.mo' \ + --exclude 'languages/*.l10n.php' \ + --exclude '*.l10n.php' (cd build && zip -rq datalumo.zip datalumo) - name: Create release and upload asset diff --git a/.gitignore b/.gitignore index 567402e..9cb1362 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ build/ *.zip .slimm/ .phpunit.cache/ +.DS_Store +languages/*.po +languages/*.mo +languages/*.l10n.php diff --git a/.wordpress-org/icon-128x128.png b/.wordpress-org/icon-128x128.png new file mode 100644 index 0000000..e997022 Binary files /dev/null and b/.wordpress-org/icon-128x128.png differ diff --git a/.wordpress-org/icon-256x256.png b/.wordpress-org/icon-256x256.png new file mode 100644 index 0000000..1f35e14 Binary files /dev/null and b/.wordpress-org/icon-256x256.png differ diff --git a/bin/ensure-index-php b/bin/ensure-index-php index cfd748b..abb7332 100755 --- a/bin/ensure-index-php +++ b/bin/ensure-index-php @@ -7,7 +7,7 @@ */ $root = dirname(__DIR__); -$skip = ['.git', '.github', '.slimm', '.phpunit.cache']; +$skip = ['.git', '.github', '.slimm', '.phpunit.cache', '.wordpress-org', 'build']; $contents = "=8.1", - "automattic/jetpack-autoloader": "^3.0", - "woocommerce/action-scheduler": "^3.0", - "yahnis-elsts/plugin-update-checker": "^5.7" + "automattic/jetpack-autoloader": "^6.0", + "woocommerce/action-scheduler": "^4.1" }, "autoload": { "psr-4": { diff --git a/datalumo.php b/datalumo.php index d643d5a..8eddbf5 100644 --- a/datalumo.php +++ b/datalumo.php @@ -7,8 +7,8 @@ * Plugin Name: Datalumo * Plugin URI: https://github.com/datalumo/wordpress * Description: Sync your WordPress content to Datalumo and add AI-powered search and chat to your site. - * Version: 0.2.0 - * Requires at least: 6.0 + * Version: 1.0.0 + * Requires at least: 6.8 * Requires PHP: 8.1 * Author: Datalumo * Author URI: https://datalumo.app @@ -25,7 +25,7 @@ } if (! defined('DATALUMO_VERSION')) { - define('DATALUMO_VERSION', '0.2.0'); + define('DATALUMO_VERSION', '1.0.0'); define('DATALUMO_FILE', __FILE__); define('DATALUMO_DIR', __DIR__); define('DATALUMO_URL', plugin_dir_url(__FILE__)); diff --git a/languages/datalumo-nl_NL.mo b/languages/datalumo-nl_NL.mo deleted file mode 100644 index 3ec7302..0000000 Binary files a/languages/datalumo-nl_NL.mo and /dev/null differ diff --git a/languages/datalumo-nl_NL.po b/languages/datalumo-nl_NL.po deleted file mode 100644 index db7e4c7..0000000 --- a/languages/datalumo-nl_NL.po +++ /dev/null @@ -1,275 +0,0 @@ -# Dutch (Netherlands) translation for Datalumo -# Copyright (C) 2026 Jeffrey van Rossum -# This file is distributed under the same license as the Datalumo plugin. -msgid "" -msgstr "" -"Project-Id-Version: Datalumo 1.0.0\n" -"Report-Msgid-Bugs-To: https://datalumo.app\n" -"POT-Creation-Date: 2026-07-18 12:00+0000\n" -"PO-Revision-Date: 2026-07-18 12:00+0000\n" -"Last-Translator: Jeffrey van Rossum\n" -"Language-Team: Dutch\n" -"Language: nl_NL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Domain: datalumo\n" - -#: datalumo.php -msgid "Sync your WordPress content to Datalumo and add AI-powered search and chat to your site." -msgstr "Synchroniseer je WordPress-content met Datalumo en voeg AI-gestuurde zoekfunctie en chat toe aan je site." - -#: src/Plugin.php -msgid "Datalumo is almost ready — connect your account to start syncing content." -msgstr "Datalumo is bijna klaar — koppel je account om content te synchroniseren." - -#: src/Plugin.php -msgid "Open settings" -msgstr "Instellingen openen" - -#: src/Admin/SettingsPage.php -#: resources/views/settings.php -msgid "Datalumo" -msgstr "Datalumo" - -#: src/Admin/SettingsPage.php -msgid "Connected to %s — %d source(s) available." -msgstr "Verbonden met %s — %d bron(nen) beschikbaar." - -#: src/Admin/SettingsPage.php -msgid "Connection failed:" -msgstr "Verbinding mislukt:" - -#: src/Admin/SettingsPage.php -msgid "%1$d of %2$d posts synced…" -msgstr "%1$d van %2$d berichten gesynchroniseerd…" - -#: src/Admin/SettingsPage.php -msgid "Sync complete." -msgstr "Synchronisatie voltooid." - -#: src/Admin/SettingsPage.php -#: src/Admin/Ajax.php -msgid "Not allowed." -msgstr "Niet toegestaan." - -#: src/Admin/Ajax.php -msgid "Both the organisation ID and an API token are required." -msgstr "Zowel het organisatienummer als een API-token zijn verplicht." - -#: src/Api/Client.php -msgid "Invalid widget key — copy it from the widget editor." -msgstr "Ongeldige widget-sleutel — kopieer deze uit de widget-editor." - -#: src/Api/Client.php -msgid "Datalumo request failed (%d)." -msgstr "Datalumo-verzoek mislukt (%d)." - -#: src/Sync/BulkSync.php -msgid "Unknown sync configuration." -msgstr "Onbekende synchronisatie-instelling." - -#: src/Search/Summary.php -msgid "AI Summary" -msgstr "AI-samenvatting" - -#: src/Search/Summary.php -msgid "Show more" -msgstr "Meer tonen" - -#: src/Search/Summary.php -msgid "Show less" -msgstr "Minder tonen" - -#: src/Search/Summary.php -msgid "Summarising the best results…" -msgstr "De beste resultaten worden samengevat…" - -#: resources/views/settings.php -msgid "Connection" -msgstr "Verbinding" - -#: resources/views/settings.php -msgid "Content sync" -msgstr "Content synchroniseren" - -#: resources/views/settings.php -msgid "Chatbot" -msgstr "Chatbot" - -#: resources/views/settings.php -msgid "Search box" -msgstr "Zoekveld" - -#: resources/views/settings.php -msgid "Enhanced search" -msgstr "Verbeterd zoeken" - -#: resources/views/settings.php -msgid "Settings saved." -msgstr "Instellingen opgeslagen." - -#: resources/views/settings.php -msgid "Datalumo URL" -msgstr "Datalumo-URL" - -#: resources/views/settings.php -msgid "Leave as-is unless you were told otherwise." -msgstr "Laat dit staan, tenzij je iets anders hebt gehoord." - -#: resources/views/settings.php -msgid "Organisation ID" -msgstr "Organisatienummer" - -#: resources/views/settings.php -msgid "Find it next to your API keys in the Datalumo dashboard." -msgstr "Je vindt dit naast je API-sleutels in het Datalumo-dashboard." - -#: resources/views/settings.php -msgid "API token" -msgstr "API-token" - -#: resources/views/settings.php -msgid "•••••••• (saved)" -msgstr "•••••••• (opgeslagen)" - -#: resources/views/settings.php -msgid "Leave blank to re-test, or paste a new token to replace it." -msgstr "Laat leeg om opnieuw te testen, of plak een nieuw token om het te vervangen." - -#: resources/views/settings.php -msgid "Create a token under API keys in your Datalumo dashboard." -msgstr "Maak een token onder API-sleutels in je Datalumo-dashboard." - -#: resources/views/settings.php -msgid "Connect & test" -msgstr "Verbinden en testen" - -#: resources/views/settings.php -msgid "Connected to %1$s (%2$d sources)." -msgstr "Verbonden met %1$s (%2$d bronnen)." - -#: resources/views/settings.php -msgid "Connect your account first." -msgstr "Koppel eerst je account." - -#: resources/views/settings.php -msgid "Each sync pushes the chosen post types into one Datalumo source. Create a source for this WordPress-site in your dashboard first." -msgstr "Elke synchronisatie stuurt de gekozen berichttypen naar één Datalumo-bron. Maak eerst een bron voor deze WordPress-site aan in je dashboard." - -#: resources/views/settings.php -msgid "Source" -msgstr "Bron" - -#: resources/views/settings.php -msgid "— choose a source —" -msgstr "— kies een bron —" - -#: resources/views/settings.php -msgid "Post types" -msgstr "Berichttypen" - -#: resources/views/settings.php -msgid "Full sync" -msgstr "Volledige synchronisatie" - -#: resources/views/settings.php -msgid "Sync now" -msgstr "Nu synchroniseren" - -#: resources/views/settings.php -msgid "Cancel" -msgstr "Annuleren" - -#: resources/views/settings.php -msgid "Custom field and taxonomy mappings can be added per sync with the datalumo_page_payload filter for now." -msgstr "Koppelingen van aangepaste velden en taxonomieën kun je voorlopig per synchronisatie toevoegen met het filter datalumo_page_payload." - -#: resources/views/settings.php -msgid "Save syncs" -msgstr "Synchronisaties opslaan" - -#: resources/views/settings.php -msgid "Chat widget" -msgstr "Chatwidget" - -#: resources/views/settings.php -msgid "Show the floating chat on every page" -msgstr "Toon de zwevende chat op elke pagina" - -#: resources/views/settings.php -msgid "Widget key" -msgstr "Widget-sleutel" - -#: resources/views/settings.php -msgid "Copy it from the widget editor (\"For developers\"). Add this site to the widget's websites list." -msgstr "Kopieer deze uit de widget-editor (\"For developers\"). Voeg deze site toe aan de websites-lijst van de widget." - -#: resources/views/settings.php -msgid "Visitor identity" -msgstr "Bezoekersidentiteit" - -#: resources/views/settings.php -msgid "Let logged-in users continue their conversations across visits" -msgstr "Laat ingelogde gebruikers hun gesprekken over bezoeken heen voortzetten" - -#: resources/views/settings.php -msgid "Identity signing secret" -msgstr "Geheim voor identiteitsondertekening" - -#: resources/views/settings.php -msgid "From the same \"For developers\" section. Never exposed to the browser — used to sign user ids server-side." -msgstr "Uit hetzelfde gedeelte \"For developers\". Wordt nooit naar de browser gestuurd — alleen server-side gebruikt om gebruikers-id's te ondertekenen." - -#: resources/views/settings.php -msgid "Tip: use the [datalumo_chat] shortcode to embed the chat inline on a page." -msgstr "Tip: gebruik de shortcode [datalumo_chat] om de chat in een pagina te plaatsen." - -#: resources/views/settings.php -msgid "A search-type widget key. Place the box anywhere with the [datalumo_search] shortcode." -msgstr "Een widget-sleutel van het type zoeken. Plaats het zoekveld overal met de shortcode [datalumo_search]." - -#: resources/views/settings.php -msgid "Serve WordPress search results from Datalumo (falls back to native search on any error)" -msgstr "Toon WordPress-zoekresultaten via Datalumo (valt bij een fout terug op de standaard zoekfunctie)" - -#: resources/views/settings.php -msgid "A search-type widget key; this site must be in its websites list." -msgstr "Een widget-sleutel van het type zoeken; deze site moet in de websites-lijst staan." - -#: resources/views/settings.php -msgid "Limit to post types" -msgstr "Beperk tot berichttypen" - -#: resources/views/settings.php -msgid "Leave all unchecked to enhance every search." -msgstr "Laat alles uit om elke zoekopdracht te verbeteren." - -#: resources/views/settings.php -msgid "AI summary" -msgstr "AI-samenvatting" - -#: resources/views/settings.php -msgid "Show a streamed AI answer above the results" -msgstr "Toon een gestreamd AI-antwoord boven de resultaten" - -#: resources/views/settings.php -msgid "Summary placement" -msgstr "Plaatsing van de samenvatting" - -#: resources/views/settings.php -msgid "auto" -msgstr "auto" - -#: resources/views/settings.php -msgid "Before results" -msgstr "Voor de resultaten" - -#: resources/views/settings.php -msgid "After results" -msgstr "Na de resultaten" - -#: resources/views/settings.php -msgid "Optional CSS selector of the results container; detected automatically when empty." -msgstr "Optionele CSS-selector van de resultatencontainer; wordt automatisch bepaald als dit leeg is." diff --git a/languages/datalumo-zh_CN.mo b/languages/datalumo-zh_CN.mo deleted file mode 100644 index b59d8a6..0000000 Binary files a/languages/datalumo-zh_CN.mo and /dev/null differ diff --git a/languages/datalumo-zh_CN.po b/languages/datalumo-zh_CN.po deleted file mode 100644 index 3de7ee7..0000000 --- a/languages/datalumo-zh_CN.po +++ /dev/null @@ -1,275 +0,0 @@ -# Simplified Chinese translation for Datalumo -# Copyright (C) 2026 Jeffrey van Rossum -# This file is distributed under the same license as the Datalumo plugin. -msgid "" -msgstr "" -"Project-Id-Version: Datalumo 1.0.0\n" -"Report-Msgid-Bugs-To: https://datalumo.app\n" -"POT-Creation-Date: 2026-07-18 12:00+0000\n" -"PO-Revision-Date: 2026-07-18 12:00+0000\n" -"Last-Translator: Jeffrey van Rossum\n" -"Language-Team: Chinese (Simplified)\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Domain: datalumo\n" - -#: datalumo.php -msgid "Sync your WordPress content to Datalumo and add AI-powered search and chat to your site." -msgstr "将 WordPress 内容同步到 Datalumo,并为网站添加 AI 搜索与聊天。" - -#: src/Plugin.php -msgid "Datalumo is almost ready — connect your account to start syncing content." -msgstr "Datalumo 即将就绪 — 连接你的账户以开始同步内容。" - -#: src/Plugin.php -msgid "Open settings" -msgstr "打开设置" - -#: src/Admin/SettingsPage.php -#: resources/views/settings.php -msgid "Datalumo" -msgstr "Datalumo" - -#: src/Admin/SettingsPage.php -msgid "Connected to %s — %d source(s) available." -msgstr "已连接到 %s — 可用 %d 个来源。" - -#: src/Admin/SettingsPage.php -msgid "Connection failed:" -msgstr "连接失败:" - -#: src/Admin/SettingsPage.php -msgid "%1$d of %2$d posts synced…" -msgstr "已同步 %1$d / %2$d 篇文章…" - -#: src/Admin/SettingsPage.php -msgid "Sync complete." -msgstr "同步完成。" - -#: src/Admin/SettingsPage.php -#: src/Admin/Ajax.php -msgid "Not allowed." -msgstr "无权操作。" - -#: src/Admin/Ajax.php -msgid "Both the organisation ID and an API token are required." -msgstr "必须同时填写组织 ID 和 API 令牌。" - -#: src/Api/Client.php -msgid "Invalid widget key — copy it from the widget editor." -msgstr "无效的小组件密钥 — 请从小组件编辑器复制。" - -#: src/Api/Client.php -msgid "Datalumo request failed (%d)." -msgstr "Datalumo 请求失败(%d)。" - -#: src/Sync/BulkSync.php -msgid "Unknown sync configuration." -msgstr "未知的同步配置。" - -#: src/Search/Summary.php -msgid "AI Summary" -msgstr "AI 摘要" - -#: src/Search/Summary.php -msgid "Show more" -msgstr "显示更多" - -#: src/Search/Summary.php -msgid "Show less" -msgstr "显示更少" - -#: src/Search/Summary.php -msgid "Summarising the best results…" -msgstr "正在汇总最佳结果…" - -#: resources/views/settings.php -msgid "Connection" -msgstr "连接" - -#: resources/views/settings.php -msgid "Content sync" -msgstr "内容同步" - -#: resources/views/settings.php -msgid "Chatbot" -msgstr "聊天机器人" - -#: resources/views/settings.php -msgid "Search box" -msgstr "搜索框" - -#: resources/views/settings.php -msgid "Enhanced search" -msgstr "增强搜索" - -#: resources/views/settings.php -msgid "Settings saved." -msgstr "设置已保存。" - -#: resources/views/settings.php -msgid "Datalumo URL" -msgstr "Datalumo 网址" - -#: resources/views/settings.php -msgid "Leave as-is unless you were told otherwise." -msgstr "除非另有说明,否则请保持默认。" - -#: resources/views/settings.php -msgid "Organisation ID" -msgstr "组织 ID" - -#: resources/views/settings.php -msgid "Find it next to your API keys in the Datalumo dashboard." -msgstr "可在 Datalumo 控制台的 API 密钥旁找到。" - -#: resources/views/settings.php -msgid "API token" -msgstr "API 令牌" - -#: resources/views/settings.php -msgid "•••••••• (saved)" -msgstr "••••••••(已保存)" - -#: resources/views/settings.php -msgid "Leave blank to re-test, or paste a new token to replace it." -msgstr "留空可重新测试,或粘贴新令牌以替换。" - -#: resources/views/settings.php -msgid "Create a token under API keys in your Datalumo dashboard." -msgstr "在 Datalumo 控制台的「API 密钥」中创建令牌。" - -#: resources/views/settings.php -msgid "Connect & test" -msgstr "连接并测试" - -#: resources/views/settings.php -msgid "Connected to %1$s (%2$d sources)." -msgstr "已连接到 %1$s(%2$d 个来源)。" - -#: resources/views/settings.php -msgid "Connect your account first." -msgstr "请先连接你的账户。" - -#: resources/views/settings.php -msgid "Each sync pushes the chosen post types into one Datalumo source. Create a source of type \"API\" in your dashboard first." -msgstr "每条同步会将所选文章类型推送到一个 Datalumo 来源。请先在控制台创建一个类型为「API」的来源。" - -#: resources/views/settings.php -msgid "Source" -msgstr "来源" - -#: resources/views/settings.php -msgid "— choose a source —" -msgstr "— 选择来源 —" - -#: resources/views/settings.php -msgid "Post types" -msgstr "文章类型" - -#: resources/views/settings.php -msgid "Full sync" -msgstr "完整同步" - -#: resources/views/settings.php -msgid "Sync now" -msgstr "立即同步" - -#: resources/views/settings.php -msgid "Cancel" -msgstr "取消" - -#: resources/views/settings.php -msgid "Custom field and taxonomy mappings can be added per sync with the datalumo_page_payload filter for now." -msgstr "目前可通过 datalumo_page_payload 过滤器为每条同步添加自定义字段和分类映射。" - -#: resources/views/settings.php -msgid "Save syncs" -msgstr "保存同步" - -#: resources/views/settings.php -msgid "Chat widget" -msgstr "聊天小组件" - -#: resources/views/settings.php -msgid "Show the floating chat on every page" -msgstr "在每个页面显示浮动聊天" - -#: resources/views/settings.php -msgid "Widget key" -msgstr "小组件密钥" - -#: resources/views/settings.php -msgid "Copy it from the widget editor (\"For developers\"). Add this site to the widget's websites list." -msgstr "从小组件编辑器(「For developers」)复制。请将本站加入该小组件的网站列表。" - -#: resources/views/settings.php -msgid "Visitor identity" -msgstr "访客身份" - -#: resources/views/settings.php -msgid "Let logged-in users continue their conversations across visits" -msgstr "允许已登录用户在多次访问间继续对话" - -#: resources/views/settings.php -msgid "Identity signing secret" -msgstr "身份签名密钥" - -#: resources/views/settings.php -msgid "From the same \"For developers\" section. Never exposed to the browser — used to sign user ids server-side." -msgstr "同样在「For developers」部分。不会暴露给浏览器 — 仅在服务器端用于签署用户 ID。" - -#: resources/views/settings.php -msgid "Tip: use the [datalumo_chat] shortcode to embed the chat inline on a page." -msgstr "提示:使用 [datalumo_chat] 短代码可在页面中嵌入聊天。" - -#: resources/views/settings.php -msgid "A search-type widget key. Place the box anywhere with the [datalumo_search] shortcode." -msgstr "搜索类型小组件密钥。使用 [datalumo_search] 短代码可在任意位置放置搜索框。" - -#: resources/views/settings.php -msgid "Serve WordPress search results from Datalumo (falls back to native search on any error)" -msgstr "通过 Datalumo 提供 WordPress 搜索结果(出错时回退到原生搜索)" - -#: resources/views/settings.php -msgid "A search-type widget key; this site must be in its websites list." -msgstr "搜索类型小组件密钥;本站必须在其网站列表中。" - -#: resources/views/settings.php -msgid "Limit to post types" -msgstr "限制文章类型" - -#: resources/views/settings.php -msgid "Leave all unchecked to enhance every search." -msgstr "全部不勾选则增强所有搜索。" - -#: resources/views/settings.php -msgid "AI summary" -msgstr "AI 摘要" - -#: resources/views/settings.php -msgid "Show a streamed AI answer above the results" -msgstr "在结果上方显示流式 AI 回答" - -#: resources/views/settings.php -msgid "Summary placement" -msgstr "摘要位置" - -#: resources/views/settings.php -msgid "auto" -msgstr "自动" - -#: resources/views/settings.php -msgid "Before results" -msgstr "结果之前" - -#: resources/views/settings.php -msgid "After results" -msgstr "结果之后" - -#: resources/views/settings.php -msgid "Optional CSS selector of the results container; detected automatically when empty." -msgstr "结果容器的可选 CSS 选择器;为空时自动检测。" diff --git a/languages/datalumo-zh_HK.mo b/languages/datalumo-zh_HK.mo deleted file mode 100644 index 62ddd62..0000000 Binary files a/languages/datalumo-zh_HK.mo and /dev/null differ diff --git a/languages/datalumo-zh_HK.po b/languages/datalumo-zh_HK.po deleted file mode 100644 index 2f0df00..0000000 --- a/languages/datalumo-zh_HK.po +++ /dev/null @@ -1,275 +0,0 @@ -# Traditional Chinese (Hong Kong) translation for Datalumo -# Copyright (C) 2026 Jeffrey van Rossum -# This file is distributed under the same license as the Datalumo plugin. -msgid "" -msgstr "" -"Project-Id-Version: Datalumo 1.0.0\n" -"Report-Msgid-Bugs-To: https://datalumo.app\n" -"POT-Creation-Date: 2026-07-18 12:00+0000\n" -"PO-Revision-Date: 2026-07-18 12:00+0000\n" -"Last-Translator: Jeffrey van Rossum\n" -"Language-Team: Chinese (Hong Kong)\n" -"Language: zh_HK\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Domain: datalumo\n" - -#: datalumo.php -msgid "Sync your WordPress content to Datalumo and add AI-powered search and chat to your site." -msgstr "將 WordPress 內容同步至 Datalumo,並為網站加入 AI 搜尋與聊天。" - -#: src/Plugin.php -msgid "Datalumo is almost ready — connect your account to start syncing content." -msgstr "Datalumo 即將準備就緒 — 連接你的帳戶以開始同步內容。" - -#: src/Plugin.php -msgid "Open settings" -msgstr "開啟設定" - -#: src/Admin/SettingsPage.php -#: resources/views/settings.php -msgid "Datalumo" -msgstr "Datalumo" - -#: src/Admin/SettingsPage.php -msgid "Connected to %s — %d source(s) available." -msgstr "已連接至 %s — 有 %d 個來源可用。" - -#: src/Admin/SettingsPage.php -msgid "Connection failed:" -msgstr "連接失敗:" - -#: src/Admin/SettingsPage.php -msgid "%1$d of %2$d posts synced…" -msgstr "已同步 %1$d / %2$d 篇文章…" - -#: src/Admin/SettingsPage.php -msgid "Sync complete." -msgstr "同步完成。" - -#: src/Admin/SettingsPage.php -#: src/Admin/Ajax.php -msgid "Not allowed." -msgstr "無權操作。" - -#: src/Admin/Ajax.php -msgid "Both the organisation ID and an API token are required." -msgstr "必須同時填寫機構 ID 及 API 權杖。" - -#: src/Api/Client.php -msgid "Invalid widget key — copy it from the widget editor." -msgstr "無效的小工具密鑰 — 請從小工具編輯器複製。" - -#: src/Api/Client.php -msgid "Datalumo request failed (%d)." -msgstr "Datalumo 請求失敗(%d)。" - -#: src/Sync/BulkSync.php -msgid "Unknown sync configuration." -msgstr "未知的同步設定。" - -#: src/Search/Summary.php -msgid "AI Summary" -msgstr "AI 摘要" - -#: src/Search/Summary.php -msgid "Show more" -msgstr "顯示更多" - -#: src/Search/Summary.php -msgid "Show less" -msgstr "顯示較少" - -#: src/Search/Summary.php -msgid "Summarising the best results…" -msgstr "正在匯總最佳結果…" - -#: resources/views/settings.php -msgid "Connection" -msgstr "連接" - -#: resources/views/settings.php -msgid "Content sync" -msgstr "內容同步" - -#: resources/views/settings.php -msgid "Chatbot" -msgstr "聊天機械人" - -#: resources/views/settings.php -msgid "Search box" -msgstr "搜尋框" - -#: resources/views/settings.php -msgid "Enhanced search" -msgstr "強化搜尋" - -#: resources/views/settings.php -msgid "Settings saved." -msgstr "設定已儲存。" - -#: resources/views/settings.php -msgid "Datalumo URL" -msgstr "Datalumo 網址" - -#: resources/views/settings.php -msgid "Leave as-is unless you were told otherwise." -msgstr "除非另有指示,否則請維持預設。" - -#: resources/views/settings.php -msgid "Organisation ID" -msgstr "機構 ID" - -#: resources/views/settings.php -msgid "Find it next to your API keys in the Datalumo dashboard." -msgstr "可在 Datalumo 控制台的 API 密鑰旁找到。" - -#: resources/views/settings.php -msgid "API token" -msgstr "API 權杖" - -#: resources/views/settings.php -msgid "•••••••• (saved)" -msgstr "••••••••(已儲存)" - -#: resources/views/settings.php -msgid "Leave blank to re-test, or paste a new token to replace it." -msgstr "留空可重新測試,或貼上新權杖以取代。" - -#: resources/views/settings.php -msgid "Create a token under API keys in your Datalumo dashboard." -msgstr "在 Datalumo 控制台的「API 密鑰」建立權杖。" - -#: resources/views/settings.php -msgid "Connect & test" -msgstr "連接並測試" - -#: resources/views/settings.php -msgid "Connected to %1$s (%2$d sources)." -msgstr "已連接至 %1$s(%2$d 個來源)。" - -#: resources/views/settings.php -msgid "Connect your account first." -msgstr "請先連接你的帳戶。" - -#: resources/views/settings.php -msgid "Each sync pushes the chosen post types into one Datalumo source. Create a source of type \"API\" in your dashboard first." -msgstr "每項同步會將所選文章類型推送至一個 Datalumo 來源。請先在控制台建立類型為「API」的來源。" - -#: resources/views/settings.php -msgid "Source" -msgstr "來源" - -#: resources/views/settings.php -msgid "— choose a source —" -msgstr "— 選擇來源 —" - -#: resources/views/settings.php -msgid "Post types" -msgstr "文章類型" - -#: resources/views/settings.php -msgid "Full sync" -msgstr "完整同步" - -#: resources/views/settings.php -msgid "Sync now" -msgstr "立即同步" - -#: resources/views/settings.php -msgid "Cancel" -msgstr "取消" - -#: resources/views/settings.php -msgid "Custom field and taxonomy mappings can be added per sync with the datalumo_page_payload filter for now." -msgstr "目前可透過 datalumo_page_payload 過濾器為每項同步加入自訂欄位及分類法對應。" - -#: resources/views/settings.php -msgid "Save syncs" -msgstr "儲存同步" - -#: resources/views/settings.php -msgid "Chat widget" -msgstr "聊天小工具" - -#: resources/views/settings.php -msgid "Show the floating chat on every page" -msgstr "在每個頁面顯示浮動聊天" - -#: resources/views/settings.php -msgid "Widget key" -msgstr "小工具密鑰" - -#: resources/views/settings.php -msgid "Copy it from the widget editor (\"For developers\"). Add this site to the widget's websites list." -msgstr "從小工具編輯器(「For developers」)複製。請將本網站加入該小工具的網站列表。" - -#: resources/views/settings.php -msgid "Visitor identity" -msgstr "訪客身分" - -#: resources/views/settings.php -msgid "Let logged-in users continue their conversations across visits" -msgstr "讓已登入用戶在多次造訪之間繼續對話" - -#: resources/views/settings.php -msgid "Identity signing secret" -msgstr "身分簽署密鑰" - -#: resources/views/settings.php -msgid "From the same \"For developers\" section. Never exposed to the browser — used to sign user ids server-side." -msgstr "同樣在「For developers」部分。不會向瀏覽器暴露 — 只在伺服器端用作簽署用戶 ID。" - -#: resources/views/settings.php -msgid "Tip: use the [datalumo_chat] shortcode to embed the chat inline on a page." -msgstr "提示:使用 [datalumo_chat] 短代碼可在頁面中嵌入聊天。" - -#: resources/views/settings.php -msgid "A search-type widget key. Place the box anywhere with the [datalumo_search] shortcode." -msgstr "搜尋類型小工具密鑰。使用 [datalumo_search] 短代碼可在任何位置放置搜尋框。" - -#: resources/views/settings.php -msgid "Serve WordPress search results from Datalumo (falls back to native search on any error)" -msgstr "透過 Datalumo 提供 WordPress 搜尋結果(出錯時會改用原生搜尋)" - -#: resources/views/settings.php -msgid "A search-type widget key; this site must be in its websites list." -msgstr "搜尋類型小工具密鑰;本網站必須在其網站列表中。" - -#: resources/views/settings.php -msgid "Limit to post types" -msgstr "限制文章類型" - -#: resources/views/settings.php -msgid "Leave all unchecked to enhance every search." -msgstr "全部不勾選則強化所有搜尋。" - -#: resources/views/settings.php -msgid "AI summary" -msgstr "AI 摘要" - -#: resources/views/settings.php -msgid "Show a streamed AI answer above the results" -msgstr "在結果上方顯示串流 AI 回答" - -#: resources/views/settings.php -msgid "Summary placement" -msgstr "摘要位置" - -#: resources/views/settings.php -msgid "auto" -msgstr "自動" - -#: resources/views/settings.php -msgid "Before results" -msgstr "結果之前" - -#: resources/views/settings.php -msgid "After results" -msgstr "結果之後" - -#: resources/views/settings.php -msgid "Optional CSS selector of the results container; detected automatically when empty." -msgstr "結果容器的可選 CSS 選擇器;留空時會自動偵測。" diff --git a/languages/datalumo-zh_TW.mo b/languages/datalumo-zh_TW.mo deleted file mode 100644 index 3b29a31..0000000 Binary files a/languages/datalumo-zh_TW.mo and /dev/null differ diff --git a/languages/datalumo-zh_TW.po b/languages/datalumo-zh_TW.po deleted file mode 100644 index 806e2dd..0000000 --- a/languages/datalumo-zh_TW.po +++ /dev/null @@ -1,275 +0,0 @@ -# Traditional Chinese (Taiwan) translation for Datalumo -# Copyright (C) 2026 Jeffrey van Rossum -# This file is distributed under the same license as the Datalumo plugin. -msgid "" -msgstr "" -"Project-Id-Version: Datalumo 1.0.0\n" -"Report-Msgid-Bugs-To: https://datalumo.app\n" -"POT-Creation-Date: 2026-07-18 12:00+0000\n" -"PO-Revision-Date: 2026-07-18 12:00+0000\n" -"Last-Translator: Jeffrey van Rossum\n" -"Language-Team: Chinese (Taiwan)\n" -"Language: zh_TW\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Domain: datalumo\n" - -#: datalumo.php -msgid "Sync your WordPress content to Datalumo and add AI-powered search and chat to your site." -msgstr "將 WordPress 內容同步至 Datalumo,並為網站加入 AI 搜尋與聊天。" - -#: src/Plugin.php -msgid "Datalumo is almost ready — connect your account to start syncing content." -msgstr "Datalumo 即將準備就緒 — 連接你的帳號以開始同步內容。" - -#: src/Plugin.php -msgid "Open settings" -msgstr "開啟設定" - -#: src/Admin/SettingsPage.php -#: resources/views/settings.php -msgid "Datalumo" -msgstr "Datalumo" - -#: src/Admin/SettingsPage.php -msgid "Connected to %s — %d source(s) available." -msgstr "已連接至 %s — 有 %d 個來源可用。" - -#: src/Admin/SettingsPage.php -msgid "Connection failed:" -msgstr "連接失敗:" - -#: src/Admin/SettingsPage.php -msgid "%1$d of %2$d posts synced…" -msgstr "已同步 %1$d / %2$d 篇文章…" - -#: src/Admin/SettingsPage.php -msgid "Sync complete." -msgstr "同步完成。" - -#: src/Admin/SettingsPage.php -#: src/Admin/Ajax.php -msgid "Not allowed." -msgstr "無權操作。" - -#: src/Admin/Ajax.php -msgid "Both the organisation ID and an API token are required." -msgstr "必須同時填寫組織 ID 與 API 權杖。" - -#: src/Api/Client.php -msgid "Invalid widget key — copy it from the widget editor." -msgstr "無效的小工具金鑰 — 請從小工具編輯器複製。" - -#: src/Api/Client.php -msgid "Datalumo request failed (%d)." -msgstr "Datalumo 請求失敗(%d)。" - -#: src/Sync/BulkSync.php -msgid "Unknown sync configuration." -msgstr "未知的同步設定。" - -#: src/Search/Summary.php -msgid "AI Summary" -msgstr "AI 摘要" - -#: src/Search/Summary.php -msgid "Show more" -msgstr "顯示更多" - -#: src/Search/Summary.php -msgid "Show less" -msgstr "顯示較少" - -#: src/Search/Summary.php -msgid "Summarising the best results…" -msgstr "正在彙總最佳結果…" - -#: resources/views/settings.php -msgid "Connection" -msgstr "連接" - -#: resources/views/settings.php -msgid "Content sync" -msgstr "內容同步" - -#: resources/views/settings.php -msgid "Chatbot" -msgstr "聊天機器人" - -#: resources/views/settings.php -msgid "Search box" -msgstr "搜尋框" - -#: resources/views/settings.php -msgid "Enhanced search" -msgstr "強化搜尋" - -#: resources/views/settings.php -msgid "Settings saved." -msgstr "設定已儲存。" - -#: resources/views/settings.php -msgid "Datalumo URL" -msgstr "Datalumo 網址" - -#: resources/views/settings.php -msgid "Leave as-is unless you were told otherwise." -msgstr "除非另有指示,否則請維持預設。" - -#: resources/views/settings.php -msgid "Organisation ID" -msgstr "組織 ID" - -#: resources/views/settings.php -msgid "Find it next to your API keys in the Datalumo dashboard." -msgstr "可在 Datalumo 控制台的 API 金鑰旁找到。" - -#: resources/views/settings.php -msgid "API token" -msgstr "API 權杖" - -#: resources/views/settings.php -msgid "•••••••• (saved)" -msgstr "••••••••(已儲存)" - -#: resources/views/settings.php -msgid "Leave blank to re-test, or paste a new token to replace it." -msgstr "留空可重新測試,或貼上新權杖以取代。" - -#: resources/views/settings.php -msgid "Create a token under API keys in your Datalumo dashboard." -msgstr "在 Datalumo 控制台的「API 金鑰」建立權杖。" - -#: resources/views/settings.php -msgid "Connect & test" -msgstr "連接並測試" - -#: resources/views/settings.php -msgid "Connected to %1$s (%2$d sources)." -msgstr "已連接至 %1$s(%2$d 個來源)。" - -#: resources/views/settings.php -msgid "Connect your account first." -msgstr "請先連接你的帳號。" - -#: resources/views/settings.php -msgid "Each sync pushes the chosen post types into one Datalumo source. Create a source of type \"API\" in your dashboard first." -msgstr "每項同步會將所選文章類型推送至一個 Datalumo 來源。請先在控制台建立類型為「API」的來源。" - -#: resources/views/settings.php -msgid "Source" -msgstr "來源" - -#: resources/views/settings.php -msgid "— choose a source —" -msgstr "— 選擇來源 —" - -#: resources/views/settings.php -msgid "Post types" -msgstr "文章類型" - -#: resources/views/settings.php -msgid "Full sync" -msgstr "完整同步" - -#: resources/views/settings.php -msgid "Sync now" -msgstr "立即同步" - -#: resources/views/settings.php -msgid "Cancel" -msgstr "取消" - -#: resources/views/settings.php -msgid "Custom field and taxonomy mappings can be added per sync with the datalumo_page_payload filter for now." -msgstr "目前可透過 datalumo_page_payload 過濾器為每項同步加入自訂欄位與分類法對應。" - -#: resources/views/settings.php -msgid "Save syncs" -msgstr "儲存同步" - -#: resources/views/settings.php -msgid "Chat widget" -msgstr "聊天小工具" - -#: resources/views/settings.php -msgid "Show the floating chat on every page" -msgstr "在每個頁面顯示浮動聊天" - -#: resources/views/settings.php -msgid "Widget key" -msgstr "小工具金鑰" - -#: resources/views/settings.php -msgid "Copy it from the widget editor (\"For developers\"). Add this site to the widget's websites list." -msgstr "從小工具編輯器(「For developers」)複製。請將本網站加入該小工具的網站列表。" - -#: resources/views/settings.php -msgid "Visitor identity" -msgstr "訪客身分" - -#: resources/views/settings.php -msgid "Let logged-in users continue their conversations across visits" -msgstr "讓已登入使用者在多次造訪之間繼續對話" - -#: resources/views/settings.php -msgid "Identity signing secret" -msgstr "身分簽署密鑰" - -#: resources/views/settings.php -msgid "From the same \"For developers\" section. Never exposed to the browser — used to sign user ids server-side." -msgstr "同樣在「For developers」部分。不會向瀏覽器暴露 — 只在伺服器端用於簽署使用者 ID。" - -#: resources/views/settings.php -msgid "Tip: use the [datalumo_chat] shortcode to embed the chat inline on a page." -msgstr "提示:使用 [datalumo_chat] 短代碼可在頁面中嵌入聊天。" - -#: resources/views/settings.php -msgid "A search-type widget key. Place the box anywhere with the [datalumo_search] shortcode." -msgstr "搜尋類型小工具金鑰。使用 [datalumo_search] 短代碼可在任何位置放置搜尋框。" - -#: resources/views/settings.php -msgid "Serve WordPress search results from Datalumo (falls back to native search on any error)" -msgstr "透過 Datalumo 提供 WordPress 搜尋結果(出錯時會改用原生搜尋)" - -#: resources/views/settings.php -msgid "A search-type widget key; this site must be in its websites list." -msgstr "搜尋類型小工具金鑰;本網站必須在其網站列表中。" - -#: resources/views/settings.php -msgid "Limit to post types" -msgstr "限制文章類型" - -#: resources/views/settings.php -msgid "Leave all unchecked to enhance every search." -msgstr "全部不勾選則強化所有搜尋。" - -#: resources/views/settings.php -msgid "AI summary" -msgstr "AI 摘要" - -#: resources/views/settings.php -msgid "Show a streamed AI answer above the results" -msgstr "在結果上方顯示串流 AI 回答" - -#: resources/views/settings.php -msgid "Summary placement" -msgstr "摘要位置" - -#: resources/views/settings.php -msgid "auto" -msgstr "自動" - -#: resources/views/settings.php -msgid "Before results" -msgstr "結果之前" - -#: resources/views/settings.php -msgid "After results" -msgstr "結果之後" - -#: resources/views/settings.php -msgid "Optional CSS selector of the results container; detected automatically when empty." -msgstr "結果容器的選用 CSS 選擇器;留空時會自動偵測。" diff --git a/languages/datalumo.pot b/languages/datalumo.pot index 174a53b..e001098 100644 --- a/languages/datalumo.pot +++ b/languages/datalumo.pot @@ -1,274 +1,491 @@ -# Copyright (C) 2026 Jeffrey van Rossum -# This file is distributed under the same license as the Datalumo plugin. +# Copyright (C) 2026 Datalumo +# This file is distributed under the GPL-2.0-or-later. msgid "" msgstr "" "Project-Id-Version: Datalumo 1.0.0\n" -"Report-Msgid-Bugs-To: https://datalumo.app\n" -"POT-Creation-Date: 2026-07-18 12:00+0000\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/datalumo\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" -"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2026-08-30T08:09:50+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.12.0\n" "X-Domain: datalumo\n" +#. Plugin Name of the plugin +#. Author of the plugin +#: datalumo.php +#: src/Admin/SettingsPage.php:25 +#: src/Admin/SettingsPage.php:26 +msgid "Datalumo" +msgstr "" + +#. Plugin URI of the plugin +#: datalumo.php +msgid "https://github.com/datalumo/wordpress" +msgstr "" + +#. Description of the plugin #: datalumo.php msgid "Sync your WordPress content to Datalumo and add AI-powered search and chat to your site." msgstr "" -#: src/Plugin.php -msgid "Datalumo is almost ready — connect your account to start syncing content." +#. Author URI of the plugin +#: datalumo.php +msgid "https://datalumo.app" msgstr "" -#: src/Plugin.php -msgid "Open settings" +#: resources/views/settings.php:19 +msgid "Connection" msgstr "" -#: src/Admin/SettingsPage.php -#: resources/views/settings.php -msgid "Datalumo" +#: resources/views/settings.php:20 +msgid "Content sync" msgstr "" -#: src/Admin/SettingsPage.php -msgid "Connected to %s — %d source(s) available." +#: resources/views/settings.php:21 +msgid "Chatbot" msgstr "" -#: src/Admin/SettingsPage.php -msgid "Connection failed:" +#: resources/views/settings.php:22 +msgid "Search box" msgstr "" -#: src/Admin/SettingsPage.php -msgid "%1$d of %2$d posts synced…" +#: resources/views/settings.php:23 +#: resources/views/settings.php:386 +msgid "Enhanced search" msgstr "" -#: src/Admin/SettingsPage.php -msgid "Sync complete." +#: resources/views/settings.php:32 +msgid "Connected to Datalumo." msgstr "" -#: src/Admin/SettingsPage.php -#: src/Admin/Ajax.php -msgid "Not allowed." +#: resources/views/settings.php:33 +msgid "Connect did not finish. Start again from this page." msgstr "" -#: src/Admin/Ajax.php -msgid "Both the organisation ID and an API token are required." +#: resources/views/settings.php:34 +msgid "Disconnected. The API key remains in Datalumo under API keys until you revoke it there." msgstr "" -#: src/Api/Client.php -msgid "Invalid widget key — copy it from the widget editor." +#: resources/views/settings.php:35 +msgid "Setup saved." msgstr "" -#: src/Api/Client.php -msgid "Datalumo request failed (%d)." +#: resources/views/settings.php:36 +msgid "Widget keys look like org-id/widget-id. An API token or secret will not work there." msgstr "" -#: src/Sync/BulkSync.php -msgid "Unknown sync configuration." +#: resources/views/settings.php:79 +msgid "Custom fields, filters, and sync details." msgstr "" -#: src/Search/Summary.php -msgid "AI Summary" +#: resources/views/settings.php:80 +msgid "Shortcodes and chat page actions." msgstr "" -#: src/Search/Summary.php -msgid "Show more" +#: resources/views/settings.php:81 +msgid "Search box shortcode and enhanced search." msgstr "" -#: src/Search/Summary.php -msgid "Show less" +#: resources/views/settings.php:82 +msgid "Connect, sync, and widgets." msgstr "" -#: src/Search/Summary.php -msgid "Summarising the best results…" +#: resources/views/settings.php:104 +msgid "Finish setup" msgstr "" -#: resources/views/settings.php -msgid "Connection" +#: resources/views/settings.php:105 +msgid "Pick a starting setup. You can change these later." msgstr "" -#: resources/views/settings.php -msgid "Content sync" +#: resources/views/settings.php:111 +msgid "What to sync" msgstr "" -#: resources/views/settings.php -msgid "Chatbot" +#: resources/views/settings.php:129 +msgid "Chat" msgstr "" -#: resources/views/settings.php -msgid "Search box" +#: resources/views/settings.php:134 +#: resources/views/settings.php:339 +msgid "Show the floating chat on every page" msgstr "" -#: resources/views/settings.php -msgid "Enhanced search" +#: resources/views/settings.php:144 +msgid "Search" +msgstr "" + +#: resources/views/settings.php:149 +msgid "Use Datalumo for WordPress search results" +msgstr "" + +#: resources/views/settings.php:156 +msgid "Save and sync now" msgstr "" -#: resources/views/settings.php -msgid "Settings saved." +#: resources/views/settings.php:157 +msgid "Save setup" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:163 +#: resources/views/settings.php:206 +msgid "Disconnect" +msgstr "" + +#: resources/views/settings.php:168 +msgid "Connect this site to Datalumo. You will pick or create a knowledge base and widgets, then we create a source and API key for you." +msgstr "" + +#: resources/views/settings.php:172 +#: resources/views/settings.php:239 +msgid "Connect with Datalumo" +msgstr "" + +#: resources/views/settings.php:179 +msgid "Using a self-hosted Datalumo?" +msgstr "" + +#: resources/views/settings.php:181 +#: resources/views/settings.php:213 msgid "Datalumo URL" msgstr "" -#: resources/views/settings.php -msgid "Leave as-is unless you were told otherwise." +#: resources/views/settings.php:189 +msgid "Manual setup" msgstr "" -#: resources/views/settings.php -msgid "Organisation ID" +#: resources/views/settings.php:202 +msgid "Organisation" msgstr "" -#: resources/views/settings.php -msgid "Find it next to your API keys in the Datalumo dashboard." +#: resources/views/settings.php:217 +msgid "Leave as-is unless you were told otherwise." msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:222 msgid "API token" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:225 +#: resources/views/settings.php:365 msgid "•••••••• (saved)" msgstr "" -#: resources/views/settings.php -msgid "Leave blank to re-test, or paste a new token to replace it." +#: resources/views/settings.php:226 +msgid "Paste a token from API keys. The organisation is inferred from the token." msgstr "" -#: resources/views/settings.php -msgid "Create a token under API keys in your Datalumo dashboard." -msgstr "" - -#: resources/views/settings.php +#: resources/views/settings.php:232 msgid "Connect & test" msgstr "" -#: resources/views/settings.php -msgid "Connected to %1$s (%2$d sources)." +#: resources/views/settings.php:234 +msgid "Save URL" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:255 msgid "Connect your account first." msgstr "" -#: resources/views/settings.php -msgid "Each sync pushes the chosen post types into one Datalumo source. Create a source of type \"API\" in your dashboard first." -msgstr "" - -#: resources/views/settings.php +#: resources/views/settings.php:267 msgid "Source" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:270 msgid "— choose a source —" msgstr "" -#: resources/views/settings.php +#. translators: %s: knowledge base name +#: resources/views/settings.php:294 +#, php-format +msgid "Knowledge base: %s" +msgstr "" + +#: resources/views/settings.php:303 msgid "Post types" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:317 msgid "Full sync" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:319 msgid "Sync now" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:320 msgid "Cancel" msgstr "" -#: resources/views/settings.php -msgid "Custom field and taxonomy mappings can be added per sync with the datalumo_page_payload filter for now." -msgstr "" - -#: resources/views/settings.php +#: resources/views/settings.php:330 msgid "Save syncs" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:335 msgid "Chat widget" msgstr "" -#: resources/views/settings.php -msgid "Show the floating chat on every page" -msgstr "" - -#: resources/views/settings.php +#: resources/views/settings.php:344 +#: resources/views/settings.php:374 +#: resources/views/settings.php:395 msgid "Widget key" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:348 msgid "Copy it from the widget editor (\"For developers\"). Add this site to the widget's websites list." msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:352 msgid "Visitor identity" msgstr "" -#: resources/views/settings.php -msgid "Let logged-in users continue their conversations across visits" +#: resources/views/settings.php:356 +msgid "Pass a signed id for logged-in users to the chat widget" msgstr "" -#: resources/views/settings.php -msgid "Identity signing secret" +#: resources/views/settings.php:358 +msgid "Optional, for custom implementations. The plugin does not resume conversations on its own." msgstr "" -#: resources/views/settings.php -msgid "From the same \"For developers\" section. Never exposed to the browser — used to sign user ids server-side." +#: resources/views/settings.php:362 +msgid "Identity signing secret" msgstr "" -#: resources/views/settings.php -msgid "Tip: use the [datalumo_chat] shortcode to embed the chat inline on a page." +#: resources/views/settings.php:366 +msgid "From the same \"For developers\" section. Never exposed to the browser. Used to sign user ids server-side." msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:378 msgid "A search-type widget key. Place the box anywhere with the [datalumo_search] shortcode." msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:390 msgid "Serve WordPress search results from Datalumo (falls back to native search on any error)" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:399 msgid "A search-type widget key; this site must be in its websites list." msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:403 msgid "Limit to post types" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:412 msgid "Leave all unchecked to enhance every search." msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:416 msgid "AI summary" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:420 msgid "Show a streamed AI answer above the results" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:425 msgid "Summary placement" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:428 msgid "auto" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:431 msgid "Before results" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:432 msgid "After results" msgstr "" -#: resources/views/settings.php +#: resources/views/settings.php:434 msgid "Optional CSS selector of the results container; detected automatically when empty." msgstr "" +#: resources/views/settings.php:447 +msgid "Need help?" +msgstr "" + +#: resources/views/settings.php:449 +msgid "Ask a question" +msgstr "" + +#: resources/views/settings.php:450 +msgid "How do I sync products?" +msgstr "" + +#: resources/views/settings.php:452 +msgid "Start chat" +msgstr "" + +#: resources/views/settings.php:461 +msgid "WordPress plugin docs" +msgstr "" + +#: src/Admin/Ajax.php:48 +msgid "An API token is required." +msgstr "" + +#: src/Admin/Ajax.php:52 +msgid "That looks like a widget key. Paste an API token from API keys instead." +msgstr "" + +#: src/Admin/Ajax.php:56 +msgid "That looks like a widget secret. Paste an API token from API keys instead." +msgstr "" + +#: src/Admin/Ajax.php:127 +#: src/Admin/SettingsPage.php:133 +#: src/Connect/Grant.php:270 +msgid "Not allowed." +msgstr "" + +#. translators: 1: organisation name, 2: number of sources +#: src/Admin/SettingsPage.php:51 +#, php-format +msgid "Connected to %1$s — %2$d source(s) available." +msgstr "" + +#: src/Admin/SettingsPage.php:52 +msgid "Connection failed:" +msgstr "" + +#: src/Admin/SettingsPage.php:53 +msgid "Starting sync…" +msgstr "" + +#. translators: 1: number of posts already synced, 2: total posts +#: src/Admin/SettingsPage.php:55 +#, php-format +msgid "%1$d of %2$d posts synced…" +msgstr "" + +#: src/Admin/SettingsPage.php:56 +msgid "Sync complete." +msgstr "" + +#: src/Api/Client.php:120 +msgid "Invalid widget key — copy it from the widget editor." +msgstr "" + +#. translators: %d: HTTP status code +#: src/Api/Client.php:163 +#, php-format +msgid "Datalumo request failed (%d)." +msgstr "" + +#: src/Integration/AddToCart.php:44 +#: src/Integration/AddToCart.php:99 +msgid "No product was specified." +msgstr "" + +#: src/Integration/AddToCart.php:45 +#: src/Integration/AddToCart.php:266 +#: src/Integration/AddToCart.php:820 +msgid "Could not add that to the cart." +msgstr "" + +#: src/Integration/AddToCart.php:211 +msgid "WooCommerce is not available." +msgstr "" + +#: src/Integration/AddToCart.php:220 +#: src/Integration/AddToCart.php:240 +msgid "That product could not be found." +msgstr "" + +#: src/Integration/AddToCart.php:247 +msgid "That product cannot be purchased." +msgstr "" + +#. translators: %s: product name +#: src/Integration/AddToCart.php:289 +#, php-format +msgid "%s added to your cart." +msgstr "" + +#: src/Integration/AddToCart.php:290 +msgid "Added to your cart." +msgstr "" + +#: src/Integration/AddToCart.php:332 +#: src/Integration/AddToCart.php:359 +msgid "This product needs a variation." +msgstr "" + +#: src/Integration/AddToCart.php:367 +msgid "This product has many options. Open the product to pick one." +msgstr "" + +#. translators: %s: product attribute name, such as colour or size +#: src/Integration/AddToCart.php:376 +#: src/Integration/AddToCart.php:525 +#, php-format +msgid "Which %s?" +msgstr "" + +#: src/Integration/HostNavigation.php:44 +msgid "Could not open that page." +msgstr "" + +#: src/Integration/HostNavigation.php:45 +#: src/Integration/HostNavigation.php:66 +msgid "No page was specified." +msgstr "" + +#: src/Integration/HostNavigation.php:46 +msgid "The cart is not available." +msgstr "" + +#: src/Integration/HostNavigation.php:47 +msgid "Checkout is not available." +msgstr "" + +#: src/Integration/HostNavigation.php:60 +msgid "Unknown action." +msgstr "" + +#: src/Plugin.php:88 +msgid "Datalumo is almost ready — connect your account to start syncing content." +msgstr "" + +#: src/Plugin.php:90 +msgid "Open settings" +msgstr "" + +#: src/Search/Summary.php:72 +msgid "AI Summary" +msgstr "" + +#: src/Search/Summary.php:73 +msgid "Show more" +msgstr "" + +#: src/Search/Summary.php:74 +msgid "Show less" +msgstr "" + +#: src/Search/Summary.php:75 +msgid "Summarising the best results…" +msgstr "" + +#: src/Sync/BulkSync.php:48 +msgid "Unknown sync configuration." +msgstr "" + +#. translators: 1: item offset, 2: error message +#: src/Sync/BulkSync.php:186 +#, php-format +msgid "Sync stopped at item %1$d: %2$s" +msgstr "" + +#. translators: 1: item offset, 2: attempt count, 3: error message +#: src/Sync/BulkSync.php:199 +#, php-format +msgid "Sync stopped at item %1$d after %2$d attempts: %3$s" +msgstr "" diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..5434d58 --- /dev/null +++ b/license.txt @@ -0,0 +1,18 @@ +Datalumo WordPress Plugin +Copyright (C) 2026 Jeffrey van Rossum + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . + +License: GPL-2.0-or-later +License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/readme.txt b/readme.txt index 53b3ac3..e585703 100644 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,10 @@ === Datalumo === -Contributors: datalumo, jeffreyvr +Contributors: jeffreyvr Tags: search, ai, chatbot, rag -Requires at least: 6.0 +Requires at least: 6.8 Tested up to: 7.1 Requires PHP: 8.1 -Stable tag: 0.2.0 +Stable tag: 1.0.0 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -18,14 +18,13 @@ Datalumo keeps a searchable, AI-ready copy of your WordPress content and gives y * **Chat widget** — a floating assistant that answers from your content, with sources. * **Search box** — drop-in search via the `[datalumo_search]` shortcode. * **Enhanced search** — serve WordPress' native search results from Datalumo's ranking, with an optional streamed AI summary above the results. Falls back to native search automatically. -* **Visitor identity** — logged-in users can continue their chat conversations across visits, verified with a server-side signature. -* **Chat page actions**: the official plugin listens for confirmed chat events (`add_to_cart`, `view_cart`, `open_checkout`, `open_page`). Add those actions in Datalumo under Reply → What it can do. +* **Chat actions** — add to cart, view cart, checkout, and open a page. Add them in Datalumo under Reply → What it can do. -This plugin is an interface to a [Datalumo](https://datalumo.app) instance. You need a Datalumo account (or a self-hosted instance). Connect with Datalumo from Settings, or use Manual setup to paste an API token. Nothing is sent until an administrator connects the site and turns a feature on. +Connect with Datalumo from Settings, or use Manual setup to paste an API token. = External services = -The plugin talks to the Datalumo instance you configure. The default host is `https://datalumo.app`; you can point it at your own instance instead. +The plugin talks to the Datalumo service. * Service: [https://datalumo.app](https://datalumo.app) * Documentation: [https://datalumo.app/docs](https://datalumo.app/docs) @@ -34,11 +33,13 @@ The plugin talks to the Datalumo instance you configure. The default host is `ht When a feature is enabled, the plugin may: -* Load the widget script from `{your Datalumo URL}/widget/v1/datalumo.js` (chat, search box, AI summary, and click tracking). -* Send published post content to the Datalumo API so it can be indexed (title, HTML, permalink, featured image URL, author display name, categories, tags, dates, and any custom field mappings you add). WooCommerce products also send short description, product categories and tags, visible attributes, SKU, and the product image when there is no featured image. -* Send visitor search queries to Datalumo when enhanced search is on. +* Load the widget script from `https://datalumo.app/widget/v1/datalumo.js` (chat, search box, AI summary, and click tracking). +* Send published content to the Datalumo API: title, HTML, permalink, featured image URL, author display name, categories, tags, dates, and any custom field mappings. WooCommerce products also send short description, product categories and tags, visible attributes, SKU, and the product image when there is no featured image. +* On Connect with Datalumo, send this site's host and URL so the grant can return to this admin. +* Send the current page id as chat context, plus product id and SKU on product pages. +* Send visitor search queries when enhanced search is on. * Send result-click events (URL, rank, search session) when enhanced search is on. -* Send a signed visitor id (`wp-user-{id}` HMAC) when visitor identity is enabled on the Chatbot tab. +* Send a signed visitor id (`wp-user-{id}` HMAC) if you enable identity on the Chatbot tab. Optional, for custom implementations. The plugin does not resume conversations. = Development = @@ -57,27 +58,27 @@ Production dependencies are installed with `composer install --no-dev`. = Do I need a Datalumo account? = -Yes. The plugin does not search or chat on its own. It connects WordPress to a Datalumo instance — the hosted service at datalumo.app or one you run yourself. +Yes. The plugin does not search or chat on its own. It connects WordPress to Datalumo. = What data is sent to Datalumo? = -Only after you connect and enable a feature. Content sync sends the published posts you choose. Enhanced search sends visitor queries and optional result-click analytics. The chat and search widgets load Datalumo's script and talk to that instance. Visitor identity is off unless you turn it on. - -= Can I use a self-hosted Datalumo instance? = - -Yes. On the Connection tab, open Using a self-hosted Datalumo? and set the URL before you connect. Manual setup has the same field. +Only after you connect and turn a feature on. The list is under External services above. = Can the chatbot add products to a WooCommerce cart? = -Yes, if WooCommerce is active. In Datalumo, add the WordPress Add to cart action (event add_to_cart, field product_id). The visitor confirms in chat. Variable products send colour, then that colour's sizes, as nested choice steps (up to 8 options per step); more than that opens the product page. +Yes, if WooCommerce is active. In Datalumo, add the WordPress Add to cart action. The visitor confirms in chat. Variable products can ask for options first. = What other chat actions does the plugin handle? = View cart and Open checkout (WooCommerce), and Open this page (a post or page by id, slug, or same-site URL). Add them from Reply → Plugin actions → WordPress. - == Changelog == += 1.0.0 = +* First WordPress.org release. +* Translations are supplied by translate.wordpress.org; locale files are not bundled. +* Jetpack Autoloader 6 and Action Scheduler 4.1. + = 0.2.0 = * Featured images sync with the page. WooCommerce products use the product image when the post has none. Removing the image and syncing again clears it in Datalumo. @@ -113,6 +114,5 @@ View cart and Open checkout (WooCommerce), and Open this page (a post or page by = 0.0.1 = * Initial public release. -* Content sync, chat/search widgets, enhanced search, and visitor identity. -* GitHub auto-updates via Plugin Update Checker (removed ahead of the WordPress.org release). +* Content sync, chat/search widgets, and enhanced search. * Dutch and Chinese translations. diff --git a/resources/js/setup-notice.js b/resources/js/setup-notice.js new file mode 100644 index 0000000..48434b6 --- /dev/null +++ b/resources/js/setup-notice.js @@ -0,0 +1,28 @@ +/** + * Persist dismissal of the dashboard setup notice. + */ +(function () { + 'use strict'; + + var config = window.datalumoSetupNotice; + + if (! config) { + return; + } + + document.addEventListener('click', function (event) { + var dismiss = event.target && event.target.closest + ? event.target.closest('.datalumo-setup-notice .notice-dismiss') + : null; + + if (! dismiss) { + return; + } + + var body = new FormData(); + body.append('action', 'datalumo_dismiss_setup_notice'); + body.append('_ajax_nonce', config.nonce); + + fetch(config.ajaxUrl, { method: 'POST', body: body, credentials: 'same-origin' }); + }); +})(); diff --git a/resources/views/settings.php b/resources/views/settings.php index a0047d5..c859022 100644 --- a/resources/views/settings.php +++ b/resources/views/settings.php @@ -77,7 +77,7 @@ class="nav-tab __('Custom fields, filters, and sync details.', 'datalumo'), - 'chatbot' => __('Shortcodes, visitor identity, and chat page actions.', 'datalumo'), + 'chatbot' => __('Shortcodes and chat page actions.', 'datalumo'), 'search-box', 'enhanced-search' => __('Search box shortcode and enhanced search.', 'datalumo'), default => __('Connect, sync, and widgets.', 'datalumo'), }; @@ -353,8 +353,9 @@ class="nav-tab +

diff --git a/src/Admin/Ajax.php b/src/Admin/Ajax.php index 4befffb..e7e483a 100644 --- a/src/Admin/Ajax.php +++ b/src/Admin/Ajax.php @@ -18,6 +18,7 @@ public function register(): void add_action('wp_ajax_datalumo_sync_start', [$this, 'syncStart']); add_action('wp_ajax_datalumo_sync_cancel', [$this, 'syncCancel']); add_action('wp_ajax_datalumo_sync_status', [$this, 'syncStatus']); + add_action('wp_ajax_datalumo_dismiss_setup_notice', [$this, 'dismissSetupNotice']); } /** @@ -30,14 +31,16 @@ public function register(): void */ public function connect(): void { - $this->authorise(); + check_ajax_referer(self::NONCE); + + if (! current_user_can('manage_options')) { + wp_send_json_error(['message' => __('Not allowed.', 'datalumo')], 403); + } - // phpcs:disable WordPress.Security.NonceVerification -- verified in authorise(). $token = sanitize_text_field(wp_unslash((string) ($_POST['token'] ?? ''))); $apiUrl = array_key_exists('api_url', $_POST) ? (esc_url_raw(wp_unslash((string) $_POST['api_url'])) ?: 'https://datalumo.app') : null; - // phpcs:enable WordPress.Security.NonceVerification if ($token === '') { $token = (string) Options::get('api_token', ''); @@ -103,9 +106,23 @@ public function syncStatus(): void wp_send_json_success((new BulkSync())->status($this->syncId())); } + public function dismissSetupNotice(): void + { + $this->authorise(); + + update_user_meta(get_current_user_id(), 'datalumo_hide_setup_notice', '1'); + + wp_send_json_success(); + } + private function syncId(): string { - // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified in authorise(). + check_ajax_referer(self::NONCE); + + if (! current_user_can('manage_options')) { + wp_send_json_error(['message' => __('Not allowed.', 'datalumo')], 403); + } + return sanitize_text_field(wp_unslash((string) ($_POST['sync_id'] ?? ''))); } diff --git a/src/Admin/SettingsPage.php b/src/Admin/SettingsPage.php index efeab38..804cdd6 100644 --- a/src/Admin/SettingsPage.php +++ b/src/Admin/SettingsPage.php @@ -117,6 +117,10 @@ private function refreshSources(): void private function requestedTab(): string { + if (! current_user_can('manage_options')) { + return 'connection'; + } + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- settings tab is a navigation query arg. $tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : ''; diff --git a/src/Connect/Grant.php b/src/Connect/Grant.php index f776426..e0ea39e 100644 --- a/src/Connect/Grant.php +++ b/src/Connect/Grant.php @@ -51,6 +51,7 @@ public static function start(): void // Off-site on purpose. wp_safe_redirect would rewrite dl.test / // datalumo.app back to this site's admin. + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Datalumo grant URL is off-site. wp_redirect($url); exit; } @@ -60,8 +61,10 @@ public static function callback(): void self::authorise(); $expected = (string) get_transient(self::TRANSIENT.'_'.get_current_user_id()); + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- OAuth IdP redirect cannot post a WP nonce; CSRF is the hashed state vs the per-user transient. $state = sanitize_text_field(wp_unslash((string) ($_GET['state'] ?? ''))); $code = sanitize_text_field(wp_unslash((string) ($_GET['code'] ?? ''))); + // phpcs:enable WordPress.Security.NonceVerification.Recommended delete_transient(self::TRANSIENT.'_'.get_current_user_id()); diff --git a/src/Integration/AddToCart.php b/src/Integration/AddToCart.php index 12471a7..7b1adf8 100644 --- a/src/Integration/AddToCart.php +++ b/src/Integration/AddToCart.php @@ -285,6 +285,7 @@ public function add(int $productId, int $quantity, int $variationId = 0, array $ return [ 'ok' => true, 'message' => $name !== '' + /* translators: %s: product name */ ? sprintf(__('%s added to your cart.', 'datalumo'), $name) : __('Added to your cart.', 'datalumo'), 'fragments' => $this->cartFragments(), @@ -371,6 +372,7 @@ private function elicitAttributes(object $product, array $rows, array $keys, arr return [ 'ok' => false, 'status' => 'choices', + /* translators: %s: product attribute name, such as colour or size */ 'message' => sprintf(__('Which %s?', 'datalumo'), self::attributeLabel($keys[0])), 'layout' => 'options', 'choices' => $choices, @@ -519,6 +521,7 @@ public static function choicesTree(array $rows, array $keys, array $selected, ob return $options; } + /* translators: %s: product attribute name, such as colour or size */ $nextMessage = sprintf(__('Which %s?', 'datalumo'), self::attributeLabel($rest[0])); $tree = []; @@ -760,9 +763,11 @@ private static function unprefixedAttributeKey(string $key): string */ private function requestPayload(): array { - // phpcs:disable WordPress.Security.NonceVerification.Missing -- verified in handle(). + check_ajax_referer(self::NONCE); + + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON decoded below. $raw = wp_unslash($_POST['payload'] ?? ''); - // phpcs:enable WordPress.Security.NonceVerification.Missing + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if (is_string($raw) && $raw !== '') { $decoded = json_decode($raw, true); @@ -788,6 +793,7 @@ private function cartFragments(): array woocommerce_mini_cart(); $miniCart = (string) ob_get_clean(); + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WooCommerce cart fragment API. $fragments = apply_filters('woocommerce_add_to_cart_fragments', [ 'div.widget_shopping_cart_content' => '
' . $miniCart . '
', ]); diff --git a/src/Integration/HostNavigation.php b/src/Integration/HostNavigation.php index 8f918b8..bbcf966 100644 --- a/src/Integration/HostNavigation.php +++ b/src/Integration/HostNavigation.php @@ -127,7 +127,7 @@ public function publishedUrlFromPayload(array $payload): ?string public function isSameSiteUrl(string $url): bool { - $parts = parse_url($url); + $parts = wp_parse_url($url); if (! is_array($parts) || ! isset($parts['scheme'], $parts['host'])) { return false; @@ -172,7 +172,7 @@ public function checkoutUrl(): string private function homeHost(): string { $home = function_exists('home_url') ? home_url('/') : ''; - $host = parse_url($home, PHP_URL_HOST); + $host = wp_parse_url($home, PHP_URL_HOST); return is_string($host) ? strtolower($host) : ''; } @@ -210,7 +210,11 @@ private function positiveId(mixed $value): ?int */ private function requestPayload(): array { + check_ajax_referer(self::NONCE); + + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON decoded below. $raw = isset($_POST['payload']) ? wp_unslash($_POST['payload']) : ''; + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if (! is_string($raw) || $raw === '') { return []; diff --git a/src/Plugin.php b/src/Plugin.php index 72eb355..c3adc3a 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -9,7 +9,7 @@ use Datalumo\Wp\Search\ClickTracking; use Datalumo\Wp\Search\Interceptor; use Datalumo\Wp\Search\Summary; -use Datalumo\Wp\Support\UpdateChecker; +use Datalumo\Wp\Support\Assets; use Datalumo\Wp\Sync\BulkSync; use Datalumo\Wp\Sync\ContentSync; @@ -32,12 +32,7 @@ public function boot(): void $this->booted = true; - // Outside admin-only hooks so WP-CLI and management tools see updates too. - UpdateChecker::register(); - add_action('plugins_loaded', function (): void { - load_plugin_textdomain('datalumo', false, dirname(plugin_basename(DATALUMO_FILE)) . '/languages'); - // Deferred — WooCommerce may load after us. if (Integration\WooCommerce::isActive()) { (new Integration\WooCommerce())->register(); @@ -58,29 +53,59 @@ public function boot(): void (new Embed())->register(); (new Integration\HostNavigation())->register(); + add_action('admin_enqueue_scripts', [$this, 'enqueueSetupNotice']); add_action('admin_notices', [$this, 'configurationNotice']); } - public function configurationNotice(): void + public function enqueueSetupNotice(string $hook): void { - if (! current_user_can('manage_options') || Support\Options::get('api_token')) { + if ($hook !== 'index.php' || ! $this->shouldShowSetupNotice()) { return; } - $screen = function_exists('get_current_screen') ? get_current_screen() : null; + wp_enqueue_script( + 'datalumo-setup-notice', + DATALUMO_URL . 'resources/js/setup-notice.js', + [], + Assets::version(), + ['in_footer' => true], + ); - if ($screen !== null && $screen->id === 'settings_page_datalumo') { + wp_localize_script('datalumo-setup-notice', 'datalumoSetupNotice', [ + 'ajaxUrl' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce(Ajax::NONCE), + ]); + } + + public function configurationNotice(): void + { + if (! $this->shouldShowSetupNotice()) { return; } printf( - '

%s %s

', + '

%s %s

', esc_html__('Datalumo is almost ready — connect your account to start syncing content.', 'datalumo'), esc_url(admin_url('options-general.php?page=datalumo')), esc_html__('Open settings', 'datalumo'), ); } + private function shouldShowSetupNotice(): bool + { + if (! is_admin() || ! current_user_can('manage_options') || Support\Options::get('api_token')) { + return false; + } + + if (get_user_meta(get_current_user_id(), 'datalumo_hide_setup_notice', true)) { + return false; + } + + $screen = function_exists('get_current_screen') ? get_current_screen() : null; + + return $screen !== null && $screen->id === 'dashboard'; + } + public static function deactivate(): void { if (! function_exists('as_unschedule_all_actions')) { diff --git a/src/Search/Interceptor.php b/src/Search/Interceptor.php index 7500ba9..1967616 100644 --- a/src/Search/Interceptor.php +++ b/src/Search/Interceptor.php @@ -58,9 +58,7 @@ public function intercept(?array $posts, WP_Query $query): ?array (string) $query->get('s'), min(self::MAX_RESULTS, (int) apply_filters('datalumo_search_max_results', self::MAX_RESULTS)), ); - } catch (ApiException $e) { - error_log(sprintf('[Datalumo] search failed (%d): %s — falling back to native.', $e->status, $e->getMessage())); - + } catch (ApiException) { return null; } @@ -126,16 +124,17 @@ private function resolvePool(array $ids, WP_Query $query): array 'post_status' => 'publish', 'posts_per_page' => count($ids), 'ignore_sticky_posts' => true, - 'suppress_filters' => true, ]; $metaQuery = $query->get('meta_query'); if (is_array($metaQuery) && $metaQuery !== []) { + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- copied from the main search query. $args['meta_query'] = $metaQuery; } if (! empty($query->tax_query->queries)) { + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- copied from the main search query. $args['tax_query'] = $query->tax_query->queries; } @@ -144,8 +143,8 @@ private function resolvePool(array $ids, WP_Query $query): array $posts = get_posts($args); // orderby => post__in isn't reliable: other plugins (Woo catalog - // ordering) hook pre_get_posts and reset the order, which - // suppress_filters doesn't prevent — so enforce the ranking in PHP. + // ordering) hook pre_get_posts and reset the order, so enforce + // the ranking in PHP. $rank = array_flip(array_values($ids)); usort($posts, fn ($a, $b) => ($rank[$a->ID] ?? PHP_INT_MAX) <=> ($rank[$b->ID] ?? PHP_INT_MAX)); @@ -162,6 +161,7 @@ private function resolvePool(array $ids, WP_Query $query): array */ private function reorder(array $pool, WP_Query $query): array { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public search sort query arg. $orderby = isset($_GET['orderby']) ? sanitize_key((string) wp_unslash($_GET['orderby'])) : ''; if ($orderby === '' || $orderby === 'relevance' || $pool === []) { diff --git a/src/Support/UpdateChecker.php b/src/Support/UpdateChecker.php deleted file mode 100644 index d2d4a76..0000000 --- a/src/Support/UpdateChecker.php +++ /dev/null @@ -1,34 +0,0 @@ -getVcsApi(); - - if ($api !== null && method_exists($api, 'enableReleaseAssets')) { - $api->enableReleaseAssets('/\.zip($|[?&#])/i'); - } - } -} diff --git a/src/Sync/BulkSync.php b/src/Sync/BulkSync.php index f58962f..8c2e79a 100644 --- a/src/Sync/BulkSync.php +++ b/src/Sync/BulkSync.php @@ -117,8 +117,7 @@ public function processBatch(string $syncId, int $offset, string $run = ''): voi if ($this->isCurrentRun($syncId, $run)) { try { $this->client()->indexSource($sync['source_id']); - } catch (ApiException $e) { - error_log(sprintf('[Datalumo] index kick for %s failed (%d): %s', $sync['source_id'], $e->status, $e->getMessage())); + } catch (ApiException) { } } @@ -142,8 +141,6 @@ public function processBatch(string $syncId, int $offset, string $run = ''): voi $this->client()->pushBatch($sync['source_id'], $pages); } } catch (ApiException $e) { - error_log(sprintf('[Datalumo] bulk batch at %d failed (%d): %s', $offset, $e->status, $e->getMessage())); - $this->handleBatchError($syncId, $run, $offset, $e); return; diff --git a/src/Sync/ContentSync.php b/src/Sync/ContentSync.php index 6e4269a..830dd4f 100644 --- a/src/Sync/ContentSync.php +++ b/src/Sync/ContentSync.php @@ -130,8 +130,6 @@ private function schedule(string $hook, array $args): void private function retryLater(string $hook, array $args, ApiException $e): void { - error_log(sprintf('[Datalumo] %s failed (%d): %s', $hook, $e->status, $e->getMessage())); - // Auth failures and a full plan won't heal on retry — don't reschedule. if ($e->isAuthentication() || $e->isQuota() || ! function_exists('as_schedule_single_action')) { return; diff --git a/tests/Unit/HostNavigationTest.php b/tests/Unit/HostNavigationTest.php index a4b9b6a..9e6df3d 100644 --- a/tests/Unit/HostNavigationTest.php +++ b/tests/Unit/HostNavigationTest.php @@ -15,6 +15,7 @@ function hostNav(): HostNavigation { Functions\when('home_url')->justReturn('https://shop.test/'); + Functions\when('wp_parse_url')->alias(fn (string $url, int $component = -1) => parse_url($url, $component)); return new HostNavigation(); } diff --git a/tests/Unit/IndexPhpTest.php b/tests/Unit/IndexPhpTest.php index 18dcb4e..bc1c36c 100644 --- a/tests/Unit/IndexPhpTest.php +++ b/tests/Unit/IndexPhpTest.php @@ -2,7 +2,7 @@ it('has a silence is golden index.php in every first-party folder', function () { $root = dirname(__DIR__, 2); - $skip = ['.git', '.github', '.slimm', '.phpunit.cache', 'vendor']; + $skip = ['.git', '.github', '.slimm', '.phpunit.cache', '.wordpress-org', 'vendor', 'build']; $iterator = new RecursiveIteratorIterator( new RecursiveCallbackFilterIterator( new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS), diff --git a/uninstall.php b/uninstall.php index 75a68d2..af2017f 100644 --- a/uninstall.php +++ b/uninstall.php @@ -6,9 +6,10 @@ delete_option('datalumo_settings'); delete_option('datalumo_sync_state'); +delete_metadata('user', 0, 'datalumo_hide_setup_notice', '', true); if (function_exists('as_unschedule_all_actions')) { - foreach (['datalumo_push_page', 'datalumo_push_batch', 'datalumo_delete_page'] as $hook) { - as_unschedule_all_actions($hook, [], 'datalumo'); + foreach (['datalumo_push_page', 'datalumo_push_batch', 'datalumo_delete_page'] as $datalumo_hook) { + as_unschedule_all_actions($datalumo_hook, [], 'datalumo'); } }