diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000000..e8735258b5 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,10 @@ +last 2 Chrome versions +last 2 Firefox versions +last 2 Safari versions +last 2 Edge versions +last 2 Opera versions +last 2 iOS versions +last 1 Android version +last 1 ChromeAndroid version +last 1 FirefoxAndroid version +> 1%, not op_mini all, not ie <= 11, not ie_mob <= 11 diff --git a/.distfiles b/.distfiles index 7afd5c18b1..ef7c085d5d 100644 --- a/.distfiles +++ b/.distfiles @@ -1,10 +1,6 @@ /lang/*.mo /lang/*.pot /lang/*.txt -/node_modules/clipboard/dist/clipboard.min.js -/node_modules/intro.js/*.css -/node_modules/intro.js/*.js -/node_modules/mt-a11y-dialog/*.min.js /README.md /src/admin-views/**/* /src/Common/**/* @@ -19,6 +15,8 @@ /src/resources/js/app/modules.min.js /src/resources/js/app/vendor.min.js /src/resources/js/utils/*.js +/src/resources/vendor/*.min.js +/src/resources/vendor/*.min.css /src/Tribe/**/* /src/views/**/* /tribe-autoload.php @@ -28,6 +26,7 @@ /vendor/datatables/**/*.png /vendor/datatables/datatables*.css /vendor/datatables/datatables*.js +/vendor/dayjs/**/*.min.js /vendor/firebase/php-jwt/src/*.php /vendor/handlebars/*.js /vendor/jquery-tribe-timepicker/**/*.css diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 83d3f42aec..6b0c596b36 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 1 submodules: recursive @@ -20,9 +20,14 @@ jobs: # ------------------------------------------------------------------------------ - name: Check for .nvmrc file id: check-nvmrc - run: echo "::set-output name=exists::$(test -f ${{ github.workspace }}/.nvmrc && echo 'true' || echo 'false')" + run: | + if [ -f "${{ github.workspace }}/.nvmrc" ]; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "exists=false" >> $GITHUB_OUTPUT + fi - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 if: steps.check-nvmrc.outputs.exists == 'true' with: node-version-file: '.nvmrc' diff --git a/.github/workflows/process-changelog.yml b/.github/workflows/process-changelog.yml index e7e6b93a44..ca6777130d 100644 --- a/.github/workflows/process-changelog.yml +++ b/.github/workflows/process-changelog.yml @@ -121,7 +121,7 @@ jobs: uses: peter-evans/create-pull-request@v6 id: cpr with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GHA_BOT_TOKEN_MANAGER }} base: ${{ github.ref }} branch: "task/process-changelog/${{ steps.format_date.outputs.RELEASE_DATE }}/${{ steps.figure_out_version.outputs.RELEASE_VERSION }}/${{ steps.vars.outputs.sha_short }}" title: "Process changelog for ${{ github.ref }}" diff --git a/.github/workflows/sync-translations.yml b/.github/workflows/sync-translations.yml new file mode 100644 index 0000000000..3f2a0f970d --- /dev/null +++ b/.github/workflows/sync-translations.yml @@ -0,0 +1,96 @@ +name: šŸ” Sync Translations + +on: + workflow_dispatch: # Allows you to run this workflow manually from the Actions tab + workflow_call: + secrets: + TRANSLATIONS_DEPLOY_HOST: + required: true + TRANSLATIONS_DEPLOY_USER: + required: true + TRANSLATIONS_DEPLOY_SSH_KEY: + required: true + TRANSLATIONS_DEPLOY_POT_LOCATION: + required: true + +jobs: + sync-translations: + name: šŸ” Sync Translations + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + + - name: Set Variables + id: vars + run: | + branch_name="${{ github.head_ref || github.ref_name }}" + should_glotpress=0 + if [[ "$branch_name" =~ ^release/.* || "$branch_name" == "master" || "$branch_name" == "main" ]]; then + should_glotpress=1 + fi + echo "should_glotpress=$should_glotpress" >> $GITHUB_OUTPUT + echo "sha_short=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_OUTPUT + + - name: Fetch Plugin Slug from .puprc + id: puprc + run: | + plugin_slug=$(jq '.i18n | map(select(.url | contains("translations.stellarwp.com")) | .slug) | first' .puprc) + echo "plugin_slug=$plugin_slug" >> $GITHUB_ENV + echo "plugin_slug=$plugin_slug" >> $GITHUB_OUTPUT + echo "## Plugin Slug: \`$plugin_slug\`" >> $GITHUB_STEP_SUMMARY + + - uses: the-events-calendar/actions/.github/actions/generate-pot@main + id: generate-pot + with: + plugin_path: ${{ github.workspace }} + pot_path: ${{github.workspace}}/lang/${{ steps.puprc.outputs.plugin_slug }}.pot + + - uses: the-events-calendar/actions/.github/actions/push-translations@main + id: push-translations + if: steps.vars.outputs.should_glotpress == '1' + with: + plugin_slug: ${{ steps.puprc.outputs.plugin_slug }} + pot_path: lang/${{ steps.puprc.outputs.plugin_slug }}.pot + env: + TRANSLATIONS_DEPLOY_HOST: ${{ secrets.TRANSLATIONS_DEPLOY_HOST }} + TRANSLATIONS_DEPLOY_USER: ${{ secrets.TRANSLATIONS_DEPLOY_USER }} + TRANSLATIONS_DEPLOY_SSH_KEY: ${{ secrets.TRANSLATIONS_DEPLOY_SSH_KEY }} + TRANSLATIONS_DEPLOY_POT_LOCATION: ${{ secrets.TRANSLATIONS_DEPLOY_POT_LOCATION }} + + - uses: the-events-calendar/actions/.github/actions/add-changelog@main + id: add-changelog-entry + if: steps.vars.outputs.should_glotpress == '1' + with: + file: "task-i18n-${{ steps.vars.outputs.sha_short }}" + significance: patch + type: language + entry: "${{ steps.push-translations.outputs.glotpress-result }}" + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + id: create-pull-request + with: + token: ${{ secrets.GHA_BOT_TOKEN_MANAGER }} + base: ${{ github.head_ref || github.ref_name }} + branch: "task/i18n-update-${{ github.head_ref || github.ref_name }}-with-${{ steps.vars.outputs.sha_short }}" + title: "Generate Pot for ${{ github.head_ref || github.ref_name }}" + assignees: ${{ github.actor }} + reviewers: the-events-calendar/engineers + body: | + This is an automated PR created by ${{ github.actor }}. + It was generated by [this GitHub Action](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}). + labels: "automation" + commit-message: | + :symbols: Updating .pot file and add language changelog for ${{ github.sha }} + + This is an automated PR created by ${{ github.actor }}. + Generated by: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Check outputs + if: ${{ steps.create-pull-request.outputs.pull-request-number }} + run: | + echo "## Pull Request" >> $GITHUB_STEP_SUMMARY + echo "* Number - ${{ steps.create-pull-request.outputs.pull-request-number }}" >> $GITHUB_STEP_SUMMARY + echo "* URL - [${{ steps.create-pull-request.outputs.pull-request-url }}](${{ steps.create-pull-request.outputs.pull-request-url }})" >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index c15600479a..a2832ecfab 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,14 @@ lang/*.mo # Local packaging files .pup-* +.pup-zip +pup.phar # Strauss bin/strauss.phar + +# GitHub Actions +DOCKER_ENV +docker_tag +output.log +Dockerfile-php-build diff --git a/.puprc b/.puprc index a4796f489c..93434e3be7 100644 --- a/.puprc +++ b/.puprc @@ -1,29 +1,33 @@ { "build": [ + "composer -- pup", "rm -rf vendor", "git checkout -- vendor", "composer install --no-dev", - "npm ci", + "npm ci --no-audit --no-fund", "npm run build" ], "build_dev": [ + "composer -- pup", "rm -rf vendor", "git checkout -- vendor", "composer install", - "npm ci", + "npm ci --no-audit --no-fund", "npm run build" ], "checks": { "tbd": { - "dirs": [ "src" ] + "dirs": [ + "src" + ] }, "version-conflict": {} }, "i18n": [ { + "slug": "tribe-common", "textdomain": "tribe-common", - "url": "https://translations.theeventscalendar.com", - "slug": "tribe-common" + "url": "https://translations.stellarwp.com/glotpress/" } ], "paths": { @@ -53,7 +57,9 @@ "regex": "(\"version\": ?\")([^\"]+)" } ], - "views": [ "src/views" ] + "views": [ + "src/views" + ] }, "zip_name": "tribe-common" } diff --git a/bin/clean-composer.sh b/bin/clean-composer.sh deleted file mode 100644 index d9769194d7..0000000000 --- a/bin/clean-composer.sh +++ /dev/null @@ -1,5 +0,0 @@ -files=("vendor/composer/autoload_files.php" "vendor/composer/autoload_static.php") -search="symfony/deprecation-contracts/function.php" -for file in "${files[@]}"; do - grep -v "$search" "$file" > temp && mv temp "$file" -done diff --git a/changelog.md b/changelog.md index 8aa5375707..25d3715ca3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,33 @@ # Changelog +### [6.4.1] 2024-12-17 + +* Feature - Add an abstract admin page to start consolidating how we do admin pages. See the "First Time Setup" page (onboarding wizard) for an example. [TEC-5294] +* Tweak - Ensure we are not loading any assets from node_modules. Include anything we need as a 3rd party code in our plugin. [TCMN-175] +* Fix - Cast `$block` argument to string to avoid PHP 8+ deprecation notice when non string (or array) variables are passed as the 3rd argument of `preg_replace`. +* Fix - Correctly identify licenses using uplink, like Event Tickets Plus. [n/a] +* Fix - Ensure that number_format is used with a float value to prevent issues with PHP 8.0+. [ETP-962] +* Fix - Ensure we get an object to test for subnav. Pass the object to class filter for more context. [n/a] +* Fix - Prevent fatal on ET integration page when used with Events Pro but without Event Tickets Plus. [TCMN-174] +* Deprecated - Integrations Tab registration in Event Ticket Settings from common. These will be registered from Event Tickets Plus only instead. [TCMN-174] +* Language - 0 new strings added, 33 updated, 1 fuzzied, and 0 obsoleted. + +### [6.4.0] 2024-12-05 + +* Feature - In-App Notifications system. [TEC-5165] +* Tweak - Added filters: `tec_common_ian_opt_in`, `tec_common_ian_conditional_php`, `tec_common_ian_conditional_wp`, `tec_common_ian_allowed_pages`, `tec_common_ian_show_icon`, `tec_common_ian_setting_optin_tooltip`, `tec_common_ian_api_url`, `tec_common_ian_slugs`, `tec_common_ian_render` +* Tweak - Added actions: `tec_common_ian_loaded` +* Language - 22 new strings added, 15 updated, 1 fuzzied, and 0 obsoleted. + +### [6.3.2] 2024-11-19 + +* Feature - Implemented the core Help Hub logic, providing a flexible framework for managing support integrations, resource templates, and plugin-specific customization. +* Feature - Introduced Asset interface which accounts for symlinks, while still provides a fluent api. [SL-246] +* Feature - Update stellarwp/assets to version 1.4.2. [SL-246] +* Tweak - Added actions: `tec_help_hub_before_render`, `tec_help_hub_after_render`, `tec_help_hub_before_iframe_render`, `tec_help_hub_after_iframe_render`, `tec_help_hub_registered`. +* Tweak - Added filters: `tec_help_hub_resource_sections_{$data_class_name}`, `tec_help_hub_resource_sections`, `tec_help_hub_body_classes`. +* Language - 0 new strings added, 0 updated, 1 fuzzied, and 0 obsoleted. + ### [6.3.1] 2024-11-04 * Fix - Prevent new Settings pages to over sanitize textarea fields, thus removing HTML from before/after in the Events UI. [TEC-5283] diff --git a/composer.json b/composer.json index 7cacf605b9..0f044f7278 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "php": "7.4.0" }, "allow-plugins": { + "cweagans/composer-patches": true, "dealerdirect/phpcodesniffer-composer-installer": true, "phpstan/extension-installer": true } @@ -24,7 +25,7 @@ "firebase/php-jwt": "~6.3.0", "lucatume/di52": "^3.3.7", "monolog/monolog": "1.24.*", - "psr/container": "^1.0.0", + "psr/container": "1.1.1", "stellarwp/arrays": "1.2.2", "stellarwp/container-contract": "^1.0.4", "stellarwp/db": "^1.0.3", @@ -32,15 +33,18 @@ "stellarwp/models": "dev-main", "stellarwp/schema": "^1.1.3", "stellarwp/telemetry": "^2.3.1", - "stellarwp/assets": "^1.2.6", - "stellarwp/uplink": "2.2.1" + "stellarwp/assets": "1.4.2", + "stellarwp/admin-notices": "^1.1", + "stellarwp/uplink": "2.2.2" }, "require-dev": { "automattic/vipwpcs": "^3.0", "wp-cli/wp-cli": ">=2.0 <3.0.0", "bordoni/phpass": "0.3.5", "codeception/codeception": "^2.5.5", + "cweagans/composer-patches": "^1.7", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "fakerphp/faker": "^1.23", "lucatume/codeception-snapshot-assertions": "^0.2.4", "lucatume/function-mocker-le": "^1.0", "lucatume/wp-browser": "^3.0.14", @@ -48,8 +52,7 @@ "the-events-calendar/tec-testing-facilities": "dev-master", "wp-cli/checksum-command": "1.0.5", "wp-coding-standards/wpcs": "^3.0.0", - "automattic/jetpack-changelogger": "^4.2", - "fakerphp/faker": "^1.23" + "automattic/jetpack-changelogger": "^4.2" }, "autoload": { "psr-4": { @@ -70,19 +73,22 @@ "test -f ./bin/strauss.phar || curl -o bin/strauss.phar -L -C - https://github.com/BrianHenryIE/strauss/releases/download/0.19.4/strauss.phar", "vendor/stellarwp/installer/bin/set-domain domain=tribe-common", "@php -d display_errors=on bin/strauss.phar", - "@composer dump-autoload", - "@clean-strauss-static-function-autoload" + "@composer dump-autoload" ], "post-install-cmd": [ - "@strauss", - "@stellar-uplink" + "@stellar-uplink", + "@strauss" ], "post-update-cmd": [ - "@strauss", - "@stellar-uplink" + "@stellar-uplink", + "@strauss" ], "stellar-uplink": [ "vendor/bin/stellar-uplink domain=tribe-common" + ], + "pup": [ + "test -f ./bin/pup.phar || curl -o ./bin/pup.phar -L -C - https://github.com/stellarwp/pup/releases/download/1.3.7/pup.phar", + "@php bin/pup.phar" ] }, "extra": { @@ -106,13 +112,10 @@ "firebase/php-jwt", "psr/container", "stellarwp/assets", - "fakerphp/faker" + "stellarwp/admin-notices" ], "exclude_from_prefix": { - "file_patterns": [], - "namespaces": [ - "Faker" - ] + "file_patterns": [] }, "delete_vendor_files": true, "include_modified_date": false, @@ -139,6 +142,11 @@ "filename": "bin/ModifiedSemverVersioning.php" }, "changes-dir": "changelog" + }, + "patches": { + "fakerphp/faker": { + "Use prefixed Psr\\Container\\ContainerInterface": "tests/_patches/faker-use-prefixed-containerinterface.patch" + } } } } diff --git a/composer.lock b/composer.lock index 2c8d9e99c9..592be73345 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "debf13c4d057fe67d06acfa7ae9872b2", + "content-hash": "3a8885ed84fad9342daf3f204a3c48aa", "packages": [ { "name": "firebase/php-jwt", @@ -197,20 +197,20 @@ }, { "name": "psr/container", - "version": "1.1.2", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=7.2.0" }, "type": "library", "autoload": { @@ -239,9 +239,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/log", @@ -293,6 +293,59 @@ }, "time": "2021-05-03T11:20:27+00:00" }, + { + "name": "stellarwp/admin-notices", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/stellarwp/admin-notices.git", + "reference": "219e081167a347f80e7eca62d41eac06e5f58dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stellarwp/admin-notices/zipball/219e081167a347f80e7eca62d41eac06e5f58dc3", + "reference": "219e081167a347f80e7eca62d41eac06e5f58dc3", + "shasum": "" + }, + "require": { + "psr/container": "1.1.1" + }, + "require-dev": { + "codeception/module-asserts": "^1.0", + "codeception/module-cli": "^1.0", + "codeception/module-db": "^1.0", + "codeception/module-filesystem": "^1.0", + "codeception/module-phpbrowser": "^1.0", + "codeception/module-rest": "^1.0", + "codeception/module-webdriver": "^1.0", + "codeception/util-universalframework": "^1.0", + "lucatume/wp-browser": "^3.6.5", + "phpunit/phpunit": "^9.5", + "szepeviktor/phpstan-wordpress": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "StellarWP\\AdminNotices\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jason Adams", + "email": "jason.adams@stellarwp.com" + } + ], + "description": "A handy package for easily displaying admin notices in WordPress with simple to complex visibility conditions", + "support": { + "issues": "https://github.com/stellarwp/admin-notices/issues", + "source": "https://github.com/stellarwp/admin-notices/tree/1.2.2" + }, + "time": "2024-10-29T14:53:11+00:00" + }, { "name": "stellarwp/arrays", "version": "1.2.2", @@ -352,16 +405,16 @@ }, { "name": "stellarwp/assets", - "version": "1.2.9", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/stellarwp/assets.git", - "reference": "1cd83c9799ff1e5c4b7f3c7d301f3b8b8b2d6ee4" + "reference": "6c26d088dcf940234c57185218258dc909be657d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stellarwp/assets/zipball/1cd83c9799ff1e5c4b7f3c7d301f3b8b8b2d6ee4", - "reference": "1cd83c9799ff1e5c4b7f3c7d301f3b8b8b2d6ee4", + "url": "https://api.github.com/repos/stellarwp/assets/zipball/6c26d088dcf940234c57185218258dc909be657d", + "reference": "6c26d088dcf940234c57185218258dc909be657d", "shasum": "" }, "require-dev": { @@ -402,9 +455,9 @@ "description": "A library for managing asset registration and enqueuing in WordPress.", "support": { "issues": "https://github.com/stellarwp/assets/issues", - "source": "https://github.com/stellarwp/assets/tree/1.2.9" + "source": "https://github.com/stellarwp/assets/tree/1.4.2" }, - "time": "2024-08-08T20:46:30+00:00" + "time": "2024-11-11T15:52:51+00:00" }, { "name": "stellarwp/container-contract", @@ -729,16 +782,16 @@ }, { "name": "stellarwp/uplink", - "version": "v2.2.1", + "version": "v2.2.2", "source": { "type": "git", "url": "https://github.com/stellarwp/uplink.git", - "reference": "57eb6264994662cb8da368cc3a9227ec716edc70" + "reference": "5bc1f115efe629dd4244bff08809aea45ed9d8f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stellarwp/uplink/zipball/57eb6264994662cb8da368cc3a9227ec716edc70", - "reference": "57eb6264994662cb8da368cc3a9227ec716edc70", + "url": "https://api.github.com/repos/stellarwp/uplink/zipball/5bc1f115efe629dd4244bff08809aea45ed9d8f1", + "reference": "5bc1f115efe629dd4244bff08809aea45ed9d8f1", "shasum": "" }, "require": { @@ -790,28 +843,28 @@ "description": "A library that integrates a WordPress product with the StellarWP Licensing system.", "support": { "issues": "https://github.com/stellarwp/uplink/issues", - "source": "https://github.com/stellarwp/uplink/tree/v2.2.1" + "source": "https://github.com/stellarwp/uplink/tree/v2.2.2" }, - "time": "2024-09-30T22:46:40+00:00" + "time": "2024-12-05T19:13:16+00:00" } ], "packages-dev": [ { "name": "antecedent/patchwork", - "version": "2.1.28", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/antecedent/patchwork.git", - "reference": "6b30aff81ebadf0f2feb9268d3e08385cebcc08d" + "reference": "b07d4fb37c3c723c8755122160c089e077d5de65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/6b30aff81ebadf0f2feb9268d3e08385cebcc08d", - "reference": "6b30aff81ebadf0f2feb9268d3e08385cebcc08d", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/b07d4fb37c3c723c8755122160c089e077d5de65", + "reference": "b07d4fb37c3c723c8755122160c089e077d5de65", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=7.1.0" }, "require-dev": { "phpunit/phpunit": ">=4" @@ -840,22 +893,22 @@ ], "support": { "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.1.28" + "source": "https://github.com/antecedent/patchwork/tree/2.2.0" }, - "time": "2024-02-06T09:26:11+00:00" + "time": "2024-09-27T16:59:55+00:00" }, { "name": "automattic/jetpack-changelogger", - "version": "v4.2.6", + "version": "v4.2.8", "source": { "type": "git", "url": "https://github.com/Automattic/jetpack-changelogger.git", - "reference": "23b7c4b3dc98ce9c78d7c14fc8f0fd0c17f7888d" + "reference": "81a4c2beba9df2b8f1907b52c4b71ce152067e2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/23b7c4b3dc98ce9c78d7c14fc8f0fd0c17f7888d", - "reference": "23b7c4b3dc98ce9c78d7c14fc8f0fd0c17f7888d", + "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/81a4c2beba9df2b8f1907b52c4b71ce152067e2e", + "reference": "81a4c2beba9df2b8f1907b52c4b71ce152067e2e", "shasum": "" }, "require": { @@ -902,9 +955,9 @@ "keepachangelog" ], "support": { - "source": "https://github.com/Automattic/jetpack-changelogger/tree/v4.2.6" + "source": "https://github.com/Automattic/jetpack-changelogger/tree/v4.2.8" }, - "time": "2024-08-22T11:01:24+00:00" + "time": "2024-11-04T09:23:29+00:00" }, { "name": "automattic/vipwpcs", @@ -962,25 +1015,25 @@ }, { "name": "behat/gherkin", - "version": "v4.9.0", + "version": "v4.10.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", "shasum": "" }, "require": { "php": "~7.2|~8.0" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-22.0.0", + "cucumber/cucumber": "dev-gherkin-24.1.0", "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5" + "symfony/yaml": "~3|~4|~5|~6|~7" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -1019,9 +1072,9 @@ ], "support": { "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + "source": "https://github.com/Behat/Gherkin/tree/v4.10.0" }, - "time": "2021-10-12T13:05:09+00:00" + "time": "2024-10-19T14:46:06+00:00" }, { "name": "bordoni/phpass", @@ -1325,6 +1378,54 @@ }, "time": "2019-03-02T15:35:10+00:00" }, + { + "name": "cweagans/composer-patches", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/cweagans/composer-patches.git", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3.0" + }, + "require-dev": { + "composer/composer": "~1.0 || ~2.0", + "phpunit/phpunit": "~4.6" + }, + "type": "composer-plugin", + "extra": { + "class": "cweagans\\Composer\\Patches" + }, + "autoload": { + "psr-4": { + "cweagans\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Cameron Eagans", + "email": "me@cweagans.net" + } + ], + "description": "Provides a way to patch Composer packages.", + "support": { + "issues": "https://github.com/cweagans/composer-patches/issues", + "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + }, + "time": "2022-12-20T22:53:13+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v0.7.2", @@ -1719,16 +1820,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + "reference": "a136842a532bac9ecd8a1c723852b09915d7db50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/a136842a532bac9ecd8a1c723852b09915d7db50", + "reference": "a136842a532bac9ecd8a1c723852b09915d7db50", "shasum": "" }, "require": { @@ -1776,9 +1877,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.0" }, - "time": "2024-01-02T13:46:09+00:00" + "time": "2024-11-07T15:11:20+00:00" }, { "name": "gajus/dindent", @@ -2699,16 +2800,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -2747,7 +2848,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -2755,7 +2856,7 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nesbot/carbon", @@ -3254,16 +3355,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", "shasum": "" }, "require": { @@ -3272,17 +3373,17 @@ "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.5", + "mockery/mockery": "~1.3.5 || ~1.6.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -3312,29 +3413,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.0" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-11-12T11:25:25+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", @@ -3370,32 +3471,33 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-11-09T15:12:26+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.19.0", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "reference": "a0165c648cab6a80311c74ffc708a07bb53ecc93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/a0165c648cab6a80311c74ffc708a07bb53ecc93", + "reference": "a0165c648cab6a80311c74ffc708a07bb53ecc93", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.*", + "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0", "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.40", "phpspec/phpspec": "^6.0 || ^7.0", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" @@ -3439,22 +3541,22 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.20.0" }, - "time": "2024-02-29T11:52:51+00:00" + "time": "2024-11-19T13:12:41+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.32.0", + "version": "1.33.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "6ca22b154efdd9e3c68c56f5d94670920a1c19a4" + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6ca22b154efdd9e3c68c56f5d94670920a1c19a4", - "reference": "6ca22b154efdd9e3c68c56f5d94670920a1c19a4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", "shasum": "" }, "require": { @@ -3486,9 +3588,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.32.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" }, - "time": "2024-09-26T07:23:32+00:00" + "time": "2024-10-13T11:25:22+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4853,16 +4955,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.3", + "version": "3.11.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", "shasum": "" }, "require": { @@ -4929,7 +5031,7 @@ "type": "open_collective" } ], - "time": "2024-09-18T10:38:58+00:00" + "time": "2024-11-16T12:02:36+00:00" }, { "name": "stellarwp/coding-standards", @@ -4937,12 +5039,12 @@ "source": { "type": "git", "url": "https://github.com/stellarwp/coding-standards.git", - "reference": "fa3c0b7387072d8fde885e386986e66ee2869f4b" + "reference": "bdf05c1ebfef736c75f94bad78df690550f1a241" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stellarwp/coding-standards/zipball/fa3c0b7387072d8fde885e386986e66ee2869f4b", - "reference": "fa3c0b7387072d8fde885e386986e66ee2869f4b", + "url": "https://api.github.com/repos/stellarwp/coding-standards/zipball/bdf05c1ebfef736c75f94bad78df690550f1a241", + "reference": "bdf05c1ebfef736c75f94bad78df690550f1a241", "shasum": "" }, "require": { @@ -4984,7 +5086,7 @@ "support": { "source": "https://github.com/stellarwp/coding-standards/tree/main" }, - "time": "2024-02-04T03:55:54+00:00" + "time": "2024-10-11T17:05:07+00:00" }, { "name": "symfony/browser-kit", @@ -7102,14 +7204,14 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "stellarwp/models": 20, "stellarwp/coding-standards": 20, + "stellarwp/models": 20, "the-events-calendar/tec-testing-facilities": 20 }, "prefer-stable": true, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "platform-overrides": { "php": "7.4.0" }, diff --git a/lang/tribe-common.pot b/lang/tribe-common.pot index bcb06736a3..046f1e0f53 100644 --- a/lang/tribe-common.pot +++ b/lang/tribe-common.pot @@ -2,35 +2,40 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Tribe Common 6.3.1\n" +"Project-Id-Version: Tribe Common 6.4.1\n" "Report-Msgid-Bugs-To: https://evnt.is/191x\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-11-04T06:44:43-08:00\n" -"PO-Revision-Date: 2024-11-04 14:44\n" -"X-Generator: WP-CLI 2.7.1\n" +"POT-Creation-Date: 2024-12-13T19:57:48+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.11.0\n" "X-Domain: tribe-common\n" #. Plugin Name of the plugin +#: tribe-common.php msgid "Tribe Common" msgstr "" #. Description of the plugin +#: tribe-common.php msgid "An event settings framework for managing shared options." msgstr "" #. Author of the plugin +#: tribe-common.php #: src/admin-views/help-calendar.php:105 -#: src/Tribe/Admin/Help_Page.php:168 +#: src/Common/Admin/Abstract_Admin_Page.php:430 +#: src/Tribe/Admin/Help_Page.php:171 #: src/Tribe/Customizer.php:664 #: src/Tribe/Plugins_API.php:25 msgid "The Events Calendar" msgstr "" #. Author URI of the plugin +#: tribe-common.php msgid "http://evnt.is/1x" msgstr "" @@ -374,13 +379,13 @@ msgid "Getting Started Guides" msgstr "" #: src/admin-views/help-calendar.php:110 -#: src/Tribe/Admin/Help_Page.php:337 +#: src/Tribe/Admin/Help_Page.php:340 #: src/Tribe/Plugins_API.php:45 msgid "Event Aggregator" msgstr "" #: src/admin-views/help-calendar.php:115 -#: src/Tribe/Admin/Help_Page.php:347 +#: src/Tribe/Admin/Help_Page.php:350 #: src/Tribe/Plugins_API.php:146 msgid "Filter Bar" msgstr "" @@ -422,7 +427,6 @@ msgstr "" #: src/admin-views/help-calendar.php:168 #: src/admin-views/help-community.php:153 #: src/admin-views/help-ticketing.php:186 -#: src/Common/Event_Automator/Admin/Tabs/Integrations.php:52 msgid "Integrations" msgstr "" @@ -478,13 +482,13 @@ msgstr "" #: src/admin-views/help-community.php:85 #: src/admin-views/help.php:54 -#: src/Tribe/Admin/Help_Page.php:329 +#: src/Tribe/Admin/Help_Page.php:332 #: src/Tribe/Plugins_API.php:166 msgid "Community" msgstr "" #: src/admin-views/help-community.php:90 -#: src/Tribe/Admin/Help_Page.php:372 +#: src/Tribe/Admin/Help_Page.php:375 msgid "Community Tickets" msgstr "" @@ -536,13 +540,263 @@ msgstr "" msgid "Automatic Updates" msgstr "" +#: src/admin-views/help-hub.php:49 +msgid "Settings Navigation" +msgstr "" + +#: src/admin-views/help-hub.php:50 +msgid "Close settings navigation" +msgstr "" + +#: src/admin-views/help-hub.php:51 +#: src/Tribe/Settings.php:886 +#: src/Tribe/Settings.php:924 +msgid "Close" +msgstr "" + +#: src/admin-views/help-hub.php:54 +msgid "Help Hub Navigation" +msgstr "" + +#: src/admin-views/help-hub.php:77 +#: src/admin-views/help-hub.php:83 +#: src/Tribe/Settings.php:1072 +msgid "Open settings navigation" +msgstr "" + +#: src/admin-views/help-hub.php:87 +msgid "Main Help Hub Navigation" +msgstr "" + +#: src/admin-views/help-hub/resources/common-issues.php:22 +msgctxt "Common issues section title" +msgid "Common issues" +msgstr "" + +#. translators: %s is the link to the AI Chatbot +#: src/admin-views/help-hub/resources/common-issues.php:28 +msgid "Having trouble? Find solutions to common issues or ask our %s." +msgstr "" + +#: src/admin-views/help-hub/resources/common-issues.php:29 +msgid "AI Chatbot" +msgstr "" + +#: src/admin-views/help-hub/resources/customization.php:22 +msgctxt "Customization guides section title" +msgid "Customization guides" +msgstr "" + +#: src/admin-views/help-hub/resources/customization.php:25 +msgctxt "Customization guides section paragraph" +msgid "Tips and tricks on making your calendar just the way you want it." +msgstr "" + +#: src/admin-views/help-hub/resources/faqs.php:22 +msgctxt "FAQs section title" +msgid "FAQs" +msgstr "" + +#: src/admin-views/help-hub/resources/faqs.php:25 +msgctxt "FAQs section paragraph" +msgid "Get quick answers to common questions" +msgstr "" + +#: src/admin-views/help-hub/resources/getting-started.php:22 +msgctxt "Getting started guide section title" +msgid "Getting started guides" +msgstr "" + +#: src/admin-views/help-hub/resources/getting-started.php:25 +msgctxt "Getting started guide section paragraph" +msgid "Easy to follow step-by-step instructions to make the most out of your calendar." +msgstr "" + +#: src/admin-views/help-hub/resources/resources.php:18 +msgctxt "Resources tab title" +msgid "Resources" +msgstr "" + +#. translators: %1$s is the link to the Knowledgebase. +#: src/admin-views/help-hub/resources/resources.php:25 +msgid "Help on setting up, customizing, and troubleshooting your calendar. See our %1$s for in-depth content." +msgstr "" + +#. translators: %s: Knowledgebase +#: src/admin-views/help-hub/resources/resources.php:26 +#: src/admin-views/tribe-options-help.php:21 +msgid "Knowledgebase" +msgstr "" + +#. translators: Placeholders are for the `a` tag that displays a link. +#: src/admin-views/help-hub/resources/resources.php:33 +msgctxt "The callout notice to try the chatbot with a link to the page" +msgid "To find the answer to all your questions use the %1$sTEC Chatbot%2$s" +msgstr "" + +#: src/admin-views/help-hub/resources/resources.php:55 +msgctxt "AI Chatbot notice title" +msgid "Our AI Chatbot is here to help you" +msgstr "" + +#: src/admin-views/help-hub/resources/resources.php:64 +msgctxt "AI Chatbot section paragraph" +msgid "You have questions? The TEC Chatbot has the answers." +msgstr "" + +#: src/admin-views/help-hub/resources/resources.php:74 +#: src/admin-views/help-hub/resources/sidebar/no-license.php:39 +msgctxt "Link to the Help Chatbot" +msgid "Talk to TEC Chatbot" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:18 +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:18 +msgctxt "Help page resources sidebar header" +msgid "Our TEC support hub now offers an improved help experience" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:27 +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:27 +msgctxt "Describes why consent is beneficial" +msgid "Our Help page is better than ever with the addition of:" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:45 +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:45 +msgctxt "AI Chatbot sidebar header" +msgid "AI Chatbot" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:54 +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:54 +msgctxt "AI Chatbot support sidebar paragraph" +msgid "Here to provide quick answers to your questions. Itā€™s never been easier to find the right resource." +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:73 +msgctxt "Get support sidebar header" +msgid "Talk to our support team" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:82 +msgctxt "Live support sidebar paragraph" +msgid "Our Support team is available to help you out 5 days a week:" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/has-license-has-consent.php:92 +msgctxt "Live support hours" +msgid "Mon-Fri from 9:00 - 20:00 PST" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/no-license.php:25 +msgctxt "Help page resources sidebar header" +msgid "Our AI Chatbot is here to help you" +msgstr "" + +#: src/admin-views/help-hub/resources/sidebar/no-license.php:29 +msgctxt "Call to action to use The Events Calendar Help Chatbot." +msgid "You have questions? The TEC Chatbot has the answers." +msgstr "" + +#: src/admin-views/help-hub/shared-live-support.php:25 +msgctxt "Get support sidebar header" +msgid "Get priority live support" +msgstr "" + +#: src/admin-views/help-hub/shared-live-support.php:34 +msgctxt "Live support sidebar paragraph" +msgid "You can get live support from The Events Calendar team if you have an active license for one of our products." +msgstr "" + +#: src/admin-views/help-hub/shared-live-support.php:44 +msgctxt "Live support sidebar link to article" +msgid "Learn how to get live support" +msgstr "" + +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:73 +msgctxt "Get support sidebar header" +msgid "In-app priority live support" +msgstr "" + +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:82 +msgctxt "Live support sidebar paragraph" +msgid "Get access to our agents or generate a support ticket from right here." +msgstr "" + +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:93 +msgctxt "Opt in sidebar paragraph" +msgid "To enhance your experience, we require your consent to collect and share some of your websiteā€™s data with our AI chatbot. " +msgstr "" + +#: src/admin-views/help-hub/shared-sidebar-has-license-no-consent.php:103 +msgctxt "Button to manage opt in status" +msgid "Manage my data sharing consent" +msgstr "" + +#: src/admin-views/help-hub/support/iframe-content.php:20 +msgid "Iframe Content" +msgstr "" + +#: src/admin-views/help-hub/support/iframe-content.php:34 +msgid "Star Icon" +msgstr "" + +#: src/admin-views/help-hub/support/iframe-content.php:37 +msgid "Our AI Chatbot can help you find solutions quickly." +msgstr "" + +#: src/admin-views/help-hub/support/iframe-content.php:39 +msgid "To enhance your experience, we require your consent to collect and share some of your websiteā€™s data with our AI chatbot." +msgstr "" + +#. translators: 1: the opening tag to the chatbot link, 2: the closing tag. +#: src/admin-views/help-hub/support/iframe-content.php:45 +msgctxt "Text for opting out of chatbot and linking to The Events Calendarā€™s Knowledgebase" +msgid "If you do not wish to consent, you could chat with the bot on %1$sThe Events Calendarā€™s Knowledgebase%2$s." +msgstr "" + +#: src/admin-views/help-hub/support/iframe-content.php:55 +msgid "Manage my data sharing consent" +msgstr "" + +#: src/admin-views/help-hub/support/sidebar/has-license-has-consent.php:24 +msgctxt "Talk to support sidebar header" +msgid "Talk to our support team" +msgstr "" + +#: src/admin-views/help-hub/support/sidebar/has-license-has-consent.php:33 +msgctxt "Contact support paragraph" +msgid "If you still need help contact us. Our Support team is available to help you out 5 days a week:" +msgstr "" + +#: src/admin-views/help-hub/support/sidebar/has-license-has-consent.php:42 +msgctxt "Support hours" +msgid "Mon-Fri from 9:00 - 20:00 PST" +msgstr "" + +#: src/admin-views/help-hub/support/sidebar/has-license-has-consent.php:52 +msgctxt "Contact support link" +msgid "Contact support" +msgstr "" + +#: src/admin-views/help-hub/support/support-hub.php:18 +msgctxt "Help page Support Hub title" +msgid "TEC Support Hub" +msgstr "" + +#: src/admin-views/help-hub/support/support-hub.php:27 +msgctxt "Help page Support Hub header paragraph" +msgid "Help on setting up, customizing and troubleshooting your calendar." +msgstr "" + #: src/admin-views/help-ticketing.php:106 #: src/admin-views/help-ticketing.php:168 msgid "book with The Events ticketing logo" msgstr "" #: src/admin-views/help-ticketing.php:114 -#: src/Tribe/Admin/Help_Page.php:187 +#: src/Tribe/Admin/Help_Page.php:190 #: src/Tribe/Plugins_API.php:86 msgid "Event Tickets" msgstr "" @@ -580,7 +834,7 @@ msgid "(Event Tickets Plus)" msgstr "" #: src/admin-views/help.php:38 -#: src/Tribe/Settings.php:802 +#: src/Tribe/Settings.php:811 msgid "Help" msgstr "" @@ -613,6 +867,61 @@ msgstr "" msgid "The Events Calendar important notice icon" msgstr "" +#: src/admin-views/notifications/notification.php:20 +msgid "Dismiss" +msgstr "" + +#: src/admin-views/notifications/notification.php:33 +msgid "Mark as read" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:17 +msgid "Notifications" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:20 +msgid "Mark all as read" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:21 +msgid "Close icon" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:21 +msgid "Close sidebar" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:28 +#: src/admin-views/notifications/sidebar.php:34 +msgid "Notifications icon" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:29 +#: src/admin-views/notifications/sidebar.php:35 +msgid "There are no notifications" +msgstr "" + +#: src/admin-views/notifications/sidebar.php:30 +msgid "Congratulations! You are up to date." +msgstr "" + +#: src/admin-views/notifications/sidebar.php:37 +msgid "Be up to date with the latest updates, fixes and features for The Events Calendar." +msgstr "" + +#: src/admin-views/notifications/sidebar.php:38 +msgid "data sharing agreement" +msgstr "" + +#. translators: %s: data sharing agreement +#: src/admin-views/notifications/sidebar.php:42 +msgid "To receive notifications you need to agree to our %s." +msgstr "" + +#: src/admin-views/notifications/sidebar.php:46 +msgid "Opt-in to notifications" +msgstr "" + #: src/admin-views/power-automate/api/components/access.php:22 #: src/admin-views/zapier/api/components/key-pair.php:22 msgctxt "Label for the consumer id and secret section." @@ -754,11 +1063,6 @@ msgstr "" msgid "Our websiteā€™s %s is a great place to find tips and tricks for using and customizing our plugins." msgstr "" -#. translators: %s: Knowledgebase -#: src/admin-views/tribe-options-help.php:21 -msgid "Knowledgebase" -msgstr "" - #: src/admin-views/tribe-options-help.php:26 msgid "Want to dive deeper?" msgstr "" @@ -999,8 +1303,8 @@ msgid "License & Usage" msgstr "" #: src/admin-views/troubleshooting/ea-status/license-key.php:43 -#: src/Tribe/PUE/Checker.php:636 -#: src/Tribe/PUE/Checker.php:647 +#: src/Tribe/PUE/Checker.php:712 +#: src/Tribe/PUE/Checker.php:723 msgid "License Key" msgstr "" @@ -1393,6 +1697,42 @@ msgstr "" msgid "Invalid class instance." msgstr "" +#: src/Common/Admin/Help_Hub/Hub.php:338 +#: src/Tribe/Admin/Help_Page.php:144 +msgctxt "Copy to clipboard button text." +msgid "Copy to clipboard" +msgstr "" + +#: src/Common/Admin/Help_Hub/Hub.php:339 +#: src/Tribe/Admin/Help_Page.php:145 +msgctxt "Copy to clipboard success message" +msgid "System info copied" +msgstr "" + +#: src/Common/Admin/Help_Hub/Hub.php:340 +#: src/Tribe/Admin/Help_Page.php:146 +msgctxt "Copy to clipboard instructions" +msgid "Press \"Cmd + C\" to copy" +msgstr "" + +#: src/Common/Admin/Help_Hub/Hub.php:341 +#: src/Tribe/Admin/Help_Page.php:147 +msgctxt "Default error message for system info optin" +msgid "Something has gone wrong!" +msgstr "" + +#: src/Common/Admin/Help_Hub/Hub.php:342 +#: src/Tribe/Admin/Help_Page.php:148 +msgctxt "Error code label for system info optin" +msgid "Code:" +msgstr "" + +#: src/Common/Admin/Help_Hub/Hub.php:343 +#: src/Tribe/Admin/Help_Page.php:149 +msgctxt "Error status label for system info optin" +msgid "Status:" +msgstr "" + #: src/Common/Event_Automator/Integrations/Admin/Abstract_Endpoints_Manager.php:171 msgctxt "Endpoint id is missing information to clear it." msgid "Endpoint was not cleared, the endpoint id was not found." @@ -2255,6 +2595,47 @@ msgctxt "Notice message after updating plugins to the merged version." msgid "Thanks for upgrading %1$s now with even more value! Learn more about the latest changes %2$shere%3$s." msgstr "" +#: src/Common/Notifications/Controller.php:85 +msgid "Read notifications" +msgstr "" + +#: src/Common/Notifications/Controller.php:213 +msgid "Enable this option to receive notifications about The Events Calendar, including updates, fixes, and features. This is enabled if you have opted in to Telemetry." +msgstr "" + +#: src/Common/Notifications/Controller.php:224 +msgid "In-App Notifications" +msgstr "" + +#: src/Common/Notifications/Notifications.php:153 +#: src/Common/Notifications/Notifications.php:171 +#: src/Common/Notifications/Notifications.php:219 +#: src/Common/Notifications/Notifications.php:247 +#: src/Common/Notifications/Notifications.php:273 +msgid "Invalid nonce" +msgstr "" + +#: src/Common/Notifications/Notifications.php:159 +msgid "Notifications opt-in successful" +msgstr "" + +#: src/Common/Notifications/Notifications.php:226 +#: src/Common/Notifications/Notifications.php:254 +msgid "Invalid notification slug" +msgstr "" + +#: src/Common/Notifications/Notifications.php:233 +msgid "Notification dismissed" +msgstr "" + +#: src/Common/Notifications/Notifications.php:261 +msgid "Notification marked as read" +msgstr "" + +#: src/Common/Notifications/Notifications.php:284 +msgid "All notifications marked as read" +msgstr "" + #. Translators: %1$s the post type label. #: src/Common/Site_Health/Fields/Post_Status_Count_Field.php:54 msgid "%1$s counts" @@ -2314,301 +2695,271 @@ msgstr "" msgid "Go to WordPress Updates page" msgstr "" -#: src/Tribe/Admin/Help_Page.php:141 -msgctxt "Copy to clipboard button text." -msgid "Copy to clipboard" -msgstr "" - -#: src/Tribe/Admin/Help_Page.php:142 -msgctxt "Copy to clipboard success message" -msgid "System info copied" -msgstr "" - -#: src/Tribe/Admin/Help_Page.php:143 -msgctxt "Copy to clipboard instructions" -msgid "Press \"Cmd + C\" to copy" -msgstr "" - -#: src/Tribe/Admin/Help_Page.php:144 -msgctxt "Default error message for system info optin" -msgid "Something has gone wrong!" -msgstr "" - -#: src/Tribe/Admin/Help_Page.php:145 -msgctxt "Error code label for system info optin" -msgid "Code:" -msgstr "" - -#: src/Tribe/Admin/Help_Page.php:146 -msgctxt "Error status label for system info optin" -msgid "Status:" -msgstr "" - -#: src/Tribe/Admin/Help_Page.php:172 +#: src/Tribe/Admin/Help_Page.php:175 msgid "The Events Calendar is a carefully crafted, extensible plugin that lets you easily share your events." msgstr "" -#: src/Tribe/Admin/Help_Page.php:191 +#: src/Tribe/Admin/Help_Page.php:194 msgid "Events Tickets is a carefully crafted, extensible plugin that lets you easily sell tickets for your events." msgstr "" -#: src/Tribe/Admin/Help_Page.php:206 +#: src/Tribe/Admin/Help_Page.php:209 msgid "Advanced Post Manager" msgstr "" -#: src/Tribe/Admin/Help_Page.php:210 +#: src/Tribe/Admin/Help_Page.php:213 msgid "Turbo charge your posts admin for any custom post type with sortable filters and columns, and auto-registration of metaboxes." msgstr "" -#: src/Tribe/Admin/Help_Page.php:288 +#: src/Tribe/Admin/Help_Page.php:291 msgid " and " msgstr "" -#: src/Tribe/Admin/Help_Page.php:312 +#: src/Tribe/Admin/Help_Page.php:315 msgid "Events Calendar PRO" msgstr "" -#: src/Tribe/Admin/Help_Page.php:321 +#: src/Tribe/Admin/Help_Page.php:324 #: src/Tribe/Plugins_API.php:187 msgid "Eventbrite Tickets" msgstr "" -#: src/Tribe/Admin/Help_Page.php:355 +#: src/Tribe/Admin/Help_Page.php:358 msgid "Virtual Events" msgstr "" -#: src/Tribe/Admin/Help_Page.php:363 +#: src/Tribe/Admin/Help_Page.php:366 #: src/Tribe/Plugins_API.php:106 msgid "Event Tickets Plus" msgstr "" -#: src/Tribe/Admin/Help_Page.php:546 +#: src/Tribe/Admin/Help_Page.php:549 msgctxt "not available" msgid "n/a" msgstr "" -#: src/Tribe/Admin/Help_Page.php:554 +#: src/Tribe/Admin/Help_Page.php:557 msgid "You need to upgrade!" msgstr "" -#: src/Tribe/Admin/Help_Page.php:554 -#: src/Tribe/Admin/Help_Page.php:930 +#: src/Tribe/Admin/Help_Page.php:557 +#: src/Tribe/Admin/Help_Page.php:933 msgid "You are up to date!" msgstr "" #. translators: %s: Plugin Name -#: src/Tribe/Admin/Help_Page.php:920 +#: src/Tribe/Admin/Help_Page.php:923 msgid "Activate %s" msgstr "" #. translators: %s: Plugin Name -#: src/Tribe/Admin/Help_Page.php:920 +#: src/Tribe/Admin/Help_Page.php:923 msgid "Activate Plugin" msgstr "" -#: src/Tribe/Admin/Help_Page.php:928 +#: src/Tribe/Admin/Help_Page.php:931 msgid "Upgrade Plugin" msgstr "" #. translators: %s: Plugin Name -#: src/Tribe/Admin/Help_Page.php:945 +#: src/Tribe/Admin/Help_Page.php:948 msgid "Install %s" msgstr "" #. translators: %s: Plugin Name -#: src/Tribe/Admin/Help_Page.php:945 +#: src/Tribe/Admin/Help_Page.php:948 msgid "Install Plugin" msgstr "" -#: src/Tribe/Admin/Help_Page.php:962 +#: src/Tribe/Admin/Help_Page.php:965 msgid "Latest Version:" msgstr "" -#: src/Tribe/Admin/Help_Page.php:965 +#: src/Tribe/Admin/Help_Page.php:968 msgid "Requires:" msgstr "" -#: src/Tribe/Admin/Help_Page.php:966 +#: src/Tribe/Admin/Help_Page.php:969 msgid "WordPress " msgstr "" -#: src/Tribe/Admin/Help_Page.php:968 +#: src/Tribe/Admin/Help_Page.php:971 msgid "Active Users:" msgstr "" -#: src/Tribe/Admin/Help_Page.php:971 +#: src/Tribe/Admin/Help_Page.php:974 msgid "Rating:" msgstr "" -#: src/Tribe/Admin/Help_Page.php:996 +#: src/Tribe/Admin/Help_Page.php:999 msgid "Premium Add-Ons" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1003 +#: src/Tribe/Admin/Help_Page.php:1006 msgid "Plugin Active" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1005 +#: src/Tribe/Admin/Help_Page.php:1008 msgid "Plugin Inactive" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1010 +#: src/Tribe/Admin/Help_Page.php:1013 msgid "Visit the Add-on Page" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1037 +#: src/Tribe/Admin/Help_Page.php:1040 msgid "Can I have more than one calendar?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1038 +#: src/Tribe/Admin/Help_Page.php:1041 msgid "No, but you can use event categories or tags to display certain events like having..." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1042 +#: src/Tribe/Admin/Help_Page.php:1045 msgid "What do I get with Events Calendar Pro?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1047 +#: src/Tribe/Admin/Help_Page.php:1050 msgid "How do I sell tickets to events?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1048 +#: src/Tribe/Admin/Help_Page.php:1051 msgid "Use our free Event Tickets plugin to get started with tickets and RSVPs." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1052 +#: src/Tribe/Admin/Help_Page.php:1055 msgid "Where can I find a list of available shortcodes?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1053 +#: src/Tribe/Admin/Help_Page.php:1056 msgid "Our plugins include many shortcodes that do everything from embedding the calendar..." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1072 +#: src/Tribe/Admin/Help_Page.php:1075 msgid "Calendar widget areas" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1073 +#: src/Tribe/Admin/Help_Page.php:1076 msgid "This extension creates a useful variety of WordPress widget areas (a.k.a. sidebars)." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1078 +#: src/Tribe/Admin/Help_Page.php:1081 msgid "Event block patterns" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1079 +#: src/Tribe/Admin/Help_Page.php:1082 msgid "This extension adds a set of block patterns for events to the WordPress block editor." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1084 +#: src/Tribe/Admin/Help_Page.php:1087 msgid "Alternative photo view" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1085 +#: src/Tribe/Admin/Help_Page.php:1088 msgid "This extension replaces photo view with a tiled grid of cards featuring event images." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1090 +#: src/Tribe/Admin/Help_Page.php:1093 msgid "The Events Calendar Tweaks" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1091 +#: src/Tribe/Admin/Help_Page.php:1094 msgid "This extension is a collection of tweaks and snippets for The Events Calendar." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1130 +#: src/Tribe/Admin/Help_Page.php:1133 msgid "How Do I create events with Tickets or RSVPā€™s?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1131 +#: src/Tribe/Admin/Help_Page.php:1134 msgid "Weā€™ve put together a video tutorial showing how to create events with Tickets using our plugins. Click on the link in the link in the title to learn more." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1135 +#: src/Tribe/Admin/Help_Page.php:1138 msgid "How Do I Set Up E-Commerce Plugins for Selling Tickets?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1136 +#: src/Tribe/Admin/Help_Page.php:1139 msgid "You can sell tickets using our built-in e-commerce option, or upgrade to Event Tickets Plus to use ecommerce plugins such as WooCommerce." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1140 +#: src/Tribe/Admin/Help_Page.php:1143 msgid "Can I have a seating chart associated with my tickets?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1141 +#: src/Tribe/Admin/Help_Page.php:1144 msgid "Yes! You can easily accomplish this task using the stock options and multiple ticket types available with Event Tickets." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1145 +#: src/Tribe/Admin/Help_Page.php:1148 msgid "How do I process refunds for tickets?" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1146 +#: src/Tribe/Admin/Help_Page.php:1149 msgid "When it comes to paid tickets, these orders can be refunded through the e-commerce platform in use." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1165 +#: src/Tribe/Admin/Help_Page.php:1168 msgid "Ticket Email Settings" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1166 +#: src/Tribe/Admin/Help_Page.php:1169 msgid "Adds a new settings panel in Events > Settings that gives more control over the ticket and rsvp emails that are sent to attendees after registration." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1171 +#: src/Tribe/Admin/Help_Page.php:1174 msgid "Per Event Check In API" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1172 +#: src/Tribe/Admin/Help_Page.php:1175 msgid "This extension shows a meta box with an API key on each Event with Ticket/RSVP." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1177 +#: src/Tribe/Admin/Help_Page.php:1180 msgid "Add Event & Attendee Info to WooCommerce Order Details" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1178 +#: src/Tribe/Admin/Help_Page.php:1181 msgid "Displays the information collected by ā€œattendee meta fieldsā€ in the WooCommerce order screens as well." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1183 +#: src/Tribe/Admin/Help_Page.php:1186 msgid "Organizer Notification Email" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1184 +#: src/Tribe/Admin/Help_Page.php:1187 msgid "This extension will send an email to event organizers whenever a user registers for their event." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1223 +#: src/Tribe/Admin/Help_Page.php:1226 msgid "Add Cost Currency Symbol" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1224 +#: src/Tribe/Admin/Help_Page.php:1227 msgid "This extension allows you to set default currency symbols for your users to choose from instead of having a plain text field." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1229 +#: src/Tribe/Admin/Help_Page.php:1232 msgid "Add Google Maps Display and Link Options" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1230 +#: src/Tribe/Admin/Help_Page.php:1233 msgid "This extension adds the ā€œShow Google Mapsā€ and ā€œShow Google Maps Linkā€ checkboxes when creating a new Venue." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1235 +#: src/Tribe/Admin/Help_Page.php:1238 msgid "Hide Othersā€™ Organizers and Venues" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1236 +#: src/Tribe/Admin/Help_Page.php:1239 msgid "This extension allows you to hide the Organizers and Venues that a visitor has not created from the Community submission form." msgstr "" -#: src/Tribe/Admin/Help_Page.php:1241 +#: src/Tribe/Admin/Help_Page.php:1244 msgid "Display Custom HTML" msgstr "" -#: src/Tribe/Admin/Help_Page.php:1242 +#: src/Tribe/Admin/Help_Page.php:1245 msgid "This extension allows you to add custom HTML content to the top of the Community submission form." msgstr "" @@ -4800,129 +5151,129 @@ msgstr "" msgid "Promoter Key" msgstr "" -#: src/Tribe/PUE/Checker.php:620 +#: src/Tribe/PUE/Checker.php:696 msgid "A valid license key is required for support and updates" msgstr "" #. Translators: %1$s and %2$s are opening and closing tags, respectively. -#: src/Tribe/PUE/Checker.php:624 +#: src/Tribe/PUE/Checker.php:700 msgid "%1$sBuy a license%2$s for the Event Aggregator service to access additional import features." msgstr "" -#: src/Tribe/PUE/Checker.php:656 -#: src/Tribe/PUE/Checker.php:690 +#: src/Tribe/PUE/Checker.php:732 +#: src/Tribe/PUE/Checker.php:766 msgid "License Key Status:" msgstr "" -#: src/Tribe/PUE/Checker.php:665 +#: src/Tribe/PUE/Checker.php:741 msgid "Override network license key" msgstr "" -#: src/Tribe/PUE/Checker.php:666 +#: src/Tribe/PUE/Checker.php:742 msgid "Check this box if you wish to override the network license key with your own" msgstr "" -#: src/Tribe/PUE/Checker.php:677 +#: src/Tribe/PUE/Checker.php:753 msgid "Site License Key" msgstr "" -#: src/Tribe/PUE/Checker.php:778 +#: src/Tribe/PUE/Checker.php:854 msgid "License key(s) updated." msgstr "" #. Translators: %1$s and %2$s are opening and closing tags, respectively. -#: src/Tribe/PUE/Checker.php:1035 +#: src/Tribe/PUE/Checker.php:1117 msgid "Hmmm... something's wrong with this validator. Please contact %1$ssupport%2$s." msgstr "" -#: src/Tribe/PUE/Checker.php:1054 +#: src/Tribe/PUE/Checker.php:1136 msgid "unknown date" msgstr "" -#: src/Tribe/PUE/Checker.php:1060 +#: src/Tribe/PUE/Checker.php:1142 msgid "Sorry, key validation server is not available." msgstr "" #. Translators: %s is the expiration date. -#: src/Tribe/PUE/Checker.php:1089 +#: src/Tribe/PUE/Checker.php:1171 msgid "Valid Key! Expires on %s" msgstr "" #. Translators: %s is the expiration date. -#: src/Tribe/PUE/Checker.php:1100 +#: src/Tribe/PUE/Checker.php:1182 msgid "Thanks for setting up a valid key. It will expire on %s" msgstr "" -#: src/Tribe/PUE/Checker.php:1138 -#: src/Tribe/PUE/Notices.php:360 +#: src/Tribe/PUE/Checker.php:1220 +#: src/Tribe/PUE/Notices.php:396 msgid "Renew Your License Now" msgstr "" -#: src/Tribe/PUE/Checker.php:1140 -#: src/Tribe/PUE/Notices.php:362 +#: src/Tribe/PUE/Checker.php:1222 +#: src/Tribe/PUE/Notices.php:398 msgid " (opens in a new window)" msgstr "" -#: src/Tribe/PUE/Checker.php:1157 +#: src/Tribe/PUE/Checker.php:1239 msgid "Please refresh the page and try your request again." msgstr "" #. Translators: %1$s is the plugin name. %2$s and %3$s are opening and closing tags, respectively. -#: src/Tribe/PUE/Checker.php:1178 +#: src/Tribe/PUE/Checker.php:1260 msgid "There is an update for %1$s. You'll need to %2$scheck your license%3$s to have access to updates, downloads, and support." msgstr "" #. Translators: %1$s is the plugin name. %2$s and %3$s are opening and closing tags, respectively. -#: src/Tribe/PUE/Checker.php:1242 +#: src/Tribe/PUE/Checker.php:1324 msgid "There is an update for %1$s. %2$sRenew your license%3$s to get access to bug fixes, security updates, and new features." msgstr "" #. Translators: %s is the plugin version number. -#: src/Tribe/PUE/Checker.php:1274 +#: src/Tribe/PUE/Checker.php:1356 msgid "Update now to version %s." msgstr "" #. Translators: %1$s is the plugin name. %2$s is the update now link. -#: src/Tribe/PUE/Checker.php:1289 +#: src/Tribe/PUE/Checker.php:1371 msgid "There is a new version of %1$s available. %2$s" msgstr "" -#: src/Tribe/PUE/Checker.php:1905 +#: src/Tribe/PUE/Checker.php:1987 msgid "A valid license has been entered by your network administrator." msgstr "" -#: src/Tribe/PUE/Checker.php:1906 +#: src/Tribe/PUE/Checker.php:1988 msgid "No license entered. Consult your network administrator." msgstr "" -#: src/Tribe/PUE/Checker.php:1907 +#: src/Tribe/PUE/Checker.php:1989 msgid "Expired license. Consult your network administrator." msgstr "" -#: src/Tribe/PUE/Notices.php:293 +#: src/Tribe/PUE/Notices.php:329 msgid "It looks like you're using %1$s, but the license key is invalid. Please download the latest version %2$sfrom your account%3$s." msgid_plural "It looks like you're using %1$s, but the license keys are invalid. Please download the latest versions %2$sfrom your account%3$s." msgstr[0] "" msgstr[1] "" -#: src/Tribe/PUE/Notices.php:347 +#: src/Tribe/PUE/Notices.php:383 msgid "There is an update available for %1$s but your license has expired. %2$sVisit the Events Calendar website to renew your license.%3$s" msgid_plural "Updates are available for %1$s but your license keys have expired. %2$sVisit the Events Calendar website to renew your licenses.%3$s" msgstr[0] "" msgstr[1] "" -#: src/Tribe/PUE/Notices.php:388 +#: src/Tribe/PUE/Notices.php:424 msgid "You have a license key for %1$s but the key is out of installs. %2$sVisit the Events Calendar website%3$s to manage your installs, upgrade your license, or purchase a new one." msgid_plural "You have license keys for %1$s but your keys are out of installs. %2$sVisit the Events Calendar website%3$s to manage your installs, upgrade your licenses, or purchase new ones." msgstr[0] "" msgstr[1] "" -#: src/Tribe/PUE/Notices.php:430 +#: src/Tribe/PUE/Notices.php:466 msgid "You can always check the status of your licenses by logging in to %1$syour account on theeventscalendar.com%2$s." msgstr "" -#: src/Tribe/PUE/Notices.php:485 -#: src/Tribe/PUE/Notices.php:528 +#: src/Tribe/PUE/Notices.php:521 +#: src/Tribe/PUE/Notices.php:564 msgctxt "formatted plugin list" msgid "%1$s and %2$s" msgstr "" @@ -4939,63 +5290,54 @@ msgstr "" msgid "Got it" msgstr "" -#: src/Tribe/Settings.php:276 +#: src/Tribe/Settings.php:277 msgid "Events" msgstr "" #. Translators: %s is the name of the menu item. -#: src/Tribe/Settings.php:612 +#: src/Tribe/Settings.php:618 msgid "%s Settings" msgstr "" -#: src/Tribe/Settings.php:744 +#: src/Tribe/Settings.php:753 msgid "You've requested a non-existent tab." msgstr "" -#: src/Tribe/Settings.php:799 +#: src/Tribe/Settings.php:808 msgid "Save Changes" msgstr "" -#: src/Tribe/Settings.php:841 +#: src/Tribe/Settings.php:847 msgid "Skip to tab content" msgstr "" -#: src/Tribe/Settings.php:875 -#: src/Tribe/Settings.php:913 -msgid "Close" -msgstr "" - -#: src/Tribe/Settings.php:1061 -msgid "Open settings navigation" -msgstr "" - -#: src/Tribe/Settings.php:1084 +#: src/Tribe/Settings.php:1095 msgid "For you, Jack!" msgstr "" -#: src/Tribe/Settings.php:1158 +#: src/Tribe/Settings.php:1169 msgid "You don't have permission to do that." msgstr "" -#: src/Tribe/Settings.php:1164 +#: src/Tribe/Settings.php:1175 msgid "The request was sent insecurely." msgstr "" -#: src/Tribe/Settings.php:1170 +#: src/Tribe/Settings.php:1181 msgid "The request wasn't sent from this tab." msgstr "" -#: src/Tribe/Settings.php:1380 +#: src/Tribe/Settings.php:1391 msgid "Your form had the following errors:" msgstr "" -#: src/Tribe/Settings.php:1391 +#: src/Tribe/Settings.php:1402 msgid "The above setting was not saved. Other settings were successfully saved." msgid_plural "The above settings were not saved. Other settings were successfully saved." msgstr[0] "" msgstr[1] "" -#: src/Tribe/Settings.php:1412 +#: src/Tribe/Settings.php:1423 msgid "Settings saved." msgstr "" diff --git a/package-lock.json b/package-lock.json index 81ed60f19c..42e26afbe5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tribe-common", - "version": "6.2.0-dev", + "version": "6.4.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tribe-common", - "version": "6.2.0-dev", + "version": "6.4.1", "dependencies": { "@babel/runtime": "^7.15.3", "@moderntribe/common": "file:src/modules", @@ -22,7 +22,6 @@ "clipboard": "^2.0.10", "file-loader": "^1.1.6", "he": "^1.2.0", - "intro.js": "^3.4.0", "jquery": "3.5.0", "lodash": "^4.17.21", "moment": "^2.29.4", @@ -4580,9 +4579,8 @@ }, "node_modules/babel-plugin-lodash/node_modules/@babel/types": { "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -5496,9 +5494,9 @@ "license": "CC-BY-4.0" }, "node_modules/caniuse-lite": { - "version": "1.0.30001616", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", - "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "dev": true, "funding": [ { @@ -12088,10 +12086,6 @@ "node": ">= 0.10" } }, - "node_modules/intro.js": { - "version": "3.4.0", - "license": "AGPL-3.0" - }, "node_modules/invariant": { "version": "2.2.4", "license": "MIT", @@ -28613,8 +28607,6 @@ "dependencies": { "@babel/types": { "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dev": true, "peer": true, "requires": { @@ -29295,9 +29287,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001616", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", - "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "dev": true }, "cardinal": { @@ -33904,9 +33896,6 @@ "version": "1.4.0", "dev": true }, - "intro.js": { - "version": "3.4.0" - }, "invariant": { "version": "2.2.4", "requires": { diff --git a/package-safelist.json b/package-safelist.json index a7248e6b0a..f084f9aa13 100644 --- a/package-safelist.json +++ b/package-safelist.json @@ -2,10 +2,6 @@ "lang/*.mo", "lang/*.pot", "lang/*.txt", - "node_modules/clipboard/dist/clipboard.min.js", - "node_modules/intro.js/*.css", - "node_modules/intro.js/*.js", - "node_modules/mt-a11y-dialog/*.min.js", "README.md", "src/admin-views/**/*", "src/Common/**/*", @@ -18,6 +14,8 @@ "src/resources/js/admin/*", "src/resources/js/app/*.min.js", "src/resources/js/utils/*", + "src/resources/vendor/*.min.js", + "src/resources/vendor/*.min.css", "src/Tribe/**/*", "src/views/**/*", "tribe-autoload.php", diff --git a/package.json b/package.json index edfbf92437..8f30eeff87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tribe-common", - "version": "6.4.0", + "version": "6.4.1", "repository": "git@github.com:the-events-calendar/tribe-common.git", "_resourcepath": "src/resources", "_domainPath": "lang", @@ -8,7 +8,10 @@ "_glotPressUrl": "https://translations.theeventscalendar.com", "_glotPressSlug": "tribe-common", "_glotPressFileFormat": "%textdomain%-%wp_locale%.%format%", - "_glotPressFormats": ["po", "mo"], + "_glotPressFormats": [ + "po", + "mo" + ], "_glotPressFilter": { "translation_sets": false, "minimum_percentage": 30, @@ -31,15 +34,20 @@ "!src/resources/postcss/datepicker.pcss", "!src/resources/postcss/tribe-ui.pcss" ], - "jest": ["src/modules/**/__tests__/**/*.js"] + "jest": [ + "src/modules/**/__tests__/**/*.js" + ] + }, + "engines": { + "node": "18.13.0", + "npm": "8.19.3" }, - "engines": { "node": "18.13.0", "npm": "8.19.3" }, "scripts": { "analyze": "webpack-bundle-analyzer -m static stats.json", "bootstrap": "./scripts/linkDependencies", "build:webpack": "export NODE_OPTIONS=--openssl-legacy-provider && cross-env NODE_ENV=production webpack -p", "build:gulp": "cross-env NODE_ENV=production gulp", - "prebuild": "npm install && node node_modules/@the-events-calendar/product-taskmaster/scripts/syncBrowserslistConfig.js", + "prebuild": "npm install", "build": "export NODE_OPTIONS=--openssl-legacy-provider && npm run build:webpack && npm run build:gulp", "rebuild": "rm -rf node_modules && npm install && npm run build", "dev": "export NODE_OPTIONS=--openssl-legacy-provider && cross-env NODE_ENV=development webpack -d --watch", @@ -48,7 +56,6 @@ "lint:eslint": "gulp eslint", "lint:stylelint": "gulp stylelint", "jest": "TZ=UTC gulp jest", - "zip": "node node_modules/@the-events-calendar/product-taskmaster/util/zip.js", "glotpress": "gulp glotpress", "changelog": "./vendor/bin/changelogger add" }, @@ -67,7 +74,6 @@ "clipboard": "^2.0.10", "file-loader": "^1.1.6", "he": "^1.2.0", - "intro.js": "^3.4.0", "jquery": "3.5.0", "lodash": "^4.17.21", "moment": "^2.29.4", @@ -107,17 +113,9 @@ "webpack-cli": "^3.1.2", "webpack-merge": "^4.1.4" }, - "overrides": { "babel-plugin-lodash": { "@babel/types": "~7.20.0" } }, - "browserslist": [ - "last 2 Chrome versions", - "last 2 Firefox versions", - "last 2 Safari versions", - "last 2 Edge versions", - "last 2 Opera versions", - "last 2 iOS versions", - "last 1 Android version", - "last 1 ChromeAndroid version", - "last 1 FirefoxAndroid version", - "> 1%, not op_mini all, not ie <= 11, not ie_mob <= 11" - ] + "overrides": { + "babel-plugin-lodash": { + "@babel/types": "~7.20.0" + } + } } diff --git a/readme.txt b/readme.txt index ab244419db..489a5eeeec 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,33 @@ == Changelog == += [6.4.1] 2024-12-17 = + +* Feature - Add an abstract admin page to start consolidating how we do admin pages. See the "First Time Setup" page (onboarding wizard) for an example. [TEC-5294] +* Tweak - Ensure we are not loading any assets from node_modules. Include anything we need as a 3rd party code in our plugin. [TCMN-175] +* Fix - Cast `$block` argument to string to avoid PHP 8+ deprecation notice when non string (or array) variables are passed as the 3rd argument of `preg_replace`. +* Fix - Correctly identify licenses using uplink, like Event Tickets Plus. [n/a] +* Fix - Ensure that number_format is used with a float value to prevent issues with PHP 8.0+. [ETP-962] +* Fix - Ensure we get an object to test for subnav. Pass the object to class filter for more context. [n/a] +* Fix - Prevent fatal on ET integration page when used with Events Pro but without Event Tickets Plus. [TCMN-174] +* Deprecated - Integrations Tab registration in Event Ticket Settings from common. These will be registered from Event Tickets Plus only instead. [TCMN-174] +* Language - 0 new strings added, 33 updated, 1 fuzzied, and 0 obsoleted. + += [6.4.0] 2024-12-05 = + +* Feature - In-App Notifications system. [TEC-5165] +* Tweak - Added filters: `tec_common_ian_opt_in`, `tec_common_ian_conditional_php`, `tec_common_ian_conditional_wp`, `tec_common_ian_allowed_pages`, `tec_common_ian_show_icon`, `tec_common_ian_setting_optin_tooltip`, `tec_common_ian_api_url`, `tec_common_ian_slugs`, `tec_common_ian_render` +* Tweak - Added actions: `tec_common_ian_loaded` +* Language - 22 new strings added, 15 updated, 1 fuzzied, and 0 obsoleted. + += [6.3.2] 2024-11-19 = + +* Feature - Implemented the core Help Hub logic, providing a flexible framework for managing support integrations, resource templates, and plugin-specific customization. +* Feature - Introduced Asset interface which accounts for symlinks, while still provides a fluent api. [SL-246] +* Feature - Update stellarwp/assets to version 1.4.2. [SL-246] +* Tweak - Added actions: `tec_help_hub_before_render`, `tec_help_hub_after_render`, `tec_help_hub_before_iframe_render`, `tec_help_hub_after_iframe_render`, `tec_help_hub_registered`. +* Tweak - Added filters: `tec_help_hub_resource_sections_{$data_class_name}`, `tec_help_hub_resource_sections`, `tec_help_hub_body_classes`. +* Language - 0 new strings added, 0 updated, 1 fuzzied, and 0 obsoleted. + = [6.3.1] 2024-11-04 = * Fix - Prevent new Settings pages to over sanitize textarea fields, thus removing HTML from before/after in the Events UI. [TEC-5283] diff --git a/src/Common/Admin/Abstract_Admin_Page.php b/src/Common/Admin/Abstract_Admin_Page.php new file mode 100644 index 0000000000..8f0303afb0 --- /dev/null +++ b/src/Common/Admin/Abstract_Admin_Page.php @@ -0,0 +1,519 @@ +get_parent_page_slug(); + + if ( ! empty( $parent_slug ) ) { + add_submenu_page( + $parent_slug, + $this->get_the_page_title(), + $this->get_the_menu_title(), + $this->required_capability(), + static::get_page_slug(), + [ $this, 'admin_page_content' ], + $this->get_position() + ); + } else { + add_menu_page( + $this->get_the_page_title(), + $this->get_the_menu_title(), + $this->required_capability(), + static::get_page_slug(), + [ $this, 'admin_page_content' ], + $this->get_page_icon_url(), + $this->get_position() + ); + } + } + + /** + * Get the page slug. + * + * @since 6.4.1 + */ + public static function get_page_slug(): string { + if ( ! empty( static::$page_slug ) ) { + return static::$page_slug; + } + + static::$page_slug = static::$slug; + + return static::$page_slug; + } + + /** + * Get the page type. + * + * @since 6.4.1 + */ + public function get_page_type(): string { + // Defined in the traits, or redefined in an extending class. + return static::$page_type; + } + + /** + * Defines wether the current page is the correct page. + * + * @since 6.4.1 + */ + public static function is_on_page(): bool { + $admin_pages = tribe( 'admin.pages' ); + $admin_page = $admin_pages->get_current_page(); + $page_slug = static::get_page_slug(); + + return ! empty( $admin_page ) && $admin_page === $page_slug; + } + + /** + * Has the page been dismissed? + * + * @since 6.4.1 + * + * @return bool + */ + public static function is_dismissed(): bool { + if ( ! static::$is_dismissible ) { + return false; + } + + return static::$is_dismissed; + } + + /** + * Get the logo source URL. + * + * @since 6.4.1 + * + * @return string The logo source URL. + */ + public function get_logo_source(): string { + $logo_source = tribe_resource_url( 'images/logo/tec-brand.svg', false, null, Tribe__Main::instance() ); + + $admin_page = static::get_page_slug(); + + /** + * Filter the admin page logo source URL. + * + * @since 6.4.1 + * + * @param string $logo_source The settings page logo resource URL. + * @param string $admin_page The admin page ID. + */ + return (string) apply_filters( 'tec_settings_page_logo_source', $logo_source, $admin_page ); + } + + /** + * Get the admin page logo. + * + * @since 6.4.1 + * + * @return void Echos the admin page logo. + */ + public function do_page_logo(): void { + // Only run once to avoid duplicating IDs. + if ( did_action( 'tribe_admin_page_after_logo' ) ) { + return; + } + + ?> + logo_classes() ); ?> + /> + menu_position ?? null; + } + + /** + * Get the classes for the header. + * + * @since 6.4.1 + * + * @return array The classes for the header. + */ + public function header_classes(): array { + $classes = [ 'tec-admin-page__header' ]; + + return (array) apply_filters( 'tec_admin_page_header_classes', $classes ); + } + + /** + * Get the classes for the logo. + * + * @since 6.4.1 + * + * @return array The classes for the logo. + */ + public function logo_classes(): array { + $classes = [ 'tec-admin-page__logo' ]; + + return (array) apply_filters( 'tec_admin_page_logo_classes', $classes ); + } + + /** + * Get the classes for the content wrapper. + * + * @since 6.4.1 + * + * @return array The classes for the content wrapper. + */ + public function content_classes(): array { + $classes = [ 'tec-admin-page__content' ]; + + return (array) apply_filters( 'tec_admin_page_content_classes', $classes ); + } + + /** + * Get the classes for the sidebar. + * + * @since 6.4.1 + * + * @return array The classes for the sidebar. + */ + public function sidebar_classes(): array { + $classes = [ 'tec-admin-page__sidebar' ]; + + return (array) apply_filters( 'tec_admin_page_sidebar_classes', $classes ); + } + + /** + * Get the classes for the footer. + * + * @since 6.4.1 + * + * @return array The classes for the footer. + */ + public function footer_classes(): array { + $classes = [ 'tec-admin-page__footer' ]; + + return (array) apply_filters( 'tec_admin_page_footer_classes', $classes ); + } + + /** + * Get the classes for the wrapper. + * + * @since 6.4.1 + * + * @return array + */ + public function wrapper_classes(): array { + $classes = [ 'tec-admin-page', 'tec-admin', 'wrap' ]; + + if ( static::$has_header ) { + $classes[] = 'tec-admin-page--header'; + } + + if ( static::$has_sidebar ) { + $classes[] = 'tec-admin-page--sidebar'; + } + + if ( static::$has_footer ) { + $classes[] = 'tec-admin-page--footer'; + } + + return (array) apply_filters( 'tec_admin_page_wrapper_classes', $classes ); + } + + /** + * Render the admin page content. + * Relies on extending classes overriding the admin_page_header, + * admin_page_title, and admin_page_main functions. + * + * HTML wrapper are used to layout of the page. + * + * @since 6.4.1 + * + * @return void Renders the entire admin page content. + */ + public function admin_page_content(): void { + do_action( 'tec_admin_page_before_wrap_start' ); + ?> + +
wrapper_classes() ); ?> > + + + admin_page_header(); ?> + + admin_page_main_content_wrapper(); ?> + + + + admin_page_sidebar_wrapper(); ?> + + admin_page_footer_wrapper(); ?> + + +
+ + +
header_classes() ); ?>> + do_page_logo(); ?> + + admin_page_title(); ?> + +
+ +

+ +
content_classes() ); ?>> + admin_page_main_content(); ?> +
+ + + +
footer_classes() ); ?>> + +
+ meta_key_time_prefix . $this->slug, time() ); - return add_user_meta( $user_id, $this->meta_key, $this->slug ); + return (bool) add_user_meta( $user_id, $this->meta_key, $this->slug ); } /** diff --git a/src/Common/Admin/Help_Hub/Hub.php b/src/Common/Admin/Help_Hub/Hub.php new file mode 100644 index 0000000000..b53e0125b4 --- /dev/null +++ b/src/Common/Admin/Help_Hub/Hub.php @@ -0,0 +1,562 @@ +config = $config; + $this->template = $template; + $this->data = $data; + + $this->setup_support_keys(); + $this->register_hooks(); + } + + /** + * Sets up support keys for embedding the chat widgets. + * + * @since 6.3.2 + * + * @link https://docsbot.ai/documentation/developer/embeddable-chat-widget Docsbot Embeddable Chat Widget Documentation + * @link https://support.zendesk.com/hc/en-us/articles/4408836216218-Using-Web-Widget-Classic-to-embed-customer-service-in-your-website Zendesk Classic Chat Widget Documentation + */ + protected function setup_support_keys() { + if ( ! defined( 'TEC_HELP_HUB_CHAT_DOCSBOT_SUPPORT_KEY' ) ) { + /** + * Docsbot key for embedding the bot iframe. + */ + define( 'TEC_HELP_HUB_CHAT_DOCSBOT_SUPPORT_KEY', 'yes2mjAljn0V5ndsWaOi/VhpexdT7TZTckW7FLyN7' ); + } + + if ( ! defined( 'TEC_HELP_HUB_CHAT_ZENDESK_CHAT_KEY' ) ) { + /** + * Zendesk key for embedding the classic chat widget. + */ + define( 'TEC_HELP_HUB_CHAT_ZENDESK_CHAT_KEY', 'd8e5e319-c54b-4da9-9d7d-e984cc3c4900' ); + } + } + + /** + * Retrieves the data object used by the Help Hub. + * + * @since 6.3.2 + * + * @return Help_Hub_Data_Interface The data object containing Help Hub resources. + */ + public function get_data(): Help_Hub_Data_Interface { + return $this->data; + } + + /** + * Registers the hooks and filters needed for Help Hub functionality. + * + * Sets up actions and filters for initializing iframe content, + * loading assets, and adding custom body classes for Help Hub pages. + * + * @since 6.3.2 + * + * @return void + */ + public function register_hooks(): void { + add_action( 'admin_init', [ $this, 'generate_iframe_content' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'load_assets' ], 1 ); + add_filter( 'admin_body_class', [ $this, 'add_help_page_body_class' ] ); + } + + /** + * Ensures that the Help Hub data object is set. + * + * Verifies that the $data property has been set. Throws a RuntimeException + * if the data has not been set using the setup method. + * + * @since 6.3.2 + * + * @return void + * @throws RuntimeException If data has not been set using setup. + */ + protected function ensure_data_is_set(): void { + if ( empty( $this->data ) ) { + throw new RuntimeException( 'The HelpHub data must be set using the setup method before calling this function.' ); + } + } + + /** + * Renders the Help Hub page. + * + * Generates necessary notices, retrieves data, and renders the appropriate Help Hub template. + * + * @since 6.3.2 + * + * @return void + * @throws RuntimeException If data is not set using the setup method before rendering. + */ + public function render(): void { + $this->ensure_data_is_set(); + + /** + * Fires before the Help Hub page is rendered. + * + * Use this hook to modify data or enqueue additional assets before the Help Hub template is generated. + * + * @since 6.3.2 + * + * @param Hub $this The Hub instance. + */ + do_action( 'tec_help_hub_before_render', $this ); + + $status = $this->get_license_and_opt_in_status(); + $template_variant = self::get_template_variant( $status['has_valid_license'], $status['is_opted_in'] ); + + $this->render_template( + 'help-hub', + [ + 'template_variant' => $template_variant, + ] + ); + + /** + * Fires after the Help Hub page is rendered. + * + * Use this hook to perform actions or cleanup tasks after the Help Hub template is generated and displayed. + * + * @since 6.3.2 + * + * @param Hub $this The Hub instance. + */ + do_action( 'tec_help_hub_after_render', $this ); + } + + /** + * Handles and filters the resource sections for the Help Hub. + * + * This method centralizes the creation and filtering of resource sections, + * allowing for a single point of modification based on the data class. + * + * @since 6.3.2 + * + * @return array The filtered resource sections. + */ + public function handle_resource_sections(): array { + $sections = $this->data->create_resource_sections(); + $data_class_name = get_class( $this->data ); + + /** + * Filter the Help Hub resource sections for a specific data class. + * + * This dynamic filter allows customization of the Help Hub resource sections specific + * to a given data class, enabling more granular control over section customization. + * + * @since 6.3.2 + * + * @param array $sections The array of resource sections. + * @param Help_Hub_Data_Interface $data The data instance used for generating sections. + */ + $sections = apply_filters( "tec_help_hub_resource_sections_{$data_class_name}", $sections, $this->data ); + + /** + * Filter the Help Hub resource sections. + * + * Allows customization of the Help Hub resource sections by other components. + * + * @since 6.3.2 + * + * @param array $sections The array of resource sections. + * @param Help_Hub_Data_Interface $data The data instance used for generating sections. + * @param string $data_class_name The name of the data class. + */ + return apply_filters( 'tec_help_hub_resource_sections', $sections, $this->data, $data_class_name ); + } + + /** + * Determines the template variant based on license validity and opt-in status. + * + * @since 6.3.2 + * + * @param bool $has_valid_license Whether the license is valid. + * @param bool $is_opted_in Whether the user has opted into telemetry. + * + * @return string The template variant. + */ + protected static function get_template_variant( bool $has_valid_license, bool $is_opted_in ): string { + if ( ! $has_valid_license ) { + return 'no-license'; + } + + return $is_opted_in ? 'has-license-has-consent' : 'has-license-no-consent'; + } + + /** + * Checks if the current page is a Help Hub page. + * + * @since 6.3.2 + * + * @return bool + */ + public static function is_current_page(): bool { + global $current_screen; + + $help_pages = [ + 'tribe_events_page_tec-events-help', + 'tribe_events_page_tec-events-help-hub', + ]; + + return in_array( $current_screen->id, $help_pages, true ); + } + + /** + * Adds custom body classes to the Help Hub page. + * + * This method adds default classes to the Help Hub page, with an option + * to customize or add additional classes via the `tec_help_hub_body_classes` filter. + * + * @since 6.3.2 + * + * @param string $classes Space-separated string of classes for the body tag. + * + * @return string Filtered list of classes. + */ + public function add_help_page_body_class( string $classes ): string { + if ( ! self::is_current_page() ) { + return $classes; + } + + // Default classes for Help Hub. + $default_classes = [ 'tribe-help', 'tec-help' ]; + + /** + * Filters the list of body classes for the Help Hub page. + * + * This filter allows customization of the body classes applied to the Help Hub page, + * enabling the addition or removal of classes as needed. + * + * @since 6.3.2 + * + * @param array $class_array The default array of body classes. + */ + $class_array = (array) apply_filters( 'tec_help_hub_body_classes', array_merge( $default_classes ) ); + + // Merge filtered classes with the existing $classes argument. + $class_array = array_merge( explode( ' ', $classes ), $class_array ); + + return implode( ' ', array_unique( $class_array ) ); + } + + /** + * Enqueues assets for the Help Hub page. + * + * @since 6.3.2 + * + * @return void + */ + public function load_assets() { + if ( ! self::is_current_page() ) { + return; + } + + tribe_asset( + Tribe__Main::instance(), + 'tec-common-help-hub-style', + 'help-hub.css', + null, + 'admin_enqueue_scripts' + ); + + tribe_asset( + Tribe__Main::instance(), + 'tribe-admin-help-page', + 'admin/help-page.js', + [ 'tribe-clipboard', 'tribe-common' ], + 'admin_enqueue_scripts', + [ + 'conditionals' => [ self::class, 'is_current_page' ], + 'localize' => [ + 'name' => 'tribe_system_info', + 'data' => [ + 'sysinfo_optin_nonce' => wp_create_nonce( 'sysinfo_optin_nonce' ), + 'clipboard_btn_text' => _x( 'Copy to clipboard', 'Copy to clipboard button text.', 'tribe-common' ), + 'clipboard_copied_text' => _x( 'System info copied', 'Copy to clipboard success message', 'tribe-common' ), + 'clipboard_fail_text' => _x( 'Press "Cmd + C" to copy', 'Copy to clipboard instructions', 'tribe-common' ), + 'sysinfo_error_message_text' => _x( 'Something has gone wrong!', 'Default error message for system info optin', 'tribe-common' ), + 'sysinfo_error_code_text' => _x( 'Code:', 'Error code label for system info optin', 'tribe-common' ), + 'sysinfo_error_status_text' => _x( 'Status:', 'Error status label for system info optin', 'tribe-common' ), + ], + ], + ] + ); + + wp_enqueue_script( 'jquery-ui-accordion' ); + } + + /** + * Generates a telemetry opt-in link. + * + * @since 6.3.2 + * + * @return string + */ + public static function get_telemetry_opt_in_link(): string { + return add_query_arg( + [ + 'page' => 'tec-events-settings', + 'tab' => 'general-debugging-tab', + 'post_type' => 'tribe_events', + ], + admin_url( 'edit.php' ) + ); + } + + /** + * Generates and outputs iframe content when appropriate. + * + * @since 6.3.2 + * + * @return void + * @throws RuntimeException If data has not been set using setup. + */ + public function generate_iframe_content(): void { + $this->ensure_data_is_set(); + $page = tribe_get_request_var( 'page' ); + $iframe = tribe_get_request_var( 'embedded_content' ); + + if ( empty( $page ) || 'tec-events-help-hub' !== $page || empty( $iframe ) ) { + return; + } + + /** + * Fires before the Help Hub iframe content is rendered. + * + * Use this hook to enqueue additional assets, modify iframe-specific content, + * or take other actions just before the iframe content is generated. + * + * @since 6.3.2 + * + * @param Hub $this The Hub instance. + */ + do_action( 'tec_help_hub_before_iframe_render', $this ); + + $this->register_iframe_hooks(); + + // phpcs:ignore WordPressVIPMinimum.UserExperience.AdminBarRemoval.RemovalDetected + show_admin_bar( false ); + $this->render_template( 'help-hub/support/iframe-content' ); + + /** + * Fires after the Help Hub iframe content has been rendered. + * + * Use this hook to perform actions or cleanup tasks after the iframe content + * has been generated and displayed. + * + * @since 6.3.2 + * + * @param Hub $this The Hub instance. + */ + do_action( 'tec_help_hub_after_iframe_render', $this ); + + exit; + } + + /** + * Registers the hooks and filters needed for Help Hub Iframe functionality. + * + * Sets up actions and filters for initializing iframe content, + * loading assets, and adding custom body classes for Help Hub pages. + * + * @since 6.3.2 + * + * @return void + */ + public function register_iframe_hooks() { + add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_help_page_iframe_assets' ] ); + add_action( 'wp_enqueue_scripts', [ __CLASS__, 'dequeue_theme_styles' ] ); + } + + /** + * Enqueues assets specific to the iframe content and removes theme styles. + * + * @since 6.3.2 + * @return void + */ + public function enqueue_help_page_iframe_assets(): void { + define( 'IFRAME_REQUEST', true ); + + tribe_asset( + Tribe__Main::instance(), + 'help-hub-iframe-style', + 'help-hub-iframe.css', + null, + 'wp_enqueue_scripts' + ); + + tribe_asset( + Tribe__Main::instance(), + 'help-hub-iframe-js', + 'admin/help-hub-iframe.js', + null, + 'wp_enqueue_scripts', + [ + 'localize' => [ + 'name' => 'helpHubSettings', + 'data' => [ + 'docsbot_key' => $this->config->get( 'TEC_HELP_HUB_CHAT_DOCSBOT_SUPPORT_KEY' ), + 'zendeskChatKey' => $this->config->get( 'TEC_HELP_HUB_CHAT_ZENDESK_CHAT_KEY' ), + ], + ], + ] + ); + } + + /** + * Removes theme-related styles to avoid iframe conflicts. + * + * @since 6.3.2 + * + * @return void + */ + public static function dequeue_theme_styles(): void { + global $wp_styles; + $theme_directory = get_template_directory_uri(); + + foreach ( $wp_styles->queue as $handle ) { + $src = $wp_styles->registered[ $handle ]->src; + if ( strpos( $src, $theme_directory ) !== false ) { + wp_dequeue_style( $handle ); + } + } + } + + /** + * Generates HTML for the admin notice. + * + * @since 6.3.2 + * + * @param string $text The text to display. + * @param string $slug Slug for the notice. + * + * @return string The HTML for the admin notice. + */ + public function generate_notice_html( string $text, string $slug ): string { + $notice_admin = ( new AdminNotice( $slug, "

$text

" ) ) + ->urgency( 'info' ) + ->inline() + ->dismissible( true ) + ->withWrapper(); + + return AdminNotices::render( $notice_admin, false ); + } + + /** + * Wrapper to get the license validity and telemetry opt-in status. + * + * @since 6.3.2 + * + * @return array Contains 'has_valid_license' and 'is_opted_in' status. + */ + public function get_license_and_opt_in_status(): array { + return $this->data->get_license_and_opt_in_status(); + } + + /** + * Wrapper to retrieve the URL for a specified icon. + * + * @since 6.3.2 + * + * @param string $icon_name The name of the icon to retrieve. + * + * @return string The URL of the specified icon, or an empty string if the icon does not exist. + */ + public function get_icon_url( string $icon_name ): string { + return $this->data->get_icon_url( $icon_name ); + } + + /** + * Renders the specified template with provided variables. + * + * @since 6.3.2 + * + * @param string $template_name The template file name (without .php). + * @param array $extra_values Extra values to pass to the template. + * + * @return void + */ + private function render_template( string $template_name, array $extra_values = [] ): void { + $main = Tribe__Main::instance(); + $template = $this->template; + + $template_values = wp_parse_args( + $extra_values, + [ + 'main' => $main, + 'help_hub' => $this, + ] + ); + + $template->set_values( $template_values ); + $template->set_template_origin( $main ); + $template->set_template_folder( 'src/admin-views' ); + $template->set_template_context_extract( true ); + $template->set_template_folder_lookup( false ); + $template->template( $template_name ); + } +} diff --git a/src/Common/Admin/Help_Hub/Provider.php b/src/Common/Admin/Help_Hub/Provider.php new file mode 100644 index 0000000000..39a9c3be49 --- /dev/null +++ b/src/Common/Admin/Help_Hub/Provider.php @@ -0,0 +1,50 @@ +container->singleton( self::class, $this ); + $this->container->bind( Hub::class ); + + /** + * Fires when the provider is registered. + * + * @since 6.3.2 + * + * @param Provider $this The provider instance. + */ + do_action( 'tec_help_hub_registered', $this ); + } +} diff --git a/src/Common/Admin/Help_Hub/Resource_Data/Help_Hub_Data_Interface.php b/src/Common/Admin/Help_Hub/Resource_Data/Help_Hub_Data_Interface.php new file mode 100644 index 0000000000..c2ae3e0a0b --- /dev/null +++ b/src/Common/Admin/Help_Hub/Resource_Data/Help_Hub_Data_Interface.php @@ -0,0 +1,69 @@ +add( new self( $slug, $file, $version, $root_path ) ); + } +} diff --git a/src/Common/Event_Automator/Admin/Tabs/Integrations.php b/src/Common/Event_Automator/Admin/Tabs/Integrations.php index 741e1543b8..913f7dd1e3 100644 --- a/src/Common/Event_Automator/Admin/Tabs/Integrations.php +++ b/src/Common/Event_Automator/Admin/Tabs/Integrations.php @@ -2,14 +2,14 @@ namespace TEC\Event_Automator\Admin\Tabs; -use Tribe\Tickets\Admin\Settings as Plugin_Settings; - /** * Class Integrations * * @since 6.0.0 Migrated to Common from Event Automator * @package TEC\Event_Automator\Admin\Tabs * + * @deprecated 6.4.1 + * */ class Integrations { @@ -18,6 +18,8 @@ class Integrations { * * @since 6.0.0 Migrated to Common from Event Automator * + * @deprecated 6.4.1 + * * @var string */ public static $slug = 'integrations'; @@ -27,29 +29,12 @@ class Integrations { * * @since 6.0.0 Migrated to Common from Event Automator * + * @deprecated 6.4.1 + * * @param string Admin page id. */ public function register_tab( $admin_page ) { - if ( ! empty( $admin_page ) && Plugin_Settings::$settings_page_id !== $admin_page ) { - return; - } - - $tab_settings = [ - 'priority' => 35, - 'fields' => $this->get_fields(), - 'show_save' => true, - ]; - - /** - * Filter the tab settings options. - * - * @since 6.0.0 Migrated to Common from Event Automator - * - * @param array Key value pairs of setting options. - */ - $tab_settings = apply_filters( 'tec_event_automator_integrations_tab_settings', $tab_settings ); - - new \Tribe__Settings_Tab( static::$slug, esc_html__( 'Integrations', 'tribe-common' ), $tab_settings ); + _deprecated_function( __METHOD__, '6.4.1' ); } /** @@ -57,12 +42,14 @@ public function register_tab( $admin_page ) { * * @since 6.0.0 Migrated to Common from Event Automator * + * @deprecated 6.4.1 + * * @param array $tabs Array of tabs IDs for the Events settings page. * * @return array The filtered list of tab ids. */ public function register_tab_id( array $tabs ): array { - $tabs[] = static::$slug; + _deprecated_function( __METHOD__, '6.4.1' ); return $tabs; } @@ -72,35 +59,13 @@ public function register_tab_id( array $tabs ): array { * * @since 6.0.0 Migrated to Common from Event Automator * + * @deprecated 6.4.1 + * * @return array Key value pair for setting options. */ public function get_fields(): array { - $settings_start = [ - 'info-start' => [ - 'type' => 'html', - 'html' => '
', - ] - ]; - - $settings_end = [ - 'info-end' => [ - 'type' => 'html', - 'html' => '
', - ] - ]; - - /** - * Filters the fields in the Tickets > Settings > Integrations tab. - * Utilizes the name from Event Tickets Plus as this is a replacement if that plugin is deactivated. - * - * @since 6.0.0 Migrated to Common from Event Automator - * - * @param array $fields The current fields. - * - * @return array The fields, as updated by the settings. - */ - $fields = apply_filters( 'tec_tickets_plus_integrations_tab_fields', [] ); + _deprecated_function( __METHOD__, '6.4.1' ); - return array_merge( $settings_start, $fields, $settings_end ); + return []; } } diff --git a/src/Common/Event_Automator/Admin/Tabs/Tabs_Provider.php b/src/Common/Event_Automator/Admin/Tabs/Tabs_Provider.php index c878a1ed5b..5e3717f022 100644 --- a/src/Common/Event_Automator/Admin/Tabs/Tabs_Provider.php +++ b/src/Common/Event_Automator/Admin/Tabs/Tabs_Provider.php @@ -10,6 +10,8 @@ * @package TEC\Event_Automator\Admin\Tabs * * @since 6.0.0 Migrated to Common from Event Automator + * + * @deprecated 6.4.1 */ class Tabs_Provider extends Service_Provider { @@ -17,40 +19,33 @@ class Tabs_Provider extends Service_Provider { * Register the provider. * * @since 6.0.0 Migrated to Common from Event Automator + * + * @deprecated 6.4.1 */ public function register() { - - // If Plugin Settings does not exist, return as there is no settings tab to add. - if ( ! class_exists('Tribe\Tickets\Admin\Settings', false ) ) { - return; - } - - // If Event Tickets Plus is Active, do not add the Integrations tab as it will do it. - if ( class_exists('TEC\Tickets_Plus\Admin\Tabs\Provider', false ) ) { - return; - } - - // Hook actions and filters. - $this->add_actions(); - $this->add_filters(); + _deprecated_function( __METHOD__, '6.4.1' ); } /** * Add the action hooks. * * @since 6.0.0 Migrated to Common from Event Automator + * + * @deprecated 6.4.1 */ public function add_actions() { - add_action( 'tribe_settings_do_tabs', [ $this, 'add_tabs' ] ); + _deprecated_function( __METHOD__, '6.4.1' ); } /** * Add fhe filter hooks. * * @since 6.0.0 Migrated to Common from Event Automator + * + * @deprecated 6.4.1 */ public function add_filters() { - add_filter( 'tec_tickets_settings_tabs_ids', [ $this, 'filter_include_integrations_tab_id' ] ); + _deprecated_function( __METHOD__, '6.4.1' ); } /** @@ -58,12 +53,14 @@ public function add_filters() { * * @since 6.0.0 Migrated to Common from Event Automator * + * @deprecated 6.4.1 + * * @param string Admin page id. * * @return void */ public function add_tabs( $admin_page ) { - $this->container->make( Integrations::class )->register_tab( $admin_page ); + _deprecated_function( __METHOD__, '6.4.1' ); } /** @@ -71,11 +68,14 @@ public function add_tabs( $admin_page ) { * * @since 6.0.0 Migrated to Common from Event Automator * + * @deprecated 6.4.1 + * * @param array $tabs Array of tabs IDs for the Events settings page. * * @return array The filtered list of tab ids. */ public function filter_include_integrations_tab_id( array $tabs ): array { - return $this->container->make( Integrations::class )->register_tab_id( $tabs ); + _deprecated_function( __METHOD__, '6.4.1' ); + return []; } } diff --git a/src/Common/Event_Automator/Hooks.php b/src/Common/Event_Automator/Hooks.php index 9e980bc2de..2f6055393e 100644 --- a/src/Common/Event_Automator/Hooks.php +++ b/src/Common/Event_Automator/Hooks.php @@ -41,7 +41,6 @@ public function register() { $this->container->singleton( 'event-automator.hooks', $this ); add_action( 'admin_init', [ $this, 'run_updates' ], 10, 0 ); - add_action( 'admin_init', [ $this, 'admin_register' ], 0 ); } /** @@ -62,8 +61,9 @@ public function run_updates() { * Register providers at admin_init, so dependencies are loaded. * * @since 6.0.0 Migrated to Common from Event Automator + * @deprecated 6.4.1 */ public function admin_register() { - $this->container->register( Tabs_Provider::class ); + _deprecated_function( __METHOD__, '6.4.1' ); } } diff --git a/src/Common/Integrations/Plugin_Merge_Provider_Abstract.php b/src/Common/Integrations/Plugin_Merge_Provider_Abstract.php index 904eaf22bb..384dcb3988 100644 --- a/src/Common/Integrations/Plugin_Merge_Provider_Abstract.php +++ b/src/Common/Integrations/Plugin_Merge_Provider_Abstract.php @@ -94,7 +94,7 @@ public function get_plugin_real_path(): string { } // Get plugin data. - $plugin_data = get_plugin_data( $plugin_file_path ); + $plugin_data = get_plugin_data( $plugin_file_path, false, false ); // Check for TextDomain and match. if ( isset( $plugin_data['TextDomain'] ) && $plugin_data['TextDomain'] === $text_domain ) { diff --git a/src/Common/Notifications/Conditionals.php b/src/Common/Notifications/Conditionals.php new file mode 100644 index 0000000000..b2e457963a --- /dev/null +++ b/src/Common/Notifications/Conditionals.php @@ -0,0 +1,186 @@ +normalize_optin_status(); + + // We don't care what the value stored in tribe_options is - give us Telemetry's Opt_In\Status value. + $status = Config::get_container()->get( Status::class ); + $telemetry = $status->get() === $status::STATUS_ACTIVE; + + // Check if the user has opted in to telemetry, then If Telemetry is off, return the IAN opt-in value. + return apply_filters( 'tec_common_ian_opt_in', tribe_is_truthy( $telemetry ) || tribe_is_truthy( tribe_get_option( 'ian-notifications-opt-in', false ) ) ); + } + + /** + * Check if the conditions are met for the notifications. + * + * @since 6.4.0 + * + * @param array $feed The feed of notifications from the server. + * + * @return array The notifications that meet the conditions. + */ + public static function filter_feed( $feed ): array { + $notifications = array_filter( + $feed, + function ( $item ) { + if ( empty( $item['conditions'] ) || ! is_array( $item['conditions'] ) ) { + return true; + } + + $matches = []; + foreach ( $item['conditions'] as $condition ) { + if ( 0 === strpos( $condition, 'wp_version' ) ) { + $version = substr( $condition, strlen( 'wp_version' ) ); + + $matches['wp_version'] = self::check_wp_version( $version ); + } elseif ( 0 === strpos( $condition, 'php_version' ) ) { + $version = substr( $condition, strlen( 'php_version' ) ); + + $matches['php_version'] = self::check_php_version( $version ); + } elseif ( 0 === strpos( $condition, 'plugin_version' ) ) { + $split = explode( ':', $condition ); + $plugins = explode( ',', $split[1] ); + + $matches['plugin_version'] = self::check_plugin_version( (array) $plugins ); + } + } + + return ! in_array( false, $matches, true ); + } + ); + + // Ensure slugs are always unique. + $notifications = array_map( + function ( $item ) { + $item['slug'] = $item['id'] . '_' . $item['slug']; + + return $item; + }, + $notifications + ); + + return array_values( $notifications ); + } + + /** + * Check if the PHP version is correct. + * + * @since 6.4.0 + * + * @param string $version The version to check against. + * + * @return bool + */ + public static function check_php_version( $version ): bool { + if ( empty( $version ) ) { + return true; + } + + $version = preg_split( '/(?=\d)/', $version, 2 ); + + return (bool) apply_filters( 'tec_common_ian_conditional_php', version_compare( PHP_VERSION, $version[1], $version[0] ?? '>=' ) ); + } + + /** + * Check if the WP version is correct. + * + * @since 6.4.0 + * + * @param string $version The version to check against. + * + * @return bool + */ + public static function check_wp_version( $version ): bool { + if ( empty( $version ) ) { + return true; + } + + global $wp_version; + $version = preg_split( '/(?=\d)/', $version, 2 ); + + return (bool) apply_filters( 'tec_common_ian_conditional_wp', version_compare( $wp_version, $version[1], $version[0] ?? '>=' ) ); + } + + /** + * Check if the plugin version matches requirements. + * + * @since 6.4.0 + * + * @param array $plugins The required plugins to check. + * + * @return bool + */ + public static function check_plugin_version( array $plugins ): bool { + // If no plugins are specified as a condition, we can assume the condition is met. + if ( empty( $plugins ) ) { + return true; + } + + // Get all installed plugins data, keyed by plugin file name. + $all_plugins = get_plugins(); + + foreach ( $plugins as $plugin ) { + $pieces = explode( '@', $plugin ); + + // Find the actual plugin directory/file from the list. + $plugin_file = ''; + foreach ( $all_plugins as $k => $data ) { + // If the plugin directory/file_name contains the required slug. + if ( strpos( $k, $pieces[0] ) !== false ) { + $plugin_file = $k; + $installed = $data['Version']; + break; + } + } + + // We didn't find the plugin in the list of installed plugins. + if ( empty( $plugin_file ) ) { + return false; + } + + // If the plugin is not active, the condition is not met. + if ( ! is_plugin_active( $plugin_file ) ) { + return false; + } + + // Plugin is installed and active so compare its version to the required. + $version = preg_split( '/(?=\d)/', $pieces[1], 2 ); + if ( ! version_compare( $installed, $version[1], $version[0] ?: '>=' ) ) { + return false; + } + } + + // All plugins met the conditions. + return true; + } +} diff --git a/src/Common/Notifications/Controller.php b/src/Common/Notifications/Controller.php new file mode 100644 index 0000000000..97b0bf9148 --- /dev/null +++ b/src/Common/Notifications/Controller.php @@ -0,0 +1,233 @@ + [ $this, 'is_ian_page' ], + 'in_footer' => false, + 'localize' => [ + 'name' => 'commonIan', + 'data' => [ + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'common_ian_nonce' ), + 'readTxt' => esc_html__( 'Read notifications', 'tribe-common' ), + 'feed' => (object) [ + 'read' => [], + 'unread' => [], + ], + ], + ], + ] + ); + } + + /** + * Define which pages will show the notification icon. + * + * @since 6.4.0 + * + * @return bool + */ + public function is_ian_page() { + $screen = get_current_screen(); + $allowed = [ 'tribe_events', 'edit-tribe_events', 'tribe_events_page_tec-events-settings' ]; + + /** + * Filter the allowed pages for the Notifications icon. + * + * @since 6.4.0 + * + * @param array $allowed The allowed pages for the Notifications icon. + */ + $allowed = apply_filters( 'tec_common_ian_allowed_pages', $allowed ); + + /** + * Filter the showing of the Notifications icon. + * + * @since 6.4.0 + * + * @param bool Whether to show the icon or not. + */ + return apply_filters( 'tec_common_ian_show_icon', in_array( $screen->id, $allowed, true ) ); + } + + /** + * Logic for if the Notifications icon should be shown. + * + * @since 6.4.0 + * + * @param string $slug The slug of the plugin to show the notifications in. + * + * @return void + */ + public function show_icon( $slug ) { + if ( self::is_ian_page() && current_user_can( 'manage_options' ) ) { + $this->container->make( Notifications::class )->show_icon( $slug ); + } + } + + /** + * AJAX handler for opting in to Notifications. + * + * @since 6.4.0 + */ + public function opt_in() { + $this->container->make( Notifications::class )->opt_in(); + } + + /** + * AJAX handler for getting notifications. + * + * @since 6.4.0 + */ + public function get_feed() { + $this->container->make( Notifications::class )->get_feed(); + } + + /** + * AJAX handler for dismissing notifications. + * + * @since 6.4.0 + */ + public function handle_dismiss() { + $this->container->make( Notifications::class )->handle_dismiss(); + } + + /** + * AJAX handler for marking notifications as read. + * + * @since 6.4.0 + */ + public function handle_read() { + $this->container->make( Notifications::class )->handle_read(); + } + + /** + * AJAX handler for marking all notifications as read. + * + * @since 6.4.0 + */ + public function handle_read_all() { + $this->container->make( Notifications::class )->handle_read_all(); + } + + /** + * Adds the opt in/out control to the general tab debug section. + * + * @since 6.1.1 + * + * @param array $fields The fields for the general tab Debugging section. + * + * @return array The fields, with the optin control appended. + */ + public function filter_tribe_general_settings_debugging_section( $fields ): array { + $telemetry = tribe( Telemetry::class ); + $telemetry->init(); + $status = $telemetry::get_status_object(); + $opted = $status->get( Telemetry::get_plugin_slug() ); + + switch ( $opted ) { + case Status::STATUS_ACTIVE: + $attributes = [ + 'disabled' => 'disabled', + 'checked' => 'checked', + ]; + break; + default: + $attributes = []; + break; + } + + $tooltip = esc_html__( 'Enable this option to receive notifications about The Events Calendar, including updates, fixes, and features. This is enabled if you have opted in to Telemetry.', 'tribe-common' ); + + /** + * Filter the tooltip text for the IAN opt-in setting. + * + * @since 6.4.0 + */ + $tooltip = apply_filters( 'tec_common_ian_setting_optin_tooltip', $tooltip ); + + $fields['ian-notifications-opt-in'] = [ + 'type' => 'checkbox_bool', + 'label' => esc_html__( 'In-App Notifications', 'tribe-common' ), + 'tooltip' => $tooltip, + 'default' => false, + 'validation_type' => 'boolean', + 'attributes' => $attributes, + ]; + + return $fields; + } +} diff --git a/src/Common/Notifications/Notifications.php b/src/Common/Notifications/Notifications.php new file mode 100644 index 0000000000..29314cb03f --- /dev/null +++ b/src/Common/Notifications/Notifications.php @@ -0,0 +1,286 @@ +api_url = $this->get_api_url(); + $this->slugs = $this->get_plugins(); + + /** + * Allow plugins to hook in and add themselves, + * running their own actions after IAN is initiated. + * + * @since 6.4.0 + * + * @param self $ian The IAN instance. + */ + do_action( 'tec_common_ian_loaded', $this ); + } + + /** + * Get the API URL for the In-App Notifications. + * + * @since 6.4.0 + * + * @return string + */ + public function get_api_url() { + $api = defined( 'TEC_COMMON_IAN_API_URL' ) ? TEC_COMMON_IAN_API_URL : 'https://ian.stellarwp.com/feed/stellar/tec/plugins.json'; + + /** + * Filter the API URL for the In-App Notifications. + * + * @since 6.4.0 + * + * @param string $api The API URL for the In-App Notifications. + * @param object $this The current instance of the class. + */ + $api = apply_filters( 'tec_common_ian_api_url', $api, $this ); + + return $api; + } + + /** + * Register the plugins that support In-App Notifications. + * + * @since 6.4.0 + * + * @return array The slugs for plugins that support IAN. + */ + public function get_plugins() { + $plugins = [ 'the-events-calendar', 'event-tickets' ]; + + /** + * Filter the plugin slugs for the In-App Notifications. + * + * @since 6.4.0 + * + * @param array $slugs The slugs for plugins that support IAN. + */ + return apply_filters( 'tec_common_ian_slugs', $plugins ); + } + + /** + * Show our notification icon. + * + * @since 6.4.0 + * + * @param string $slug The plugin slug for IAN. + * + * @return void + */ + public function show_icon( $slug ): void { + if ( ! in_array( $slug, $this->get_plugins(), true ) ) { + return; + } + + /** + * Filter allowing disabling of the Notifications by returning false. + * + * @since 6.4.0 + * + * @param bool $show Whether to render the IAN sidebar or not. + */ + $show = (bool) apply_filters( 'tec_common_ian_render', true, $slug ); + + if ( ! $show ) { + return; + } + + $template = new Template(); + $template->render_sidebar( [ 'slug' => $slug ], true ); + } + + /** + * Optin to IAN notifications. + * + * @since 6.4.0 + * + * @return void + */ + public function opt_in() { + if ( ! wp_verify_nonce( tec_get_request_var( 'nonce' ), 'common_ian_nonce' ) ) { + wp_send_json_error( esc_html__( 'Invalid nonce', 'tribe-common' ), 403 ); + return; + } + + tribe_update_option( 'ian-notifications-opt-in', 1 ); + + wp_send_json_success( esc_html__( 'Notifications opt-in successful', 'tribe-common' ), 200 ); + } + + /** + * Get the IAN notifications. + * + * @since 6.4.0 + * + * @return void + */ + public function get_feed() { + if ( ! wp_verify_nonce( tec_get_request_var( 'nonce' ), 'common_ian_nonce' ) ) { + wp_send_json_error( esc_html__( 'Invalid nonce', 'tribe-common' ), 403 ); + return; + } + + $cache = tribe_cache(); + $feed = $cache->get_transient( 'tec_ian_api_feed' ); + if ( false === $feed || ! is_array( $feed ) ) { + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get + $response = wp_remote_get( $this->api_url ); + if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) { + $cache->set_transient( 'tec_ian_api_feed', [], 15 * MINUTE_IN_SECONDS ); + wp_send_json_error( wp_remote_retrieve_response_message( $response ), wp_remote_retrieve_response_code( $response ) ); + return; + } + $body = json_decode( wp_remote_retrieve_body( $response ), true ); + $feed = Conditionals::filter_feed( $body['notifications_by_area']['general-tec'] ?? [] ); + $cache->set_transient( 'tec_ian_api_feed', $feed, 15 * MINUTE_IN_SECONDS ); + } + + $template = new Template(); + foreach ( $feed as $k => $notification ) { + $this->slug = $notification['slug']; + if ( $this->has_user_dismissed() ) { + unset( $feed[ $k ] ); + continue; + } + + $notification['read'] = $this->has_user_read(); + + $feed[ $k ]['html'] = $template->render_notification( $notification, false ); + $feed[ $k ]['read'] = $notification['read'] ?? false; + } + array_values( $feed ); + + wp_send_json_success( $feed, 200 ); + } + + /** + * AJAX handler for dismissing IAN notifications. + * + * @since 6.4.0 + * + * @return void + */ + public function handle_dismiss(): void { + $id = tec_get_request_var( 'id' ); + + if ( ! wp_verify_nonce( tec_get_request_var( 'nonce' ), 'ian_nonce_' . $id ) ) { + wp_send_json_error( esc_html__( 'Invalid nonce', 'tribe-common' ), 403 ); + return; + } + + $slug = tec_get_request_var( 'slug' ); + + if ( empty( $slug ) ) { + wp_send_json_error( esc_html__( 'Invalid notification slug', 'tribe-common' ), 403 ); + return; + } + + $this->slug = $slug; + $this->dismiss(); + + wp_send_json_success( esc_html__( 'Notification dismissed', 'tribe-common' ), 200 ); + } + + /** + * AJAX handler for marking IAN notifications as read. + * + * @since 6.4.0 + * + * @return void + */ + public function handle_read(): void { + $id = tec_get_request_var( 'id' ); + + if ( ! wp_verify_nonce( tec_get_request_var( 'nonce' ), 'ian_nonce_' . $id ) ) { + wp_send_json_error( esc_html__( 'Invalid nonce', 'tribe-common' ), 403 ); + return; + } + + $slug = tec_get_request_var( 'slug' ); + + if ( empty( $slug ) ) { + wp_send_json_error( esc_html__( 'Invalid notification slug', 'tribe-common' ), 403 ); + return; + } + + $this->slug = $slug; + $this->read(); + + wp_send_json_success( esc_html__( 'Notification marked as read', 'tribe-common' ), 200 ); + } + + /** + * AJAX handler for marking all IAN notifications as read. + * + * @since 6.4.0 + * + * @return void + */ + public function handle_read_all(): void { + if ( ! wp_verify_nonce( tec_get_request_var( 'nonce' ), 'common_ian_nonce' ) ) { + wp_send_json_error( esc_html__( 'Invalid nonce', 'tribe-common' ), 403 ); + return; + } + + $unread = json_decode( stripslashes( tec_get_request_var( 'unread' ) ), true ); + + foreach ( $unread as $slug ) { + $this->slug = $slug; + $this->read(); + } + + wp_send_json_success( esc_html__( 'All notifications marked as read', 'tribe-common' ), 200 ); + } +} diff --git a/src/Common/Notifications/Readable_Trait.php b/src/Common/Notifications/Readable_Trait.php new file mode 100644 index 0000000000..3f166574bc --- /dev/null +++ b/src/Common/Notifications/Readable_Trait.php @@ -0,0 +1,189 @@ +slug ) ) { + return ''; + } + + return $this->read_nonce_action_prefix . $this->slug; + } + + /** + * Get the nonce for this readable content. + * + * @since 6.4.0 + * + * @return string + */ + public function get_read_nonce(): string { + return wp_create_nonce( $this->get_read_nonce_action() ); + } + + /** + * This will allow the user to read the notification using JS. + * + * @since 6.4.0 + * + * @return void + */ + public function handle_read(): void { + if ( empty( $this->slug ) ) { + wp_send_json( false ); + } + + $slug = tribe_get_request_var( 'slug', false ); + if ( empty( $slug ) ) { + wp_send_json( false ); + } + + $slug = sanitize_key( $slug ); + + if ( $this->slug !== $slug ) { + wp_send_json( false ); + } + + $nonce = tribe_get_request_var( 'nonce', false ); + $nonce_action = $this->get_read_nonce_action(); + + if ( ! wp_verify_nonce( $nonce, $nonce_action ) ) { + wp_send_json( false ); + } + + // Send a JSON answer with the status of reading. + wp_send_json( $this->read() ); + } + + /** + * A Method to add the Meta value that this notification has been read. + * + * @since 6.4.0 + * + * @param int|null|string $user_id The user ID. + * + * @return boolean + */ + protected function read( $user_id = null ): bool { + if ( empty( $this->slug ) ) { + return false; + } + + if ( is_null( $user_id ) ) { + $user_id = get_current_user_id(); + } + + // If this user has read we don't care either. + if ( $this->has_user_read( $user_id ) ) { + return true; + } + + update_user_meta( $user_id, $this->read_meta_key_time_prefix . $this->slug, time() ); + + return (bool) add_user_meta( $user_id, $this->read_meta_key, $this->slug ); + } + + /** + * Removes the user meta that holds if this content has been read. + * + * @since 6.4.0 + * + * @param int|null|string $user_id The user ID. + * + * @return boolean + */ + public function unread( $user_id = null ): bool { + if ( empty( $this->slug ) ) { + return false; + } + + if ( null === $user_id ) { + $user_id = get_current_user_id(); + } + + // If this user has read we don't care either. + if ( ! $this->has_user_read( $user_id ) ) { + return false; + } + + return delete_user_meta( $user_id, $this->read_meta_key, $this->slug ); + } + + /** + * Checks if a given user has read a given notification. + * + * @since 6.4.0 + * + * @param int|null|string $user_id The user ID. + * + * @return boolean + */ + public function has_user_read( $user_id = null ): bool { + if ( empty( $this->slug ) ) { + return false; + } + + if ( null === $user_id ) { + $user_id = get_current_user_id(); + } + + $read_notifications = get_user_meta( $user_id, $this->read_meta_key ); + + if ( ! is_array( $read_notifications ) ) { + return false; + } + + if ( ! in_array( $this->slug, $read_notifications, true ) ) { + return false; + } + + return true; + } +} diff --git a/src/Common/Notifications/Template.php b/src/Common/Notifications/Template.php new file mode 100644 index 0000000000..d9330184fb --- /dev/null +++ b/src/Common/Notifications/Template.php @@ -0,0 +1,88 @@ +set_template_origin( Tribe__Main::instance() ); + $this->set_template_folder( 'src/admin-views/notifications' ); + $this->set_template_context_extract( true ); + $this->set_template_folder_lookup( false ); + } + + /** + * Render the notification sidebar. + * + * @since 6.4.0 + * + * @param array $args Array of arguments that will ultimately be sent to the template. + * @param bool $output Whether or not to echo the HTML. Defaults to true. + * + * @return string HTML of notification sidebar. + */ + public function render_sidebar( $args, $output = true ) { + $args = wp_parse_args( + $args, + [ + 'slug' => $args['slug'], + 'main' => Tribe__Main::instance(), + 'optin' => Conditionals::get_opt_in(), + 'url' => Telemetry::get_permissions_url(), + ] + ); + + return $this->template( 'sidebar', $args, $output ); + } + + /** + * Render the notification. + * + * @since 6.4.0 + * + * @param array $args Array of arguments that will ultimately be sent to the template. + * @param bool $output Whether or not to echo the HTML. Defaults to true. + * + * @return string HTML of notification. + */ + public function render_notification( $args, $output = true ) { + $args = wp_parse_args( + $args, + [ + 'type' => $args['type'] ?? 'notice', + 'id' => $args['id'] ?? '', + 'dismissible' => $args['dismissible'] ?? true, + 'slug' => $args['slug'] ?? '', + 'title' => $args['title'] ?? '', + 'html' => $args['html'] ?? '', + 'actions' => $args['actions'] ?? [], + 'read' => $args['read'] ?? false, + ] + ); + + return $this->template( 'notification', $args, $output ); + } +} diff --git a/src/Tribe/Admin/Help_Page.php b/src/Tribe/Admin/Help_Page.php index d839013ca0..5640f2a79c 100644 --- a/src/Tribe/Admin/Help_Page.php +++ b/src/Tribe/Admin/Help_Page.php @@ -4,6 +4,7 @@ * Administration Help Page * * @since 4.0 + * @deprecated 6.3.2 This class is deprecated and should no longer be used. Use \TEC\Common\Admin\Help_Hub\Hub instead. */ // Don't load directly. @@ -15,6 +16,7 @@ * Class with a few helpers for the Administration Pages * * @since 4.0 + * @deprecated 6.3.2 This class is deprecated. Use \TEC\Common\Admin\Help_Hub\Hub instead. */ class Tribe__Admin__Help_Page { //phpcs:ignore - legacy class naming. @@ -24,6 +26,7 @@ class Tribe__Admin__Help_Page { * @return Tribe__Admin__Help_Page */ public static function instance() { + _deprecated_function( __METHOD__, '6.3.2', '\TEC\Common\Admin\Help_Hub\Hub' ); return tribe( static::class ); } @@ -543,7 +546,7 @@ private function get_plugin_api_data( $plugin = null ) { if ( ! is_wp_error( $data ) ) { // Format Downloaded Infomation. - $data->downloaded = $data->downloaded ? number_format( $data->downloaded ) : _x( 'n/a', 'not available', 'tribe-common' ); + $data->downloaded = $data->downloaded ? number_format( (float) $data->downloaded ) : _x( 'n/a', 'not available', 'tribe-common' ); } else { // If there was a bug on the Current Request just leave. return false; @@ -966,7 +969,7 @@ public function print_plugin_box( $plugin ) {
requires ); ?>+
-
active_installs ) ); ?>+
+
active_installs ) ); ?>+
diff --git a/src/Tribe/Admin/Notice/Date_Based.php b/src/Tribe/Admin/Notice/Date_Based.php index be2ed2e1ca..192f7bed26 100644 --- a/src/Tribe/Admin/Notice/Date_Based.php +++ b/src/Tribe/Admin/Notice/Date_Based.php @@ -112,7 +112,7 @@ abstract class Date_Based { 'events_page_tribe-app-shop', // App shop. 'toplevel_page_tec-events', // New Events Welcome. 'tribe_events_page_tec-events-settings', // New Events Settings. - 'tribe_events_page_tec-events-help', // New Events Help. + 'tribe_events_page_tec-events-help-hub', // New Events Help. 'tribe_events_page_tec-troubleshooting', // New Events Troubleshooting. 'tickets_page_tec-tickets-settings', // New Tickets Settings. 'toplevel_page_tec-tickets', // New Tickets Welcome. diff --git a/src/Tribe/Admin/Notice/Service_Provider.php b/src/Tribe/Admin/Notice/Service_Provider.php index 67e744bf17..0085f297b8 100644 --- a/src/Tribe/Admin/Notice/Service_Provider.php +++ b/src/Tribe/Admin/Notice/Service_Provider.php @@ -10,6 +10,7 @@ namespace Tribe\Admin\Notice; use TEC\Common\Contracts\Service_Provider as Provider_Contract; +use TEC\Common\StellarWP\AdminNotices\AdminNotices; /** * Class Notice @@ -30,6 +31,10 @@ public function register() { tribe_singleton( 'pue.notices', 'Tribe__PUE__Notices' ); tribe_singleton( WP_Version::class, WP_Version::class, [ 'hook' ] ); tribe_singleton( 'admin.notice.php.version', \Tribe__Admin__Notice__Php_Version::class, [ 'hook' ] ); + AdminNotices::initialize( + 'tec_common', + plugin_dir_url( \Tribe__Main::instance()->plugin_path ) . 'common/vendor/vendor-prefixed/stellarwp/admin-notices' + ); $this->hooks(); } diff --git a/src/Tribe/Admin/Notices.php b/src/Tribe/Admin/Notices.php index 86072e4bfc..e975abd632 100644 --- a/src/Tribe/Admin/Notices.php +++ b/src/Tribe/Admin/Notices.php @@ -292,6 +292,8 @@ public function render( $slug, $content = null, $return = true, $wrap = false ) $classes[] = 'inline'; } + $content ??= $notice->content; + // Prevents Empty Notices if ( empty( $content ) ) { return false; diff --git a/src/Tribe/Cost_Utils.php b/src/Tribe/Cost_Utils.php index 42a112a19f..8243a9297b 100644 --- a/src/Tribe/Cost_Utils.php +++ b/src/Tribe/Cost_Utils.php @@ -109,7 +109,7 @@ public function maybe_replace_cost_with_free( $cost ) { if ( is_numeric( $cost_with_period ) - && '0.00' === number_format( $cost_with_period, 2, '.', ',' ) + && '0.00' === number_format( (float) $cost_with_period, 2, '.', ',' ) ) { return esc_html__( 'Free', 'tribe-common' ); } @@ -353,7 +353,7 @@ public function parse_cost_range( $costs, $max_decimals = null, $sort = true ) { if ( is_numeric( $numeric_cost ) ) { // Creates a Well Balanced Index that will perform good on a Key Sorting method - $index = str_replace( [ '.', ',' ], '', number_format( $numeric_cost, $max ) ); + $index = str_replace( [ '.', ',' ], '', number_format( (float) $numeric_cost, $max ) ); } else { // Makes sure that we have "index-safe" string $index = sanitize_title( $numeric_cost ); diff --git a/src/Tribe/Log/Admin.php b/src/Tribe/Log/Admin.php index c5aa2472df..fc7ac420ca 100644 --- a/src/Tribe/Log/Admin.php +++ b/src/Tribe/Log/Admin.php @@ -127,7 +127,7 @@ public function register_script() { * @return boolean True if the assets should be enqueued. */ public function should_enqueue_assets() { - return Tribe__Admin__Help_Page::instance()->is_current_page() || tribe( Troubleshooting::class )->is_current_page(); + return tribe( Troubleshooting::class )->is_current_page(); } /** diff --git a/src/Tribe/Main.php b/src/Tribe/Main.php index a983307159..6c4e188c57 100644 --- a/src/Tribe/Main.php +++ b/src/Tribe/Main.php @@ -4,6 +4,7 @@ use TEC\Common\Translations_Loader; use Tribe\Admin\Settings; use Tribe\DB_Lock; +use TEC\Common\Asset; // Don't load directly. if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +20,7 @@ class Tribe__Main { const OPTIONNAME = 'tribe_events_calendar_options'; const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options'; const FEED_URL = 'https://theeventscalendar.com/feed/'; - const VERSION = '6.4.0'; + const VERSION = '6.4.1'; protected $plugin_context; protected $plugin_context_class; @@ -221,13 +222,21 @@ public function init_libraries() { * Registers resources that can/should be enqueued */ public function load_assets() { + Asset::add( + 'tribe-clipboard', + 'vendor/clipboard.min.js', + self::VERSION + ) + ->prefix_asset_directory( false ) + ->use_asset_file( false ) + ->register(); + // These ones are only registered tribe_assets( $this, [ [ 'tribe-accessibility-css', 'accessibility.css' ], [ 'tribe-query-string', 'utils/query-string.js' ], - [ 'tribe-clipboard', 'node_modules/clipboard/dist/clipboard.min.js' ], [ 'datatables', 'vendor/datatables/datatables.js', [ 'jquery' ] ], [ 'tribe-select2', 'vendor/tribe-selectWoo/dist/js/selectWoo.full.js', [ 'jquery' ] ], [ 'tribe-select2-css', 'vendor/tribe-selectWoo/dist/css/selectWoo.css' ], @@ -759,6 +768,7 @@ public function bind_implementations() { tribe_register_provider( Tribe\Service_Providers\Onboarding::class ); tribe_register_provider( Tribe\Admin\Notice\Service_Provider::class ); tribe_register_provider( \TEC\Common\Admin\Conditional_Content\Controller::class ); + tribe_register_provider( \TEC\Common\Notifications\Controller::class ); tribe_register_provider( Libraries\Provider::class ); // Load the new third-party integration system. @@ -766,6 +776,9 @@ public function bind_implementations() { // Load Site Health and Telemetry. tribe_register_provider( TEC\Common\Site_Health\Provider::class ); tribe_register_provider( TEC\Common\Telemetry\Provider::class ); + + // Load Help Hub. + tribe_register_provider( TEC\Common\Admin\Help_Hub\Provider::class ); } /** diff --git a/src/Tribe/PUE/Checker.php b/src/Tribe/PUE/Checker.php index 4d4d6def15..486bd87c53 100755 --- a/src/Tribe/PUE/Checker.php +++ b/src/Tribe/PUE/Checker.php @@ -181,6 +181,20 @@ class Tribe__PUE__Checker { */ private $validate_query = []; + /** + * A unique list of instances of the PUE Checker that has been initialized. + * + * @since 6.3.2 + * + * @var Tribe__PUE__Checker[] Instances of checkers that have been registered. + */ + protected static $instances = []; + + /** + * @var string The transient key. + */ + public const IS_ANY_LICENSE_VALID_TRANSIENT_KEY = 'TEC_IS_ANY_LICENSE_VALID_TRANSIENT'; + /** * Class constructor. * @@ -206,14 +220,23 @@ public function __construct( $pue_update_url, $slug = '', $options = [], $plugin $this->set_options( $options ); $this->hooks(); $this->set_key_status_name(); + // So we can reference our "registered" instances later. + self::$instances[ $slug ] ??= $this; } /** * Gets whether the license key is valid or not. * * @since 4.14.9 + * @since 6.4.1 Added uplink resource check. */ public function is_key_valid() { + $uplink_resource = get_resource( $this->get_slug() ); + + if ( $uplink_resource ) { + return $uplink_resource->has_valid_license(); + } + // @todo remove transient in a major feature release where we release all plugins. $status = get_transient( $this->pue_key_status_transient_name ); @@ -224,6 +247,57 @@ public function is_key_valid() { return 'valid' === $status; } + /** + * Iterate on all the registered PUE Product Licenses we have and find if any are valid. + * Will revalidate the licenses if none are found to be valid. + * + * @todo In scenarios where a user goes from a Free license to an active license the transient may give a false positive. + * + * @since 6.3.2 + * + * @return bool + */ + public static function is_any_license_valid(): bool { + $valid_slug = 'valid'; + $has_valid = false; + + // Check our transient. + $transient_value = get_transient( self::IS_ANY_LICENSE_VALID_TRANSIENT_KEY ); + if ( ! empty( $transient_value ) ) { + return $transient_value === $valid_slug; + } + + // Check our local transient/cache first. + foreach ( self::$instances as $checker ) { + if ( $checker->is_key_valid() ) { + set_transient( self::IS_ANY_LICENSE_VALID_TRANSIENT_KEY, $valid_slug, HOUR_IN_SECONDS ); + $has_valid = true; + break; + } + } + + if ( ! $has_valid ) { + // Revalidate if we haven't found a valid license yet. + foreach ( self::$instances as $checker ) { + $license = get_option( $checker->get_license_option_key() ); + $response = $checker->validate_key( $license ); + // Is it valid? + if ( ! empty( $response['status'] ) ) { + set_transient( self::IS_ANY_LICENSE_VALID_TRANSIENT_KEY, $valid_slug, HOUR_IN_SECONDS ); + $has_valid = true; + break; + } + } + } + + // We found no valid licenses above. + if ( ! $has_valid ) { + set_transient( self::IS_ANY_LICENSE_VALID_TRANSIENT_KEY, 'invalid', HOUR_IN_SECONDS ); + } + + return get_transient( self::IS_ANY_LICENSE_VALID_TRANSIENT_KEY ) === $valid_slug; + } + /** * Gets whether or not the PUE key validation check is expired. * @@ -325,6 +399,8 @@ public function hooks() { add_filter( 'upgrader_pre_download', [ Tribe__PUE__Package_Handler::instance(), 'filter_upgrader_pre_download' ], 5, 3 ); } + + /********************** Getter / Setter Functions **********************/ /** @@ -1029,6 +1105,12 @@ public function validate_key( $key, $network = false ) { $response = []; $response['status'] = 0; + $uplink_resource = get_resource( $this->get_slug() ); + + if ( $uplink_resource ) { + $key = $uplink_resource->get_license_key(); + } + if ( ! $key ) { $response['message'] = sprintf( /* Translators: %1$s and %2$s are opening and closing tags, respectively. */ diff --git a/src/Tribe/PUE/Notices.php b/src/Tribe/PUE/Notices.php index 4f9c8702df..8d8eb00ca1 100644 --- a/src/Tribe/PUE/Notices.php +++ b/src/Tribe/PUE/Notices.php @@ -1,4 +1,5 @@ populate(); add_action( 'current_screen', [ $this, 'setup_notices' ] ); add_action( 'tribe_pue_notices_save_notices', [ $this, 'maybe_undismiss_notices' ] ); + add_filter( 'stellarwp/uplink/tec/client_validate_license', [ $this, 'clear_notices_after_uplink_connect' ] ); + } + + /** + * Clears any license key notices for the specified plugin. + * + * @since 6.4.1 + * + * @param Validation_Response $results The validation results from uplink. + * + * @return Validation_Response + */ + public function clear_notices_after_uplink_connect( Validation_Response $results ): Validation_Response { + if ( ! $results->is_valid() ) { + return $results; + } + + $this->populate(); + if ( empty( $this->notices ) || ! is_array( $this->notices ) ) { + return $results; + } + + foreach ( $this->notices as $key => $data ) { + if ( isset( $results->slug ) ) { + unset( $this->notices[ $key ][ $results->slug ] ); + } + + if ( isset( $results->name ) ) { + unset( $this->notices[ $key ][ $results->name ] ); + } + } + + $this->save_notices(); + + return $results; } /** diff --git a/src/Tribe/Service_Providers/Onboarding.php b/src/Tribe/Service_Providers/Onboarding.php index b26b29fa73..ec763f8540 100644 --- a/src/Tribe/Service_Providers/Onboarding.php +++ b/src/Tribe/Service_Providers/Onboarding.php @@ -3,6 +3,7 @@ use \Tribe\Onboarding\Main as Onboarding_Main; use TEC\Common\Contracts\Service_Provider; +use TEC\Common\Asset; /** * Class Onboarding @@ -57,29 +58,30 @@ protected function hooks() { public function register_assets() { $main = \Tribe__Main::instance(); - tribe_asset( - $main, + Asset::add( 'tec-intro-js', - 'node_modules/intro.js/intro.js', - [], - [ 'admin_enqueue_scripts' ], - [ - 'groups' => self::$group_key, - 'conditionals' => [ $this, 'should_enqueue_assets' ], - ] - ); - - tribe_asset( - $main, + 'vendor/intro.min.js', + \Tribe__Main::VERSION + ) + ->add_to_group( self::$group_key ) + ->set_condition( [ $this, 'should_enqueue_assets' ] ) + ->enqueue_on( 'admin_enqueue_scripts' ) + ->prefix_asset_directory( false ) + ->use_asset_file( false ) + ->register(); + + Asset::add( 'tec-intro-styles', - 'node_modules/intro.js/introjs.css', - [], - [ 'admin_enqueue_scripts' ], - [ - 'groups' => self::$group_key, - 'conditionals' => [ $this, 'should_enqueue_assets' ], - ] - ); + 'vendor/introjs.min.css', + \Tribe__Main::VERSION + ) + ->add_to_group( self::$group_key ) + ->set_condition( [ $this, 'should_enqueue_assets' ] ) + ->enqueue_on( 'admin_enqueue_scripts' ) + ->prefix_asset_directory( false ) + ->use_asset_file( false ) + ->register(); + tribe_asset( $main, diff --git a/src/Tribe/Settings.php b/src/Tribe/Settings.php index 4a984298bd..e0cafbbe12 100755 --- a/src/Tribe/Settings.php +++ b/src/Tribe/Settings.php @@ -13,6 +13,7 @@ use TEC\Common\Admin\Entities\Element_With_Children; use TEC\Common\Admin\Entities\Field_Wrapper; use Tribe\Admin\Pages as Admin_Pages; +use TEC\Common\Notifications\Controller; if ( did_action( 'tec_settings_init' ) ) { return; @@ -304,7 +305,7 @@ public function __construct() { /** * Magic getter for deprecated properties. * - * @since TBD + * @since 6.3.1 * * @param string $name The property name we are looking for. * @@ -589,12 +590,17 @@ public function get_settings_page_url( array $args = [] ) { */ public function do_page_header( $admin_page ): void { ?> -

- is_event_settings() ) : ?> - get_page_logo( $admin_page ) ); ?> +
+

+ is_event_settings() ) : ?> + get_page_logo( $admin_page ) ); ?> + + get_page_title( $admin_page ) ); ?> +

+ is_ian_page() ) : ?> +
- get_page_title( $admin_page ) ); ?> -

+ get_current_tab(); $wrap_classes = apply_filters( 'tribe_settings_wrap_classes', [ 'tribe_settings', 'wrap' ], $admin_page ); $is_event_settings = $this->is_event_settings( $admin_page ); - $form_classes = [ "tec-settings-form__{$current_tab}-tab--active" ]; - - if ( $this->get_tab( $current_tab )->has_parent() ) { - $form_classes[] = 'tec-settings-form__subnav-active'; - } + $tab_object = $this->get_tab( $current_tab ); + $form_classes = [ + "tec-settings-form__{$current_tab}-tab--active" => true, + 'tec-settings-form__subnav-active' => ( $tab_object && $tab_object->has_parent() ), + ]; /** * Filter the classes for the settings form. * * @since 6.1.0 * - * @param array $form_classes The classes for the settings form. + * @param array $form_classes The classes for the settings form. + * @param string $admin_page The admin page ID. + * @param Tribe__Settings_Tab|null $tab_object The current tab object. */ - $form_classes = apply_filters( 'tribe_settings_form_class', $form_classes, $admin_page ); + $form_classes = apply_filters( 'tribe_settings_form_class', $form_classes, $admin_page, $tab_object ); ob_start(); do_action( 'tribe_settings_top', $admin_page ); @@ -822,16 +831,13 @@ public function generate_tabs( $modal = false ): void { } $nav_id = $modal ? 'tec-settings-modal-nav' : 'tribe-settings-tabs'; + $tab_object = $this->get_tab( $this->get_current_tab() ); $wrapper_classes = [ 'tec-nav__wrapper' => true, - 'tec-settings__nav-wrapper' => $this->is_event_settings(), - 'tec-nav__wrapper--subnav-active' => false, + 'tec-settings__nav-wrapper' => (bool) $this->is_event_settings(), + 'tec-nav__wrapper--subnav-active' => (bool) ( $tab_object && $tab_object->has_parent() ), ]; - if ( $this->get_tab( $this->get_current_tab() )->has_parent() ) { - $wrapper_classes['tec-nav__wrapper--subnav-active'] = true; - } - ob_start(); ?>