diff --git a/.github/workflows/buildvue.yml b/.github/workflows/buildvue.yml index fe783e32f51..fe140fdc6d6 100644 --- a/.github/workflows/buildvue.yml +++ b/.github/workflows/buildvue.yml @@ -19,6 +19,7 @@ permissions: jobs: build: runs-on: ubuntu-latest + name: build vue files steps: - name: Detect branch for PR id: vars diff --git a/.github/workflows/buildwoff2.yml b/.github/workflows/buildwoff2.yml index 58fef86f427..ed26b7c5533 100644 --- a/.github/workflows/buildwoff2.yml +++ b/.github/workflows/buildwoff2.yml @@ -19,6 +19,7 @@ permissions: jobs: build: runs-on: ubuntu-latest + name: build woff2 steps: - name: Detect branch for PR id: vars diff --git a/.github/workflows/composer-update.yml b/.github/workflows/composer-update.yml index 8bdcf308dc0..088cbd09756 100644 --- a/.github/workflows/composer-update.yml +++ b/.github/workflows/composer-update.yml @@ -84,7 +84,7 @@ jobs: message= curl \ --request POST \ - --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + --header 'authorization: Bearer ${{ secrets.CUSTOM_ACCESS_TOKEN }}' \ --header 'content-type: application/json' \ --data '{ "title":"[automatic composer updates]", diff --git a/.github/workflows/matomo-tests.yml b/.github/workflows/matomo-tests.yml index 154ce2c2440..1ff74e9486a 100644 --- a/.github/workflows/matomo-tests.yml +++ b/.github/workflows/matomo-tests.yml @@ -13,6 +13,12 @@ on: - '**.x-dev' - 'next_release' workflow_dispatch: + workflow_call: + inputs: + is_preview: + type: boolean + required: false + default: false permissions: actions: read @@ -34,7 +40,7 @@ jobs: PHP: runs-on: ubuntu-20.04 strategy: - fail-fast: false + fail-fast: ${{ inputs.is_preview == true }} matrix: type: [ 'UnitTests', 'SystemTestsPlugins', 'SystemTestsCore', 'IntegrationTestsCore', 'IntegrationTestsPlugins' ] php: [ '7.2', '8.2', '8.3' ] @@ -60,6 +66,7 @@ jobs: persist-credentials: false submodules: true path: matomo + ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }} - name: running tests uses: matomo-org/github-action-tests@main with: @@ -81,6 +88,7 @@ jobs: persist-credentials: false submodules: true path: matomo + ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }} - name: running tests uses: matomo-org/github-action-tests@main with: @@ -97,6 +105,7 @@ jobs: persist-credentials: false submodules: true path: matomo + ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }} - name: running tests uses: matomo-org/github-action-tests@main with: @@ -106,7 +115,7 @@ jobs: UI: runs-on: ubuntu-20.04 strategy: - fail-fast: false + fail-fast: ${{ inputs.is_preview == true }} matrix: parts: [ 0,1,2,3 ] steps: @@ -116,6 +125,7 @@ jobs: persist-credentials: false submodules: true path: matomo + ref: ${{ inputs.is_preview == true && '5.x-preview' || github.ref }} - name: running tests uses: matomo-org/github-action-tests@main with: diff --git a/.github/workflows/release-preview.yml b/.github/workflows/release-preview.yml new file mode 100644 index 00000000000..7b6c1930856 --- /dev/null +++ b/.github/workflows/release-preview.yml @@ -0,0 +1,169 @@ +# Matomo release action for automated PREVIEW releases +# +# Required GitHub secrets: +# +# GPG_CERTIFICATE | ASCII armored or Base64 encoded GPG certificate that is used to create the signatures for the archives +# GPG_CERTIFICATE_PASS | Passphrase of the GPG key + +name: Build preview release + +permissions: + actions: read # required for the tests job + checks: none + contents: write # required to create tag and release + deployments: none + issues: read # required for the tests job + packages: none + pull-requests: read # required for the tests jobs + repository-projects: none + security-events: none + statuses: none + +on: + # TODO: remove manual dispatch after testing and enable cron + workflow_dispatch: + branches: + - 5.x-dev + inputs: + password: + description: 'Release password' + required: true + #schedule: + # - cron: '0 1 * * *' # 1am daily +env: + RELEASE_PASSWORD: ${{ secrets.RELEASE_PASSWORD }} +jobs: + prepare_preview_version: + runs-on: ubuntu-latest + outputs: + do_release: ${{ steps.changes.outputs.do_release }} + has_new_version: ${{ steps.version.outputs.has_new_version }} + steps: + - name: "Check release password" + if: ${{ github.event.inputs.password != env.RELEASE_PASSWORD }} + uses: actions/github-script@v6 + with: + script: | + core.setFailed('Release password didn\'t match.') + - name: "Check if user is allowed" + if: ${{ github.actor != 'mattab' && github.actor != 'tsteur' && github.actor != 'sgiehl' && github.actor != 'mneudert' && github.actor != 'michalkleiner' && github.actor != 'caddoo'}} + uses: actions/github-script@v6 + with: + script: | + core.setFailed('User is not allowed to release.') + - uses: actions/checkout@v4 + with: + lfs: false + fetch-tags: true + fetch-depth: 0 + + - name: Prepare git config + run: | + cat <<- EOF > $HOME/.netrc + machine github.com + login $GITHUB_ACTOR + password $GITHUB_TOKEN + machine api.github.com + login $GITHUB_ACTOR + password $GITHUB_TOKEN + EOF + chmod 600 $HOME/.netrc + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" + git config --global user.name "$GITHUB_ACTOR" + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if there are any changes to create a preview release for + id: changes + run: | + LATEST_PREVIEW=$(git tag --sort=-creatordate | grep -E '\.[0-9]{14}$' | head -n 1) + + DIFF="" + if [ -n "$LATEST_PREVIEW" ]; then + # using || true to always exit either with a diff or a success exit code to not fail the whole workflow + DIFF=$(git diff $LATEST_PREVIEW..5.x-dev --unified=0 | grep -vE "^\+\+\+|---" | grep "^[+-]" | grep -v "public const VERSION = '.*';" || true) + fi + + if [ -z "$DIFF" ]; then + echo "No changes in 5.x-dev since last preview version was created." + DO_RELEASE=0 + else + DO_RELEASE=1 + fi + + echo "do_release=$DO_RELEASE" >> $GITHUB_OUTPUT + + - name: Determine new preview version number + id: version + if: steps.changes.outputs.do_release == '1' + run: | + OLD_VERSION=$(php -r "include_once 'core/Version.php'; echo \Piwik\Version::VERSION;") + NEW_VERSION=$(php -r "include_once 'core/Version.php'; \$v = new \Piwik\Version(); echo \$v->nextPreviewVersion(\Piwik\Version::VERSION);") + + if [ "$NEW_VERSION" == "" ]; then + HAS_NEW_VERSION=0 + else + HAS_NEW_VERSION=1 + fi + + echo "OLD_VERSION=$OLD_VERSION" >> $GITHUB_ENV + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + + echo "has_new_version=$HAS_NEW_VERSION" >> $GITHUB_OUTPUT + echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT + + - name: Check if the previous version has been released + if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1' + run: | + TAG_EXISTS=$( git tag --list "$OLD_VERSION" ) + + # x.y.z-alpha would not be released, all other versions should have an existing tag (a release) + if [[ ! $OLD_VERSION =~ -alpha$ ]] && [[ -z "$TAG_EXISTS" ]]; then + echo "$OLD_VERSION (as indicated in core/Version.php) has not been released yet." + exit 1 + fi + + - name: Update 5.x-preview branch to latest 5.x-dev + if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1' + run: | + git checkout -B 5.x-preview + + - name: Update version file with new version + if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1' + run: | + sed -i "s/VERSION = '${OLD_VERSION}';/VERSION = '${NEW_VERSION}';/g" core/Version.php + + - name: Commit version file changes + if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1' + run: | + git add core/Version.php + git commit -m "Update version to ${NEW_VERSION}" + + - name: Push changes to 5.x-preview + if: steps.changes.outputs.do_release == '1' && steps.version.outputs.has_new_version == '1' + run: | + git push -f origin 5.x-preview + + run_matomo_tests: + needs: [prepare_preview_version] + uses: ./.github/workflows/matomo-tests.yml + if: | + always() && + needs.prepare_preview_version.result == 'success' && + needs.prepare_preview_version.outputs.do_release == '1' && + needs.prepare_preview_version.outputs.has_new_version == '1' + with: + is_preview: true + + release_preview_version: + needs: [run_matomo_tests] + uses: ./.github/workflows/release.yml + if: | + always() && + needs.prepare_preview_version.result == 'success' && + needs.run_matomo_tests.result == 'success' && + needs.prepare_preview_version.outputs.do_release == '1' && + needs.prepare_preview_version.outputs.has_new_version == '1' + with: + is_preview: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6fa380ccf2..85a1574b5e2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,6 +30,12 @@ on: password: description: 'Release password' required: true + workflow_call: + inputs: + is_preview: + type: boolean + required: false + default: false env: RELEASE_PASSWORD: ${{ secrets.RELEASE_PASSWORD }} @@ -39,13 +45,13 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check release password" - if: ${{ github.event.inputs.password != env.RELEASE_PASSWORD }} + if: ${{ inputs.is_preview != true && github.event.inputs.password != env.RELEASE_PASSWORD }} uses: actions/github-script@v6 with: script: | core.setFailed('Release password didn\'t match.') - name: "Check if user is allowed" - if: ${{ github.actor != 'mattab' && github.actor != 'tsteur' && github.actor != 'sgiehl' && github.actor != 'mneudert' && github.actor != 'michalkleiner' && github.actor != 'caddoo'}} + if: ${{ inputs.is_preview != true && github.actor != 'mattab' && github.actor != 'tsteur' && github.actor != 'sgiehl' && github.actor != 'mneudert' && github.actor != 'michalkleiner' && github.actor != 'caddoo'}} uses: actions/github-script@v6 with: script: | @@ -97,15 +103,21 @@ jobs: exit 1 fi - if ! [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-dev$ || ${GITHUB_REF#refs/heads/} == "next_release" ]] + if ! [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-(dev|preview)$ || ${GITHUB_REF#refs/heads/} == "next_release" ]] then - echo "A tag can only be created from branches '5.x-dev' and 'next_release'. Please create the tag manually if a release needs to be built from another branch." + echo "A tag can only be created from branches '5.x-dev', '5.x-preview' or 'next_release'. Please create the tag manually if a release needs to be built from another branch." exit 1 fi if [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-dev$ && $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$ ]] then - echo "Only beta/preview release tags can be created from ${GITHUB_REF#refs/heads/} branch." + echo "Only beta release tags can be created from ${GITHUB_REF#refs/heads/} branch." + exit 1 + fi + + if [[ ${GITHUB_REF#refs/heads/} =~ ^[4-9]\.x-preview$ && $version =~ [0-9]{14}$ ]] + then + echo "Only preview release tags can be created from ${GITHUB_REF#refs/heads/} branch." exit 1 fi diff --git a/.github/workflows/submodules.yml b/.github/workflows/submodules.yml index 9762bcbc665..287448021e9 100644 --- a/.github/workflows/submodules.yml +++ b/.github/workflows/submodules.yml @@ -91,7 +91,7 @@ jobs: run: | curl \ --request POST \ - --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + --header 'authorization: Bearer ${{ secrets.CUSTOM_ACCESS_TOKEN }}' \ --header 'content-type: application/json' \ --data '{ "title":"[automatic submodule updates]", diff --git a/composer.lock b/composer.lock index 745a35e3cdf..615eb83de93 100644 --- a/composer.lock +++ b/composer.lock @@ -713,12 +713,12 @@ "source": { "type": "git", "url": "https://github.com/matomo-org/referrer-spam-list.git", - "reference": "a214dcf1266141623aaed95ab14af4a64da6b7bb" + "reference": "d9c2fa740ac2055f82240130518b33213b5beb1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matomo-org/referrer-spam-list/zipball/a214dcf1266141623aaed95ab14af4a64da6b7bb", - "reference": "a214dcf1266141623aaed95ab14af4a64da6b7bb", + "url": "https://api.github.com/repos/matomo-org/referrer-spam-list/zipball/d9c2fa740ac2055f82240130518b33213b5beb1d", + "reference": "d9c2fa740ac2055f82240130518b33213b5beb1d", "shasum": "" }, "replace": { @@ -736,7 +736,7 @@ "issues": "https://github.com/matomo-org/referrer-spam-list/issues", "source": "https://github.com/matomo-org/referrer-spam-list/tree/master" }, - "time": "2024-07-11T22:09:32+00:00" + "time": "2024-07-29T20:28:18+00:00" }, { "name": "matomo/searchengine-and-social-list", @@ -1714,16 +1714,16 @@ }, { "name": "symfony/console", - "version": "v5.4.41", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6473d441a913cb997123b59ff2dbe3d1cf9e11ba" + "reference": "cef62396a0477e94fc52e87a17c6e5c32e226b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6473d441a913cb997123b59ff2dbe3d1cf9e11ba", - "reference": "6473d441a913cb997123b59ff2dbe3d1cf9e11ba", + "url": "https://api.github.com/repos/symfony/console/zipball/cef62396a0477e94fc52e87a17c6e5c32e226b7f", + "reference": "cef62396a0477e94fc52e87a17c6e5c32e226b7f", "shasum": "" }, "require": { @@ -1793,7 +1793,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.41" + "source": "https://github.com/symfony/console/tree/v5.4.42" }, "funding": [ { @@ -1809,7 +1809,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T07:48:55+00:00" + "time": "2024-07-26T12:21:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1880,16 +1880,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.4.41", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "c25da5cc2de4e6f96b3a0a2813050355a20dd0e1" + "reference": "db15ba0fd50890156ed40087ccedc7851a1f5b76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/c25da5cc2de4e6f96b3a0a2813050355a20dd0e1", - "reference": "c25da5cc2de4e6f96b3a0a2813050355a20dd0e1", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/db15ba0fd50890156ed40087ccedc7851a1f5b76", + "reference": "db15ba0fd50890156ed40087ccedc7851a1f5b76", "shasum": "" }, "require": { @@ -1931,7 +1931,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.4.41" + "source": "https://github.com/symfony/error-handler/tree/v5.4.42" }, "funding": [ { @@ -1947,7 +1947,7 @@ "type": "tidelift" } ], - "time": "2024-06-09T18:59:35+00:00" + "time": "2024-07-23T12:34:05+00:00" }, { "name": "symfony/event-dispatcher", @@ -2115,16 +2115,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.4.40", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cf4893ca4eca3fac4ae06da1590afdbbb4217847" + "reference": "9c375b2abef0b657aa0b7612b763df5c12a465ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cf4893ca4eca3fac4ae06da1590afdbbb4217847", - "reference": "cf4893ca4eca3fac4ae06da1590afdbbb4217847", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9c375b2abef0b657aa0b7612b763df5c12a465ab", + "reference": "9c375b2abef0b657aa0b7612b763df5c12a465ab", "shasum": "" }, "require": { @@ -2171,7 +2171,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.40" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.42" }, "funding": [ { @@ -2187,20 +2187,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-07-26T11:59:59+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.41", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "aad4078e1210343b7cd5acb803c02f8b02f002b2" + "reference": "948db7caf761dacc8abb9a59465f0639c30cc6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/aad4078e1210343b7cd5acb803c02f8b02f002b2", - "reference": "aad4078e1210343b7cd5acb803c02f8b02f002b2", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/948db7caf761dacc8abb9a59465f0639c30cc6dd", + "reference": "948db7caf761dacc8abb9a59465f0639c30cc6dd", "shasum": "" }, "require": { @@ -2284,7 +2284,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.41" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.42" }, "funding": [ { @@ -2300,7 +2300,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T11:42:41+00:00" + "time": "2024-07-26T14:46:22+00:00" }, { "name": "symfony/monolog-bridge", @@ -3025,16 +3025,16 @@ }, { "name": "symfony/string", - "version": "v5.4.41", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "065a9611e0b1fd2197a867e1fb7f2238191b7096" + "reference": "909cec913edea162a3b2836788228ad45fcab337" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/065a9611e0b1fd2197a867e1fb7f2238191b7096", - "reference": "065a9611e0b1fd2197a867e1fb7f2238191b7096", + "url": "https://api.github.com/repos/symfony/string/zipball/909cec913edea162a3b2836788228ad45fcab337", + "reference": "909cec913edea162a3b2836788228ad45fcab337", "shasum": "" }, "require": { @@ -3091,7 +3091,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.41" + "source": "https://github.com/symfony/string/tree/v5.4.42" }, "funding": [ { @@ -3107,20 +3107,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:20:55+00:00" + "time": "2024-07-20T18:38:32+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.40", + "version": "v5.4.42", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "af8868a6e9d6082dfca11f1a1f205ae93a8b6d93" + "reference": "0c17c56d8ea052fc33942251c75d0e28936e043d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/af8868a6e9d6082dfca11f1a1f205ae93a8b6d93", - "reference": "af8868a6e9d6082dfca11f1a1f205ae93a8b6d93", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0c17c56d8ea052fc33942251c75d0e28936e043d", + "reference": "0c17c56d8ea052fc33942251c75d0e28936e043d", "shasum": "" }, "require": { @@ -3180,7 +3180,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.40" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.42" }, "funding": [ { @@ -3196,7 +3196,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-07-26T12:23:09+00:00" }, { "name": "szymach/c-pchart", diff --git a/core/Config/DatabaseConfig.php b/core/Config/DatabaseConfig.php index c14a135fc17..24cc2610b9a 100644 --- a/core/Config/DatabaseConfig.php +++ b/core/Config/DatabaseConfig.php @@ -15,4 +15,9 @@ public static function getSectionName(): string { return 'database'; } + + public static function isTiDb(): bool + { + return self::getConfigValue('schema') === 'Tidb'; + } } diff --git a/core/DataAccess/ArchiveTableDao.php b/core/DataAccess/ArchiveTableDao.php index 6ec040ef787..41ab3094fac 100644 --- a/core/DataAccess/ArchiveTableDao.php +++ b/core/DataAccess/ArchiveTableDao.php @@ -36,14 +36,14 @@ class ArchiveTableDao */ public function getArchiveTableAnalysis($tableDate) { - $numericQueryEmptyRow = array( + $numericQueryEmptyRow = [ 'count_archives' => '-', 'count_invalidated_archives' => '-', 'count_temporary_archives' => '-', 'count_error_archives' => '-', 'count_segment_archives' => '-', 'count_numeric_rows' => '-', - ); + ]; $tableDate = str_replace("`", "", $tableDate); // for sanity @@ -60,7 +60,7 @@ public function getArchiveTableAnalysis($tableDate) SUM(CASE WHEN name NOT LIKE 'done%' THEN 1 ELSE 0 END) AS count_numeric_rows, 0 AS count_blob_rows FROM `$numericTable` - GROUP BY idsite, date1, date2, period"; + GROUP BY idsite, date1, date2, period ORDER BY idsite, period, date1, date2"; $rows = Db::fetchAll($sql, array(ArchiveWriter::DONE_INVALIDATED, ArchiveWriter::DONE_OK_TEMPORARY, ArchiveWriter::DONE_ERROR, ArchiveWriter::DONE_ERROR_INVALIDATED)); @@ -76,14 +76,21 @@ public function getArchiveTableAnalysis($tableDate) COUNT(*) AS count_blob_rows, SUM(OCTET_LENGTH(value)) AS sum_blob_length FROM `$blobTable` - GROUP BY idsite, date1, date1, period"; + GROUP BY idsite, date1, date2, period ORDER BY idsite, period, date1, date2"; foreach (Db::fetchAll($sql) as $blobStatsRow) { $label = $blobStatsRow['label']; + if (isset($result[$label])) { $result[$label] = array_merge($result[$label], $blobStatsRow); } else { - $result[$label] = $blobStatsRow + $numericQueryEmptyRow; + // ensure rows without numeric entries have the + // same internal result array key order + $result[$label] = array_merge( + ['label' => $label], + $numericQueryEmptyRow, + $blobStatsRow + ); } } diff --git a/core/DataAccess/Model.php b/core/DataAccess/Model.php index 4f256ea032b..61f8c9f5bec 100644 --- a/core/DataAccess/Model.php +++ b/core/DataAccess/Model.php @@ -63,7 +63,7 @@ public function getInvalidatedArchiveIdsSafeToDelete($archiveTable, $setGroupCon } $sql = "SELECT idsite, date1, date2, period, name, - GROUP_CONCAT(idarchive, '.', value ORDER BY ts_archived DESC) as archives + GROUP_CONCAT(idarchive, '.', value ORDER BY ts_archived DESC, idarchive DESC) as archives FROM `$archiveTable` WHERE name LIKE 'done%' AND `value` NOT IN (" . ArchiveWriter::DONE_ERROR . ", " . ArchiveWriter::DONE_ERROR_INVALIDATED . ") diff --git a/core/DataTable/Filter/ReplaceColumnNames.php b/core/DataTable/Filter/ReplaceColumnNames.php index cd00441a5da..4141049ef69 100644 --- a/core/DataTable/Filter/ReplaceColumnNames.php +++ b/core/DataTable/Filter/ReplaceColumnNames.php @@ -162,6 +162,8 @@ protected function getRenamedColumns($columns) protected function flattenGoalColumns($columnValue) { $newSubColumns = array(); + // sort by key (idgoal) to ensure a static result + ksort($columnValue); foreach ($columnValue as $idGoal => $goalValues) { $mapping = Metrics::$mappingFromIdToNameGoal; if ($idGoal == GoalManager::IDGOAL_CART) { diff --git a/core/Db/BatchInsert.php b/core/Db/BatchInsert.php index 73d3c18c357..c25d7b198ac 100644 --- a/core/Db/BatchInsert.php +++ b/core/Db/BatchInsert.php @@ -12,6 +12,7 @@ use Exception; use Piwik\Common; use Piwik\Config; +use Piwik\Config\DatabaseConfig; use Piwik\Container\StaticContainer; use Piwik\Db; use Piwik\Log; @@ -97,6 +98,10 @@ public static function tableInsertBatch($tableName, $fields, $values, $throwExce } $filePath = $path . $tableName . '-' . $instanceId . Common::generateUniqId() . '.csv'; + // always use utf8 for TiDb, as TiDb has problems with latin1 + if (DatabaseConfig::isTiDb()) { + $charset = 'utf8'; + } try { $fileSpec = array( diff --git a/core/Version.php b/core/Version.php index 3641107d4f7..6b41cf16222 100644 --- a/core/Version.php +++ b/core/Version.php @@ -41,12 +41,12 @@ public function isVersionNumber($version): bool private function isNonStableVersion($version): bool { - return (bool) preg_match('/^\d+\.\d+\.\d+((-.{1,4}\d+(\.\d{14})?)|(-alpha\.\d{14}))$/i', $version); + return (bool) preg_match('/^\d+\.\d+\.\d+(-((rc|b|beta)\d+|alpha)(\.\d{14})?)$/i', $version); } public function isPreviewVersion($version): bool { - if (\preg_match('/^\d+\.\d+\.\d+((-(rc|b|beta)\d+(\.\d{14})?)|(-alpha\.\d{14}))?$/i', $version)) { + if ($this->isNonStableVersion($version)) { if (\preg_match('/\.(\d{14})$/', $version, $matches)) { $dt = DateTime::createFromFormat('YmdHis', $matches[1]); @@ -56,4 +56,49 @@ public function isPreviewVersion($version): bool return false; } + + public function nextPreviewVersion($version): string + { + if (!$this->isVersionNumber($version)) { + return ''; + } + + $dt = date('YmdHis'); + + if ($this->isPreviewVersion($version)) { + // already a preview, update dt and check it's newer + $newVersion = substr($version, 0, -14) . $dt; + if (version_compare($version, $newVersion, '<')) { + return $newVersion; + } + return ''; + } elseif ($this->isStableVersion($version)) { + // no suffix yet, we need to bump the patch first + $newVersion = preg_replace_callback( + '/^(\d+\.\d+\.)(\d+)$/', + function ($matches) { + $matches[2] = $matches[2] + 1; + return $matches[1] . $matches[2]; + }, + $version + ); + + return sprintf('%s-alpha.%s', $newVersion, $dt); + } elseif ('alpha' === substr($version, -5)) { + // -alpha + return $version . '.' . $dt; + } else { + // -b1, -rc1 + $newVersion = preg_replace_callback( + '/^(\d+\.\d+\.\d+-(?:rc|b|beta))(\d+)$/i', + function ($matches) { + $matches[2] = $matches[2] + 1; + return $matches[1] . $matches[2]; + }, + $version + ); + + return $newVersion . '.' . $dt; + } + } } diff --git a/plugins/CoreAdminHome/tests/Integration/Model/DuplicateActionRemoverTest.php b/plugins/CoreAdminHome/tests/Integration/Model/DuplicateActionRemoverTest.php index 17ac048fd5a..ecc4b8d0d12 100644 --- a/plugins/CoreAdminHome/tests/Integration/Model/DuplicateActionRemoverTest.php +++ b/plugins/CoreAdminHome/tests/Integration/Model/DuplicateActionRemoverTest.php @@ -41,11 +41,17 @@ public function testGetDuplicateIdActionsReturnsDuplicateIdActionsAndTreatsLowes { $expectedResult = array( array('name' => 'action1', 'idaction' => 1, 'duplicateIdActions' => array(2, 3)), - array('name' => 'ACTION2', 'idaction' => 5, 'duplicateIdActions' => array(7, 11)), array('name' => 'action2', 'idaction' => 4, 'duplicateIdActions' => array(9)), + array('name' => 'ACTION2', 'idaction' => 5, 'duplicateIdActions' => array(7, 11)), array('name' => 'action4', 'idaction' => 6, 'duplicateIdActions' => array(10, 12)), ); $actualResult = $this->duplicateActionRemover->getDuplicateIdActions(); + + // order of element is dependent to database engine, so sort before checking results + usort($actualResult, function ($a, $b) { + return $a['idaction'] <=> $b['idaction']; + }); + $this->assertEquals($expectedResult, $actualResult); } diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index 83b1ffb6bb1..a57ced98c3e 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -474,7 +474,7 @@ public function activate($redirectAfter = true) if (!empty($redirectTo) && $redirectTo === 'marketplace') { $this->redirectToIndex('Marketplace', 'overview'); } elseif (!empty($redirectTo) && $redirectTo === 'tagmanager') { - $this->redirectToIndex('TagManager', 'gettingStarted'); + $this->redirectToIndex('TagManager', 'manageContainers'); } elseif (!empty($redirectTo) && $redirectTo === 'referrer') { $this->redirectAfterModification($redirectAfter); } else { diff --git a/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js b/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js index 5e9ad03d528..52f8055ae52 100644 --- a/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js +++ b/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js @@ -63,7 +63,7 @@ describe("TagManagerTeaser", function () { await page.type('#login_form_password', superUserPassword); await page.click('#login_form_submit'); - await page.waitForSelector('.tagManagerGettingStarted'); + await page.waitForSelector('.manageContainer'); await page.waitForNetworkIdle(); await page.waitForTimeout(250); diff --git a/plugins/CorePluginsAdmin/tests/UI/expected-screenshots/TagManagerTeaser_super_user_activate_plugin.png b/plugins/CorePluginsAdmin/tests/UI/expected-screenshots/TagManagerTeaser_super_user_activate_plugin.png index a693f5f8922..6e4f3e46838 100644 --- a/plugins/CorePluginsAdmin/tests/UI/expected-screenshots/TagManagerTeaser_super_user_activate_plugin.png +++ b/plugins/CorePluginsAdmin/tests/UI/expected-screenshots/TagManagerTeaser_super_user_activate_plugin.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffac267901af2d688520fc0a93a89d40306b331c9db67d750eff29274cb2273f -size 200573 +oid sha256:314ba7fbf008d7e047aae72f68deeb7fbda4aed79d8e6b8b1e5f0ae29c0544c9 +size 57471 diff --git a/plugins/Diagnostics/Commands/AnalyzeArchiveTable.php b/plugins/Diagnostics/Commands/AnalyzeArchiveTable.php index de97fce40d1..7283515586c 100644 --- a/plugins/Diagnostics/Commands/AnalyzeArchiveTable.php +++ b/plugins/Diagnostics/Commands/AnalyzeArchiveTable.php @@ -62,14 +62,16 @@ protected function doExecute(): int $totalError = 0; $totalSegment = 0; $totalBlobLength = 0; + foreach ($rows as $row) { - $totalArchives += $row['count_archives']; - $totalInvalidated += $row['count_invalidated_archives']; - $totalTemporary += $row['count_temporary_archives']; - $totalError += $row['count_error_archives']; - $totalSegment += $row['count_segment_archives']; + $totalArchives += (int) $row['count_archives']; + $totalInvalidated += (int) $row['count_invalidated_archives']; + $totalTemporary += (int) $row['count_temporary_archives']; + $totalError += (int) $row['count_error_archives']; + $totalSegment += (int) $row['count_segment_archives']; + if (isset($row['sum_blob_length'])) { - $totalBlobLength += $row['sum_blob_length']; + $totalBlobLength += (int) $row['sum_blob_length']; } } diff --git a/plugins/Diagnostics/lang/de.json b/plugins/Diagnostics/lang/de.json index 53023776b4a..b656c063e18 100644 --- a/plugins/Diagnostics/lang/de.json +++ b/plugins/Diagnostics/lang/de.json @@ -19,6 +19,7 @@ "EnableRequiredDirectoriesDiagnostic": "Diese Prüfung wurde übersprungen, da diese Prüfung in der Konfiguration deaktiviert ist. Um diese Prüfung zu aktivieren, setzen Sie [General] enable_required_directories_diagnostic = 1 in der Datei \"config/config.ini.php\".", "HideUnchanged": "Falls Sie nur die geänderten Werte einsehen möchten, können Sie %1$salle unveränderten Werte ausblenden%2$s.", "HtaccessWarningNginx": "Um sicherzustellen, dass auf sensible Dateien nicht direkt zugegriffen werden kann, wird empfohlen, Ihren Webserver so zu konfigurieren, dass der Zugriff auf bestimmte Verzeichnisse eingeschränkt wird. Weitere Informationen finden Sie in der %1$s offiziellen nginx-Serverkonfiguration %2$s", + "MariaDbNotConfigured": "Ihre Datenbankversion deutet darauf hin, dass Sie möglicherweise einen MariaDb-Server verwenden. Wenn dies der Fall ist, stellen Sie bitte sicher, dass Sie [database] schema = Mariadb in der Datei \"config/config.ini.php\" setzen, um sicherzustellen, dass alle Datenbank-Features wie erwartet funktionieren.", "MysqlMaxPacketSize": "Maximale Packetgröße", "MysqlMaxPacketSizeWarning": "Es wird empfohlen die '%1$smax_allowed_packet%2$s' Größe in Ihrer MySQL Datenbank auf mindestens %3$s zu erhöhen. Aktuell ist %4$s eingestellt.", "MysqlTemporaryTablesWarning": "Die MySQL Berechtigung CREATE TEMPORARY TABLES wird benötigt, damit Matomo korrekt funktioniert.", diff --git a/plugins/Diagnostics/tests/Integration/Commands/AnalyzeArchiveTableTest.php b/plugins/Diagnostics/tests/Integration/Commands/AnalyzeArchiveTableTest.php index 8c98f5a691e..d5b3c83e859 100644 --- a/plugins/Diagnostics/tests/Integration/Commands/AnalyzeArchiveTableTest.php +++ b/plugins/Diagnostics/tests/Integration/Commands/AnalyzeArchiveTableTest.php @@ -42,9 +42,9 @@ public function testCommandOutputIsAsExpected() +-------------------------------------------+------------+---------------+-------------+---------+-----------+----------------+-------------+-------------+ | Group | # Archives | # Invalidated | # Temporary | # Error | # Segment | # Numeric Rows | # Blob Rows | # Blob Data | +-------------------------------------------+------------+---------------+-------------+---------+-----------+----------------+-------------+-------------+ +| day[2010-03-06 - 2010-03-06] idSite = 1 | 7 | 0 | 0 | 0 | 6 | 74 | 75 | %d | | week[2010-03-01 - 2010-03-07] idSite = 1 | 7 | 0 | 0 | 0 | 6 | 74 | 97 | %d | | month[2010-03-01 - 2010-03-31] idSite = 1 | 7 | 0 | 0 | 0 | 6 | 74 | 97 | %d | -| day[2010-03-06 - 2010-03-06] idSite = 1 | 7 | 0 | 0 | 0 | 6 | 74 | 75 | %d | +-------------------------------------------+------------+---------------+-------------+---------+-----------+----------------+-------------+-------------+ Total # Archives: 21 diff --git a/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml b/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml index 6d8e2d50cc8..1829fbed93f 100644 --- a/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml +++ b/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml @@ -419,11 +419,6 @@ 0 2 - - 1 - 1 - 10 - 2 2 @@ -440,6 +435,11 @@ 666 10 + + 1 + 1 + 10 + 3 3121.11 diff --git a/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml b/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml index fc96705a870..9643dee4c77 100644 --- a/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml +++ b/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__API.getProcessedReport_day.xml @@ -111,11 +111,6 @@ 0 2 - - 1 - 1 - 10 - 2 2 @@ -132,6 +127,11 @@ 666 10 + + 1 + 1 + 10 + 3 3121.11 diff --git a/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml b/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml index 9133dc02298..101d7fce4f8 100644 --- a/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml +++ b/plugins/Ecommerce/tests/System/expected/test_ecommerceOrderWithItems__UserCountry.getContinent_day.xml @@ -11,11 +11,6 @@ 0 2 - - 1 - 1 - 10 - 2 2 @@ -32,6 +27,11 @@ 666 10 + + 1 + 1 + 10 + 3 3121.11 diff --git a/plugins/GeoIp2/lang/ga.json b/plugins/GeoIp2/lang/ga.json index 0967ef424bc..78ae24f65b7 100644 --- a/plugins/GeoIp2/lang/ga.json +++ b/plugins/GeoIp2/lang/ga.json @@ -1 +1,62 @@ -{} +{ + "GeoIp2": { + "AssumingNonApache": "Ní féidir feidhm apache_get_modules a aimsiú, ag glacadh leis nach freastalaí gréasáin Apache é.", + "AutomaticSetup": "Cumraigh geolocation go huathoibríoch ag baint úsáide as bunachar sonraí dbip", + "AutomaticSetupDescription": "Chun geolocation ceart a fháil teastaíonn bunachar sonraí seachtrach ó Matomo. Ag baint úsáide as an rogha seo, déanfar Matomo a chumrú go huathoibríoch chun an bunachar sonraí dbip is déanaí ar leibhéal na cathrach a íoslódáil agus a úsáid. [%1$sFéach ar théarmaí ceadúnaithe %2$s]", + "CannotFindGeoIPDatabaseInArchive": "Níorbh fhéidir bunachar sonraí bailí DBIP / GeoIP a aimsiú i gcartlann tarra %1$s!", + "CannotFindGeoIPServerVar": "Níl an athróg %s socraithe. Seans nach bhfuil do fhreastalaí cumraithe i gceart.", + "CannotListContent": "Níorbh fhéidir ábhar do %1$s a liostú: %2$s", + "CannotSetupGeoIPAutoUpdating": "Dealraíonn sé go bhfuil do bhunachair shonraí geolocation á stóráil agat lasmuigh de Matomo (is féidir linn a insint ós rud é nach bhfuil aon bhunachair shonraí san fhochomhadlann ilghnéitheach, ach tá do GeoIP ag obair). Ní féidir le Matomo do bhunachair shonraí geoshuímh a nuashonrú go huathoibríoch má tá siad suite lasmuigh den eolaire ilghnéitheach.", + "CannotUnzipGeoIPFile": "Níorbh fhéidir comhad GeoIP a dhízipeáil i %1$s: %2$s", + "DownloadNewDatabasesEvery": "Íoslódáil bunachar sonraí cothrom le dáta gach", + "DownloadingDb": "%s á íosluchtú", + "FatalErrorDuringDownload": "Tharla earráid mharfach agus an comhad seo á íoslódáil. Seans go bhfuil rud éigin cearr le do nasc idirlín, leis an mbunachar sonraí geolocation a d’íoslódáil tú nó Matomo. Bain triail as é a íoslódáil agus a shuiteáil de láimh.", + "FoundApacheModules": "D'aimsigh Matomo na modúil Apache seo a leanas", + "GeoIPDatabases": "Bunachair Sonraí GeoIP", + "GeoIPImplHasAccessTo": "Tá rochtain ag cur chun feidhme GeoIP seo ar na cineálacha bunachair shonraí seo a leanas", + "GeoIPLocationProviderDesc_ServerBasedAnonWarn": "Nóta: Níl aon éifeacht ag anaithnidiú IP ar na láithreacha a thuairiscíonn an soláthraí seo. Sula n-úsáideann tú é le anaithnidiú IP, déan cinnte nach sáraíonn sé seo aon dlíthe príobháideachais a d'fhéadfadh tú a bheith faoina réir.", + "GeoIPLocationProviderNotRecommended": "Oibríonn Geolocation, ach níl ceann de na soláthraithe molta á úsáid agat.", + "GeoIPNoDatabaseFound": "Ní raibh an forfheidhmiú GeoIP seo in ann aon bhunachar sonraí a aimsiú.", + "GeoIPNoServerVars": "Ní féidir le Matomo athróg GeoIP %s ar bith a aimsiú.", + "GeoIPServerVarsFound": "Aimsíonn Matomo na hathróga GeoIP %s seo a leanas", + "GeoIPUpdaterInstructions": "Cuir isteach na naisc íoslódála le haghaidh do bhunachair shonraí thíos. Má cheannaigh tú bunachair shonraí ó %3$sdbip%4$s nó %1$sMaxMind%2$s, is féidir leat na naisc seo a aimsiú i do chuntas dbip nó MaxMind. Déan teagmháil le do sholáthraí bunachar sonraí geolocation má tá deacracht agat iad a rochtain.", + "GeoIPUpdaterIntro": "Tá nuashonruithe á mbainistiú ag Matomo do na bunachair shonraí seo a leanas faoi láthair", + "GeoIPVariablesConfigurationHere": "Is féidir leat athróga freastalaí úsáidte %1$shere%2$s a chumrú.", + "GeoLiteCityLink": "Má tá an bunachar sonraí dbip city lite in úsáid agat, úsáid an nasc seo: %1$s%2$s%3$s", + "HowToInstallApacheModule": "Conas an modúl GeoIP a shuiteáil le haghaidh Apache?", + "HowToInstallNginxModule": "Conas an modúl GeoIP a shuiteáil do Nginx?", + "HowToSetupGeoIP": "Conas geolocation cruinn a shocrú le dbip", + "HowToSetupGeoIPIntro": "Ní cosúil go bhfuil socrú cruinn Geolocation agat. Is gné úsáideach é seo agus gan é ní fheicfidh tú eolas suímh cruinn agus iomlán do do chuairteoirí. Seo mar is féidir leat tosú go tapa á úsáid:", + "HowToSetupGeoIP_Step1": "%1$s Íoslódáil %2$s bunachar sonraí DBIP City Lite ó %3$sdbip%4$s.", + "HowToSetupGeoIP_Step2": "Bain an comhad seo agus cóipeáil an toradh, %1$s isteach san fhochomhadlann Matomo %2$smisc%3$s agus athainmnigh go %4$s é (is féidir leat é seo a dhéanamh trí FTP nó SSH).", + "HowToSetupGeoIP_Step3": "Athlódáil an scáileán seo. Beidh an soláthraí %1$sDBIP / GeoIP (PHP)%2$s %3$ssuiteáilte %4$s anois. Roghnaigh é.", + "HowToSetupGeoIP_Step4": "Agus tá tú críochnaithe! Tá Matomo díreach socraithe agat chun DBIP a úsáid, rud a chiallaíonn go mbeidh tú in ann réigiúin agus cathracha do chuairteoirí a fheiceáil mar aon le heolas tíre atá an-chruinn.", + "HttpServerModule": "Modúl Freastalaí HTTP", + "IPurchasedGeoIPDBs": "Cheannaigh mé bunachair shonraí níos cruinne ó %3$sdbip%4$s nó %1$sMaxMind%2$s agus ba mhaith liom nuashonruithe uathoibríocha a shocrú.", + "ISPDatabase": "Bunachar sonraí ISP", + "ISPRequiresProviderPlugin": "Chun soláthraithe seirbhíse Idirlín a rianú ní mór an breiseán Soláthraí a shuiteáil agus a ghníomhachtú.", + "IWantToDownloadFreeGeoIP": "Ba mhaith liom bunachar sonraí DBIP a íoslódáil saor in aisce…", + "InvalidGeoIPUpdateHost": "Ní chuirtear muinín in óstach url nuashonraithe GeoIP %1$s. Chun nuashonruithe GeoIP a íoslódáil ó óstaigh seachas %2$s, cuir in oiriúint do %3$s sa chumrú.", + "InvalidGeoIPUpdatePeriod": "Tréimhse neamhbhailí don nuashonrú GeoIP: %1$s. Is iad %2$s na luachanna bailí.", + "LocationDatabase": "Bunachar Sonraí Suíomhanna", + "LocationDatabaseHint": "Is bunachar sonraí de thír, réigiún nó cathrach é bunachar sonraí suímh.", + "LocationProviderDesc_Php": "Is é an soláthraí suímh seo an ceann is simplí le suiteáil mar níl cumraíocht an fhreastalaí ag teastáil uaidh (oiriúnach le haghaidh óstáil roinnte!). Úsáideann sé bunachar sonraí DBIP nó GeoIP 2 agus API PHP MaxMind chun suíomh do chuairteoirí a chinneadh go cruinn.", + "LocationProviderDesc_Php_WithExtension": "Tá an t-eisínteacht %1$smaxminddb%2$s suiteáilte níos tapúla do sholáthraí na suíomhanna seo.", + "LocationProviderDesc_ServerModule": "Úsáideann an soláthraí suímh seo an modúl GeoIP 2 atá suiteáilte i do fhreastalaí HTTP. Tá an soláthraí seo tapa agus cruinn, ach ní féidir %1$s a úsáid ach amháin le gnáthrianú brabhsálaí.%2$s", + "LocationProviderDesc_ServerModule2": "Má tá ort logchomhaid a iompórtáil nó rud éigin eile a dhéanamh a theastaíonn uait seoltaí IP a shocrú, úsáid forfheidhmithe %3$sPHP GeoIP 2%4$s agus suiteáil iarmhír %1$smaxminddb%2$s.", + "MalFormedUpdateUrl": "Is cosúil go bhfuil an url %1$s neamhbhailí. Cinntigh le do thoil URL bailí a chur isteach ag tosú le http:// nó https://", + "NotManagingGeoIPDBs": "Níl aon bhunachair shonraí DBIP nó MaxMind á mbainistiú ag Matomo faoi láthair.", + "PluginDescription": "Soláthraíonn soláthraithe suímh DBIP / GeoIP2.", + "ServerBasedVariablesConfiguration": "Cumraíocht d'athróga freastalaí a úsáideann modúil freastalaí GeoIP 2", + "ServerVariableFor": "Athróg freastalaí le haghaidh %s", + "SetupAutomaticUpdatesOfGeoIP": "Socraigh nuashonruithe uathoibríocha bunachar sonraí geolocation", + "ShowCustomServerVariablesConfig": "Úsáidim modúl freastalaí Geoip2 (Nginx, Apache…) agus ba mhaith liom athróga freastalaí a chumrú", + "ThisUrlIsNotAValidGeoIPDB": "Ní bunachar sonraí geolocation bailí é an comhad a íoslódáladh. Athsheiceáil an URL nó íoslódáil an comhad de láimh.", + "UnsupportedArchiveType": "Tháinig cineál cartlainne %1$s nach dtacaítear leis.", + "UpdaterHasNotBeenRun": "Níor ritheadh an t-uasdátú riamh.", + "UpdaterIsNotScheduledToRun": "Níl sé sceidealta le reáchtáil sa todhchaí.", + "UpdaterScheduledForNextRun": "Tá sé sceidealta le rith le linn an chéad chron eile croí: cur i gcrích ordaithe cartlainne.", + "UpdaterWasLastRun": "Ritheadh an nuashonrú an uair dheireanach ar %s.", + "UpdaterWillRunNext": "Tá sé sceidealaithe le rith ar %s eile." + } +} diff --git a/plugins/Goals/tests/System/expected/test_goals_ecommerceshowSalesByPages__Actions.getPageUrls_day.xml b/plugins/Goals/tests/System/expected/test_goals_ecommerceshowSalesByPages__Actions.getPageUrls_day.xml index e479c6185b1..45359ea21dc 100644 --- a/plugins/Goals/tests/System/expected/test_goals_ecommerceshowSalesByPages__Actions.getPageUrls_day.xml +++ b/plugins/Goals/tests/System/expected/test_goals_ecommerceshowSalesByPages__Actions.getPageUrls_day.xml @@ -18,19 +18,6 @@ 1 3 - - 1 - 10 - 1 - 1 - 1 - 1 - 10 - 10 - 0.333 - 3.333 - 1 - 9 22590.99 @@ -50,6 +37,19 @@ 1037.037 2 + + 1 + 10 + 1 + 1 + 1 + 1 + 10 + 10 + 0.333 + 3.333 + 1 + 0 0 diff --git a/plugins/Installation/lang/es.json b/plugins/Installation/lang/es.json index f228fe59e16..bb9762ce5f0 100644 --- a/plugins/Installation/lang/es.json +++ b/plugins/Installation/lang/es.json @@ -6,7 +6,7 @@ "ConfigurationHelp": "Corrija su archivo de configuración de Matomo eliminando config/config.ini.php y reanudando la instalación, o corrigiendo la configuración de la conexión a la base de datos.", "ConfirmDeleteExistingTables": "¿Borrar las tablas %s de su base de datos? ADVERTENCIA: ¡LOS DATOS DE ESTAS TABLAS NO SE PUEDEN RECUPERAR!", "Congratulations": "Enhorabuena", - "CongratulationsHelp": "

¡Felicidades! Su instalación de Matomo está completa.

Asegúrese de que su código de seguimiento está introducido en sus páginas, y espere a sus primeros visitantes.

<p", + "CongratulationsHelp": "

¡Felicidades! Su instalación de Matomo está completa.

Asegúrese de que su código de seguimiento está introducido en sus páginas, y espere a sus primeros visitantes.

", "CopyBelowInfoForSupport": "Copie o descargue la información a continuación, en caso de que nuestro equipo de soporte le solicite esta información.", "CopySystemCheck": "Copiar la comprobación del sistema", "DatabaseAbilities": "Capacidades de la base de datos", diff --git a/plugins/Live/lang/fr.json b/plugins/Live/lang/fr.json index 0076f3b6ec6..a8fc803249c 100644 --- a/plugins/Live/lang/fr.json +++ b/plugins/Live/lang/fr.json @@ -8,7 +8,7 @@ "ClickToViewMoreAboutVisit": "Cliquez pour plus d'informations sur cette visite", "ConvertedNGoals": "Objectifs convertis %s", "DisableVisitorProfile": "Désactiver les profils des visiteurs", - "DisableVisitorProfileDescription": "Ici vous pouvez désactiver la fonction de profil du visiteur. Toutes les fonctions liées au journal des visites resteront actives.", + "DisableVisitorProfileDescription": "Toutes les fonctions liées au journal des visites resteront actives.", "DisableVisitsLogAndProfile": "Désactiver le journal des visites et le profil du visiteur", "DisableVisitsLogAndProfileDescription": "Ici, vous pouvez désactiver le journal des visites et la fonction de profil du visiteur. Cela désactivera également les fonctions qui en dépendent, telles que le journal du commerce électronique, le journal des visites segmenté, la carte en temps réel ou le widget temps réel. Cela peut être nécessaire pour respecter les lois locales et les bonnes pratiques en matière de protection de la vie privée.", "FirstVisit": "Première visite", @@ -30,7 +30,7 @@ "OnClickPause": "%s est démarré. Cliquer pour mettre en pause.", "OnClickStart": "%s est arrêté. Cliquer pour démarrer.", "PageRefreshed": "Nombre de fois où cette page a été vue / rafraîchie d'affilée.", - "PluginDescription": "Fournit le log en temps réel des visiteurs et vous permet de visualiser vos visiteurs en temps réel au sein d'un gadget du tableau de bord. Ce composant vous permet aussi de voir le profil d'un visiteur pour n'importe quel utilisateur.", + "PluginDescription": "Fournit le log en temps réel des visiteurs et vous permet de visualiser vos visiteurs en temps réel au sein d'un gadget du tableau de bord. Le profil d'un visiteur peut être consulté pour n'importe quel utilisateur.", "PreviousVisitor": "Visiteur précédent", "QueryMaxExecutionTimeExceeded": "Impossible d'exécuter la requête à temps.", "QueryMaxExecutionTimeExceededReasonDateRange": "Cela peut se produire si la plage de dates sélectionnée est trop large. Veuillez essayer avec une plage de dates plus petite.", diff --git a/plugins/Marketplace/Controller.php b/plugins/Marketplace/Controller.php index 03c5c8153e0..4f3d281b417 100644 --- a/plugins/Marketplace/Controller.php +++ b/plugins/Marketplace/Controller.php @@ -280,6 +280,7 @@ public function overview() $view->isPluginUploadEnabled = CorePluginsAdmin::isPluginUploadEnabled(); $view->uploadLimit = SettingsServer::getPostMaxUploadSize(); $view->inReportingMenu = (bool) Common::getRequestVar('embed', 0, 'int'); + $view->numUsers = $this->environment->getNumUsers(); return $view->render(); } diff --git a/plugins/Marketplace/Marketplace.php b/plugins/Marketplace/Marketplace.php index f859f9e93c4..1d3254b9041 100644 --- a/plugins/Marketplace/Marketplace.php +++ b/plugins/Marketplace/Marketplace.php @@ -167,7 +167,6 @@ public function getClientSideTranslationKeys(&$translationKeys) $translationKeys[] = 'Marketplace_AutoUpdateDisabledWarning'; $translationKeys[] = 'Marketplace_ByXDevelopers'; $translationKeys[] = 'Marketplace_ClickToCompletePurchase'; - $translationKeys[] = 'Marketplace_CurrentNumPiwikUsers'; $translationKeys[] = 'Marketplace_Developer'; $translationKeys[] = 'Marketplace_FeaturedPlugin'; $translationKeys[] = 'Marketplace_LastCommitTime'; diff --git a/plugins/Marketplace/stylesheets/plugin-details.less b/plugins/Marketplace/stylesheets/plugin-details.less index 262bf5e2781..d023982b3e0 100644 --- a/plugins/Marketplace/stylesheets/plugin-details.less +++ b/plugins/Marketplace/stylesheets/plugin-details.less @@ -308,6 +308,16 @@ max-height: calc(~"100vh - 250px"); } + &--with-free-trial { + @media (max-width: 660px) { + max-height: calc(~"90vh - 270px"); + } + + @media (max-width: 480px) { + max-height: calc(~"100vh - 270px"); + } + } + h2, h3, h4, h5, h6 { margin: 20px 0 10px 0; color: #000000; @@ -455,6 +465,8 @@ padding: 24px; height: 90px; border-top: 1px solid #aaa; + margin-top: 1px; // to prevent images overflowing the border + box-sizing: border-box; display: flex; justify-content: space-between; @@ -480,10 +492,117 @@ .matomo-badge-modal { position: initial; + width: 64px; + height: 40px; @media (max-width: 480px) { display: none; } } + + &--with-free-trial { + @media (max-width: 660px) { + padding: 16px 24px; + height: 110px; + } + + .cta-container-modal { + justify-content: flex-end; + } + + .cta-container { + width: 100%; + margin-left: 1rem; + display: flex; + justify-content: flex-end; + box-sizing: border-box; + + .free-trial { + display: flex; + } + + .free-trial-lead-in { + color: #5bb75b; + font-size: 12px; + font-weight: bold; + display: flex; + text-align: right; + flex-grow: 1; + flex-shrink: 1; + flex-wrap: wrap; + align-content: center; + justify-content: flex-end; + padding-right: 1rem; + } + + .free-trial-dropdown { + width: 240px; + height: 36px; + vertical-align: top; + flex-shrink: 0; + } + + .addToCartLink { + width: auto; + max-width: 240px; + vertical-align: top; + padding: 0 1rem; + margin-left: 1rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 0; + } + + @media (max-width: 660px) { + margin-left: 0; + flex-direction: column; /* Stack items vertically */ + justify-content: flex-start; /* Align items to the start */ + + .free-trial-lead-in { + width: 50%; + } + + .free-trial-dropdown { + width: 50%; + } + + .addToCartLink { + width: 50%; + min-width: 50%; + margin-top: 10px; /* space between rows */ + margin-left: 0; + align-self: flex-end; + } + } + + @media (max-width: 400px) { + .addToCartLink { + width: 100%; + min-width: 100%; + } + } + } + + .matomo-badge-modal { + position: initial; + + @media (max-width: 767px) { + width: 48px; + height: 32px; + } + + @media (max-width: 660px) { + display: initial; + position: absolute; + bottom: 16px; + } + + @media (max-width: 400px) { + display: none; + } + } + + } } } diff --git a/plugins/Marketplace/templates/overview.twig b/plugins/Marketplace/templates/overview.twig index 4cf0168b4e1..c971830b1b2 100644 --- a/plugins/Marketplace/templates/overview.twig +++ b/plugins/Marketplace/templates/overview.twig @@ -26,6 +26,7 @@ default-sort="{{ defaultSort|json_encode }}" plugin-sort-options="{{ pluginSortOptions|json_encode }}" num-available-plugins-by-type="{{ numAvailablePluginsByType|json_encode }}" + num-users="{{ numUsers|json_encode }}" > -