diff options
589 files changed, 10273 insertions, 4282 deletions
diff --git a/.gitignore b/.gitignore index bbc7bd85e0..a1cd27d0cd 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ php_errors.log /.project /.settings /*.buildpath -/tests/javascript/enable_sqlite /tests/javascript/unittest.dbf /tests/javascript/unittest2.dbf /tests/lib/geoip-files/*.dat* diff --git a/.gitmodules b/.gitmodules index 6f50e7405e..8a6eb61371 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,6 +29,7 @@ [submodule "tests/UI/expected-ui-screenshots"] path = tests/UI/expected-ui-screenshots url = https://github.com/piwik/piwik-ui-tests.git + branch = master [submodule "tests/travis"] path = tests/travis url = https://github.com/piwik/travis-scripts @@ -40,9 +41,14 @@ [submodule "plugins/AnonymousPiwikUsageMeasurement"] path = plugins/AnonymousPiwikUsageMeasurement url = https://github.com/piwik/plugin-AnonymousPiwikUsageMeasurement.git + branch = master +[submodule "plugins/CustomDimensions"] + path = plugins/CustomDimensions + url = https://github.com/piwik/plugin-CustomDimensions.git + branch = master -# Add new Plugin submodule above this line ^ +# Add new Plugin submodule above this line ^^ # # Note: when you add a submodule that SHOULD be left in the packaged release such as the few submodules below, # then you MUST add these submodules names in the SUBMODULES_PACKAGED_WITH_CORE variable in: @@ -55,3 +61,6 @@ path = misc/log-analytics url = https://github.com/piwik/piwik-log-analytics.git branch = master + +# Note: do not add new plugin submodules here, but a few lines above + diff --git a/.travis.yml b/.travis.yml index 191839ee03..a672f9eb38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ language: php +group: legacy + php: - 5.6 - 5.5 @@ -17,6 +19,18 @@ php: services: - redis-server +addons: + apt: + sources: + - deadsnakes + + packages: + - python2.6 + - python2.6-dev + - nginx + - realpath + - lftp + # Separate different test suites env: matrix: @@ -25,7 +39,7 @@ env: - TEST_SUITE=IntegrationTests MYSQL_ADAPTER=PDO_MYSQL - TEST_SUITE=UnitTests MYSQL_ADAPTER=PDO_MYSQL # Javascript tests - - TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL SKIP_COMPOSER_INSTALL=1 + - TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL - TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL SKIP_COMPOSER_INSTALL=1 # All tests after another - TEST_SUITE=AllTests MYSQL_ADAPTER=PDO_MYSQL @@ -78,11 +92,11 @@ matrix: env: TEST_SUITE=AllTests MYSQL_ADAPTER=MYSQLI # Javascript tests need to run only on one PHP version - php: 5.5 - env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL SKIP_COMPOSER_INSTALL=1 + env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL - php: hhvm - env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL SKIP_COMPOSER_INSTALL=1 + env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL - php: 7 - env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL SKIP_COMPOSER_INSTALL=1 + env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL # AngularJS tests need to run only on one PHP version - php: 5.5 env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL SKIP_COMPOSER_INSTALL=1 @@ -102,11 +116,6 @@ install: #- export GENERATE_TRAVIS_YML_COMMAND="php ./tests/travis/generator/main.php generate:travis-yml --core --verbose" #- '[[ "$TRAVIS_JOB_NUMBER" != *.1 || "$TRAVIS_PULL_REQUEST" != "false" ]] || ./tests/travis/autoupdate_travis_yml.sh' - - '[ ! -f ./tests/travis/install_mysql_5.6.sh ] || ./tests/travis/install_mysql_5.6.sh' - - # Make sure we use Python 2.6 - - '[ ! -f ./tests/travis/install_python_2.6.sh ] || ./tests/travis/install_python_2.6.sh' - - ./tests/travis/configure_git.sh # travis now complains about this failing 9 times out of 10, so removing it @@ -155,7 +164,7 @@ after_script: - cd $PIWIK_ROOT_DIR # output contents of files w/ debugging info to screen - - cat /var/log/nginx/error.log + - cat $PIWIK_ROOT_DIR/tests/travis/error.log - cat $PIWIK_ROOT_DIR/tmp/php-fpm.log - cat $PIWIK_ROOT_DIR/tmp/logs/piwik.log - cat $PIWIK_ROOT_DIR/config/config.ini.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d52a9162b..96e4d5361d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,22 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API' ### Internal change * Support for IE8 was dropped. This affects only the Piwik UI, not the Piwik.js Tracker. -* Required PHP version was changed from 5.3 to 5.4 +* Required PHP version was changed from 5.3 to 5.5 + +## Piwik 2.15.1 + +### New features + * New segment `actionType` lets you segment all actions of a given type, eg. `actionType==events` or `actionType==downloads`. Action types values are: `pageviews`, `contents`, `sitesearches`, `events`, `outlinks`, `downloads` + +### Internal change + * When generating a new plugin skeleton via `generate:plugin` command, plugin name must now contain only letters and numbers. + * JavaScript Tracker tests no longer require `SQLite`. The existing MySQL configuration for tests is used now. In order to run the tests make sure Piwik is installed and `[database_tests]` is configured in `config/config.ini.php`. + * The definitions for search engine and social network detection have been moved from bundled data files to a separate package (see [https://github.com/piwik/searchengine-and-social-list](https://github.com/piwik/searchengine-and-social-list)). + * In [UI screenshot tests](https://developer.piwik.org/guides/tests-ui), a test environment `configOverride` setting should be no longer overwritten. Instead new values should be added to the existing `configOverride` array in PHP or JavaScript. For example instead of `testEnvironment.configOverride = {group: {name: 1}}` use `testEnvironment.overrideConfig('group', 'name', '1')`. + +### New APIs + * Add your own SMS/Text provider by creating a new class in the `SMSProvider` directory of your plugin. The class has to extend `Piwik\Plugins\MobileMessaging\SMSProvider` and implement the required methods. + * Segments can now be composed by a union of multiple segments. To do this set an array of segments that shall be used for that segment `$segment->setUnionOfSegments(array('outlinkUrl', 'downloadUrl'))` instead of defining a SQL column. ## Piwik 2.15.0 diff --git a/composer.json b/composer.json index 2d476afbce..8bcd8c8f27 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,8 @@ "symfony/event-dispatcher": "~2.6.0", "pear/pear_exception": "~1.0.0", "piwik/referrer-spam-blacklist": "~1.0", - "tecnick.com/tcpdf": "~6.0" + "piwik/searchengine-and-social-list": "~1.0", + "tecnickcom/tcpdf": "~6.0" }, "require-dev": { "aws/aws-sdk-php": "2.7.1", diff --git a/composer.lock b/composer.lock index acb0776a04..2a53020e14 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "500550a2b3c6f91d0a34461f2e6f8654", + "hash": "ab862f8d87513f76161fb7aed61b226a", "packages": [ { "name": "container-interop/container-interop", @@ -303,16 +303,16 @@ }, { "name": "monolog/monolog", - "version": "1.17.1", + "version": "1.17.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "0524c87587ab85bc4c2d6f5b41253ccb930a5422" + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/0524c87587ab85bc4c2d6f5b41253ccb930a5422", - "reference": "0524c87587ab85bc4c2d6f5b41253ccb930a5422", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", "shasum": "" }, "require": { @@ -326,10 +326,11 @@ "aws/aws-sdk-php": "^2.4.9", "doctrine/couchdb": "~1.0@dev", "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", "php-console/php-console": "^3.1.3", "phpunit/phpunit": "~4.5", "phpunit/phpunit-mock-objects": "2.3.0", - "raven/raven": "~0.11", + "raven/raven": "^0.13", "ruflin/elastica": ">=0.90 <3.0", "swiftmailer/swiftmailer": "~5.3", "videlalvaro/php-amqplib": "~2.4" @@ -375,7 +376,7 @@ "logging", "psr-3" ], - "time": "2015-08-31 09:17:37" + "time": "2015-10-14 12:51:02" }, { "name": "mustangostang/spyc", @@ -426,21 +427,21 @@ }, { "name": "pear/archive_tar", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/pear/Archive_Tar.git", - "reference": "e337c40bf467a1fcde6b770fce8ca6dc32dcc38f" + "reference": "fc2937c0e5a2a1c62a378d16394893172f970064" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/e337c40bf467a1fcde6b770fce8ca6dc32dcc38f", - "reference": "e337c40bf467a1fcde6b770fce8ca6dc32dcc38f", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/fc2937c0e5a2a1c62a378d16394893172f970064", + "reference": "fc2937c0e5a2a1c62a378d16394893172f970064", "shasum": "" }, "require": { - "pear/pear-core-minimal": "^1.9.5", - "php": ">=4.3.0" + "pear/pear-core-minimal": "^1.10.0alpha2", + "php": ">=5.2.0" }, "require-dev": { "phpunit/phpunit": "*" @@ -488,7 +489,7 @@ "archive", "tar" ], - "time": "2015-07-20 07:52:03" + "time": "2015-08-05 12:31:03" }, { "name": "pear/console_getopt", @@ -539,24 +540,24 @@ }, { "name": "pear/pear-core-minimal", - "version": "v1.9.5", + "version": "v1.10.1", "source": { "type": "git", "url": "https://github.com/pear/pear-core-minimal.git", - "reference": "bdfefcacb3b388a1050a58eff9ba5a8f43de2411" + "reference": "cae0f1ce0cb5bddb611b0a652d322905a65a5896" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/bdfefcacb3b388a1050a58eff9ba5a8f43de2411", - "reference": "bdfefcacb3b388a1050a58eff9ba5a8f43de2411", + "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/cae0f1ce0cb5bddb611b0a652d322905a65a5896", + "reference": "cae0f1ce0cb5bddb611b0a652d322905a65a5896", "shasum": "" }, "require": { "pear/console_getopt": "~1.3", "pear/pear_exception": "~1.0" }, - "provide": { - "rsky/pear-core-min": "*" + "replace": { + "rsky/pear-core-min": "self.version" }, "type": "library", "autoload": { @@ -569,7 +570,7 @@ "src/" ], "license": [ - "New BSD" + "BSD-3-Clause" ], "authors": [ { @@ -579,7 +580,7 @@ } ], "description": "Minimal set of PEAR core files to be used as composer dependency", - "time": "2015-02-10 20:55:39" + "time": "2015-10-17 11:41:19" }, { "name": "pear/pear_exception", @@ -638,16 +639,16 @@ }, { "name": "php-di/invoker", - "version": "1.0.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/PHP-DI/Invoker.git", - "reference": "2eb8f3a9b44c1427865134ef585d986ca89bce36" + "reference": "9949fff87fcf14e8f2ccfbe36dac1e5921944c48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/2eb8f3a9b44c1427865134ef585d986ca89bce36", - "reference": "2eb8f3a9b44c1427865134ef585d986ca89bce36", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/9949fff87fcf14e8f2ccfbe36dac1e5921944c48", + "reference": "9949fff87fcf14e8f2ccfbe36dac1e5921944c48", "shasum": "" }, "require": { @@ -677,7 +678,7 @@ "invoke", "invoker" ], - "time": "2015-09-02 16:01:10" + "time": "2015-10-22 19:49:23" }, { "name": "php-di/php-di", @@ -738,16 +739,16 @@ }, { "name": "piwik/cache", - "version": "0.2.5", + "version": "0.2.6", "source": { "type": "git", "url": "https://github.com/piwik/component-cache.git", - "reference": "e97ed763c6e710734194664099edd5ee7a41d6b0" + "reference": "b8f2d18069c77726862f67d0199896d13073a831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/piwik/component-cache/zipball/e97ed763c6e710734194664099edd5ee7a41d6b0", - "reference": "e97ed763c6e710734194664099edd5ee7a41d6b0", + "url": "https://api.github.com/repos/piwik/component-cache/zipball/b8f2d18069c77726862f67d0199896d13073a831", + "reference": "b8f2d18069c77726862f67d0199896d13073a831", "shasum": "" }, "require": { @@ -781,7 +782,7 @@ "file", "redis" ], - "time": "2015-02-11 22:35:22" + "time": "2015-09-29 16:50:32" }, { "name": "piwik/decompress", @@ -958,6 +959,28 @@ "time": "2015-10-07 10:17:59" }, { + "name": "piwik/searchengine-and-social-list", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/piwik/searchengine-and-social-list.git", + "reference": "e2b97a1cd9ed2dde735de49f8ef9afc26b3df80b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/piwik/searchengine-and-social-list/zipball/e2b97a1cd9ed2dde735de49f8ef9afc26b3df80b", + "reference": "e2b97a1cd9ed2dde735de49f8ef9afc26b3df80b", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Public Domain" + ], + "description": "Search engine and social network definitions used by Piwik", + "time": "2015-11-06 21:32:51" + }, + { "name": "psr/log", "version": "1.0.0", "source": { @@ -1001,12 +1024,12 @@ "target-dir": "Symfony/Component/Console", "source": { "type": "git", - "url": "https://github.com/symfony/Console.git", + "url": "https://github.com/symfony/console.git", "reference": "0e5e18ae09d3f5c06367759be940e9ed3f568359" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/0e5e18ae09d3f5c06367759be940e9ed3f568359", + "url": "https://api.github.com/repos/symfony/console/zipball/0e5e18ae09d3f5c06367759be940e9ed3f568359", "reference": "0e5e18ae09d3f5c06367759be940e9ed3f568359", "shasum": "" }, @@ -1059,12 +1082,12 @@ "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", + "url": "https://github.com/symfony/event-dispatcher.git", "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02", "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02", "shasum": "" }, @@ -1118,12 +1141,12 @@ "target-dir": "Symfony/Bridge/Monolog", "source": { "type": "git", - "url": "https://github.com/symfony/MonologBridge.git", + "url": "https://github.com/symfony/monolog-bridge.git", "reference": "ba66eeabaa004e3ab70764cab59b056b182aa535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/MonologBridge/zipball/ba66eeabaa004e3ab70764cab59b056b182aa535", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/ba66eeabaa004e3ab70764cab59b056b182aa535", "reference": "ba66eeabaa004e3ab70764cab59b056b182aa535", "shasum": "" }, @@ -1172,17 +1195,17 @@ "time": "2015-06-25 11:21:15" }, { - "name": "tecnick.com/tcpdf", - "version": "6.2.11", + "name": "tecnickcom/tcpdf", + "version": "6.2.12", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "354433a33946ae7497c3eab291eaaf814bccbfab" + "reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/354433a33946ae7497c3eab291eaaf814bccbfab", - "reference": "354433a33946ae7497c3eab291eaaf814bccbfab", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/2f732eaa91b5665274689b1d40b285a7bacdc37f", + "reference": "2f732eaa91b5665274689b1d40b285a7bacdc37f", "shasum": "" }, "require": { @@ -1232,8 +1255,7 @@ "pdf417", "qrcode" ], - "abandoned": "tecnickcom/tcpdf", - "time": "2015-08-02 12:30:27" + "time": "2015-09-12 10:08:34" }, { "name": "tedivm/jshrink", @@ -1278,16 +1300,16 @@ }, { "name": "twig/twig", - "version": "v1.21.1", + "version": "v1.22.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ca8d3aa90b6a01c82e07909fe815d6b443e75a23" + "reference": "ebfc36b7e77b0c1175afe30459cf943010245540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ca8d3aa90b6a01c82e07909fe815d6b443e75a23", - "reference": "ca8d3aa90b6a01c82e07909fe815d6b443e75a23", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ebfc36b7e77b0c1175afe30459cf943010245540", + "reference": "ebfc36b7e77b0c1175afe30459cf943010245540", "shasum": "" }, "require": { @@ -1300,7 +1322,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "1.22-dev" } }, "autoload": { @@ -1335,7 +1357,7 @@ "keywords": [ "templating" ], - "time": "2015-08-26 08:58:31" + "time": "2015-10-13 07:07:02" } ], "packages-dev": [ @@ -1487,7 +1509,7 @@ "performance", "profiling" ], - "time": "2015-02-26 14:37:51" + "time": "2014-08-28 17:34:52" }, { "name": "guzzle/guzzle", @@ -1793,16 +1815,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.2.2", + "version": "2.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c" + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c", - "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", "shasum": "" }, "require": { @@ -1851,7 +1873,7 @@ "testing", "xunit" ], - "time": "2015-08-04 03:42:39" + "time": "2015-10-06 15:47:00" }, { "name": "phpunit/php-file-iterator", @@ -1984,16 +2006,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.6", + "version": "1.4.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b" + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3ab72c62e550370a6cd5dc873e1a04ab57562f5b", - "reference": "3ab72c62e550370a6cd5dc873e1a04ab57562f5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", "shasum": "" }, "require": { @@ -2029,20 +2051,20 @@ "keywords": [ "tokenizer" ], - "time": "2015-08-16 08:51:00" + "time": "2015-09-15 10:49:45" }, { "name": "phpunit/phpunit", - "version": "4.8.6", + "version": "4.8.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2246830f4a1a551c67933e4171bf2126dc29d357" + "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2246830f4a1a551c67933e4171bf2126dc29d357", - "reference": "2246830f4a1a551c67933e4171bf2126dc29d357", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e", + "reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e", "shasum": "" }, "require": { @@ -2101,20 +2123,20 @@ "testing", "xunit" ], - "time": "2015-08-24 04:09:38" + "time": "2015-10-23 06:48:33" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.7", + "version": "2.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "5e2645ad49d196e020b85598d7c97e482725786a" + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5e2645ad49d196e020b85598d7c97e482725786a", - "reference": "5e2645ad49d196e020b85598d7c97e482725786a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", "shasum": "" }, "require": { @@ -2157,7 +2179,7 @@ "mock", "xunit" ], - "time": "2015-08-19 09:14:08" + "time": "2015-10-02 06:51:40" }, { "name": "sebastian/comparator", @@ -2393,16 +2415,16 @@ }, { "name": "sebastian/global-state", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", - "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", "shasum": "" }, "require": { @@ -2440,7 +2462,7 @@ "keywords": [ "global state" ], - "time": "2014-10-06 09:23:50" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", diff --git a/config/environment/ui-test.php b/config/environment/ui-test.php new file mode 100644 index 0000000000..b5ef0fede4 --- /dev/null +++ b/config/environment/ui-test.php @@ -0,0 +1,62 @@ +<?php + +use Piwik\Container\StaticContainer; + +return array( + + // UI tests will remove the port from all URLs to the test server. if a test + // requires the ports in UI tests (eg, Overlay), add the api/controller methods + // to one of these blacklists + 'tests.ui.url_normalizer_blacklist.api' => array(), + 'tests.ui.url_normalizer_blacklist.controller' => array(), + + 'Piwik\Config' => \DI\decorate(function (\Piwik\Config $config) { + $config->General['cors_domains'][] = '*'; + $config->General['trusted_hosts'][] = $config->tests['http_host']; + $config->General['trusted_hosts'][] = $config->tests['http_host'] . ':' . $config->tests['port']; + return $config; + }), + + 'observers.global' => \DI\add(array( + + // removes port from all URLs to the test Piwik server so UI tests will pass no matter + // what port is used + array('Request.dispatch.end', function (&$result) { + $request = $_GET + $_POST; + + $apiblacklist = StaticContainer::get('tests.ui.url_normalizer_blacklist.api'); + if (!empty($request['method']) + && in_array($request['method'], $apiblacklist) + ) { + return; + } + + $controllerActionblacklist = StaticContainer::get('tests.ui.url_normalizer_blacklist.controller'); + if (!empty($request['module']) + && !empty($request['action']) + ) { + $controllerAction = $request['module'] . '.' . $request['action']; + if (in_array($controllerAction, $controllerActionblacklist)) { + return; + } + } + + $config = \Piwik\Config::getInstance(); + $host = $config->tests['http_host']; + $port = $config->tests['port']; + + if (!empty($port)) { + // remove the port from URLs if any so UI tests won't fail if the port isn't 80 + $result = str_replace($host . ':' . $port, $host, $result); + } + + // remove PIWIK_INCLUDE_PATH from result so tests don't change based on the machine used + $result = str_replace(realpath(PIWIK_INCLUDE_PATH), '', $result); + }), + + array('Controller.ExampleRssWidget.rssPiwik.end', function (&$result, $parameters) { + $result = ""; + }), + )), + +); diff --git a/config/global.ini.php b/config/global.ini.php index 7dd55d3719..aad0fd3381 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -44,6 +44,7 @@ schema = Mysql http_host = localhost remote_addr = "127.0.0.1" request_uri = "@REQUEST_URI@" +port = ; access key and secret as listed in AWS -> IAM -> Users aws_accesskey = "" @@ -248,8 +249,9 @@ default_language = en datatable_default_limit = 10 ; Each datatable report has a Row Limit selector at the bottom right. -; By default you can select from 5 to 500 rows. You may customise the values below: -datatable_row_limits = "5,10,25,50,100,250,500" +; By default you can select from 5 to 500 rows. You may customise the values below +; -1 will be displayed as 'all' and it will export all rows (filter_limit=-1) +datatable_row_limits = "5,10,25,50,100,250,500,-1" ; default number of rows returned in API responses ; this value is overwritten by the '# Rows to display' selector. diff --git a/config/global.php b/config/global.php index a54565498f..36e04970aa 100644 --- a/config/global.php +++ b/config/global.php @@ -73,9 +73,6 @@ return array( )))); }, - // contains RequestProcessor instances, currently used by Piwik\Tracker\Visit - 'tracker.request.processors' => array(), - 'Piwik\Tracker\VisitorRecognizer' => DI\object() ->constructorParameter('trustCookiesOnly', DI\get('ini.Tracker.trust_visitors_cookies')) ->constructorParameter('visitStandardLength', DI\get('ini.Tracker.visit_standard_length')) diff --git a/core/API/DataTableManipulator.php b/core/API/DataTableManipulator.php index 76c52f4958..d084d6d156 100644 --- a/core/API/DataTableManipulator.php +++ b/core/API/DataTableManipulator.php @@ -152,7 +152,15 @@ abstract class DataTableManipulator $idSite = 'all'; } - $meta = API::getInstance()->getMetadata($idSite, $this->apiModule, $this->apiMethod); + $apiParameters = array(); + if (!empty($request['idDimension'])) { + $apiParameters['idDimension'] = $request['idDimension']; + } + if (!empty($request['idGoal'])) { + $apiParameters['idGoal'] = $request['idGoal']; + } + + $meta = API::getInstance()->getMetadata($idSite, $this->apiModule, $this->apiMethod, $apiParameters); if (empty($meta)) { throw new Exception(sprintf( diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php index 64c25bf9b0..60807267df 100644 --- a/core/API/DocumentationGenerator.php +++ b/core/API/DocumentationGenerator.php @@ -291,6 +291,7 @@ class DocumentationGenerator $aParameters['disable_queued_filters'] = false; $aParameters['disable_generic_filters'] = false; $aParameters['expanded'] = false; + $aParameters['idDimenson'] = false; $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class); $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters); diff --git a/core/API/Proxy.php b/core/API/Proxy.php index 26e2ceb4cc..1d348e1b87 100644 --- a/core/API/Proxy.php +++ b/core/API/Proxy.php @@ -262,7 +262,7 @@ class Proxy extends Singleton * **Example** * * // append (0 hits) to the end of row labels whose row has 0 hits for any report that has the 'nb_hits' metric - * Piwik::addAction('API.Actions.getPageUrls', function (&$returnValue, $info)) { + * Piwik::addAction('API.Actions.getPageUrls.end', function (&$returnValue, $info)) { * // don't process non-DataTable reports and reports that don't have the nb_hits column * if (!($returnValue instanceof DataTableInterface) * || in_array('nb_hits', $returnValue->getColumns()) diff --git a/core/API/Request.php b/core/API/Request.php index 834d83290e..6ddb8333f5 100644 --- a/core/API/Request.php +++ b/core/API/Request.php @@ -36,12 +36,10 @@ use Piwik\Plugin\Manager as PluginManager; * ### Post-processing * * The return value of API methods undergo some extra processing before being returned by Request. - * To learn more about what happens to API results, read [this](/guides/piwiks-web-api#extra-report-processing). * * ### Output Formats * * The value returned by Request will be serialized to a certain format before being returned. - * To see the list of supported output formats, read [this](/guides/piwiks-web-api#output-formats). * * ### Examples * @@ -390,6 +388,7 @@ class Request { $params = array(); $params['format'] = 'original'; + $params['serialize'] = '0'; $params['module'] = 'API'; $params['method'] = $method; $params = $paramOverride + $params; diff --git a/core/Archive.php b/core/Archive.php index 80ef151f38..2efe230318 100644 --- a/core/Archive.php +++ b/core/Archive.php @@ -619,7 +619,7 @@ class Archive if ($isNumeric) { $row['value'] = $this->formatNumericValue($row['value']); } else { - $result->addMetadata($row['idsite'], $periodStr, 'ts_archived', $row['ts_archived']); + $result->addMetadata($row['idsite'], $periodStr, DataTable::ARCHIVED_DATE_METADATA_NAME, $row['ts_archived']); } $result->set($row['idsite'], $periodStr, $row['name'], $row['value']); diff --git a/core/Columns/Dimension.php b/core/Columns/Dimension.php index c4895371ad..c89ae637a5 100644 --- a/core/Columns/Dimension.php +++ b/core/Columns/Dimension.php @@ -149,7 +149,7 @@ abstract class Dimension * This would only happen if the dimension is located in the wrong directory. * @api */ - final public function getId() + public function getId() { $className = get_class($this); diff --git a/core/Common.php b/core/Common.php index 7e3296bee1..85120c97fa 100644 --- a/core/Common.php +++ b/core/Common.php @@ -273,12 +273,6 @@ class Common return $value; } elseif (is_string($value)) { $value = self::sanitizeString($value); - - if (!$alreadyStripslashed) { - // a JSON array was already stripslashed, don't do it again for each value - - $value = self::undoMagicQuotes($value); - } } elseif (is_array($value)) { foreach (array_keys($value) as $key) { $newKey = $key; @@ -379,27 +373,6 @@ class Common } /** - * Undo the damage caused by magic_quotes; deprecated in php 5.3 but not removed until php 5.4 - * - * @param string - * @return string modified or not - */ - private static function undoMagicQuotes($value) - { - static $shouldUndo; - - if (!isset($shouldUndo)) { - $shouldUndo = version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc(); - } - - if ($shouldUndo) { - $value = stripslashes($value); - } - - return $value; - } - - /** * @param string $value * @return string Line breaks and line carriage removed */ @@ -475,7 +448,7 @@ class Common // we deal w/ json differently if ($varType == 'json') { - $value = self::undoMagicQuotes($requestArrayToUse[$varName]); + $value = $requestArrayToUse[$varName]; $value = json_decode($value, $assoc = true); return self::sanitizeInputValues($value, $alreadyStripslashed = true); } @@ -816,86 +789,6 @@ class Common } /** - * Returns list of search engines by URL - * - * @see core/DataFiles/SearchEngines.php - * - * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) ) - */ - public static function getSearchEngineUrls() - { - $cacheId = 'Common.getSearchEngineUrls'; - $cache = Cache::getTransientCache(); - $searchEngines = $cache->fetch($cacheId); - - if (empty($searchEngines)) { - require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/SearchEngines.php'; - - $searchEngines = $GLOBALS['Piwik_SearchEngines']; - - Piwik::postEvent('Referrer.addSearchEngineUrls', array(&$searchEngines)); - - $cache->save($cacheId, $searchEngines); - } - - return $searchEngines; - } - - /** - * Returns list of search engines by name - * - * @see core/DataFiles/SearchEngines.php - * - * @return array Array of ( searchEngineName => URL ) - */ - public static function getSearchEngineNames() - { - $cacheId = 'Common.getSearchEngineNames'; - $cache = Cache::getTransientCache(); - $nameToUrl = $cache->fetch($cacheId); - - if (empty($nameToUrl)) { - $searchEngines = self::getSearchEngineUrls(); - - $nameToUrl = array(); - foreach ($searchEngines as $url => $info) { - if (!isset($nameToUrl[$info[0]])) { - $nameToUrl[$info[0]] = $url; - } - } - $cache->save($cacheId, $nameToUrl); - } - - return $nameToUrl; - } - - /** - * Returns list of social networks by URL - * - * @see core/DataFiles/Socials.php - * - * @return array Array of ( URL => Social Network Name ) - */ - public static function getSocialUrls() - { - $cacheId = 'Common.getSocialUrls'; - $cache = Cache::getTransientCache(); - $socialUrls = $cache->fetch($cacheId); - - if (empty($socialUrls)) { - require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - - $socialUrls = $GLOBALS['Piwik_socialUrl']; - - Piwik::postEvent('Referrer.addSocialUrls', array(&$socialUrls)); - - $cache->save($cacheId, $socialUrls); - } - - return $socialUrls; - } - - /** * Returns list of provider names * * @see core/DataFiles/Providers.php diff --git a/core/Container/ContainerFactory.php b/core/Container/ContainerFactory.php index 75735036aa..9c5eed1276 100644 --- a/core/Container/ContainerFactory.php +++ b/core/Container/ContainerFactory.php @@ -79,12 +79,7 @@ class ContainerFactory // Development config if ($this->isDevelopmentModeEnabled()) { - $builder->addDefinitions(PIWIK_USER_PATH . '/config/environment/dev.php'); - } - - // User config - if (file_exists(PIWIK_USER_PATH . '/config/config.php')) { - $builder->addDefinitions(PIWIK_USER_PATH . '/config/config.php'); + $this->addEnvironmentConfig($builder, 'dev'); } // Environment config @@ -92,6 +87,11 @@ class ContainerFactory $this->addEnvironmentConfig($builder, $environment); } + // User config + if (file_exists(PIWIK_USER_PATH . '/config/config.php')) { + $builder->addDefinitions(PIWIK_USER_PATH . '/config/config.php'); + } + if (!empty($this->definitions)) { foreach ($this->definitions as $definitionArray) { $builder->addDefinitions($definitionArray); diff --git a/core/CronArchive.php b/core/CronArchive.php index 4b9d3556d3..c929f88809 100644 --- a/core/CronArchive.php +++ b/core/CronArchive.php @@ -367,7 +367,7 @@ class CronArchive continue; } - $shouldCheckIfArchivingIsNeeded = !$this->shouldArchiveSpecifiedSites && !$this->shouldArchiveAllSites; + $shouldCheckIfArchivingIsNeeded = !$this->shouldArchiveSpecifiedSites && !$this->shouldArchiveAllSites && !$this->dateLastForced; $hasWebsiteDayFinishedSinceLastRun = in_array($idSite, $this->websiteDayHasFinishedSinceLastRun); $isOldReportInvalidatedForWebsite = $this->isOldReportInvalidatedForWebsite($idSite); diff --git a/core/DataAccess/LogQueryBuilder.php b/core/DataAccess/LogQueryBuilder.php index 5e36b4617c..c491b934ce 100644 --- a/core/DataAccess/LogQueryBuilder.php +++ b/core/DataAccess/LogQueryBuilder.php @@ -44,6 +44,39 @@ class LogQueryBuilder ); } + private function hasJoinedTableAlreadyManually($tableToFind, $joinToFind, $tables) + { + foreach ($tables as $index => $table) { + if (is_array($table) + && !empty($table['table']) + && $table['table'] === $tableToFind + && (!isset($table['tableAlias']) || $table['tableAlias'] === $tableToFind) + && isset($table['joinOn']) && $table['joinOn'] === $joinToFind) { + return true; + } + } + + return false; + } + + private function findIndexOfManuallyAddedTable($tableToFind, $tables) + { + foreach ($tables as $index => $table) { + if (is_array($table) + && !empty($table['table']) + && $table['table'] === $tableToFind + && (!isset($table['tableAlias']) || $table['tableAlias'] === $tableToFind)) { + return $index; + } + } + } + + private function hasTableAddedManually($tableToFind, $tables) + { + $table = $this->findIndexOfManuallyAddedTable($tableToFind, $tables); + + return isset($table); + } /** * Generate the join sql based on the needed tables @@ -51,10 +84,12 @@ class LogQueryBuilder * @throws Exception if tables can't be joined * @return array */ - private function generateJoinsString($tables) + private function generateJoinsString(&$tables) { - $knownTables = array("log_visit", "log_link_visit_action", "log_conversion", "log_conversion_item"); - $visitsAvailable = $actionsAvailable = $conversionsAvailable = $conversionItemAvailable = false; + $knownTables = array("log_action", "log_visit", "log_link_visit_action", "log_conversion", "log_conversion_item"); + $visitsAvailable = $linkVisitActionsTableAvailable = $conversionsAvailable = $conversionItemAvailable = $actionsTableAvailable = false; + $defaultLogActionJoin = "log_link_visit_action.idaction_url = log_action.idaction"; + $joinWithSubSelect = false; $sql = ''; @@ -67,7 +102,6 @@ class LogQueryBuilder $tables[$actionIndex] = "log_conversion"; $tables[$conversionIndex] = "log_link_visit_action"; } - // same as above: action before visit $actionIndex = array_search("log_link_visit_action", $tables); $visitIndex = array_search("log_visit", $tables); @@ -76,6 +110,29 @@ class LogQueryBuilder $tables[$visitIndex] = "log_link_visit_action"; } + // we need to add log_link_visit_action dynamically to join eg visit with action + $linkVisitAction = array_search("log_link_visit_action", $tables); + $actionIndex = array_search("log_action", $tables); + if ($linkVisitAction === false && $actionIndex > 0) { + $tables[] = "log_link_visit_action"; + } + + if ($actionIndex > 0 + && $this->hasTableAddedManually('log_action', $tables) + && !$this->hasJoinedTableAlreadyManually('log_action', $defaultLogActionJoin, $tables)) { + // we cannot join the same table with same alias twice, therefore we need to combine the join via AND + $tableIndex = $this->findIndexOfManuallyAddedTable('log_action', $tables); + $defaultLogActionJoin = '(' . $tables[$tableIndex]['joinOn'] . ' AND ' . $defaultLogActionJoin . ')'; + unset($tables[$tableIndex]); + } + + $linkVisitAction = array_search("log_link_visit_action", $tables); + $actionIndex = array_search("log_action", $tables); + if ($linkVisitAction > 0 && $actionIndex > 0 && $linkVisitAction > $actionIndex) { + $tables[$actionIndex] = "log_link_visit_action"; + $tables[$linkVisitAction] = "log_action"; + } + foreach ($tables as $i => $table) { if (is_array($table)) { // join condition provided @@ -96,17 +153,38 @@ class LogQueryBuilder // first table $sql .= $tableSql; } else { - if ($actionsAvailable && $table == "log_conversion") { + + if ($linkVisitActionsTableAvailable && $table === 'log_action') { + $join = $defaultLogActionJoin; + + if ($this->hasJoinedTableAlreadyManually($table, $join, $tables)) { + $actionsTableAvailable = true; + continue; + } + + } elseif ($linkVisitActionsTableAvailable && $table == "log_conversion") { // have actions, need conversions => join on idvisit $join = "log_conversion.idvisit = log_link_visit_action.idvisit"; - } elseif ($actionsAvailable && $table == "log_visit") { + } elseif ($linkVisitActionsTableAvailable && $table == "log_visit") { // have actions, need visits => join on idvisit $join = "log_visit.idvisit = log_link_visit_action.idvisit"; + + if ($this->hasJoinedTableAlreadyManually($table, $join, $tables)) { + $visitsAvailable = true; + continue; + } + } elseif ($visitsAvailable && $table == "log_link_visit_action") { // have visits, need actions => we have to use a more complex join // we don't hande this here, we just return joinWithSubSelect=true in this case $joinWithSubSelect = true; $join = "log_link_visit_action.idvisit = log_visit.idvisit"; + + if ($this->hasJoinedTableAlreadyManually($table, $join, $tables)) { + $linkVisitActionsTableAvailable = true; + continue; + } + } elseif ($conversionsAvailable && $table == "log_link_visit_action") { // have conversions, need actions => join on idvisit $join = "log_conversion.idvisit = log_link_visit_action.idvisit"; @@ -138,7 +216,8 @@ class LogQueryBuilder // remember which tables are available $visitsAvailable = ($visitsAvailable || $table == "log_visit"); - $actionsAvailable = ($actionsAvailable || $table == "log_link_visit_action"); + $linkVisitActionsTableAvailable = ($linkVisitActionsTableAvailable || $table == "log_link_visit_action"); + $actionsTableAvailable = ($actionsTableAvailable || $table == "log_action"); $conversionsAvailable = ($conversionsAvailable || $table == "log_conversion"); $conversionItemAvailable = ($conversionItemAvailable || $table == "log_conversion_item"); } diff --git a/core/DataArray.php b/core/DataArray.php index 386eae29a3..1be1942312 100644 --- a/core/DataArray.php +++ b/core/DataArray.php @@ -47,7 +47,7 @@ class DataArray public function sumMetricsVisits($label, $row) { if (!isset($this->data[$label])) { - $this->data[$label] = self::makeEmptyRow(); + $this->data[$label] = static::makeEmptyRow(); } $this->doSumVisitsMetrics($row, $this->data[$label]); } @@ -80,7 +80,7 @@ class DataArray * * @return void */ - protected function doSumVisitsMetrics($newRowToAdd, &$oldRowToUpdate, $onlyMetricsAvailableInActionsTable = false) + protected function doSumVisitsMetrics($newRowToAdd, &$oldRowToUpdate) { // Pre 1.2 format: string indexed rows are returned from the DB // Left here for Backward compatibility with plugins doing custom SQL queries using these metrics as string @@ -88,9 +88,6 @@ class DataArray $oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd['nb_visits']; $oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions']; $oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors']; - if ($onlyMetricsAvailableInActionsTable) { - return; - } $oldRowToUpdate[Metrics::INDEX_NB_USERS] += $newRowToAdd['nb_users']; $oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd['max_actions'], $oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS]); $oldRowToUpdate[Metrics::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd['sum_visit_length']; @@ -107,9 +104,6 @@ class DataArray $oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd[Metrics::INDEX_NB_VISITS]; $oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd[Metrics::INDEX_NB_ACTIONS]; $oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Metrics::INDEX_NB_UNIQ_VISITORS]; - if ($onlyMetricsAvailableInActionsTable) { - return; - } // In case the existing Row had no action metrics (eg. Custom Variable XYZ with "visit" scope) // but the new Row has action metrics (eg. same Custom Variable XYZ this time with a "page" scope) @@ -133,11 +127,50 @@ class DataArray $oldRowToUpdate[Metrics::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd[Metrics::INDEX_NB_VISITS_CONVERTED]; } + /** + * Adds the given row $newRowToAdd to the existing $oldRowToUpdate passed by reference + * The rows are php arrays Name => value + * + * @param array $newRowToAdd + * @param array $oldRowToUpdate + * @param bool $onlyMetricsAvailableInActionsTable + * + * @return void + */ + protected function doSumActionsMetrics($newRowToAdd, &$oldRowToUpdate) + { + // Pre 1.2 format: string indexed rows are returned from the DB + // Left here for Backward compatibility with plugins doing custom SQL queries using these metrics as string + if (!isset($newRowToAdd[Metrics::INDEX_NB_VISITS])) { + $oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd['nb_visits']; + $oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions']; + $oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors']; + return; + } + + // Edge case fail safe + if (!isset($oldRowToUpdate[Metrics::INDEX_NB_VISITS])) { + return; + } + + $oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd[Metrics::INDEX_NB_VISITS]; + if (array_key_exists(Metrics::INDEX_NB_ACTIONS, $newRowToAdd)) { + $oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd[Metrics::INDEX_NB_ACTIONS]; + } + if (array_key_exists(Metrics::INDEX_PAGE_NB_HITS, $newRowToAdd)) { + if (!array_key_exists(Metrics::INDEX_PAGE_NB_HITS, $oldRowToUpdate)) { + $oldRowToUpdate[Metrics::INDEX_PAGE_NB_HITS] = 0; + } + $oldRowToUpdate[Metrics::INDEX_PAGE_NB_HITS] += $newRowToAdd[Metrics::INDEX_PAGE_NB_HITS]; + } + $oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Metrics::INDEX_NB_UNIQ_VISITORS]; + } + public function sumMetricsGoals($label, $row) { $idGoal = $row['idgoal']; if (!isset($this->data[$label][Metrics::INDEX_GOALS][$idGoal])) { - $this->data[$label][Metrics::INDEX_GOALS][$idGoal] = self::makeEmptyGoalRow($idGoal); + $this->data[$label][Metrics::INDEX_GOALS][$idGoal] = static::makeEmptyGoalRow($idGoal); } $this->doSumGoalsMetrics($row, $this->data[$label][Metrics::INDEX_GOALS][$idGoal]); } @@ -201,9 +234,10 @@ class DataArray public function sumMetricsActions($label, $row) { if (!isset($this->data[$label])) { - $this->data[$label] = self::makeEmptyActionRow(); + $this->data[$label] = static::makeEmptyActionRow(); } - $this->doSumVisitsMetrics($row, $this->data[$label], $onlyMetricsAvailableInActionsTable = true); + + $this->doSumActionsMetrics($row, $this->data[$label]); } protected static function makeEmptyActionRow() @@ -218,7 +252,7 @@ class DataArray public function sumMetricsEvents($label, $row) { if (!isset($this->data[$label])) { - $this->data[$label] = self::makeEmptyEventRow(); + $this->data[$label] = static::makeEmptyEventRow(); } $this->doSumEventsMetrics($row, $this->data[$label], $onlyMetricsAvailableInActionsTable = true); } @@ -250,16 +284,16 @@ class DataArray $oldRowToUpdate[Metrics::INDEX_EVENT_NB_HITS] += $newRowToAdd[Metrics::INDEX_EVENT_NB_HITS]; $oldRowToUpdate[Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE] += $newRowToAdd[Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE]; - $newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE], self::EVENT_VALUE_PRECISION); + $newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE], static::EVENT_VALUE_PRECISION); $oldRowToUpdate[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] += $newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE]; - $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE] = round(max($newRowToAdd[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]), self::EVENT_VALUE_PRECISION); + $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE] = round(max($newRowToAdd[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]), static::EVENT_VALUE_PRECISION); // Update minimum only if it is set if ($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] !== false) { if ($oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] === false) { - $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], self::EVENT_VALUE_PRECISION); + $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], static::EVENT_VALUE_PRECISION); } else { - $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round(min($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]), self::EVENT_VALUE_PRECISION); + $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round(min($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]), static::EVENT_VALUE_PRECISION); } } } @@ -290,7 +324,7 @@ class DataArray public function sumMetricsVisitsPivot($parentLabel, $label, $row) { if (!isset($this->dataTwoLevels[$parentLabel][$label])) { - $this->dataTwoLevels[$parentLabel][$label] = self::makeEmptyRow(); + $this->dataTwoLevels[$parentLabel][$label] = static::makeEmptyRow(); } $this->doSumVisitsMetrics($row, $this->dataTwoLevels[$parentLabel][$label]); } @@ -299,7 +333,7 @@ class DataArray { $idGoal = $row['idgoal']; if (!isset($this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal])) { - $this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal] = self::makeEmptyGoalRow($idGoal); + $this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal] = static::makeEmptyGoalRow($idGoal); } $this->doSumGoalsMetrics($row, $this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal]); } @@ -309,7 +343,7 @@ class DataArray if (!isset($this->dataTwoLevels[$parentLabel][$label])) { $this->dataTwoLevels[$parentLabel][$label] = $this->makeEmptyActionRow(); } - $this->doSumVisitsMetrics($row, $this->dataTwoLevels[$parentLabel][$label], $onlyMetricsAvailableInActionsTable = true); + $this->doSumActionsMetrics($row, $this->dataTwoLevels[$parentLabel][$label]); } public function sumMetricsEventsPivot($parentLabel, $label, $row) @@ -382,7 +416,7 @@ class DataArray */ public static function isRowActions($row) { - return (count($row) == count(self::makeEmptyActionRow())) && isset($row[Metrics::INDEX_NB_ACTIONS]); + return (count($row) == count(static::makeEmptyActionRow())) && isset($row[Metrics::INDEX_NB_ACTIONS]); } /** diff --git a/core/DataFiles/SearchEngines.php b/core/DataFiles/SearchEngines.php deleted file mode 100644 index 52b59713f6..0000000000 --- a/core/DataFiles/SearchEngines.php +++ /dev/null @@ -1,1140 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -/** - * Search Engine database - * - * ====================================== - * HOW TO ADD A SEARCH ENGINE TO THE LIST - * ====================================== - * If you want to add a new entry, please email us the information + icon at - * hello at piwik.org - * - * See also: http://piwik.org/faq/general/#faq_39 - * - * Detail of a line: - * Url => array( SearchEngineName, KeywordParameter, [path containing the keyword], [charset used by the search engine]) - * - * The main search engine URL has to be at the top of the list for the given - * search Engine. This serves as the master record so additional URLs - * don't have to duplicate all the information, but can override when needed. - * - * The URL, "example.com", will match "example.com", "m.example.com", - * "www.example.com", and "search.example.com". - * - * For region-specific search engines, the URL, "{}.example.com" will match - * any ISO3166-1 alpha2 country code against "{}". Similarly, "example.{}" - * will match against valid country TLDs, but should be used sparingly to - * avoid false positives. - * - * The charset should be an encoding supported by mbstring. If unspecified, - * we'll assume it's UTF-8. - * Reference: http://www.php.net/manual/en/mbstring.encodings.php - * - * You can add new search engines icons by adding the icon in the - * plugins/Referrers/images/searchEngines directory using the format - * 'mainSearchEngineUrl.png'. Example: www.google.com.png - * - * To help Piwik link directly the search engine result page for the keyword, - * specify the third entry in the array using the macro {k} that will - * automatically be replaced by the keyword. - * - * A simple example is: - * 'www.google.com' => array('Google', 'q', 'search?q={k}'), - * - * A more complicated example, with an array of possible variable names, and a custom charset: - * 'www.baidu.com' => array('Baidu', array('wd', 'word', 'kw'), 's?wd={k}', 'gb2312'), - * - * Another example using a regular expression to parse the path for keywords: - * 'infospace.com' => array('InfoSpace', array('/dir1\/(pattern)\/dir2/'), '/dir1/{k}/dir2/stuff/'), - */ -if (!isset($GLOBALS['Piwik_SearchEngines'])) { - $GLOBALS['Piwik_SearchEngines'] = array( - // 1 - '1.cz' => array('1.cz', array('/s\/([^\/]+)/', 'q'), 's/{k}', 'iso-8859-2'), - - // 123people - 'www.123people.com' => array('123people', array('/s\/([^\/]+)/', 'search_term'), 's/{k}'), - '123people.{}' => array('123people'), - - // 360search - 'so.360.cn' => array('360search', 'q', 's?q={k}', array('UTF-8', 'gb2312')), - 'www.so.com' => array('360search', 'q', 's?q={k}', array('UTF-8', 'gb2312')), - - // Abacho - 'www.abacho.de' => array('Abacho', 'q', 'suche?q={k}'), - 'www.abacho.com' => array('Abacho'), - 'www.abacho.co.uk' => array('Abacho'), - 'www.se.abacho.com' => array('Abacho'), - 'www.tr.abacho.com' => array('Abacho'), - 'www.abacho.at' => array('Abacho'), - 'www.abacho.fr' => array('Abacho'), - 'www.abacho.es' => array('Abacho'), - 'www.abacho.ch' => array('Abacho'), - 'www.abacho.it' => array('Abacho'), - - // ABCsøk - 'abcsok.no' => array('ABCsøk', 'q', '?q={k}'), - 'verden.abcsok.no' => array('ABCsøk'), - - // Acoon - 'www.acoon.de' => array('Acoon', 'begriff', 'cgi-bin/search.exe?begriff={k}'), - - // Aguea - 'chercherfr.aguea.com' => array('Aguea', 'q', 's.py?q={k}'), - - // Alexa - 'alexa.com' => array('Alexa', 'q', 'search?q={k}'), - 'search.toolbars.alexa.com' => array('Alexa'), - - // Alice Adsl - 'rechercher.aliceadsl.fr' => array('Alice Adsl', 'qs', 'google.pl?qs={k}'), - - // Allesklar - 'www.allesklar.de' => array('Allesklar', 'words', '?words={k}'), - 'www.allesklar.at' => array('Allesklar'), - 'www.allesklar.ch' => array('Allesklar'), - - // AllTheWeb - 'www.alltheweb.com' => array('AllTheWeb', 'q', 'search?q={k}'), - - // all.by - 'all.by' => array('All.by', 'query', 'cgi-bin/search.cgi?mode=by&query={k}'), - - // Altavista - 'www.altavista.com' => array('AltaVista', 'q', 'web/results?q={k}'), - 'search.altavista.com' => array('AltaVista'), - 'listings.altavista.com' => array('AltaVista'), - 'altavista.de' => array('AltaVista'), - 'altavista.fr' => array('AltaVista'), - '{}.altavista.com' => array('AltaVista'), - 'be-nl.altavista.com' => array('AltaVista'), - 'be-fr.altavista.com' => array('AltaVista'), - - // Apollo Latvia - 'apollo.lv/portal/search/' => array('Apollo lv', 'q', '?cof=FORID%3A11&q={k}&search_where=www'), - - // APOLLO7 - 'apollo7.de' => array('Apollo7', 'query', 'a7db/index.php?query={k}&de_sharelook=true&de_bing=true&de_witch=true&de_google=true&de_yahoo=true&de_lycos=true'), - - // AOL - 'search.aol.com' => array('AOL', array('query', 'q'), 'aol/search?q={k}'), - 'search.aol.it' => array('AOL'), - 'aolsearch.aol.com' => array('AOL'), - 'www.aolrecherche.aol.fr' => array('AOL'), - 'www.aolrecherches.aol.fr' => array('AOL'), - 'www.aolimages.aol.fr' => array('AOL'), - 'aim.search.aol.com' => array('AOL'), - 'www.recherche.aol.fr' => array('AOL'), - 'recherche.aol.fr' => array('AOL'), - 'find.web.aol.com' => array('AOL'), - 'recherche.aol.ca' => array('AOL'), - 'aolsearch.aol.co.uk' => array('AOL'), - 'search.aol.co.uk' => array('AOL'), - 'aolrecherche.aol.fr' => array('AOL'), - 'sucheaol.aol.de' => array('AOL'), - 'suche.aol.de' => array('AOL'), - 'o2suche.aol.de' => array('AOL'), - 'suche.aolsvc.de' => array('AOL'), - 'aolbusqueda.aol.com.mx' => array('AOL'), - 'alicesuche.aol.de' => array('AOL'), - 'alicesuchet.aol.de' => array('AOL'), - 'suchet2.aol.de' => array('AOL'), - 'search.hp.my.aol.com.au' => array('AOL'), - 'search.hp.my.aol.de' => array('AOL'), - 'search.hp.my.aol.it' => array('AOL'), - 'search-intl.netscape.com' => array('AOL'), - 'de.aolsearch.com' => array('AOL', 'q', 'search?q={k}'), - - // Aport - 'sm.aport.ru' => array('Aport', 'r', 'search?r={k}'), - - // arama - 'arama.com' => array('Arama', 'q', 'search.php3?q={k}'), - - // Arcor - 'www.arcor.de' => array('Arcor', 'Keywords', 'content/searchresult.jsp?Keywords={k}'), - - // Arianna (Libero.it) - 'arianna.libero.it' => array('Arianna', 'query', 'search/abin/integrata.cgi?query={k}'), - 'www.arianna.com' => array('Arianna'), - - // Ask (IAC Search & Media) - 'ask.com' => array('Ask', array('ask', 'q', 'searchfor'), 'web?q={k}'), - 'web.ask.com' => array('Ask'), - 'int.ask.com' => array('Ask'), - 'mws.ask.com' => array('Ask'), - 'images.ask.com' => array('Ask'), - 'images.{}.ask.com' => array('Ask'), - 'ask.reference.com' => array('Ask'), - 'www.askkids.com' => array('Ask'), - 'iwon.ask.com' => array('Ask'), - 'www.ask.co.uk' => array('Ask'), - '{}.ask.com' => array('Ask'), - 'www.qbyrd.com' => array('Ask'), - '{}.qbyrd.com' => array('Ask'), - 'www.search-results.com' => array('Ask'), - 'www1.search-results.com' => array('Ask'), - 'int.search-results.com' => array('Ask'), - '{}.search-results.com' => array('Ask'), - 'search.ask.com' => array('Ask'), - '{}.search.ask.com' => array('Ask'), - 'avira-int.ask.com' => array('Ask'), - 'searchqu.com' => array('Ask'), - 'search.tb.ask.com' => array('Ask'), - 'nortonsafe.search.ask.com' => array('Ask'), - 'safesearch.avira.com' => array('Ask'), - 'avira.search.ask.com' => array('Ask'), - 'int.search.tb.ask.com' => array('Ask'), - - // Atlas - 'searchatlas.centrum.cz' => array('Atlas', 'q', '?q={k}'), - - // auone - 'search.auone.jp' => array('auone', 'q', '?q={k}'), - 'sp-search.auone.jp' => array('auone'), - 'sp-image.search.auone.jp' => array('auone Images', 'q', '?q={k}'), - - // Austronaut - 'www2.austronaut.at' => array('Austronaut', 'q'), - 'www1.austronaut.at' => array('Austronaut'), - - // Babylon (Enhanced by Google), - 'search.babylon.com' => array('Babylon', array('q', '/\/web\/(.*)/'), '?q={k}'), - 'searchassist.babylon.com' => array('Babylon'), - - // Baidu - 'www.baidu.com' => array('Baidu', array('wd', 'word', 'kw'), 's?wd={k}', array('UTF-8', 'gb2312')), - 'www1.baidu.com' => array('Baidu'), - 'm.baidu.com' => array('Baidu'), - 'www.baidu.co.th' => array('Baidu'), - 'zhidao.baidu.com' => array('Baidu'), - 'tieba.baidu.com' => array('Baidu'), - 'news.baidu.com' => array('Baidu'), - 'web.gougou.com' => array('Baidu', 'search', 'search?search={k}'), // uses baidu search - - // Biglobe - 'cgi.search.biglobe.ne.jp' => array('Biglobe', 'q', 'cgi-bin/search-st?q={k}'), - 'images.search.biglobe.ne.jp' => array('Biglobe Images', 'q', 'cgi-bin/search-st?q={k}'), - - // Bing - 'bing.com' => array('Bing', array('q', 'Q'), 'search?q={k}'), - '{}.bing.com' => array('Bing'), - 'msnbc.msn.com' => array('Bing'), - 'dizionario.it.msn.com' => array('Bing'), - 'enciclopedia.it.msn.com' => array('Bing'), - - // Bing Cache - 'cc.bingj.com' => array('Bing'), - - // Bing Images - 'bing.com/images/search' => array('Bing Images', array('q', 'Q'), '?q={k}'), - '{}.bing.com/images/search' => array('Bing Images'), - - // blekko - 'blekko.com' => array('blekko', array('q', '/\/ws\/(.*)/'), 'ws/{k}'), - - // Blogdigger - 'www.blogdigger.com' => array('Blogdigger', 'q'), - - // Blogpulse - 'www.blogpulse.com' => array('Blogpulse', 'query', 'search?query={k}'), - - // Bluewin - 'search.bluewin.ch' => array('Bluewin', array('searchTerm', 'q'), 'v2/index.php?q={k}'), - - // canoe.ca - 'web.canoe.ca' => array('Canoe.ca', 'q', 'search?q={k}'), - - // Centrum - 'search.centrum.cz' => array('Centrum', 'q', '?q={k}'), - 'morfeo.centrum.cz' => array('Centrum'), - - // Charter - 'www.charter.net' => array('Charter', 'q', 'search/index.php?q={k}'), - - // Claro Search - 'claro-search.com' => array('Claro Search', 'q', '?q={k}'), - - // Clix (Enhanced by Google) - 'pesquisa.clix.pt' => array('Clix', 'question', 'resultado.html?in=Mundial&question={k}'), - - // Conduit - 'search.conduit.com' => array('Conduit.com', 'q', 'Results.aspx?q={k}'), - 'images.search.conduit.com' => array('Conduit.com'), - - // Comcast - 'search.comcast.net' => array('Comcast', 'q', '?q={k}'), - - // Crawler - 'www.crawler.com' => array('Crawler', 'q', 'search/results1.aspx?q={k}'), - - // Compuserve - 'websearch.cs.com' => array('Compuserve.com (Enhanced by Google)', 'query', 'cs/search?query={k}'), - - // Cuil - 'www.cuil.com' => array('Cuil', 'q', 'search?q={k}'), - - // Daemon search - 'daemon-search.com' => array('Daemon search', 'q', 'explore/web?q={k}'), - 'my.daemon-search.com' => array('Daemon search'), - - // DasOertliche - 'www.dasoertliche.de' => array('DasOertliche', 'kw'), - 'www2.dasoertliche.de' => array('DasOertliche', array('ph', 'kw')), - - // DasTelefonbuch - 'www1.dastelefonbuch.de' => array('DasTelefonbuch', 'kw'), - - // Daum - 'search.daum.net' => array('Daum', 'q', 'search?q={k}'), - - // Delfi Latvia - 'smart.delfi.lv' => array('Delfi lv', 'q', 'find?q={k}'), - - // Delfi - 'otsing.delfi.ee' => array('Delfi EE', 'q', 'find?q={k}'), - - // Digg - 'digg.com' => array('Digg', 's', 'search?s={k}'), - - // dir.com - 'fr.dir.com' => array('dir.com', 'req'), - - // dmoz - 'dmoz.org' => array('dmoz', 'search'), - 'editors.dmoz.org' => array('dmoz'), - - // DuckDuckGo - 'duckduckgo.com' => array('DuckDuckGo', 'q', '?q={k}'), - 'r.duckduckgo.com' => array('DuckDuckGo'), - - // earthlink - 'search.earthlink.net' => array('Earthlink', 'q', 'search?q={k}'), - - // Ecosia (powered by Bing) - 'ecosia.org' => array('Ecosia', 'q', 'search.php?q={k}'), - - // Eniro - 'www.eniro.se' => array('Eniro', array('q', 'search_word'), 'query?q={k}'), - - // Eurip - 'www.eurip.com' => array('Eurip', 'q', 'search/?q={k}'), - - // Euroseek - 'www.euroseek.com' => array('Euroseek', 'string', 'system/search.cgi?string={k}'), - - // Everyclick - 'www.everyclick.com' => array('Everyclick', 'keyword'), - - // Excite - 'search.excite.it' => array('Excite', 'q', 'web/?q={k}'), - 'search.excite.fr' => array('Excite'), - 'search.excite.de' => array('Excite'), - 'search.excite.co.uk' => array('Excite'), - 'search.excite.es' => array('Excite'), - 'search.excite.nl' => array('Excite'), - 'msxml.excite.com' => array('Excite', '/\/[^\/]+\/ws\/results\/[^\/]+\/([^\/]+)/'), - 'www.excite.co.jp' => array('Excite', 'search', 'search.gw?search={k}', 'SHIFT_JIS'), - - // Exalead - 'www.exalead.fr' => array('Exalead', 'q', 'search/results?q={k}'), - 'www.exalead.com' => array('Exalead'), - - // eo - 'eo.st' => array('eo', 'x_query', 'cgi-bin/eolost.cgi?x_query={k}'), - - // Facebook - 'www.facebook.com' => array('Facebook', 'q', 'search/?q={k}'), - - // Fast Browser Search - 'www.fastbrowsersearch.com' => array('Fast Browser Search', 'q', 'results/results.aspx?q={k}'), - - // Francite - 'recherche.francite.com' => array('Francite', 'name'), - - // Findhurtig - 'www.findhurtig.dk' => array('Findhurtig', 'q', 'web?q={k}'), - - // Fireball - 'www.fireball.de' => array('Fireball', 'q', 'ajax.asp?q={k}'), - - // Firstfind - 'www.firstsfind.com' => array('Firstsfind', 'qry'), - - // Fixsuche - 'www.fixsuche.de' => array('Fixsuche', 'q'), - - // Flix - 'www.flix.de' => array('Flix.de', 'keyword'), - - // Fooooo - 'search.fooooo.com' => array('Fooooo', 'q', 'web/?q={k}'), - - // Forestle - 'forestle.org' => array('Forestle', 'q', 'search.php?q={k}'), - '{}.forestle.org' => array('Forestle'), - 'forestle.mobi' => array('Forestle'), - - // Free - 'search.free.fr' => array('Free', array('q', 'qs')), - 'search1-2.free.fr' => array('Free'), - 'search1-1.free.fr' => array('Free'), - - // Freecause - 'search.freecause.com' => array('FreeCause', 'p', '?p={k}'), - - // Freenet - 'suche.freenet.de' => array('Freenet', array('query', 'Keywords'), 'suche/?query={k}'), - - // FriendFeed - 'friendfeed.com' => array('FriendFeed', 'q', 'search?q={k}'), - - // GAIS - 'gais.cs.ccu.edu.tw' => array('GAIS', 'q', 'search.php?q={k}'), - - // Genieo - 'search.genieo.com' => array('Genieo', 'q', '&q={k}'), - - // Geona - 'geona.net' => array('Geona', 'q', 'search?q={k}'), - - // Gigablast - 'www.gigablast.com' => array('Gigablast', 'q', 'search?q={k}'), - 'dir.gigablast.com' => array('Gigablast (Directory)', 'q'), - - // Gnadenmeer - 'www.gnadenmeer.de' => array('Gnadenmeer', 'keyword'), - - // Gomeo - 'www.gomeo.com' => array('Gomeo', array('Keywords', '/\/search\/([^\/]+)/'), '/search/{k}'), - - // goo - 'search.goo.ne.jp' => array('goo', 'MT', 'web.jsp?MT={k}'), - 'ocnsearch.goo.ne.jp' => array('goo'), - - // Google - 'google.com' => array('Google', 'q', 'search?q={k}'), - 'google.{}' => array('Google'), - 'www2.google.com' => array('Google'), - 'ipv6.google.com' => array('Google'), - 'go.google.com' => array('Google'), - - // Google vs typo squatters - 'wwwgoogle.com' => array('Google'), - 'wwwgoogle.{}' => array('Google'), - 'gogole.com' => array('Google'), - 'gogole.{}' => array('Google'), - 'gppgle.com' => array('Google'), - 'gppgle.{}' => array('Google'), - 'googel.com' => array('Google'), - 'googel.{}' => array('Google'), - - // Powered by Google - 'search.avg.com' => array('Google'), - 'isearch.avg.com' => array('Google'), - 'search.chedot.com' => array('Google', 'text'), - 'www.cnn.com' => array('Google', 'query'), - 'darkoogle.com' => array('Google'), - 'search.darkoogle.com' => array('Google'), - 'search.foxtab.com' => array('Google'), - 'www.gooofullsearch.com' => array('Google', 'Keywords'), - 'search.hiyo.com' => array('Google'), - 'search.incredimail.com' => array('Google'), - 'search1.incredimail.com' => array('Google'), - 'search2.incredimail.com' => array('Google'), - 'search3.incredimail.com' => array('Google'), - 'search4.incredimail.com' => array('Google'), - 'search.sweetim.com' => array('Google'), - 'www.fastweb.it' => array('Google'), - 'search.juno.com' => array('Google', 'query'), - 'find.tdc.dk' => array('Google'), - 'it.luna.tv' => array('Google'), - 'searchresults.verizon.com' => array('Google'), - 'search.walla.co.il' => array('Google'), - 'search.alot.com' => array('Google'), - 'suche.gmx.net' => array('Google', 'q', 'web?q={k}'), - 'search.incredibar.com' => array('Google', 'q', 'search.php?q={k}'), - 'www.delta-search.com' => array('Google', 'q'), - 'www1.delta-search.com' => array('Google', 'q'), - 'search.1und1.de' => array('Google', 'q', 'web?q={k}'), - 'suche.1und1.de' => array('Google', 'q', 'web?q={k}'), - 'search.zonealarm.com' => array('Google'), - 'start.lenovo.com' => array('Google', 'q', 'search/index.php?q={k}'), - 'wow.com' => array('Google'), - '{}.wow.com' => array('Google'), - 'search.leonardo.it' => array('Google'), - 'www.optuszoo.com.au' => array('Google'), - 'search.dolphin-browser.jp' => array('Google'), - 'netlavis.azione.jp' => array('Google'), - 'search.nan.so' => array('Google'), - 'cgi2.nintendo.co.jp' => array('Google', 'gsc.q'), - 'search.smt.docomo.ne.jp' => array('Google', 'MT'), - 'image.search.smt.docomo.ne.jp' => array('Google', 'MT'), - 'gfsoso.com' => array('Google', 'q'), - 'searches.safehomepage.com' => array('Google', 'q'), - 'searches.f-secure.com' => array('Google', 'query', 'search?query={k}'), - - // Google Cache - 'webcache.googleusercontent.com' => array('Google', '/\/search\?q=cache:[A-Za-z0-9]+:[^+]+([^&]+)/', 'search?q={k}'), - - // Google SSL - 'encrypted.google.com' => array('Google SSL', 'q', 'search?q={k}'), - - // Google Blogsearch - 'blogsearch.google.com' => array('Google Blogsearch', 'q', 'blogsearch?q={k}'), - 'blogsearch.google.{}' => array('Google Blogsearch'), - - // Google Custom Search - 'google.com/cse' => array('Google Custom Search', array('q', 'query')), - 'google.{}/cse' => array('Google Custom Search'), - 'google.com/custom' => array('Google Custom Search'), - 'google.{}/custom' => array('Google Custom Search'), - - // Google Translation - 'translate.google.com' => array('Google Translations', 'q'), - - // Google Images - 'images.google.com' => array('Google Images', 'q', 'images?q={k}'), - 'images.google.{}' => array('Google Images'), - - // Google Maps - 'maps.google.com' => array('Google Maps', 'q', 'maps?q={k}'), - 'maps.google.{}' => array('Google Maps'), - - // Google News - 'news.google.com' => array('Google News', 'q'), - 'news.google.{}' => array('Google News'), - - // Google Shopping - 'google.com/products' => array('Google Shopping', 'q', '?q={k}&tbm=shop'), - 'google.{}/products' => array('Google Shopping'), - - // Google syndicated search - 'googlesyndicatedsearch.com' => array('Google syndicated search', 'q'), - - // Google Video - 'video.google.com' => array('Google Video', 'q', 'search?q={k}&tbm=vid'), - - // Google Scholar - 'scholar.google.com' => array('Google Scholar', 'q', 'scholar?q={k}'), - 'scholar.google.{}' => array('Google Scholar'), - - // Google Wireless Transcoder - // - does not appear to execute JavaScript -// 'google.com/gwt/n' => array('Google Wireless Transcoder'), - - // Goyellow.de - 'www.goyellow.de' => array('GoYellow.de', 'MDN'), - - // Gule Sider - 'www.gulesider.no' => array('Gule Sider', 'q'), - - // Haosou - 'www.haosou.com' => array('Haosou', 'q', 's?q={k}'), - - // HighBeam - 'www.highbeam.com' => array('HighBeam', 'q', 'Search.aspx?q={k}'), - - // Hit-Parade - 'req.hit-parade.com' => array('Hit-Parade', 'p7', 'general/recherche.asp?p7={k}'), - 'class.hit-parade.com' => array('Hit-Parade'), - 'www.hit-parade.com' => array('Hit-Parade'), - - // Holmes.ge - 'holmes.ge' => array('Holmes', 'q', 'search.htm?q={k}'), - - // Hooseek.com - 'www.hooseek.com' => array('Hooseek', 'recherche', 'web?recherche={k}'), - - // Hotbot - 'www.hotbot.com' => array('Hotbot', 'query'), - - // Icerocket - 'blogs.icerocket.com' => array('Icerocket', 'q', 'search?q={k}'), - - // ICQ - 'www.icq.com' => array('ICQ', 'q', 'search/results.php?q={k}'), - 'search.icq.com' => array('ICQ'), - - // Ilse - 'www.ilse.nl' => array('Ilse NL', 'search_for', '?search_for={k}'), - - // iMesh - 'search.imesh.com' => array('iMesh', array('q', 'si'), 'web?q={k}'), - - // Inbox.com - 'www2.inbox.com' => array('Inbox', 'q', 'search/results1.aspx?q={k}'), - - // InfoSpace (and related web properties) - 'infospace.com' => array('InfoSpace', 'q', '/search/web?q={k}'), - 'dogpile.com' => array('InfoSpace'), - 'tattoodle.com' => array('InfoSpace'), - 'metacrawler.com' => array('InfoSpace'), - 'webfetch.com' => array('InfoSpace'), - 'webcrawler.com' => array('InfoSpace'), - 'search.kiwee.com' => array('InfoSpace'), - 'searches.vi-view.com' => array('InfoSpace'), - 'search.webssearches.com' => array('InfoSpace'), - 'search.fbdownloader.com' => array('InfoSpace'), - 'searches3.globososo.com' => array('InfoSpace'), - - // old infospace system - 'wsdsold.infospace.com' => array('InfoSpace', '/\/[^\/]+\/ws\/results\/[^\/]+\/([^\/]+)/', 'pemonitorhosted/ws/results/Web/{k}/1/417/TopNavigation/Source/'), - - // Powered by InfoSpace - 'isearch.babylon.com' => array('InfoSpace', 'q'), - 'start.facemoods.com' => array('InfoSpace', 's'), - 'start.funmoods.com' => array('InfoSpace', 'q'), - 'search.magentic.com' => array('InfoSpace', 'q'), - 'search.searchcompletion.com' => array('InfoSpace', 'q'), - 'www.searchmobileonline.com' => array('InfoSpace', 'q'), - 'isearch.glarysoft.com' => array('InfoSpace', 'q'), - 'search.chatzum.com' => array('InfoSpace', 'q'), - 'home.speedbit.com' => array('InfoSpace', 'q'), - 'search.b1.org' => array('InfoSpace', 'q'), - 'searchya.com' => array('InfoSpace', 'q'), - 'search.handycafe.com' => array('InfoSpace', 'q'), - 'search.v9.com' => array('InfoSpace', 'q'), - 'search.iminent.com' => array('InfoSpace', 'q'), - 'utorrent.inspsearch.com' => array('InfoSpace', 'q'), - - /* - * Other InfoSpace powered metasearches are handled in Common::extractSearchEngineInformationFromUrl() - * - * This includes sites such as: - * - search.nation.com - * - ws.copernic.com - * - result.iminent.com - */ - - // Interia - 'www.google.interia.pl' => array('Interia', 'q', 'szukaj?q={k}'), - - // I-play - 'start.iplay.com' => array('I-play', 'q', 'searchresults.aspx?q={k}'), - - // Ixquick - 'ixquick.com' => array('Ixquick', 'query'), - 'www.eu.ixquick.com' => array('Ixquick'), - 'ixquick.de' => array('Ixquick'), - 'www.ixquick.de' => array('Ixquick'), - 'us.ixquick.com' => array('Ixquick'), - 's1.us.ixquick.com' => array('Ixquick'), - 's2.us.ixquick.com' => array('Ixquick'), - 's3.us.ixquick.com' => array('Ixquick'), - 's4.us.ixquick.com' => array('Ixquick'), - 's5.us.ixquick.com' => array('Ixquick'), - 'eu.ixquick.com' => array('Ixquick'), - 's8-eu.ixquick.com' => array('Ixquick'), - 's1-eu.ixquick.de' => array('Ixquick'), - 's2-eu4.ixquick.com' => array('Ixquick'), - 's5-eu4.ixquick.com' => array('Ixquick'), - - // Jyxo - 'jyxo.1188.cz' => array('Jyxo', 'q', 's?q={k}'), - - // Jungle Spider - 'www.jungle-spider.de' => array('Jungle Spider', 'q'), - - // Jungle key - 'junglekey.com' => array('Jungle Key', 'query', 'search.php?query={k}&type=web&lang=en'), - 'junglekey.fr' => array('Jungle Key'), - - // K9 Safe Search - 'k9safesearch.com' => array('K9 Safe Search', 'q', 'search.jsp?q={k}'), - - // Kataweb - 'www.kataweb.it' => array('Kataweb', 'q'), - - // Kensaq - 'www.kensaq.com' => array('Kensaq', 'q', 'web?q={k}'), - - // Kvasir - 'www.kvasir.no' => array('Kvasir', 'q', 'alle?q={k}'), - - // 묻지마 검색 - 'kwzf.net' => array('묻지마 검색', 'search', '#search={k}'), - - // Latne - 'www.latne.lv' => array('Latne', 'q', 'siets.php?q={k}'), - - // La Toile Du Québec via Google - 'www.toile.com' => array('La Toile Du Québec (Google)', 'q', 'search?q={k}'), - 'web.toile.com' => array('La Toile Du Québec (Google)'), - - // LookAny - 'www.lookany.com' => array('LookAny', '/(?:search|images|videos)\/([^\/]+)/'), - - // Looksmart - 'www.looksmart.com' => array('Looksmart', 'key'), - - // Lo.st (Enhanced by Google) - 'lo.st' => array('Lo.st', 'x_query', 'cgi-bin/eolost.cgi?x_query={k}'), - - // Lycos - 'search.lycos.com' => array('Lycos', 'query', '?query={k}'), - 'lycos.{}' => array('Lycos'), - - // maailm.com - 'www.maailm.com' => array('maailm.com', 'tekst'), - - // Mail.ru - 'go.mail.ru' => array('Mailru', 'q', 'search?rch=e&q={k}', array('UTF-8', 'windows-1251')), - - // Mamma - 'www.mamma.com' => array('Mamma', 'query', 'result.php?q={k}'), - 'mamma75.mamma.com' => array('Mamma'), - - // Meta - 'meta.ua' => array('Meta.ua', 'q', 'search.asp?q={k}'), - - // MetaCrawler.de - 's1.metacrawler.de' => array('MetaCrawler DE', 'qry', '?qry={k}'), - 's2.metacrawler.de' => array('MetaCrawler DE'), - 's3.metacrawler.de' => array('MetaCrawler DE'), - - // Metager - 'meta.rrzn.uni-hannover.de' => array('Metager', 'eingabe', 'meta/cgi-bin/meta.ger1?eingabe={k}'), - 'www.metager.de' => array('Metager'), - 'metager.de' => array('Metager'), - - // Metager2 - 'metager2.de' => array('Metager2', 'q', 'search/index.php?q={k}'), - - // Meinestadt - 'www.meinestadt.de' => array('Meinestadt.de', 'words'), - - // Mister Wong - 'www.mister-wong.com' => array('Mister Wong', 'keywords', 'search/?keywords={k}'), - 'www.mister-wong.de' => array('Mister Wong'), - - // Monstercrawler - 'www.monstercrawler.com' => array('Monstercrawler', 'qry'), - - // Mozbot - 'www.mozbot.fr' => array('mozbot', 'q', 'results.php?q={k}'), - 'www.mozbot.co.uk' => array('mozbot'), - 'www.mozbot.com' => array('mozbot'), - - // El Mundo - 'ariadna.elmundo.es' => array('El Mundo', 'q'), - - // MySpace - 'searchservice.myspace.com' => array('MySpace', 'qry', 'index.cfm?fuseaction=sitesearch.results&type=Web&qry={k}'), - - // MySearch / MyWay / MyWebSearch (default: powered by Ask.com) - 'www.mysearch.com' => array('MyWebSearch', array('searchfor', 'searchFor'), 'search/Ajmain.jhtml?searchfor={k}'), - 'ms114.mysearch.com' => array('MyWebSearch'), - 'ms146.mysearch.com' => array('MyWebSearch'), - 'kf.mysearch.myway.com' => array('MyWebSearch'), - 'ki.mysearch.myway.com' => array('MyWebSearch'), - 'search.myway.com' => array('MyWebSearch'), - 'search.mywebsearch.com' => array('MyWebSearch'), - - // Najdi - 'www.najdi.si' => array('Najdi.si', 'q', 'search.jsp?q={k}'), - - // Nate - 'search.nate.com' => array('Nate', 'q', 'search/all.html?q={k}', 'EUC-KR'), - - // Naver - 'search.naver.com' => array('Naver', 'query', 'search.naver?query={k}'), - - // Needtofind - 'ko.search.need2find.com' => array('Needtofind', 'searchfor', 'search/AJmain.jhtml?searchfor={k}'), - - // Neti - 'www.neti.ee' => array('Neti', 'query', 'cgi-bin/otsing?query={k}', 'iso-8859-1'), - - // Nifty - 'search.nifty.com' => array('Nifty', array('q', 'Text'), 'websearch/search?q={k}'), - 'search.azby.fmworld.net' => array('Nifty'), - 'videosearch.nifty.com' => array('Nifty Videos', 'kw', 'search?kw={k}'), - - // Nigma - 'nigma.ru' => array('Nigma', 's', 'index.php?s={k}'), - - // Onet - 'szukaj.onet.pl' => array('Onet.pl', 'qt', 'query.html?qt={k}'), - - // Online.no - 'online.no' => array('Online.no', 'q', 'google/index.jsp?q={k}'), - - // Opplysningen 1881 - 'www.1881.no' => array('Opplysningen 1881', 'Query', 'Multi/?Query={k}'), - - // Orange - 'busca.orange.es' => array('Orange', 'q', 'search?q={k}'), - 'lemoteur.ke.voila.fr' => array('Orange', 'kw', '?kw={k}'), - - // Paperball - 'www.paperball.de' => array('Paperball', 'q', 'suche/s/?q={k}'), - - // PeoplePC - 'search.peoplepc.com' => array('PeoplePC', 'q', 'search?q={k}'), - - // PeopleCheck - 'extern.peoplecheck.de' => array('PeopleCheck', 'q', 'link.php?q={k}'), - - // Picsearch - 'www.picsearch.com' => array('Picsearch', 'q', 'index.cgi?q={k}'), - - // Plazoo - 'www.plazoo.com' => array('Plazoo', 'q'), - - // PlusNetwork - 'plusnetwork.com' => array('PlusNetwork', 'q', '?q={k}'), - - // Poisk.Ru - 'poisk.ru' => array('Poisk.Ru', 'text', 'cgi-bin/poisk?text={k}', 'windows-1251'), - - // qip - 'search.qip.ru' => array('qip.ru', 'query', 'search?query={k}'), - - // Qualigo - 'www.qualigo.at' => array('Qualigo', 'q'), - 'www.qualigo.ch' => array('Qualigo'), - 'www.qualigo.de' => array('Qualigo'), - 'www.qualigo.nl' => array('Qualigo'), - - // Qwant - 'www.qwant.com' => array('Qwant', 'q'), - - // Rakuten - 'websearch.rakuten.co.jp' => array('Rakuten', 'qt', 'WebIS?qt={k}'), - - // Rambler - 'nova.rambler.ru' => array('Rambler', array('query', 'words'), 'search?query={k}'), - - // RPMFind - 'rpmfind.net' => array('rpmfind', 'query', 'linux/rpm2html/search.php?query={k}'), - 'fr2.rpmfind.net' => array('rpmfind'), - - // Road Runner Search - 'search.rr.com' => array('Road Runner', 'q', '?q={k}'), - - // Sapo - 'pesquisa.sapo.pt' => array('Sapo', 'q', '?q={k}'), - - // scour.com - 'scour.com' => array('Scour.com', '/search\/[^\/]+\/(.*)/', 'search/web/{k}'), - - // Search.com - 'www.search.com' => array('Search.com', 'q', 'search?q={k}'), - - // Search.ch - 'www.search.ch' => array('Search.ch', 'q', '?q={k}'), - - // Searchalot - 'searchalot.com' => array('Searchalot', 'q', '?q={k}'), - - // SearchCanvas - 'www.searchcanvas.com' => array('SearchCanvas', 'q', 'web?q={k}'), - - // Searchy - 'www.searchy.co.uk' => array('Searchy', 'q', 'index.html?q={k}'), - - // Setooz - // 2010-09-13: the mismatches are because subdomains are language codes - // (not country codes) - 'bg.setooz.com' => array('Setooz', 'query', 'search?query={k}'), - 'da.setooz.com' => array('Setooz'), - 'el.setooz.com' => array('Setooz'), - 'fa.setooz.com' => array('Setooz'), - 'ur.setooz.com' => array('Setooz'), - '{}.setooz.com' => array('Setooz'), - - // Seznam - 'search.seznam.cz' => array('Seznam', 'q', '?q={k}'), - - // Seznam Videa (Video) - 'videa.seznam.cz' => array('Seznam Videa', 'q', '?q={k}'), - - // Sharelook - 'www.sharelook.fr' => array('Sharelook', 'keyword'), - - // Skynet - 'www.skynet.be' => array('Skynet', 'q', 'services/recherche/google?q={k}'), - - // sm.cn - 'm.sm.cn' => array('sm.cn', 'q', 's?q={k}'), - 'm.sp.sm.cn' => array('sm.cn'), - - // sm.de - 'www.sm.de' => array('sm.de', 'q', '?q={k}'), - - // SmartAdressbar - 'search.smartaddressbar.com' => array('SmartAddressbar', 's', '?s={k}'), - - // Snap.do - 'search.snap.do' => array('Snap.do', 'q', '?q={k}'), - - // SeeSaa - 'search.seesaa.jp' => array('SeeSaa', '/\/([^\/]+)\/index\.html/', '{k}/index.html'), - - // So-net - 'www.so-net.ne.jp' => array('So-net', 'query', 'search/web/?query={k}'), - 'video.so-net.ne.jp' => array('So-net Videos', 'kw', 'search/?kw={k}'), - - // Sogou - 'www.sogou.com' => array('Sogou', 'query', 'web?query={k}', 'gb2312'), - 'm.sogou.com' => array('Sogou', 'keyword'), - - // Softonic - 'search.softonic.com' => array('Softonic', 'q', 'default/default?q={k}'), - - // soso.com - 'www.soso.com' => array('Soso', 'w', 'q?w={k}', 'gb2312'), - - // sputnik.ru - 'www.sputnik.ru' => array('Sputnik', 'q', 'search?q={k}'), - - // Startpagina - 'startgoogle.startpagina.nl' => array('Startpagina (Google)', 'q', '?q={k}'), - - // Startsiden - 'www.startsiden.no' => array('Startsiden', 'q', 'sok/index.html?q={k}'), - - // suche.info - 'suche.info' => array('Suche.info', 'Keywords', 'suche.php?Keywords={k}'), - - // Suchmaschine.com - 'www.suchmaschine.com' => array('Suchmaschine.com', 'suchstr', 'cgi-bin/wo.cgi?suchstr={k}'), - - // Suchnase - 'www.suchnase.de' => array('Suchnase', 'q'), - - // Surf Canyon - 'surfcanyon.com' => array('Surf Canyon', 'q'), - - // talimba - 'www.talimba.com' => array('talimba', 'search', 'index.php?page=search/web&search={k}'), - - // TalkTalk - 'www.talktalk.co.uk' => array('TalkTalk', 'query', 'search/results.html?query={k}'), - - // Technorati - 'technorati.com' => array('Technorati', 'q', 'search?return=sites&authority=all&q={k}'), - - // Teoma - 'www.teoma.com' => array('Teoma', 'q', 'web?q={k}'), - - // Terra -- referrer does not contain search phrase (keywords) - 'buscador.terra.es' => array('Terra', 'query', 'Default.aspx?source=Search&query={k}'), - 'buscador.terra.cl' => array('Terra'), - 'buscador.terra.com.br' => array('Terra'), - - // Tiscali - 'search.tiscali.it' => array('Tiscali', array('q', 'key'), '?q={k}'), - 'search-dyn.tiscali.it' => array('Tiscali'), - 'hledani.tiscali.cz' => array('Tiscali', 'query'), - - // Tixuma - 'www.tixuma.de' => array('Tixuma', 'sc', 'index.php?mp=search&stp=&sc={k}&tg=0'), - - // T-Online - 'suche.t-online.de' => array('T-Online', 'q', 'fast-cgi/tsc?mandant=toi&context=internet-tab&q={k}'), - 'brisbane.t-online.de' => array('T-Online'), - 'navigationshilfe.t-online.de' => array('T-Online', 'q', 'dtag/dns/results?mode=search_top&q={k}'), - - // Toolbarhome - 'www.toolbarhome.com' => array('Toolbarhome', 'q', 'search.aspx?q={k}'), - 'vshare.toolbarhome.com' => array('Toolbarhome'), - - // Toppreise.ch - 'www.toppreise.ch' => array('Toppreise.ch', 'search', 'index.php?search={k}', 'ISO-8859-1'), - 'toppreise.ch' => array('Toppreise.ch', null, null, 'ISO-8859-1'), - 'fr.toppreise.ch' => array('Toppreise.ch', null, null, 'ISO-8859-1'), - 'de.toppreise.ch' => array('Toppreise.ch', null, null, 'ISO-8859-1'), - 'en.toppreise.ch' => array('Toppreise.ch', null, null, 'ISO-8859-1'), - - // Trouvez.com - 'www.trouvez.com' => array('Trouvez.com', 'query'), - - // TrovaRapido - 'www.trovarapido.com' => array('TrovaRapido', 'q', 'result.php?q={k}'), - - // Trusted-Search - 'www.trusted-search.com' => array('Trusted Search', 'w', 'search?w={k}'), - - // Twingly - 'www.twingly.com' => array('Twingly', 'q', 'search?q={k}'), - - // uol.com.br - 'busca.uol.com.br' => array('uol.com.br', 'q', '/web/?q={k}'), - - // URL.ORGanzier - 'www.url.org' => array('URL.ORGanzier', 'q', '?l=de&q={k}'), - - // Vinden - 'www.vinden.nl' => array('Vinden', 'q', '?q={k}'), - - // Vindex - 'www.vindex.nl' => array('Vindex', 'search_for', '/web?search_for={k}'), - 'search.vindex.nl' => array('Vindex'), - - // Virgilio - 'ricerca.virgilio.it' => array('Virgilio', 'qs', 'ricerca?qs={k}'), - 'ricercaimmagini.virgilio.it' => array('Virgilio'), - 'ricercavideo.virgilio.it' => array('Virgilio'), - 'ricercanews.virgilio.it' => array('Virgilio'), - 'mobile.virgilio.it' => array('Virgilio', 'qrs'), - - // Voila - 'search.ke.voila.fr' => array('Voila', 'rdata', 'S/voila?rdata={k}'), - 'www.lemoteur.fr' => array('Voila'), // uses voila search - - // Volny - 'web.volny.cz' => array('Volny', 'search', 'fulltext/?search={k}', 'windows-1250'), - - // Walhello - 'www.walhello.info' => array('Walhello', 'key', 'search?key={k}'), - 'www.walhello.com' => array('Walhello'), - 'www.walhello.de' => array('Walhello'), - 'www.walhello.nl' => array('Walhello'), - - // Web.de - 'suche.web.de' => array('Web.de', array('su', 'q'), 'search/web/?su={k}'), - 'm.suche.web.de' => array('Web.de'), - - // Web.nl - 'www.web.nl' => array('Web.nl', 'zoekwoord'), - - // Weborama - 'www.weborama.fr' => array('weborama', 'QUERY'), - - // WebSearch - 'www.websearch.com' => array('WebSearch', array('qkw', 'q'), 'search/results2.aspx?q={k}'), - - // Wedoo - // 2011-02-15 - keyword no longer appears to be in Referrer URL; candidate for removal? - 'fr.wedoo.com' => array('Wedoo', 'keyword'), - 'en.wedoo.com' => array('Wedoo'), - 'es.wedoo.com' => array('Wedoo'), - - // Winamp (Enhanced by Google) - 'search.winamp.com' => array('Winamp', 'q', 'search/search?q={k}'), - - // Witch - 'www.witch.de' => array('Witch', 'search', 'search-result.php?cn=0&search={k}'), - - // Wirtualna Polska - 'szukaj.wp.pl' => array('Wirtualna Polska', 'szukaj', 'http://szukaj.wp.pl/szukaj.html?szukaj={k}'), - - // Woopie - 'www.woopie.jp' => array('Woopie', 'kw', 'search?kw={k}'), - - // WWW - 'search.www.ee' => array('www värav', 'query'), - - // X-recherche - 'www.x-recherche.com' => array('X-Recherche', 'MOTS', 'cgi-bin/websearch?MOTS={k}'), - - // Yahoo! Japan - 'search.yahoo.co.jp' => array('Yahoo! Japan', array('p', 'vp'), 'search?p={k}'), - 'jp.hao123.com' => array('Yahoo! Japan', 'query'), - 'home.kingsoft.jp' => array('Yahoo! Japan', 'keyword'), - 'video.search.yahoo.co.jp' => array('Yahoo! Japan Videos', 'p', 'search?p={k}'), - 'image.search.yahoo.co.jp' => array('Yahoo! Japan Images', 'p', 'search?p={k}'), - - // Yahoo - 'search.yahoo.com' => array('Yahoo!', array('p', 'q'), 'search?p={k}'), -// '*.search.yahoo.com' => array('Yahoo!'), // see built-in helper in Common.php - 'yahoo.com' => array('Yahoo!'), - 'yahoo.{}' => array('Yahoo!'), - '{}.yahoo.com' => array('Yahoo!'), - 'cade.yahoo.com' => array('Yahoo!'), - 'espanol.yahoo.com' => array('Yahoo!'), - 'qc.yahoo.com' => array('Yahoo!'), - 'one.cn.yahoo.com' => array('Yahoo!'), - - // Powered by Yahoo APIs - 'www.cercato.it' => array('Yahoo!', 'q'), - 'search.offerbox.com' => array('Yahoo!', 'q'), - 'www.benefind.de' => array('Yahoo!', 'q'), - - // Powered by Yahoo! Search Marketing (Overture) - 'ys.mirostart.com' => array('Yahoo!', 'q'), - - // Yahoo! Directory - 'search.yahoo.com/search/dir' => array('Yahoo! Directory', 'p', '?p={k}'), -// '{}.dir.yahoo.com' => array('Yahoo! Directory'), - - // Yahoo! Images - 'images.search.yahoo.com' => array('Yahoo! Images', array('p', 'va'), 'search/images?p={k}'), -// '*.images.search.yahoo.com'=> array('Yahoo! Images'), // see built-in helper in Common.php - '{}.images.yahoo.com' => array('Yahoo! Images'), - 'cade.images.yahoo.com' => array('Yahoo! Images'), - 'espanol.images.yahoo.com' => array('Yahoo! Images'), - 'qc.images.yahoo.com' => array('Yahoo! Images'), - - // Yam - 'search.yam.com' => array('Yam', 'k', 'Search/Web/?SearchType=web&k={k}'), - - // Yandex - 'yandex.ru' => array('Yandex', 'text', 'yandsearch?text={k}'), - 'yandex.com' => array('Yandex'), - 'yandex.{}' => array('Yandex'), - - // Yandex Images - 'images.yandex.ru' => array('Yandex Images', 'text', 'yandsearch?text={k}'), - 'images.yandex.com' => array('Yandex Images'), - 'images.yandex.{}' => array('Yandex Images'), - - // Yasni - 'www.yasni.de' => array('Yasni', 'query'), - 'www.yasni.com' => array('Yasni'), - 'www.yasni.co.uk' => array('Yasni'), - 'www.yasni.ch' => array('Yasni'), - 'www.yasni.at' => array('Yasni'), - - // Yatedo - 'www.yatedo.com' => array('Yatedo', 'q', 'search/profil?q={k}'), - 'www.yatedo.fr' => array('Yatedo'), - - // Yellowmap - 'yellowmap.de' => array('Yellowmap', ' '), - - // Yippy - 'search.yippy.com' => array('Yippy', 'query', 'search?query={k}'), - - // YouGoo - 'www.yougoo.fr' => array('YouGoo', 'q', '?cx=search&q={k}'), - - // Zapmeta - 'www.zapmeta.com' => array('Zapmeta', array('q', 'query'), '?q={k}'), - 'zapmeta.{}' => array('Zapmeta'), - 'uk.zapmeta.com' => array('Zapmeta'), - 'ar.zapmeta.com' => array('Zapmeta'), - 'au.zapmeta.com' => array('Zapmeta'), - 'ca.zapmeta.com' => array('Zapmeta'), - 'fi.zapmeta.com' => array('Zapmeta'), - 'no.zapmeta.com' => array('Zapmeta'), - 'tr.zapmeta.com' => array('Zapmeta'), - - // Zoek - 'www3.zoek.nl' => array('Zoek', 'q'), - - // Zhongsou - 'p.zhongsou.com' => array('Zhongsou', 'w', 'p?w={k}'), - - // Zoeken - 'www.zoeken.nl' => array('Zoeken', 'q', '?q={k}'), - - // Zoohoo - 'zoohoo.cz' => array('Zoohoo', 'q', '?q={k}', 'windows-1250'), - - // Zoznam - 'www.zoznam.sk' => array('Zoznam', 's', 'hladaj.fcgi?s={k}&co=svet'), - - // Zxuso - 'www.zxuso.com' => array('Zxuso', 'wd', 'ri/?wd={k}'), - ); -} diff --git a/core/DataFiles/Socials.php b/core/DataFiles/Socials.php deleted file mode 100755 index 92b131b496..0000000000 --- a/core/DataFiles/Socials.php +++ /dev/null @@ -1,230 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ - -if (!isset($GLOBALS['Piwik_socialUrl'])) { - // Note: the key of the array should have max 3 elements eg. sub.domain.ext - $GLOBALS['Piwik_socialUrl'] = array( - - // Facebook - 'facebook.com' => 'Facebook', - 'fb.me' => 'Facebook', - - // Ozone - 'qzone.qq.com' => 'Qzone', - - // Haboo - 'habbo.com' => 'Haboo', - - // Twitter - 'twitter.com' => 'Twitter', - 't.co' => 'Twitter', - - // Renren - 'renren.com' => 'Renren', - - // Windows Live Spaces - 'login.live.com' => 'Windows Live Spaces', - - // LinkedIn - 'linkedin.com' => 'LinkedIn', - - // Bebo - 'bebo.com' => 'Bebo', - - // Vkontakte - 'vk.com' => 'Vkontakte', - 'vkontakte.ru' => 'Vkontakte', - - // Tagged - 'login.tagged.com' => 'Tagged', - - // Orkut - 'orkut.com' => 'Orkut', - - // Myspace - 'myspace.com' => 'Myspace', - - // Frinedster - 'friendster.com' => 'Friendster', - - // Badoo - 'badoo.com' => 'Badoo', - - // hi5 - 'hi5.com' => 'hi5', - - // Netlog - 'netlog.com' => 'Netlog', - - // Flixster - 'flixster.com' => 'Flixster', - - // MyLife - 'mylife.ru' => 'MyLife', - - // Classmates.com - 'classmates.com' => 'Classmates.com', - - // Github - 'github.com' => 'Github', - - // Google+ - 'plus.google.com' => 'Google%2B', - 'url.google.com' => 'Google%2B', - - // douban - 'douban.com' => 'douban', - - // dribbble - 'dribbble.com' => 'dribbble', - - // Odnoklassniki - 'odnoklassniki.ru' => 'Odnoklassniki', - - // Viadeo - 'viadeo.com' => 'Viadeo', - - // Flickr - 'flickr.com' => 'Flickr', - - // WeeWorld - 'weeworld.com' => 'WeeWorld', - - // Last.fm - 'last.fm' => 'Last.fm', - 'lastfm.ru' => 'Last.fm', - 'lastfm.de' => 'Last.fm', - 'lastfm.es' => 'Last.fm', - 'lastfm.fr' => 'Last.fm', - 'lastfm.it' => 'Last.fm', - 'lastfm.jp' => 'Last.fm', - 'lastfm.pl' => 'Last.fm', - 'lastfm.com.br' => 'Last.fm', - 'lastfm.se' => 'Last.fm', - 'lastfm.com.tr' => 'Last.fm', - - // MyHeritage - 'myheritage.com' => 'MyHeritage', - - // Xanga - 'xanga.com' => 'Xanga', - - // Mixi - 'mixi.jp' => 'Mixi', - - // Cyworld - 'global.cyworld.com' => 'Cyworld', - - // Gaia Online - 'gaiaonline.com' => 'Gaia Online', - - // Skyrock - 'skyrock.com' => 'Skyrock', - - // BlackPlanet - 'blackplanet.com' => 'BlackPlanet', - - // myYearbook - 'myyearbook.com' => 'myYearbook', - - // Fotolog - 'fotolog.com' => 'Fotolog', - - // Friends Reunited - 'friendsreunited.com' => 'Friends Reunited', - - // LiveJournal - 'livejournal.ru' => 'LiveJournal', - 'livejournal.com' => 'LiveJournal', - - // StudiVZ/MeinVZ - 'studivz.net' => 'StudiVZ', - 'meinvz.net' => 'MeinVZ', - - // StackOverflow - 'stackoverflow.com' => 'StackOverflow', - - // Sonico.com - 'sonico.com' => 'Sonico.com', - - // Pinterest - 'pinterest.com' => 'Pinterest', - - // Plaxo - 'plaxo.com' => 'Plaxo', - - // Geni.com - 'geni.com' => 'Geni.com', - - // Tuenti - 'tuenti.com' => 'Tuenti', - - // XING - 'xing.com' => 'XING', - - // Taringa! - 'taringa.net' => 'Taringa!', - - // Nasza-klasa.pl - 'nk.pl' => 'Nasza-klasa.pl', - - // StumbleUpon - 'stumbleupon.com' => 'StumbleUpon', - - // Sourceforge - 'sourceforge.net' => 'SourceForge', - - // Hyves - 'hyves.nl' => 'Hyves', - - // WAYN - 'wayn.com' => 'WAYN', - - // Buzznet - 'buzznet.com' => 'Buzznet', - - // Multiply - 'multiply.com' => 'Multiply', - - // Foursquare - 'foursquare.com' => 'Foursquare', - - // vkrugudruzei.ru - 'vkrugudruzei.ru' => 'vkrugudruzei.ru', - - // my.mail.ru - 'my.mail.ru' => 'my.mail.ru', - - //MoiKrug.ru - 'moikrug.ru' => 'moikrug.ru', - - // Reddit - 'reddit.com' => 'reddit', - - // HackerNews - 'news.ycombinator.com' => 'Hacker News', - - // Identi.ca - 'identi.ca' => 'identi.ca', - - // Weibo - 'weibo.com' => 'Weibo', - 't.cn' => 'Weibo', - - // YouTube - 'youtube.com' => 'YouTube', - 'youtu.be' => 'YouTube', - - // Vimeo - 'vimeo.com' => 'Vimeo', - - //tumblr - 'tumblr.com' => 'tumblr', - ); -} diff --git a/core/DataTable.php b/core/DataTable.php index a297b6c70b..326f25fc6f 100644 --- a/core/DataTable.php +++ b/core/DataTable.php @@ -170,7 +170,7 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess const MAX_DEPTH_DEFAULT = 15; /** Name for metadata that describes when a report was archived. */ - const ARCHIVED_DATE_METADATA_NAME = 'archived_date'; + const ARCHIVED_DATE_METADATA_NAME = 'ts_archived'; /** Name for metadata that describes which columns are empty and should not be shown. */ const EMPTY_COLUMNS_METADATA_NAME = 'empty_column'; diff --git a/core/DataTable/Renderer/Csv.php b/core/DataTable/Renderer/Csv.php index 4eee1949b7..c3fb08b15a 100644 --- a/core/DataTable/Renderer/Csv.php +++ b/core/DataTable/Renderer/Csv.php @@ -70,6 +70,8 @@ class Csv extends Renderer */ const NO_DATA_AVAILABLE = 'No data available'; + private $unsupportedColumns = array(); + /** * Computes the dataTable output and returns the string/binary * @@ -213,6 +215,12 @@ class Csv extends Renderer */ private function getHeaderLine($columnMetrics) { + foreach ($columnMetrics as $index => $value) { + if (in_array($value, $this->unsupportedColumns)) { + unset($columnMetrics[$index]); + } + } + if ($this->translateColumnNames) { $columnMetrics = $this->translateColumnNames($columnMetrics); } @@ -391,12 +399,23 @@ class Csv extends Renderer $name = 'metadata_' . $name; } - $csvRow[$name] = $value; + if (is_array($value)) { + if (!in_array($name, $this->unsupportedColumns)) { + $this->unsupportedColumns[] = $name; + } + } else { + $csvRow[$name] = $value; + } + } } foreach ($csvRow as $name => $value) { - $allColumns[$name] = true; + if (in_array($name, $this->unsupportedColumns)) { + unset($allColumns[$name]); + } else { + $allColumns[$name] = true; + } } if ($this->exportIdSubtable) { @@ -410,6 +429,15 @@ class Csv extends Renderer $csv[] = $csvRow; } + + if (!empty($this->unsupportedColumns)) { + foreach ($this->unsupportedColumns as $unsupportedColumn) { + foreach ($csv as $index => $row) { + unset($row[$index][$unsupportedColumn]); + } + } + } + return $csv; } diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php index 70ef662710..3bd3b023d8 100644 --- a/core/DataTable/Row.php +++ b/core/DataTable/Row.php @@ -480,7 +480,7 @@ class Row extends \ArrayObject } if ($enableCopyMetadata) { - $this->sumRowMetadata($rowToSum); + $this->sumRowMetadata($rowToSum, $aggregationOperations); } } @@ -507,6 +507,19 @@ class Row extends \ArrayObject case 'sum': $newValue = $this->sumRowArray($thisColumnValue, $columnToSumValue); break; + case 'uniquearraymerge': + if (is_array($thisColumnValue) && is_array($columnToSumValue)) { + foreach ($columnToSumValue as $columnSum) { + if (!in_array($columnSum, $thisColumnValue)) { + $thisColumnValue[] = $columnSum; + } + } + } elseif (!is_array($thisColumnValue) && is_array($columnToSumValue)) { + $thisColumnValue = $columnToSumValue; + } + + $newValue = $thisColumnValue; + break; default: throw new Exception("Unknown operation '$operation'."); } @@ -517,12 +530,29 @@ class Row extends \ArrayObject * Sums the metadata in `$rowToSum` with the metadata in `$this` row. * * @param Row $rowToSum + * @param array $aggregationOperations */ - public function sumRowMetadata($rowToSum) + public function sumRowMetadata($rowToSum, $aggregationOperations = array()) { if (!empty($rowToSum->metadata) && !$this->isSummaryRow() ) { + $aggregatedMetadata = array(); + + if (is_array($aggregationOperations)) { + // we need to aggregate value before value is overwritten by maybe another row + foreach ($aggregationOperations as $columnn => $operation) { + $thisMetadata = $this->getMetadata($columnn); + $sumMetadata = $rowToSum->getMetadata($columnn); + + if ($thisMetadata === false && $sumMetadata === false) { + continue; + } + + $aggregatedMetadata[$columnn] = $this->getColumnValuesMerged($operation, $thisMetadata, $sumMetadata); + } + } + // We shall update metadata, and keep the metadata with the _most visits or pageviews_, rather than first or last seen $visits = max($rowToSum->getColumn(Metrics::INDEX_PAGE_NB_HITS) || $rowToSum->getColumn(Metrics::INDEX_NB_VISITS), // Old format pre-1.2, @see also method doSumVisitsMetrics() @@ -533,6 +563,11 @@ class Row extends \ArrayObject $this->maxVisitsSummed = $visits; $this->metadata = $rowToSum->metadata; } + + foreach ($aggregatedMetadata as $column => $value) { + // we need to make sure aggregated value is used, and not metadata from $rowToSum + $this->setMetadata($column, $value); + } } } diff --git a/core/Date.php b/core/Date.php index bac8ce7db2..cbbe866708 100644 --- a/core/Date.php +++ b/core/Date.php @@ -11,6 +11,8 @@ namespace Piwik; use Exception; use Piwik\Container\StaticContainer; +use Piwik\Intl\Data\Provider\DateTimeFormatProvider; +use Piwik\Plugins\LanguagesManager\LanguagesManager; /** * Utility class that wraps date/time related PHP functions. Using this class can @@ -41,15 +43,15 @@ class Date /** The default date time string format. */ const DATE_TIME_FORMAT = 'Y-m-d H:i:s'; - const DATETIME_FORMAT_LONG = 'Intl_Format_DateTime_Long'; - const DATETIME_FORMAT_SHORT = 'Intl_Format_DateTime_Short'; - const DATE_FORMAT_LONG = 'Intl_Format_Date_Long'; - const DATE_FORMAT_DAY_MONTH = 'Intl_Format_Date_Day_Month'; - const DATE_FORMAT_SHORT = 'Intl_Format_Date_Short'; - const DATE_FORMAT_MONTH_SHORT = 'Intl_Format_Month_Short'; - const DATE_FORMAT_MONTH_LONG = 'Intl_Format_Month_Long'; - const DATE_FORMAT_YEAR = 'Intl_Format_Year'; - const TIME_FORMAT = 'Intl_Format_Time'; + const DATETIME_FORMAT_LONG = DateTimeFormatProvider::DATE_FORMAT_LONG; + const DATETIME_FORMAT_SHORT = DateTimeFormatProvider::DATETIME_FORMAT_SHORT; + const DATE_FORMAT_LONG = DateTimeFormatProvider::DATE_FORMAT_LONG; + const DATE_FORMAT_DAY_MONTH = DateTimeFormatProvider::DATE_FORMAT_DAY_MONTH; + const DATE_FORMAT_SHORT = DateTimeFormatProvider::DATE_FORMAT_SHORT; + const DATE_FORMAT_MONTH_SHORT = DateTimeFormatProvider::DATE_FORMAT_MONTH_SHORT; + const DATE_FORMAT_MONTH_LONG = DateTimeFormatProvider::DATE_FORMAT_MONTH_LONG; + const DATE_FORMAT_YEAR = DateTimeFormatProvider::DATE_FORMAT_YEAR; + const TIME_FORMAT = DateTimeFormatProvider::TIME_FORMAT; /** * Max days for months (non-leap-year). See {@link addPeriod()} implementation. @@ -622,10 +624,9 @@ class Date { $template = $this->replaceLegacyPlaceholders($template); - if (substr($template, 0, 5) == 'Intl_') { - $translator = StaticContainer::get('Piwik\Translation\Translator'); - $template = $translator->translate($template); - } + $dateTimeFormatProvider = StaticContainer::get('Piwik\Intl\Data\Provider\DateTimeFormatProvider'); + + $template = $dateTimeFormatProvider->getFormatPattern($template); $tokens = self::parseFormat($template); diff --git a/core/Db.php b/core/Db.php index 12798d2ff5..f7eae8c8b2 100644 --- a/core/Db.php +++ b/core/Db.php @@ -33,7 +33,7 @@ use Piwik\Db\Adapter; */ class Db { - const SQL_MODE = 'ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_ZERO_DATE,NO_ZERO_IN_DATE'; + const SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO'; private static $connection = null; diff --git a/core/Db/Adapter/Pdo/Mysql.php b/core/Db/Adapter/Pdo/Mysql.php index aaf93e1b6b..9e26fb7a2b 100644 --- a/core/Db/Adapter/Pdo/Mysql.php +++ b/core/Db/Adapter/Pdo/Mysql.php @@ -246,4 +246,19 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface $this->cachePreparedStatement[$sql] = $stmt; return $stmt; } + + /** + * Override _dsn() to ensure host and port to not be passed along + * if unix_socket is set since setting both causes unexpected behaviour + * @see http://php.net/manual/en/ref.pdo-mysql.connection.php + */ + protected function _dsn() + { + if (!empty($this->_config['unix_socket'])) { + unset($this->_config['host']); + unset($this->_config['port']); + } + + return parent::_dsn(); + } } diff --git a/core/Filesystem.php b/core/Filesystem.php index 7549192a2c..c22a4d0b21 100644 --- a/core/Filesystem.php +++ b/core/Filesystem.php @@ -135,9 +135,8 @@ class Filesystem $output = @shell_exec($command); if ($output) { - $output = explode("\n", trim($output)); - $commandFailed = (false !== strpos($output, "no file systems processed")); + $output = explode("\n", trim($output)); if (!$commandFailed && count($output) > 1) { // check if filesystem is NFS diff --git a/core/FrontController.php b/core/FrontController.php index 4b47d9024b..e276ed53e7 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -66,6 +66,11 @@ class FrontController extends Singleton public static $enableDispatch = true; /** + * @var bool + */ + private $initialized = false; + + /** * Executes the requested plugin controller method. * * @throws Exception|\Piwik\PluginDeactivatedException in case the plugin doesn't exist, the action doesn't exist, @@ -197,11 +202,11 @@ class FrontController extends Singleton */ public function init() { - static $initialized = false; - if ($initialized) { + if ($this->initialized) { return; } - $initialized = true; + + $this->initialized = true; $tmpPath = StaticContainer::get('path.tmp'); diff --git a/core/IP.php b/core/IP.php index 6e27832e48..670c674544 100644 --- a/core/IP.php +++ b/core/IP.php @@ -101,7 +101,7 @@ class IP * * @param string $csv Comma separated list of elements. * @param array $excludedIps Optional list of excluded IP addresses (or IP address ranges). - * @return string Last (non-excluded) IP address in the list. + * @return string Last (non-excluded) IP address in the list or an empty string if all given IPs are excluded. */ public static function getLastIpFromList($csv, $excludedIps = null) { @@ -115,6 +115,8 @@ class IP return $element; } } + + return ''; } return trim(Common::sanitizeInputValue($csv)); } diff --git a/core/Intl/Data/Provider/DateTimeFormatProvider.php b/core/Intl/Data/Provider/DateTimeFormatProvider.php new file mode 100644 index 0000000000..90ffad609f --- /dev/null +++ b/core/Intl/Data/Provider/DateTimeFormatProvider.php @@ -0,0 +1,83 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Intl\Data\Provider; + +/** + * Provides date and time formats. + */ +class DateTimeFormatProvider +{ + const DATETIME_FORMAT_LONG = 1; + const DATETIME_FORMAT_SHORT = 2; + const DATE_FORMAT_LONG = 10; + const DATE_FORMAT_DAY_MONTH = 11; + const DATE_FORMAT_SHORT = 12; + const DATE_FORMAT_MONTH_SHORT = 13; + const DATE_FORMAT_MONTH_LONG = 14; + const DATE_FORMAT_YEAR = 15; + const TIME_FORMAT = 20; + + /** + * Returns the format pattern for the given format type + * + * @param int $format one of the format constants + * + * @return string + */ + public function getFormatPattern($format) + { + switch ($format) { + case self::DATETIME_FORMAT_LONG: + return 'EEEE, MMMM d, y HH:mm:ss'; + + case self::DATETIME_FORMAT_SHORT: + return 'MMM d, y HH:mm:ss'; + + case self::DATE_FORMAT_LONG: + return 'EEEE, MMMM d, y'; + + case self::DATE_FORMAT_DAY_MONTH: + return 'E, MMM d'; + + case self::DATE_FORMAT_SHORT: + return 'MMM d, y'; + + case self::DATE_FORMAT_MONTH_SHORT: + return 'MMM y'; + + case self::DATE_FORMAT_MONTH_LONG: + return 'MMMM y'; + + case self::DATE_FORMAT_YEAR: + return 'y'; + + case self::TIME_FORMAT: + return 'HH:mm:ss'; + } + + return $format; + } + + /** + * Returns interval format pattern for the given format type + * + * @param bool $short whether to return short or long format pattern + * @param string $maxDifference maximal difference in interval dates (Y, M or D) + * + * @return string + */ + public function getRangeFormatPattern($short=false, $maxDifference='Y') + { + if ($short) { + return 'MMM d, y – MMM d, y'; + } + + return 'MMMM d, y – MMMM d, y'; + } +} diff --git a/core/Metrics/Formatter.php b/core/Metrics/Formatter.php index 077646ba00..d62db65f31 100644 --- a/core/Metrics/Formatter.php +++ b/core/Metrics/Formatter.php @@ -72,10 +72,15 @@ class Formatter // Display 01:45:17 time format if ($displayTimeAsSentence === false) { - $hours = floor($numberOfSeconds / 3600); - $minutes = floor(($reminder = ($numberOfSeconds - $hours * 3600)) / 60); + $days = floor($numberOfSeconds / 86400); + $hours = floor(($reminder = ($numberOfSeconds - $days * 86400)) / 3600); + $minutes = floor(($reminder = ($reminder - $hours * 3600)) / 60); $seconds = floor($reminder - $minutes * 60); - $time = sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds); + if ($days == 0) { + $time = sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds); + } else { + $time = sprintf(Piwik::translate('Intl_NDays'), $days) . " " . sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds); + } $centiSeconds = ($numberOfSeconds * 100) % 100; if ($centiSeconds) { $time .= '.' . sprintf("%02s", $centiSeconds); diff --git a/core/Period.php b/core/Period.php index 70a47d2905..aecf6362bf 100644 --- a/core/Period.php +++ b/core/Period.php @@ -371,12 +371,9 @@ abstract class Period $maxDifference = 'M'; } - return $this->translator->translate( - sprintf( - 'Intl_Format_Interval_%s_%s', - $short ? 'Short' : 'Long', - $maxDifference - )); + $dateTimeFormatProvider = StaticContainer::get('Piwik\Intl\Data\Provider\DateTimeFormatProvider'); + + return $dateTimeFormatProvider->getRangeFormatPattern($short, $maxDifference); } /** diff --git a/core/Plugin/ControllerAdmin.php b/core/Plugin/ControllerAdmin.php index 44357955be..1ee1b33ad7 100644 --- a/core/Plugin/ControllerAdmin.php +++ b/core/Plugin/ControllerAdmin.php @@ -124,12 +124,13 @@ abstract class ControllerAdmin extends Controller private static function notifyWhenPhpVersionIsEOL() { - $notifyPhpIsEOL = Piwik::hasUserSuperUserAccess() && self::isPhpVersion54(); + return; // no supported version (5.5+) has currently ended support + $notifyPhpIsEOL = Piwik::hasUserSuperUserAccess() && self::isPhpVersionAtLeast55(); if (!$notifyPhpIsEOL) { return; } - $message = Piwik::translate('General_WarningPhpVersionXIsTooOld', '5.4'); + $message = Piwik::translate('General_WarningPhpVersionXIsTooOld', '5.5'); $notification = new Notification($message); $notification->title = Piwik::translate('General_Warning'); $notification->priority = Notification::PRIORITY_LOW; @@ -234,11 +235,11 @@ abstract class ControllerAdmin extends Controller private static function checkPhpVersion($view) { $view->phpVersion = PHP_VERSION; - $view->phpIsNewEnough = version_compare($view->phpVersion, '5.4.0', '>='); + $view->phpIsNewEnough = self::isPhpVersionAtLeast55(); } - private static function isPhpVersion54() + private static function isPhpVersionAtLeast55() { - return strpos(PHP_VERSION, '5.4') === 0; + return version_compare(PHP_VERSION, '5.5', '>='); } } diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index 77b058b113..c3974937a0 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -440,16 +440,10 @@ class Manager public function installLoadedPlugins() { Log::debug("Loaded plugins: " . implode(", ", array_keys($this->getLoadedPlugins()))); - $messages = array(); + foreach ($this->getLoadedPlugins() as $plugin) { - try { - $this->installPluginIfNecessary($plugin); - } catch (\Exception $e) { - $messages[] = $e->getMessage(); - } + $this->installPluginIfNecessary($plugin); } - - return $messages; } /** @@ -499,7 +493,7 @@ class Manager { $existingPlugins = $this->readPluginsDirectory(); $isPluginInFilesystem = array_search($pluginName, $existingPlugins) !== false; - return Filesystem::isValidFilename($pluginName) + return $this->isValidPluginName($pluginName) && $isPluginInFilesystem; } @@ -896,6 +890,11 @@ class Manager return $newPlugin; } + public function isValidPluginName($pluginName) + { + return (bool) preg_match('/^[a-zA-Z]([a-zA-Z0-9]*)$/D', $pluginName); + } + /** * @param $pluginName * @return Plugin @@ -906,8 +905,8 @@ class Manager $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName); $pluginClassName = $pluginName; - if (!Filesystem::isValidFilename($pluginName)) { - throw new \Exception(sprintf("The plugin filename '%s' is not a valid filename", $pluginFileName)); + if (!$this->isValidPluginName($pluginName)) { + throw new \Exception(sprintf("The plugin filename '%s' is not a valid plugin name", $pluginFileName)); } $path = self::getPluginsDirectory() . $pluginFileName; @@ -1071,11 +1070,20 @@ class Manager if ($saveConfig) { PiwikConfig::getInstance()->forceSave(); + $this->clearCache($pluginName); } } public function isTrackerPlugin(Plugin $plugin) { + if (!$this->isPluginInstalled($plugin->getPluginName())) { + return false; + } + + if ($plugin->isTrackerPlugin()) { + return true; + } + $dimensions = VisitDimension::getDimensions($plugin); if (!empty($dimensions)) { return true; @@ -1102,10 +1110,6 @@ class Manager return true; } - if ($plugin->isTrackerPlugin()) { - return true; - } - return false; } diff --git a/core/Plugin/PluginException.php b/core/Plugin/PluginException.php index 90aa03a342..83816ca365 100644 --- a/core/Plugin/PluginException.php +++ b/core/Plugin/PluginException.php @@ -8,14 +8,28 @@ namespace Piwik\Plugin; +use Piwik\Common; + class PluginException extends \Exception { public function __construct($pluginName, $message) { - parent::__construct("There was a problem installing the plugin " . $pluginName . ": " . $message . " - If this plugin has already been installed, and if you want to hide this message</b>, you must add the following line under the - [PluginsInstalled] - entry in your config/config.ini.php file: - PluginsInstalled[] = $pluginName"); + $pluginName = Common::sanitizeInputValue($pluginName); + $message = Common::sanitizeInputValue($message); + + parent::__construct("There was a problem installing the plugin $pluginName: <br /><br /> + $message + <br /><br /> + If you want to hide this message you must remove the following line under the [Plugins] entry in your + 'config/config.ini.php' file to disable this plugin.<br /> + Plugins[] = $pluginName + <br /><br />If this plugin has already been installed, you must add the following line under the + [PluginsInstalled] entry in your 'config/config.ini.php' file:<br /> + PluginsInstalled[] = $pluginName"); + } + + public function isHtmlMessage() + { + return true; } } diff --git a/core/Plugin/RequestProcessors.php b/core/Plugin/RequestProcessors.php new file mode 100644 index 0000000000..827274485e --- /dev/null +++ b/core/Plugin/RequestProcessors.php @@ -0,0 +1,27 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugin; + +use Piwik\Container\StaticContainer; + +class RequestProcessors +{ + public function getRequestProcessors() + { + $manager = Manager::getInstance(); + $processors = $manager->findMultipleComponents('Tracker', 'Piwik\\Tracker\\RequestProcessor'); + + $instances = array(); + foreach ($processors as $processor) { + $instances[] = StaticContainer::get($processor); + } + + return $instances; + } +} diff --git a/core/Plugin/Segment.php b/core/Plugin/Segment.php index 8a9688fb0b..2a20208ea2 100644 --- a/core/Plugin/Segment.php +++ b/core/Plugin/Segment.php @@ -7,6 +7,7 @@ * */ namespace Piwik\Plugin; +use Exception; /** * Creates a new segment that can be used for instance within the {@link \Piwik\Columns\Dimension::configureSegment()} @@ -51,6 +52,7 @@ class Segment private $acceptValues; private $permission; private $suggestedValuesCallback; + private $unionOfSegments; /** * If true, this segment will only be visible to the user if the user has view access @@ -122,6 +124,7 @@ class Segment public function setSegment($segment) { $this->segment = $segment; + $this->check(); } /** @@ -165,6 +168,29 @@ class Segment public function setSqlSegment($sqlSegment) { $this->sqlSegment = $sqlSegment; + $this->check(); + } + + /** + * Set a list of segments that should be used instead of fetching the values from a single column. + * All set segments will be applied via an OR operator. + * + * @param array $segments + * @api + */ + public function setUnionOfSegments($segments) + { + $this->unionOfSegments = $segments; + $this->check(); + } + + /** + * @return array + * @ignore + */ + public function getUnionOfSegments() + { + return $this->unionOfSegments; } /** @@ -196,6 +222,15 @@ class Segment } /** + * @return string + * @ignore + */ + public function getName() + { + return $this->name; + } + + /** * Returns the name of this segment as it should appear in segment expressions. * * @return string @@ -241,6 +276,10 @@ class Segment 'sqlSegment' => $this->sqlSegment, ); + if (!empty($this->unionOfSegments)) { + $segment['unionOfSegments'] = $this->unionOfSegments; + } + if (!empty($this->sqlFilter)) { $segment['sqlFilter'] = $this->sqlFilter; } @@ -288,4 +327,15 @@ class Segment { $this->requiresAtLeastViewAccess = $requiresAtLeastViewAccess; } + + private function check() + { + if ($this->sqlSegment && $this->unionOfSegments) { + throw new Exception(sprintf('Union of segments and SQL segment is set for segment "%s", use only one of them', $this->name)); + } + + if ($this->segment && $this->unionOfSegments && in_array($this->segment, $this->unionOfSegments, true)) { + throw new Exception(sprintf('The segment %s contains a union segment to itself', $this->name)); + } + } } diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php index 81e99c9f25..da2c60c18e 100644 --- a/core/Plugin/Visualization.php +++ b/core/Plugin/Visualization.php @@ -273,7 +273,18 @@ class Visualization extends ViewDataTable $idSite = Common::getRequestVar('idSite', null, 'string', $request); $module = $this->requestConfig->getApiModuleToRequest(); $action = $this->requestConfig->getApiMethodToRequest(); - $metadata = ApiApi::getInstance()->getMetadata($idSite, $module, $action); + + $apiParameters = array(); + $idDimension = Common::getRequestVar('idDimension', 0, 'int'); + $idGoal = Common::getRequestVar('idGoal', 0, 'int'); + if ($idDimension > 0) { + $apiParameters['idDimension'] = $idDimension; + } + if ($idGoal > 0) { + $apiParameters['idGoal'] = $idGoal; + } + + $metadata = ApiApi::getInstance()->getMetadata($idSite, $module, $action, $apiParameters); if (!empty($metadata)) { return array_shift($metadata); @@ -360,7 +371,7 @@ class Visualization extends ViewDataTable $this->metadata = $this->dataTable->getAllTableMetadata(); if (isset($this->metadata[DataTable::ARCHIVED_DATE_METADATA_NAME])) { - $this->config->report_last_updated_message = $this->makePrettyArchivedOnText(); + $this->reportLastUpdatedMessage = $this->makePrettyArchivedOnText(); } } @@ -474,7 +485,8 @@ class Visualization extends ViewDataTable $prettyDate = $date->getLocalized(Date::DATE_FORMAT_SHORT); - return Piwik::translate('CoreHome_ReportGeneratedOn', $prettyDate); + $timezoneAppend = ' (UTC)'; + return Piwik::translate('CoreHome_ReportGeneratedOn', $prettyDate) . $timezoneAppend; } /** diff --git a/core/Profiler.php b/core/Profiler.php index c1b0875fa6..12c1e46ff6 100644 --- a/core/Profiler.php +++ b/core/Profiler.php @@ -209,13 +209,9 @@ class Profiler return; } - $xhProfPath = PIWIK_INCLUDE_PATH . '/vendor/facebook/xhprof/extension/modules/xhprof.so'; - if (!file_exists($xhProfPath)) { - throw new Exception("Cannot find xhprof, run 'composer install --dev' and build the extension."); - } - if (!function_exists('xhprof_enable')) { - throw new Exception("Cannot find xhprof_enable, make sure to add 'extension=$xhProfPath' to your php.ini."); + $xhProfPath = PIWIK_INCLUDE_PATH . '/vendor/facebook/xhprof/extension/modules/xhprof.so'; + throw new Exception("Cannot find xhprof_enable, make sure to 1) install xhprof: run 'composer install --dev' and build the extension, and 2) add 'extension=$xhProfPath' to your php.ini."); } $outputDir = ini_get("xhprof.output_dir"); diff --git a/core/Segment.php b/core/Segment.php index 451afdb393..d9f8d163c6 100644 --- a/core/Segment.php +++ b/core/Segment.php @@ -110,6 +110,35 @@ class Segment } } + private function getAvailableSegments() + { + // segment metadata + if (empty($this->availableSegments)) { + $this->availableSegments = API::getInstance()->getSegmentsMetadata($this->idSites, $_hideImplementationData = false); + } + + return $this->availableSegments; + } + + private function getSegmentByName($name) + { + $segments = $this->getAvailableSegments(); + + foreach ($segments as $segment) { + if ($segment['segment'] == $name && !empty($name)) { + + // check permission + if (isset($segment['permission']) && $segment['permission'] != 1) { + throw new NoAccessException("You do not have enough permission to access the segment " . $name); + } + + return $segment; + } + } + + throw new Exception("Segment '$name' is not a supported segment."); + } + /** * @param $string * @param $idSites @@ -127,6 +156,7 @@ class Segment // parse segments $expressions = $segment->parseSubExpressions(); + $expressions = $this->getExpressionsWithUnionsResolved($expressions); // convert segments name to sql segment // check that user is allowed to view this segment @@ -142,6 +172,41 @@ class Segment $segment->setSubExpressionsAfterCleanup($cleanedExpressions); } + private function getExpressionsWithUnionsResolved($expressions) + { + $expressionsWithUnions = array(); + foreach ($expressions as $expression) { + $operand = $expression[SegmentExpression::INDEX_OPERAND]; + $name = $operand[SegmentExpression::INDEX_OPERAND_NAME]; + + $availableSegment = $this->getSegmentByName($name); + + if (!empty($availableSegment['unionOfSegments'])) { + $count = 0; + foreach ($availableSegment['unionOfSegments'] as $segmentNameOfUnion) { + $count++; + $operator = SegmentExpression::BOOL_OPERATOR_OR; // we connect all segments within that union via OR + if ($count === count($availableSegment['unionOfSegments'])) { + $operator = $expression[SegmentExpression::INDEX_BOOL_OPERATOR]; + } + + $operand[SegmentExpression::INDEX_OPERAND_NAME] = $segmentNameOfUnion; + $expressionsWithUnions[] = array( + SegmentExpression::INDEX_BOOL_OPERATOR => $operator, + SegmentExpression::INDEX_OPERAND => $operand + ); + } + } else { + $expressionsWithUnions[] = array( + SegmentExpression::INDEX_BOOL_OPERATOR => $expression[SegmentExpression::INDEX_BOOL_OPERATOR], + SegmentExpression::INDEX_OPERAND => $operand + ); + } + } + + return $expressionsWithUnions; + } + /** * Returns `true` if the segment is empty, `false` if otherwise. */ @@ -154,33 +219,15 @@ class Segment protected function getCleanedExpression($expression) { - if (empty($this->availableSegments)) { - $this->availableSegments = API::getInstance()->getSegmentsMetadata($this->idSites, $_hideImplementationData = false); - } + $name = $expression[SegmentExpression::INDEX_OPERAND_NAME]; + $matchType = $expression[SegmentExpression::INDEX_OPERAND_OPERATOR]; + $value = $expression[SegmentExpression::INDEX_OPERAND_VALUE]; - $name = $expression[0]; - $matchType = $expression[1]; - $value = $expression[2]; - $sqlName = ''; + $segment = $this->getSegmentByName($name); + $sqlName = $segment['sqlSegment']; - foreach ($this->availableSegments as $segment) { - if ($segment['segment'] != $name) { - continue; - } - - $sqlName = $segment['sqlSegment']; - - // check permission - if (isset($segment['permission']) - && $segment['permission'] != 1 - ) { - throw new NoAccessException("You do not have enough permission to access the segment " . $name); - } - - if ($matchType == SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY - || $matchType == SegmentExpression::MATCH_IS_NULL_OR_EMPTY) { - break; - } + if ($matchType != SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY + && $matchType != SegmentExpression::MATCH_IS_NULL_OR_EMPTY) { if (isset($segment['sqlFilterValue'])) { $value = call_user_func($segment['sqlFilterValue'], $value); @@ -201,12 +248,6 @@ class Segment $matchType = SegmentExpression::MATCH_ACTIONS_CONTAINS; } } - - break; - } - - if (empty($sqlName)) { - throw new Exception("Segment '$name' is not a supported segment."); } return array($sqlName, $matchType, $value); diff --git a/core/Segment/SegmentExpression.php b/core/Segment/SegmentExpression.php index 7eac1bcdb0..605cd13682 100644 --- a/core/Segment/SegmentExpression.php +++ b/core/Segment/SegmentExpression.php @@ -27,6 +27,12 @@ class SegmentExpression const MATCH_LESS = '<'; const MATCH_CONTAINS = '=@'; const MATCH_DOES_NOT_CONTAIN = '!@'; + const MATCH_STARTS_WITH = '=^'; + const MATCH_ENDS_WITH = '=$'; + + const BOOL_OPERATOR_OR = 'OR'; + const BOOL_OPERATOR_AND = 'AND'; + const BOOL_OPERATOR_END = ''; // Note: you can't write this in the API, but access this feature // via field!= <- IS NOT NULL @@ -40,6 +46,10 @@ class SegmentExpression const INDEX_BOOL_OPERATOR = 0; const INDEX_OPERAND = 1; + const INDEX_OPERAND_NAME = 0; + const INDEX_OPERAND_OPERATOR = 1; + const INDEX_OPERAND_VALUE = 2; + const SQL_WHERE_DO_NOT_MATCH_ANY_ROW = "(1 = 0)"; const SQL_WHERE_MATCHES_ALL_ROWS = "(1 = 1)"; @@ -89,7 +99,9 @@ class SegmentExpression . self::MATCH_LESS_OR_EQUAL . '|' . self::MATCH_LESS . '|' . self::MATCH_CONTAINS . '|' - . self::MATCH_DOES_NOT_CONTAIN + . self::MATCH_DOES_NOT_CONTAIN . '|' + . preg_quote(self::MATCH_STARTS_WITH) . '|' + . preg_quote(self::MATCH_ENDS_WITH) . '){1}(.*)/'; $match = preg_match($pattern, $operand, $matches); if ($match == 0) { @@ -115,9 +127,9 @@ class SegmentExpression $parsedSubExpressions[] = array( self::INDEX_BOOL_OPERATOR => $operator, self::INDEX_OPERAND => array( - $leftMember, - $operation, - $valueRightMember, + self::INDEX_OPERAND_NAME => $leftMember, + self::INDEX_OPERAND_OPERATOR => $operation, + self::INDEX_OPERAND_VALUE => $valueRightMember, )); } $this->parsedSubExpressions = $parsedSubExpressions; @@ -146,14 +158,17 @@ class SegmentExpression $operator = $leaf[self::INDEX_BOOL_OPERATOR]; $operandDefinition = $leaf[self::INDEX_OPERAND]; - $operand = $this->getSqlMatchFromDefinition($operandDefinition, $availableTables); - if ($operand[1] !== null) { - $this->valuesBind = array_merge($this->valuesBind, $operand[1]); + if ($operand[self::INDEX_OPERAND_OPERATOR] !== null) { + if (is_array($operand[self::INDEX_OPERAND_OPERATOR])) { + $this->valuesBind = array_merge($this->valuesBind, $operand[self::INDEX_OPERAND_OPERATOR]); + } else { + $this->valuesBind[] = $operand[self::INDEX_OPERAND_OPERATOR]; + } } - $operand = $operand[0]; + $operand = $operand[self::INDEX_OPERAND_NAME]; $sqlSubExpressions[] = array( self::INDEX_BOOL_OPERATOR => $operator, @@ -177,12 +192,12 @@ class SegmentExpression */ protected function getSqlMatchFromDefinition($def, &$availableTables) { - $fields = $def[0]; + $field = $def[0]; $matchType = $def[1]; $value = $def[2]; // Segment::getCleanedExpression() may return array(null, $matchType, null) - $operandWillNotMatchAnyRow = empty($fields) && is_null($value); + $operandWillNotMatchAnyRow = empty($field) && is_null($value); if($operandWillNotMatchAnyRow) { if($matchType == self::MATCH_EQUAL) { // eg. pageUrl==DoesNotExist @@ -193,7 +208,9 @@ class SegmentExpression // Not equal to NULL means it matches all rows $sqlExpression = self::SQL_WHERE_MATCHES_ALL_ROWS; } elseif($matchType == self::MATCH_CONTAINS - || $matchType == self::MATCH_DOES_NOT_CONTAIN) { + || $matchType == self::MATCH_DOES_NOT_CONTAIN + || $matchType == self::MATCH_STARTS_WITH + || $matchType == self::MATCH_ENDS_WITH) { // no action was found for CONTAINS / DOES NOT CONTAIN // eg. pageUrl=@DoesNotExist -> matches no row // eg. pageUrl!@DoesNotExist -> matches no rows @@ -207,10 +224,6 @@ class SegmentExpression return array($sqlExpression, $value = null); } - if (!is_array($fields)) { - $fields = array($fields); - } - $alsoMatchNULLValues = false; switch ($matchType) { case self::MATCH_EQUAL: @@ -241,6 +254,14 @@ class SegmentExpression $value = '%' . $this->escapeLikeString($value) . '%'; $alsoMatchNULLValues = true; break; + case self::MATCH_STARTS_WITH: + $sqlMatch = '%s LIKE'; + $value = $this->escapeLikeString($value) . '%'; + break; + case self::MATCH_ENDS_WITH: + $sqlMatch = '%s LIKE'; + $value = '%' . $this->escapeLikeString($value); + break; case self::MATCH_IS_NOT_NULL_NOR_EMPTY: $sqlMatch = '%s IS NOT NULL AND (%s <> \'\' OR %s = 0)'; @@ -258,7 +279,7 @@ class SegmentExpression // it can be used internally to inject sub-expressions into the query. // see Segment::getCleanedExpression() $sqlMatch = '%s IN (' . $value['SQL'] . ')'; - $value = $this->escapeLikeString($value['bind']); + $value = $value['bind']; break; default: throw new Exception("Filter contains the match type '" . $matchType . "' which is not supported"); @@ -267,44 +288,23 @@ class SegmentExpression // We match NULL values when rows are excluded only when we are not doing a $alsoMatchNULLValues = $alsoMatchNULLValues && !empty($value); + $sqlMatch = str_replace('%s', $field, $sqlMatch); - $sqlExpressions = array(); - $values = array(); - foreach ($fields as $field) { - $sqlMatchReplaced = str_replace('%s', $field, $sqlMatch); - - if ($matchType === self::MATCH_ACTIONS_CONTAINS - || is_null($value) - ) { - $sqlExpression = "( $sqlMatchReplaced )"; + if ($matchType === self::MATCH_ACTIONS_CONTAINS + || is_null($value) + ) { + $sqlExpression = "( $sqlMatch )"; + } else { + if ($alsoMatchNULLValues) { + $sqlExpression = "( $field IS NULL OR $sqlMatch ? )"; } else { - if ($alsoMatchNULLValues) { - $sqlExpression = "( $field IS NULL OR $sqlMatchReplaced ? )"; - } else { - $sqlExpression = "$sqlMatchReplaced ?"; - } - } - - $sqlExpressions[] = $sqlExpression; - - if ($value !== null) { - if(is_array($value)) { - $values = array_merge($values, $value); - } else { - $values[] = $value; - } + $sqlExpression = "$sqlMatch ?"; } - - $this->checkFieldIsAvailable($field, $availableTables); } - if (count($fields) == 1) { - $sqlExpression = reset($sqlExpressions); - } else { - $sqlExpression = '((' . implode(") OR (", $sqlExpressions) . '))'; - } + $this->checkFieldIsAvailable($field, $availableTables); - return array($sqlExpression, $values); + return array($sqlExpression, $value); } /** @@ -337,8 +337,14 @@ class SegmentExpression */ private function escapeLikeString($str) { - $str = str_replace("%", "\%", $str); - $str = str_replace("_", "\_", $str); + if (false !== strpos($str, '%')) { + $str = str_replace("%", "\%", $str); + } + + if (false !== strpos($str, '_')) { + $str = str_replace("_", "\_", $str); + } + return $str; } @@ -372,15 +378,15 @@ class SegmentExpression $operand = substr($operand, 0, -1); } $operand .= $char; - $tree[] = array(self::INDEX_BOOL_OPERATOR => '', self::INDEX_OPERAND => $operand); + $tree[] = array(self::INDEX_BOOL_OPERATOR => self::BOOL_OPERATOR_END, self::INDEX_OPERAND => $operand); break; } if ($isAND && !$isBackslash) { - $tree[] = array(self::INDEX_BOOL_OPERATOR => 'AND', self::INDEX_OPERAND => $operand); + $tree[] = array(self::INDEX_BOOL_OPERATOR => self::BOOL_OPERATOR_AND, self::INDEX_OPERAND => $operand); $operand = ''; } elseif ($isOR && !$isBackslash) { - $tree[] = array(self::INDEX_BOOL_OPERATOR => 'OR', self::INDEX_OPERAND => $operand); + $tree[] = array(self::INDEX_BOOL_OPERATOR => self::BOOL_OPERATOR_OR, self::INDEX_OPERAND => $operand); $operand = ''; } else { if ($isBackslash && ($isAND || $isOR)) { @@ -413,7 +419,7 @@ class SegmentExpression $operator = $expression[self::INDEX_BOOL_OPERATOR]; $operand = $expression[self::INDEX_OPERAND]; - if ($operator == 'OR' + if ($operator == self::BOOL_OPERATOR_OR && !$subExpression ) { $sql .= ' ('; @@ -424,7 +430,7 @@ class SegmentExpression $sql .= $operand; - if ($operator == 'AND' + if ($operator == self::BOOL_OPERATOR_AND && $subExpression ) { $sql .= ')'; diff --git a/core/SettingsServer.php b/core/SettingsServer.php index 3f6fbb8878..d84e3ff483 100644 --- a/core/SettingsServer.php +++ b/core/SettingsServer.php @@ -127,9 +127,14 @@ class SettingsServer { static $gd = null; if (is_null($gd)) { + $gd = false; + $extensions = @get_loaded_extensions(); - $gd = in_array('gd', $extensions) && function_exists('imageftbbox'); + if (is_array($extensions)) { + $gd = in_array('gd', $extensions) && function_exists('imageftbbox'); + } } + return $gd; } diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php index b30d695e73..b841c210a9 100644 --- a/core/Tracker/Action.php +++ b/core/Tracker/Action.php @@ -62,6 +62,7 @@ abstract class Action private $idLinkVisitAction; private $actionIdsCached = array(); + private $customFields = array(); private $actionName; private $actionType; @@ -228,6 +229,16 @@ abstract class Action return false; } + public function setCustomField($field, $value) + { + $this->customFields[$field] = $value; + } + + public function getCustomFields() + { + return $this->customFields; + } + public function getIdActionUrl() { $idUrl = $this->actionIdsCached['idaction_url']; @@ -379,13 +390,7 @@ abstract class Action $visitAction[self::DB_COLUMN_CUSTOM_FLOAT] = Common::forceDotAsSeparatorForDecimalPoint($customValue); } - $customVariables = $this->getCustomVariables(); - if (!empty($customVariables)) { - Common::printDebug("Page level Custom Variables: "); - Common::printDebug($customVariables); - } - - $visitAction = array_merge($visitAction, $customVariables); + $visitAction = array_merge($visitAction, $this->customFields); $this->idLinkVisitAction = $this->getModel()->createAction($visitAction); diff --git a/core/Tracker/Model.php b/core/Tracker/Model.php index c39820e571..afffd5faeb 100644 --- a/core/Tracker/Model.php +++ b/core/Tracker/Model.php @@ -289,7 +289,7 @@ class Model public function updateVisit($idSite, $idVisit, $valuesToUpdate) { - list($updateParts, $sqlBind) = $this->visitFieldsToQuery($valuesToUpdate); + list($updateParts, $sqlBind) = $this->fieldsToQuery($valuesToUpdate); $parts = implode($updateParts, ', '); $table = Common::prefixTable('log_visit'); @@ -312,6 +312,34 @@ class Model return $wasInserted; } + public function updateAction($idLinkVa, $valuesToUpdate) + { + if (empty($idLinkVa)) { + return; + } + + list($updateParts, $sqlBind) = $this->fieldsToQuery($valuesToUpdate); + + $parts = implode($updateParts, ', '); + $table = Common::prefixTable('log_link_visit_action'); + + $sqlQuery = "UPDATE $table SET $parts WHERE idlink_va = ?"; + + $sqlBind[] = $idLinkVa; + + $db = $this->getDb(); + $result = $db->query($sqlQuery, $sqlBind); + $wasInserted = $db->rowCount($result) != 0; + + if (!$wasInserted) { + Common::printDebug("Action with this idLinkVa wasn't found in the DB."); + Common::printDebug("$sqlQuery --- "); + Common::printDebug($sqlBind); + } + + return $wasInserted; + } + public function findVisitor($idSite, $configId, $idVisitor, $fieldsToRead, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead) { $selectCustomVariables = ''; @@ -396,7 +424,7 @@ class Model return $result == null; } - private function visitFieldsToQuery($valuesToUpdate) + private function fieldsToQuery($valuesToUpdate) { $updateParts = array(); $sqlBind = array(); diff --git a/core/Tracker/Request.php b/core/Tracker/Request.php index 7f194f7d1a..e714ecded7 100644 --- a/core/Tracker/Request.php +++ b/core/Tracker/Request.php @@ -288,6 +288,15 @@ class Request 'i' => (string)Common::getRequestVar('m', $this->getCurrentDate("i"), 'int', $this->params), 's' => (string)Common::getRequestVar('s', $this->getCurrentDate("s"), 'int', $this->params) ); + if($localTimes['h'] < 0 || $localTimes['h'] > 23) { + $localTimes['h'] = 0; + } + if($localTimes['i'] < 0 || $localTimes['i'] > 59) { + $localTimes['i'] = 0; + } + if($localTimes['s'] < 0 || $localTimes['s'] > 59) { + $localTimes['s'] = 0; + } foreach ($localTimes as $k => $time) { if (strlen($time) == 1) { $localTimes[$k] = '0' . $time; diff --git a/core/Tracker/RequestProcessor.php b/core/Tracker/RequestProcessor.php index 3a8b01549a..8d50d3b28f 100644 --- a/core/Tracker/RequestProcessor.php +++ b/core/Tracker/RequestProcessor.php @@ -68,14 +68,7 @@ use Piwik\Tracker\Visit\VisitProperties; * a {@link Dimension} class._ * * To create a new RequestProcessor, create a new class that derives from this one, and implement the - * methods you need. Then in your plugin's DI config file (located at - * `/path/to/piwik/plugins/YourPlugin/config/config.php`), add the following to the array: - * - * ``` - * 'tracker.request.processors' => DI\add(array( - * DI\get('Piwik\Plugins\Goals\Tracker\GoalsRequestProcessor'), - * )), - * ``` + * methods you need. Then put this class inside the `Tracker` directory of your plugin. * * Final note: RequestProcessors are shared between tracking requests, and so, should ideally be * stateless. They are stored in DI, so they can contain references to other objects in DI, but diff --git a/core/Tracker/TableLogAction.php b/core/Tracker/TableLogAction.php index db9f7859fd..9552e7ae81 100644 --- a/core/Tracker/TableLogAction.php +++ b/core/Tracker/TableLogAction.php @@ -65,13 +65,21 @@ class TableLogAction $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE %s AND type = ' . $actionType . ' )'; switch ($matchType) { - case '=@': + case SegmentExpression::MATCH_CONTAINS: // use concat to make sure, no %s occurs because some plugins use %s in their sql $where = '( name LIKE CONCAT(\'%\', ?, \'%\') '; break; - case '!@': + case SegmentExpression::MATCH_DOES_NOT_CONTAIN: $where = '( name NOT LIKE CONCAT(\'%\', ?, \'%\') '; break; + case SegmentExpression::MATCH_STARTS_WITH: + // use concat to make sure, no %s occurs because some plugins use %s in their sql + $where = '( name LIKE CONCAT(?, \'%\') '; + break; + case SegmentExpression::MATCH_ENDS_WITH: + // use concat to make sure, no %s occurs because some plugins use %s in their sql + $where = '( name LIKE CONCAT(\'%\', ?) '; + break; default: throw new \Exception("This match type $matchType is not available for action-segments."); break; @@ -166,29 +174,34 @@ class TableLogAction */ public static function getIdActionFromSegment($valueToMatch, $sqlField, $matchType, $segmentName) { - $actionType = self::guessActionTypeFromSegment($segmentName); - - if ($actionType == Action::TYPE_PAGE_URL) { - // for urls trim protocol and www because it is not recorded in the db - $valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch); - } - - $valueToMatch = self::normaliseActionString($actionType, $valueToMatch); + if ($segmentName === 'actionType') { + $actionType = (int) $valueToMatch; + $valueToMatch = array(); + $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE type = ' . $actionType . ' )'; + } else { + $actionType = self::guessActionTypeFromSegment($segmentName); + if ($actionType == Action::TYPE_PAGE_URL) { + // for urls trim protocol and www because it is not recorded in the db + $valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch); + } - if ($matchType == SegmentExpression::MATCH_EQUAL - || $matchType == SegmentExpression::MATCH_NOT_EQUAL - ) { - $idAction = self::getModel()->getIdActionMatchingNameAndType($valueToMatch, $actionType); - // Action is not found (eg. &segment=pageTitle==Větrnásssssss) - if (empty($idAction)) { - $idAction = null; + $valueToMatch = self::normaliseActionString($actionType, $valueToMatch); + if ($matchType == SegmentExpression::MATCH_EQUAL + || $matchType == SegmentExpression::MATCH_NOT_EQUAL + ) { + $idAction = self::getModel()->getIdActionMatchingNameAndType($valueToMatch, $actionType); + // Action is not found (eg. &segment=pageTitle==Větrnásssssss) + if (empty($idAction)) { + $idAction = null; + } + return $idAction; } - return $idAction; + + // "name contains $string" match can match several idaction so we cannot return yet an idaction + // special case + $sql = self::getSelectQueryWhereNameContains($matchType, $actionType); } - // "name contains $string" match can match several idaction so we cannot return yet an idaction - // special case - $sql = TableLogAction::getSelectQueryWhereNameContains($matchType, $actionType); $cache = StaticContainer::get('Piwik\Tracker\TableLogAction\Cache'); return $cache->getIdActionFromSegment($valueToMatch, $sql); @@ -261,6 +274,8 @@ class TableLogAction $actionsTypesStoredUnsanitized = array( $actionType == Action::TYPE_DOWNLOAD, $actionType == Action::TYPE_OUTLINK, + $actionType == Action::TYPE_PAGE_URL, + $actionType == Action::TYPE_CONTENT, ); return in_array($actionType, $actionsTypesStoredUnsanitized); diff --git a/core/Tracker/TrackerCodeGenerator.php b/core/Tracker/TrackerCodeGenerator.php index fbf4b4c2be..c985db88e7 100644 --- a/core/Tracker/TrackerCodeGenerator.php +++ b/core/Tracker/TrackerCodeGenerator.php @@ -172,13 +172,23 @@ class TrackerCodeGenerator } // We need to parse_url to isolate hosts $websiteHosts = array(); + $firstHost = null; foreach ($websiteUrls as $site_url) { $referrerParsed = parse_url($site_url); - $websiteHosts[] = $referrerParsed['host']; + + if (!isset($firstHost)) { + $firstHost = $referrerParsed['host']; + } + + $url = $referrerParsed['host']; + if (!empty($referrerParsed['path'])) { + $url .= $referrerParsed['path']; + } + $websiteHosts[] = $url; } $options = ''; - if ($mergeSubdomains && !empty($websiteHosts)) { - $options .= ' _paq.push(["setCookieDomain", "*.' . $websiteHosts[0] . '"]);' . "\n"; + if ($mergeSubdomains && !empty($firstHost)) { + $options .= ' _paq.push(["setCookieDomain", "*.' . $firstHost . '"]);' . "\n"; } if ($mergeAliasUrls && !empty($websiteHosts)) { $urls = '["*.' . implode('","*.', $websiteHosts) . '"]'; diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index a78bb30b40..80d8d13613 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -17,6 +17,7 @@ use Piwik\Date; use Piwik\Exception\UnexpectedWebsiteFoundException; use Piwik\Network\IPUtils; use Piwik\Piwik; +use Piwik\Plugin; use Piwik\Plugin\Dimension\VisitDimension; use Piwik\Tracker; use Piwik\Tracker\Visit\VisitProperties; @@ -76,7 +77,8 @@ class Visit implements VisitInterface public function __construct() { - $this->requestProcessors = StaticContainer::get('tracker.request.processors'); + $requestProcessors = StaticContainer::get('Piwik\Plugin\RequestProcessors'); + $this->requestProcessors = $requestProcessors->getRequestProcessors(); $this->visitorRecognizer = StaticContainer::get('Piwik\Tracker\VisitorRecognizer'); $this->visitProperties = null; $this->userSettings = StaticContainer::get('Piwik\Tracker\Settings'); diff --git a/core/Tracker/Visit/ReferrerSpamFilter.php b/core/Tracker/Visit/ReferrerSpamFilter.php index 3d49362444..177200bf6a 100644 --- a/core/Tracker/Visit/ReferrerSpamFilter.php +++ b/core/Tracker/Visit/ReferrerSpamFilter.php @@ -58,6 +58,10 @@ class ReferrerSpamFilter $cache->save($cacheId, $list); } + if(!is_array($list)) { + Common::printDebug('Warning: could not read list of spammers from cache.'); + return array(); + } return $list; } diff --git a/core/Tracker/VisitExcluded.php b/core/Tracker/VisitExcluded.php index 0bc6fa2fbb..a644d4479f 100644 --- a/core/Tracker/VisitExcluded.php +++ b/core/Tracker/VisitExcluded.php @@ -13,6 +13,7 @@ use Piwik\Common; use Piwik\DeviceDetectorFactory; use Piwik\Network\IP; use Piwik\Piwik; +use Piwik\Plugins\SitesManager\SiteUrls; use Piwik\Tracker\Visit\ReferrerSpamFilter; /** @@ -290,14 +291,17 @@ class VisitExcluded { $site = Cache::getCacheWebsiteAttributes($this->idSite); - if (!empty($site['exclude_unknown_urls']) && !empty($site['hosts'])) { - $trackingHost = parse_url($this->request->getParam('url'), PHP_URL_HOST); - foreach ($site['hosts'] as $siteHost) { - if ($trackingHost == $siteHost || (substr($trackingHost, -strlen($siteHost) - 1) === ('.' . $siteHost))) { - return false; - } - } - return true; + if (!empty($site['exclude_unknown_urls']) && !empty($site['urls'])) { + $url = $this->request->getParam('url'); + $parsedUrl = parse_url($url); + + $trackingUrl = new SiteUrls(); + $urls = $trackingUrl->groupUrlsByHost(array($this->idSite => $site['urls'])); + + $idSites = $trackingUrl->getIdSitesMatchingUrl($parsedUrl, $urls); + $isUrlExcluded = !isset($idSites) || !in_array($this->idSite, $idSites); + + return $isUrlExcluded; } return false; diff --git a/core/Url.php b/core/Url.php index fc618d752b..cacf76ad06 100644 --- a/core/Url.php +++ b/core/Url.php @@ -181,11 +181,12 @@ class Url } catch (Exception $e) { $assume_secure_protocol = false; } - if ($assume_secure_protocol - || (isset($_SERVER['HTTPS']) - && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)) - || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') - ) { + if ($assume_secure_protocol) { + return 'https'; + } + if( (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)) + || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')){ + return 'https'; } return 'http'; @@ -216,6 +217,7 @@ class Url return true; } } + // if host is in hardcoded whitelist, assume it's valid if (in_array($host, self::getAlwaysTrustedHosts())) { return true; diff --git a/core/UrlHelper.php b/core/UrlHelper.php index 4a0ac0fa0a..66a0e64e25 100644 --- a/core/UrlHelper.php +++ b/core/UrlHelper.php @@ -259,242 +259,6 @@ class UrlHelper } /** - * Extracts a keyword from a raw not encoded URL. - * Will only extract keyword if a known search engine has been detected. - * Returns the keyword: - * - in UTF8: automatically converted from other charsets when applicable - * - strtolowered: "QUErY test!" will return "query test!" - * - trimmed: extra spaces before and after are removed - * - * Lists of supported search engines can be found in /core/DataFiles/SearchEngines.php - * The function returns false when a keyword couldn't be found. - * eg. if the url is "http://www.google.com/partners.html" this will return false, - * as the google keyword parameter couldn't be found. - * - * @see unit tests in /tests/core/Common.test.php - * @param string $referrerUrl URL referrer URL, eg. $_SERVER['HTTP_REFERER'] - * @return array|bool false if a keyword couldn't be extracted, - * or array( - * 'name' => 'Google', - * 'keywords' => 'my searched keywords') - */ - public static function extractSearchEngineInformationFromUrl($referrerUrl) - { - $referrerParsed = @parse_url($referrerUrl); - $referrerHost = ''; - if (isset($referrerParsed['host'])) { - $referrerHost = $referrerParsed['host']; - } - if (empty($referrerHost)) { - return false; - } - // some search engines (eg. Bing Images) use the same domain - // as an existing search engine (eg. Bing), we must also use the url path - $referrerPath = ''; - if (isset($referrerParsed['path'])) { - $referrerPath = $referrerParsed['path']; - } - - // no search query - if (!isset($referrerParsed['query'])) { - $referrerParsed['query'] = ''; - } - $query = $referrerParsed['query']; - - // Google Referrers URLs sometimes have the fragment which contains the keyword - if (!empty($referrerParsed['fragment'])) { - $query .= '&' . $referrerParsed['fragment']; - } - - $searchEngines = Common::getSearchEngineUrls(); - - $hostPattern = self::getLossyUrl($referrerHost); - /* - * Try to get the best matching 'host' in definitions - * 1. check if host + path matches an definition - * 2. check if host only matches - * 3. check if host pattern + path matches - * 4. check if host pattern matches - * 5. special handling - */ - if (array_key_exists($referrerHost . $referrerPath, $searchEngines)) { - $referrerHost = $referrerHost . $referrerPath; - } elseif (array_key_exists($referrerHost, $searchEngines)) { - // no need to change host - } elseif (array_key_exists($hostPattern . $referrerPath, $searchEngines)) { - $referrerHost = $hostPattern . $referrerPath; - } elseif (array_key_exists($hostPattern, $searchEngines)) { - $referrerHost = $hostPattern; - } elseif (!array_key_exists($referrerHost, $searchEngines)) { - if (!strncmp($query, 'cx=partner-pub-', 15)) { - // Google custom search engine - $referrerHost = 'google.com/cse'; - } elseif (!strncmp($referrerPath, '/pemonitorhosted/ws/results/', 28)) { - // private-label search powered by InfoSpace Metasearch - $referrerHost = 'wsdsold.infospace.com'; - } elseif (strpos($referrerHost, '.images.search.yahoo.com') != false) { - // Yahoo! Images - $referrerHost = 'images.search.yahoo.com'; - } elseif (strpos($referrerHost, '.search.yahoo.com') != false) { - // Yahoo! - $referrerHost = 'search.yahoo.com'; - } else { - return false; - } - } - $searchEngineName = $searchEngines[$referrerHost][0]; - $variableNames = null; - if (isset($searchEngines[$referrerHost][1])) { - $variableNames = $searchEngines[$referrerHost][1]; - } - if (!$variableNames) { - $searchEngineNames = Common::getSearchEngineNames(); - $url = $searchEngineNames[$searchEngineName]; - $variableNames = $searchEngines[$url][1]; - } - if (!is_array($variableNames)) { - $variableNames = array($variableNames); - } - - $key = null; - if ($searchEngineName === 'Google Images' - || ($searchEngineName === 'Google' && strpos($referrerUrl, '/imgres') !== false) - ) { - if (strpos($query, '&prev') !== false) { - $query = urldecode(trim(self::getParameterFromQueryString($query, 'prev'))); - $query = str_replace('&', '&', strstr($query, '?')); - } - $searchEngineName = 'Google Images'; - } elseif ($searchEngineName === 'Google' - && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0) - ) { - $keys = array(); - $key = self::getParameterFromQueryString($query, 'as_q'); - if (!empty($key)) { - array_push($keys, $key); - } - $key = self::getParameterFromQueryString($query, 'as_oq'); - if (!empty($key)) { - array_push($keys, str_replace('+', ' OR ', $key)); - } - $key = self::getParameterFromQueryString($query, 'as_epq'); - if (!empty($key)) { - array_push($keys, "\"$key\""); - } - $key = self::getParameterFromQueryString($query, 'as_eq'); - if (!empty($key)) { - array_push($keys, "-$key"); - } - $key = trim(urldecode(implode(' ', $keys))); - } - - if ($searchEngineName === 'Google') { - // top bar menu - $tbm = self::getParameterFromQueryString($query, 'tbm'); - switch ($tbm) { - case 'isch': - $searchEngineName = 'Google Images'; - break; - case 'vid': - $searchEngineName = 'Google Video'; - break; - case 'shop': - $searchEngineName = 'Google Shopping'; - break; - } - } - - if (empty($key)) { - foreach ($variableNames as $variableName) { - if ($variableName[0] == '/') { - // regular expression match - if (preg_match($variableName, $referrerUrl, $matches)) { - $key = trim(urldecode($matches[1])); - break; - } - } else { - // search for keywords now &vname=keyword - $key = self::getParameterFromQueryString($query, $variableName); - $key = trim(urldecode($key)); - - // Special cases: empty or no keywords - if (empty($key) - && ( - // Google search with no keyword - ($searchEngineName == 'Google' - && (empty($query) && (empty($referrerPath) || $referrerPath == '/') && empty($referrerParsed['fragment'])) - ) - - // Yahoo search with no keyword - || ($searchEngineName == 'Yahoo!' - && ($referrerParsed['host'] == 'r.search.yahoo.com') - ) - - // empty keyword parameter - || strpos($query, sprintf('&%s=', $variableName)) !== false - || strpos($query, sprintf('?%s=', $variableName)) !== false - - // search engines with no keyword - || $searchEngineName == 'Ixquick' - || $searchEngineName == 'Google Images' - || $searchEngineName == 'DuckDuckGo') - ) { - $key = false; - } - if (!empty($key) - || $key === false - ) { - break; - } - } - } - } - - // $key === false is the special case "No keyword provided" which is a Search engine match - if ($key === null - || $key === '' - ) { - return false; - } - - if (!empty($key)) { - if (function_exists('iconv') - && isset($searchEngines[$referrerHost][3]) - ) { - // accepts string, array, or comma-separated list string in preferred order - $charsets = $searchEngines[$referrerHost][3]; - if (!is_array($charsets)) { - $charsets = explode(',', $charsets); - } - - if (!empty($charsets)) { - $charset = $charsets[0]; - if (count($charsets) > 1 - && function_exists('mb_detect_encoding') - ) { - $charset = mb_detect_encoding($key, $charsets); - if ($charset === false) { - $charset = $charsets[0]; - } - } - - $newkey = @iconv($charset, 'UTF-8//IGNORE', $key); - if (!empty($newkey)) { - $key = $newkey; - } - } - } - - $key = Common::mb_strtolower($key); - } - - return array( - 'name' => $searchEngineName, - 'keywords' => $key, - ); - } - - /** * Returns the query part from any valid url and adds additional parameters to the query part if needed. * * @param string $url Any url eg `"http://example.com/piwik/?foo=bar"` diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php index 22cb8c4429..f275aebe58 100644 --- a/core/ViewDataTable/Config.php +++ b/core/ViewDataTable/Config.php @@ -10,6 +10,7 @@ namespace Piwik\ViewDataTable; use Piwik\API\Request as ApiRequest; +use Piwik\Common; use Piwik\DataTable; use Piwik\DataTable\Filter\PivotByDimension; use Piwik\Metrics; @@ -486,7 +487,28 @@ class Config { $this->metrics_documentation = array(); - $report = API::getInstance()->getMetadata(0, $this->controllerName, $this->controllerAction); + $idSite = Common::getRequestVar('idSite', 0, 'int'); + + if ($idSite < 1) { + return; + } + + $apiParameters = array(); + $idDimension = Common::getRequestVar('idDimension', 0, 'int'); + $idGoal = Common::getRequestVar('idGoal', 0, 'int'); + if ($idDimension > 0) { + $apiParameters['idDimension'] = $idDimension; + } + if ($idGoal > 0) { + $apiParameters['idGoal'] = $idGoal; + } + + $report = API::getInstance()->getMetadata($idSite, $this->controllerName, $this->controllerAction, $apiParameters); + + if (empty($report)) { + return; + } + $report = $report[0]; if (isset($report['metricsDocumentation'])) { @@ -556,6 +578,17 @@ class Config $this->columns_to_display = array_filter($columnsToDisplay); } + public function removeColumnToDisplay($columnToRemove) + { + if (!empty($this->columns_to_display)) { + + $key = array_search($columnToRemove, $this->columns_to_display); + if (false !== $key) { + unset($this->columns_to_display[$key]); + } + } + } + /** * @ignore */ diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php index 5c041d8ec6..707d0e1615 100644 --- a/core/testMinimumPhpVersion.php +++ b/core/testMinimumPhpVersion.php @@ -16,7 +16,10 @@ $piwik_errorMessage = ''; // Minimum requirement: stream_resolve_include_path, working json_encode in 5.3.3, namespaces in 5.3 -$piwik_minimumPHPVersion = '5.4.0'; +// NOTE: when changing this variable, we also need to update +// 1) api.piwik.org +// 2) tests/travis/generator/Generator.php +$piwik_minimumPHPVersion = '5.5.0'; $piwik_currentPHPVersion = PHP_VERSION; $minimumPhpInvalid = version_compare($piwik_minimumPHPVersion, $piwik_currentPHPVersion) > 0; if ($minimumPhpInvalid) { diff --git a/js/README.md b/js/README.md index 06c7cf0ae3..d24f8d8c9d 100644 --- a/js/README.md +++ b/js/README.md @@ -48,9 +48,6 @@ The js/ folder contains: * In a production environment, the tests/javascript folder is not used and can be removed (if present). - Note: if the file "js/tests/enable_sqlite" exists, additional unit tests - (requires the sqlite extension) are enabled. - * We use /*! to include Piwik's license header in the minified source. Read Stallman's "The JavaScript Trap" for more information. diff --git a/js/piwik.js b/js/piwik.js index b5608f1230..344e5436e9 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -28,7 +28,7 @@ /*global JSON2:true */ -if (typeof JSON2 !== 'object' && window.JSON) { +if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON.stringify && window.JSON.parse) { JSON2 = window.JSON; } else { (function () { @@ -979,8 +979,8 @@ if (typeof JSON2 !== 'object' && window.JSON) { getAttributionReferrerTimestamp, getAttributionReferrerUrl, setCustomData, getCustomData, setCustomRequestProcessing, - setCustomVariable, getCustomVariable, deleteCustomVariable, storeCustomVariablesInCookie, - setDownloadExtensions, addDownloadExtensions, removeDownloadExtensions, + setCustomVariable, getCustomVariable, deleteCustomVariable, storeCustomVariablesInCookie, setCustomDimension, getCustomDimension, + deleteCustomDimension, setDownloadExtensions, addDownloadExtensions, removeDownloadExtensions, setDomains, setIgnoreClasses, setRequestMethod, setRequestContentType, setReferrerUrl, setCustomUrl, setAPIUrl, setDocumentTitle, setDownloadClasses, setLinkClasses, @@ -1021,7 +1021,7 @@ if (typeof JSON2 !== 'object' && window.JSON) { trackVisibleContentImpressions, isTrackOnlyVisibleContentEnabled, port, isUrlToCurrentDomain, isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink, getConfigDownloadExtensions, disableLinkTracking, substr, setAnyAttribute, wasContentTargetAttrReplaced, max, abs, childNodes, compareDocumentPosition, body, - getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout, + getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout, getDomains, getConfigCookiePath, newVisitor, uuid, createTs, visitCount, currentVisitTs, lastVisitTs, lastEcommerceOrderTs, "", "\b", "\t", "\n", "\f", "\r", "\"", "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, @@ -1143,6 +1143,23 @@ if (typeof Piwik !== 'object') { return typeof property === 'string' || property instanceof String; } + function isObjectEmpty(property) + { + if (!property) { + return true; + } + + var i; + var isEmpty = true; + for (i in property) { + if (Object.prototype.hasOwnProperty.call(property, i)) { + isEmpty = false; + } + } + + return isEmpty; + } + /* * apply wrapper * @@ -1535,6 +1552,14 @@ if (typeof Piwik !== 'object') { * Fix-up URL when page rendered from search engine cache or translated page */ function urlFixup(hostName, href, referrer) { + if (!hostName) { + hostName = ''; + } + + if (!href) { + href = ''; + } + if (hostName === 'translate.googleusercontent.com') { // Google if (referrer === '') { referrer = href; @@ -1568,6 +1593,10 @@ if (typeof Piwik !== 'object') { domain = domain.slice(1); } + if (domain.indexOf('/') !== -1) { + domain = domain.substr(0, domain.indexOf('/')); + } + return domain; } @@ -2648,7 +2677,7 @@ if (typeof Piwik !== 'object') { // check whether we were redirected from the piwik overlay plugin var referrerRegExp = new RegExp('index\\.php\\?module=Overlay&action=startOverlaySession' - + '&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)$'); + + '&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$'); var match = referrerRegExp.exec(documentAlias.referrer); @@ -2662,15 +2691,22 @@ if (typeof Piwik !== 'object') { // store overlay session info in window name var period = match[2], - date = match[3]; + date = match[3], + segment = match[4]; + + if (!segment) { + segment = ''; + } else if (segment.indexOf('&segment=') === 0) { + segment = segment.substr('&segment='.length); + } - windowAlias.name = windowName + '###' + period + '###' + date; + windowAlias.name = windowName + '###' + period + '###' + date + '###' + segment; } // retrieve and check data from window name var windowNameParts = windowAlias.name.split('###'); - return windowNameParts.length === 3 && windowNameParts[0] === windowName; + return windowNameParts.length === 4 && windowNameParts[0] === windowName; } /* @@ -2680,12 +2716,13 @@ if (typeof Piwik !== 'object') { var windowNameParts = windowAlias.name.split('###'), period = windowNameParts[1], date = windowNameParts[2], + segment = windowNameParts[3], piwikUrl = getPiwikUrlForOverlay(configTrackerUrl, configApiUrl); loadScript( piwikUrl + 'plugins/Overlay/client/client.js?v=1', function () { - Piwik_Overlay_Client.initialize(piwikUrl, configTrackerSiteId, period, date); + Piwik_Overlay_Client.initialize(piwikUrl, configTrackerSiteId, period, date, segment); } ); } @@ -2848,6 +2885,9 @@ if (typeof Piwik !== 'object') { // Custom Variables, scope "event" customVariablesEvent = {}, + // Custom Dimensions (can be any scope) + customDimensions = {}, + // Custom Variables names and values are each truncated before being sent in the request or recorded in the cookie customVariableMaximumLength = 200, @@ -2976,10 +3016,115 @@ if (typeof Piwik !== 'object') { return baseUrl + url; } + function isSameHost (hostName, alias) { + var offset; + + hostName = String(hostName).toLowerCase(); + alias = String(alias).toLowerCase(); + + if (hostName === alias) { + return true; + } + + if (alias.slice(0, 1) === '.') { + if (hostName === alias.slice(1)) { + return true; + } + + offset = hostName.length - alias.length; + + if ((offset > 0) && (hostName.slice(offset) === alias)) { + return true; + } + } + + return false; + } + + function stringEndsWith(str, suffix) { + str = String(str); + return str.indexOf(suffix, str.length - suffix.length) !== -1; + } + + function removeCharactersFromEndOfString(str, numCharactersToRemove) { + str = String(str); + return str.substr(0, str.length - numCharactersToRemove); + } + + /* + * Extract pathname from URL. element.pathname is actually supported by pretty much all browsers including + * IE6 apart from some rare very old ones + */ + function getPathName(url) { + var parser = document.createElement('a'); + if (url.indexOf('//') !== 0 && url.indexOf('http') !== 0) { + url = 'http://' + url; + } + + parser.href = content.toAbsoluteUrl(url); + if (parser.pathname) { + return parser.pathname; + } + + return ''; + } + + function isSitePath (path, pathAlias) + { + var matchesAnyPath = (!pathAlias || pathAlias === '/'); + + if (matchesAnyPath) { + return true; + } + + if (path === pathAlias) { + return true; + } + + if (!path) { + return false; + } + + pathAlias = String(pathAlias).toLowerCase(); + path = String(path).toLowerCase(); + + // we need to append slashes so /foobarbaz won't match a site /foobar + if (!stringEndsWith(path, '/')) { + path += '/'; + } + + if (!stringEndsWith(pathAlias, '/')) { + pathAlias += '/'; + } + + return path.indexOf(pathAlias) === 0; + } + + function isSiteHostPath(host, path) + { + var i, + alias, + configAlias, + aliasHost, + aliasPath; + + for (i = 0; i < configHostsAlias.length; i++) { + aliasHost = domainFixup(configHostsAlias[i]); + aliasPath = getPathName(configHostsAlias[i]); + + if (isSameHost(host, aliasHost) && isSitePath(path, aliasPath)) { + return true; + } + } + + return false; + } + /* * Is the host local? (i.e., not an outlink) */ function isSiteHostName(hostName) { + var i, alias, offset; @@ -3678,6 +3823,34 @@ if (typeof Piwik !== 'object') { } } + var customDimensionIdsAlreadyHandled = []; + if (customData) { + for (i in customData) { + if (Object.prototype.hasOwnProperty.call(customData, i) && /^dimension\d+$/.test(i)) { + var index = i.replace('dimension', ''); + customDimensionIdsAlreadyHandled.push(parseInt(index, 10)); + customDimensionIdsAlreadyHandled.push(String(index)); + request += '&' + i + '=' + customData[i]; + delete customData[i]; + } + } + } + + if (customData && isObjectEmpty(customData)) { + customData = null; + // we deleted all keys from custom data + } + + // custom dimensions + for (i in customDimensions) { + if (Object.prototype.hasOwnProperty.call(customDimensions, i)) { + var isNotSetYet = (-1 === customDimensionIdsAlreadyHandled.indexOf(i)); + if (isNotSetYet) { + request += '&dimension' + i + '=' + customDimensions[i]; + } + } + } + // custom data if (customData) { request += '&data=' + encodeWrapper(JSON2.stringify(customData)); @@ -3940,6 +4113,8 @@ if (typeof Piwik !== 'object') { return; } + var originalSourcePath = sourceElement.pathname || getPathName(sourceElement.href); + // browsers, such as Safari, don't downcase hostname and href var originalSourceHostName = sourceElement.hostname || getHostName(sourceElement.href); var sourceHostName = originalSourceHostName.toLowerCase(); @@ -3950,7 +4125,7 @@ if (typeof Piwik !== 'object') { if (!scriptProtocol.test(sourceHref)) { // track outlinks and all downloads - var linkType = getLinkType(sourceElement.className, sourceHref, isSiteHostName(sourceHostName), query.hasNodeAttribute(sourceElement, 'download')); + var linkType = getLinkType(sourceElement.className, sourceHref, isSiteHostPath(sourceHostName, originalSourcePath), query.hasNodeAttribute(sourceElement, 'download')); if (linkType) { return { @@ -4716,6 +4891,60 @@ if (typeof Piwik !== 'object') { }); } + /** + * Note: While we check whether the user is on a configHostAlias path we do not check whether the user is + * actually on the configHostAlias domain. This is already done where this method is called and for + * simplicity we do not check this again. + * + * Also we currently assume that all configHostAlias domains start with the same wild card of '*.', '.' or + * none. Eg either all like '*.piwik.org' or '.piwik.org' or 'piwik.org'. Piwik always adds '*.' so it + * should be fine. + */ + function findConfigCookiePathToUse(configHostAlias, currentUrl) + { + var aliasPath = getPathName(configHostAlias); + var currentPath = getPathName(currentUrl); + + if (!aliasPath || aliasPath === '/' || !currentPath || currentPath === '/') { + // no path set that would be useful for cookiePath + return; + } + + var aliasDomain = domainFixup(configHostAlias); + + if (isSiteHostPath(aliasDomain, '/')) { + // there is another configHostsAlias having same domain that allows all paths + // eg this alias is for piwik.org/support but there is another alias allowing + // piwik.org + return; + } + + if (stringEndsWith(aliasPath, '/')) { + aliasPath = removeCharactersFromEndOfString(aliasPath, 1); + } + + // eg if we're in the case of "apache.piwik/foo/bar" we check whether there is maybe + // also a config alias allowing "apache.piwik/foo". In this case we're not allowed to set + // the cookie for "/foo/bar" but "/foo" + var pathAliasParts = aliasPath.split('/'); + var i; + for (i = 2; i < pathAliasParts.length; i++) { + var lessRestrctivePath = pathAliasParts.slice(0, i).join('/'); + if (isSiteHostPath(aliasDomain, lessRestrctivePath)) { + aliasPath = lessRestrctivePath; + break; + } + } + + if (!isSitePath(currentPath, aliasPath)) { + // current path of current URL does not match the alias + // eg user is on piwik.org/demo but configHostAlias is for piwik.org/support + return; + } + + return aliasPath; + } + /* * Browser features (plugins, resolution, cookies) */ @@ -4853,6 +5082,12 @@ if (typeof Piwik !== 'object') { internalIsNodeVisible: isVisible, isNodeAuthorizedToTriggerInteraction: isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink: replaceHrefIfInternalLink, + getDomains: function () { + return configHostsAlias; + }, + getConfigCookiePath: function () { + return configCookiePath; + }, getConfigDownloadExtensions: function () { return configDownloadExtensions; }, @@ -5093,6 +5328,50 @@ if (typeof Piwik !== 'object') { }, /** + * Set Custom Dimensions. Any set Custom Dimension will be cleared after a tracked pageview. Make + * sure to set them again if needed. + * + * @param int index A Custom Dimension index + * @param string value + */ + setCustomDimension: function (customDimensionId, value) { + customDimensionId = parseInt(customDimensionId, 10); + if (customDimensionId > 0) { + if (!isDefined(value)) { + value = ''; + } + if (!isString(value)) { + value = String(value); + } + customDimensions[customDimensionId] = value; + } + }, + + /** + * Get a stored value for a specific Custom Dimension index. + * + * @param int index A Custom Dimension index + */ + getCustomDimension: function (customDimensionId) { + customDimensionId = parseInt(customDimensionId, 10); + if (customDimensionId > 0 && Object.prototype.hasOwnProperty.call(customDimensions, customDimensionId)) { + return customDimensions[customDimensionId]; + } + }, + + /** + * Delete a custom dimension. + * + * @param int index Custom dimension Id + */ + deleteCustomDimension: function (customDimensionId) { + customDimensionId = parseInt(customDimensionId, 10); + if (customDimensionId > 0) { + delete customDimensions[customDimensionId]; + } + }, + + /** * Set custom variable within this visit * * @param int index Custom variable slot ID from 1-5 @@ -5165,6 +5444,7 @@ if (typeof Piwik !== 'object') { * Delete custom variable * * @param int index Custom variable slot ID from 1-5 + * @param string scope */ deleteCustomVariable: function (index, scope) { // Only delete if it was there already @@ -5238,13 +5518,44 @@ if (typeof Piwik !== 'object') { }, /** - * Set array of domains to be treated as local + * Set array of domains to be treated as local. Also supports path, eg '.piwik.org/subsite1'. In this + * case all links that don't go to '*.piwik.org/subsite1/ *' would be treated as outlinks. + * For example a link to 'piwik.org/' or 'piwik.org/subsite2' both would be treated as outlinks. + * + * We might automatically set a cookieConfigPath to avoid creating several cookies under one domain + * if there is a hostAlias defined with a path. Say a user is visiting 'http://piwik.org/subsite1' + * and '.piwik.org/subsite1' is set as a hostsAlias. Piwik will automatically use '/subsite1' as + * cookieConfigPath. * * @param string|array hostsAlias */ setDomains: function (hostsAlias) { configHostsAlias = isString(hostsAlias) ? [hostsAlias] : hostsAlias; - configHostsAlias.push(domainAlias); + + var hasDomainAliasAlready = false, i; + for (i in configHostsAlias) { + if (Object.prototype.hasOwnProperty.call(configHostsAlias, i) + && isSameHost(domainAlias, domainFixup(String(configHostsAlias[i])))) { + hasDomainAliasAlready = true; + + if (!configCookiePath) { + var path = findConfigCookiePathToUse(configHostsAlias[i], locationHrefAlias); + if (path) { + this.setCookiePath(path); + } + + break; + } + } + } + + if (!hasDomainAliasAlready) { + /** + * eg if domainAlias = 'piwik.org' and someone set hostsAlias = ['piwik.org/foo'] then we should + * not add piwik.org as it would increase the allowed scope. + */ + configHostsAlias.push(domainAlias); + } }, /** @@ -5904,10 +6215,11 @@ if (typeof Piwik !== 'object') { * @param string action The Event's Action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...) * @param string name (optional) The Event's object Name (a particular Movie name, or Song name, or File name...) * @param float value (optional) The Event's value + * @param mixed customData */ - trackEvent: function (category, action, name, value) { + trackEvent: function (category, action, name, value, customData) { trackCallback(function () { - logEvent(category, action, name, value); + logEvent(category, action, name, value, customData); }); }, @@ -5917,10 +6229,11 @@ if (typeof Piwik !== 'object') { * @param string keyword * @param string category * @param int resultsCount + * @param mixed customData */ - trackSiteSearch: function (keyword, category, resultsCount) { + trackSiteSearch: function (keyword, category, resultsCount, customData) { trackCallback(function () { - logSiteSearch(keyword, category, resultsCount); + logSiteSearch(keyword, category, resultsCount, customData); }); }, @@ -6084,7 +6397,7 @@ if (typeof Piwik !== 'object') { asyncTracker = new Tracker(); - var applyFirst = ['disableCookies', 'setTrackerUrl', 'setAPIUrl', 'setCookiePath', 'setCookieDomain', 'setUserId', 'setSiteId', 'enableLinkTracking']; + var applyFirst = ['disableCookies', 'setTrackerUrl', 'setAPIUrl', 'setCookiePath', 'setCookieDomain', 'setDomains', 'setUserId', 'setSiteId', 'enableLinkTracking']; _paq = applyMethodsInOrder(_paq, applyFirst); // apply the queue of actions diff --git a/lang/bg.json b/lang/bg.json index f42c8bfe23..b1e7601900 100644 --- a/lang/bg.json +++ b/lang/bg.json @@ -367,7 +367,6 @@ "VisitorSettings": "Настройки на посетителя", "VisitTypeExample": "Например, ако изберете всички посетители, които са се завърнали на сайта, включително тези, които са купили нещо при предишните посещения, API заявката ще съдържа %s", "Warning": "Предупреждение", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik ще спре поддръжката на тази PHP версия на %s. Обновете вашата PHP версия преди крайният срок да е изтекъл!", "WarningFileIntegrityNoManifest": "Цялостната проверка на файла не може да бъде изпълнена поради липсата на manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "В случай, че Piwik се внедрява посредством Git, е нормално това съобщение да се появява.", "WarningFileIntegrityNoMd5file": "Цялостната проверка не може да бъде осъществена поради липсата на md5_file() функцията.", diff --git a/lang/cs.json b/lang/cs.json index b4e6e92ba4..1050a3f259 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "12-hodinový formát", + "24HourClock": "24-hodinový formát", "AbandonedCarts": "Neobjednané košíky", "AboutPiwikX": "O Piwiku %s", "Action": "Akce", @@ -11,6 +13,7 @@ "AllWebsitesDashboard": "Nástěnka pro všechny weby", "And": "a", "API": "API", + "Apply": "Použít", "ArchivingInlineHelp": "Pro web stránky se střední nebo vysokou návštěvností je doporučeno zakázat archivování Piwiku z webového prohlížeče. Místo toho doporučujeme vytvoření úlohy pro cron", "ArchivingTriggerDescription": "Doporučeno pro větší instalace Piwiku, potřebujete %snastavit cron úlohu%s na automatické spracování hlášení.", "AuthenticationMethodSmtp": "Autentizační metoda SMTP", @@ -33,7 +36,7 @@ "Close": "Zavřít", "ClickToSearch": "Klikněte pro hledání", "ColumnActionsPerVisit": "Akcí za návštěvu", - "ColumnActionsPerVisitDocumentation": "Průměrný počet akcí (zobrazení stránek, stažení nebo externích odkazů) za návštěvu.", + "ColumnActionsPerVisitDocumentation": "Průměrný počet akcí (zobrazení stránek, stažení nebo externích odkazů) provedených během návštěvy.", "ColumnAverageGenerationTime": "Průměrný čas generování", "ColumnAverageGenerationTimeDocumentation": "Průměrný čas, za který byla stránka vygenerována. Toto měření zahrnuje čas, který potřeboval server k vygenerování stránky plus čas, který potřeboval klient k jejímu stažení. Kratší průměrný čas znamená rychlejší stránku.", "ColumnAverageTimeOnPage": "Průměrný čas na stránce", @@ -43,7 +46,7 @@ "ColumnBounceRate": "Odchozí frekvence", "ColumnBounceRateDocumentation": "Procento návštěv, které měly jedno zobrazení. To znamená návštěvníci, kteří po zobrazení stránky okamžitě odešli.", "ColumnBounces": "Ihned odchozí", - "ColumnBouncesDocumentation": "Počet návštěv, které začaly a skončily na této stránce. To znamená, kteří navštívily jen tuto stránku.", + "ColumnBouncesDocumentation": "Počet návštěv, které začaly a skončily na této stránce. To znamená, že návštěvník opustil web po zobrazení pouze této stránky.", "ColumnConversionRate": "Frekvence konverzí", "ColumnConversionRateDocumentation": "Procento návštěv, které provedly konverzi Cíle.", "ColumnDestinationPage": "Cílová stránka", @@ -60,11 +63,11 @@ "ColumnNbActions": "Akce", "ColumnNbActionsDocumentation": "Počet akcí Vašich návštěvníků. Akcí se rozumí zobrazení stránky, stažení a kliknutí na externí odkazy.", "ColumnNbUniqVisitors": "Jedineční návštěvníci", - "ColumnNbUniqVisitorsDocumentation": "Počet neduplicitních návštěvníků, kteří navštívili vaše stránky. Každý návštěvník je počítán pouze jednou, ikdyž třeba přišel několikrát za den.", + "ColumnNbUniqVisitorsDocumentation": "Počet neduplicitních návštěvníků, kteří navštívili vaše stránky. Každý návštěvník je počítán pouze jednou, i pokud přišel několikrát za den.", "ColumnNbUsers": "Uživatelé", "ColumnNbUsersDocumentation": "Počet uživatelů přihlášených na vašich stránkách. Je to počet unikátních aktivních uživatelů, kteří mají nastavené ID pomocí sledovací funkce 'setUserId'.", "ColumnNbVisits": "Návštěv", - "ColumnNbVisitsDocumentation": "Pokud návštěvník přijde na vaše stránky poprvé nebo po třiceti minutách, je tato návštěva započítána jako nová.", + "ColumnNbVisitsDocumentation": "Pokud návštěvník přijde na stránku poprvé nebo opakovaně po více než 30ti minutách, je návštěva započítána jako nová.", "ColumnPageBounceRateDocumentation": "Procento návštěv, které začaly touto stránkou a ihned ji opustili.", "ColumnPageviews": "Zobrazení stránek", "ColumnPageviewsDocumentation": "Počet návštěv této stránky.", @@ -98,6 +101,7 @@ "DateRange": "Rozsah:", "DateRangeFrom": "Od", "DateRangeFromTo": "Od %s do %s", + "DateRangeInPeriodList": "Rozsah datumů", "DateRangeTo": "Do", "DaysHours": "%1$s dní %2$s hodin", "DaysSinceFirstVisit": "Dnů od první návštěvy", @@ -131,14 +135,14 @@ "Error": "Chyba", "ErrorRequest": "Ajaj! Při požadavku došlo k chybě. Na serveru mohlo dojít k dočasnému problému, nebo jste požádali o hlášení s příliš mnoha daty. Prosím, zkuste to znovu. Pokud se bude problém opakovat, %skontaktujte svého administrátora Piwiku%s.", "EvolutionOverPeriod": "Vývoj za periodu", - "EvolutionSummaryGeneric": "%1$s v %2$s srovnáno s %3$s v %4$s. Vývin: %5$s", - "ExceptionContactSupportGeneric": "Pokud problém přetrvá, %skontaktujte pro pomoc vašeho Piwik administrátora%s.", + "EvolutionSummaryGeneric": "%1$s v %2$s srovnáno s %3$s v %4$s. Vývoj: %5$s", + "ExceptionContactSupportGeneric": "Pokud problém přetrvá, %skontaktujte pro pomoc svého Piwik administrátora%s.", "ExceptionCheckUserHasSuperUserAccessOrIsTheUser": "Uživatel musí být super uživatel nebo uživatel %s.", "ExceptionConfigurationFileNotFound": "Konfigurační soubor {%s} nebyl nalezen", "ExceptionConfigurationFileNotFound2": "Pokud soubor existuje, ověřte, že %s může číst uživatel '%s'.", "ExceptionDatabaseVersion": "Vaše %1$s verze je %2$s ale Piwik vyžaduje minimálně %3$s.", "ExceptionDatabaseVersionNewerThanCodebase": "Kód Piwiku je z verze %1$s, ale bylo zjištěno, že databáze byla již aktualizována na verzi %2$s.", - "ExceptionDatabaseVersionNewerThanCodebaseWait": "Vaši administrátoři možná pracují na aktualizaci. Zkuste to, prosím, za pár minut.", + "ExceptionDatabaseVersionNewerThanCodebaseWait": "Vaši administrátoři možná pracují na aktualizaci. Zkuste to prosím za pár minut.", "ExceptionFileIntegrity": "Test integrity selhal: %s", "ExceptionFilesizeMismatch": "Nesouhlasí velikost souboru: %1$s (očekávaná délka: %2$s, nalezeno: %3$s)", "ExceptionIncompatibleClientServerVersions": "Vaše %1$s verze klienta je %2$s tato je ale nekompatibilní se serverem %3$s.", @@ -167,7 +171,7 @@ "ExpandDataTableFooter": "Změnit vizualizaci nebo nastavit hlášení", "Export": "Exportovat", "ExportAsImage": "Exportovat obrázek", - "ExportThisReport": "Exportovat data v ostatních formátech", + "ExportThisReport": "Exportovat data v jiných formátech", "Faq": "FAQ", "FileIntegrityWarningExplanation": "Test integrity selhal a nahlásil nějaké chyby. To je kvůli částečně nebo špatně nahraným souborům Piwiku. Měli byste znovu nahrát všechny soubory Piwiku v módu BINARY.", "First": "První", @@ -184,11 +188,11 @@ "GraphHelp": "Více informací o zobrazování grafů v Piwiku", "HelloUser": "Ahoj, %s!", "Help": "Pomoc", - "HelpTranslatePiwik": "Možná byste chtěli %1$snám pomoct s překladem Piwiku%2$s.", + "HelpTranslatePiwik": "Možná byste nám chtěli %1$s pomoci s překladem Piwiku%2$s.", "Hide": "skrýt", "HoursMinutes": "%1$s hodin %2$s min.", "Id": "ID", - "IfArchivingIsFastYouCanSetupCronRunMoreOften": "V případě, že je zálohování dost rychlé, můžete jej spouštět v cronu častěji", + "IfArchivingIsFastYouCanSetupCronRunMoreOften": "V případě, že je zálohování dost rychlé, jej můžete spouštět v cronu častěji", "InfoFor": "Informace o %s", "Installed": "Nainstalováno", "InvalidDateRange": "Chybný rozsah, vyberte jej znovu", @@ -200,12 +204,12 @@ "LastDaysShort": "Posledních %s dní", "LearnMore": "%1$sdozvědět se více%2$s", "Live": "Živě", - "Loading": "Načítám...", - "LoadingData": "Načítám data...", - "LoadingPopover": "Načítám %s...", - "LoadingPopoverFor": "Načítám %s", + "Loading": "Načítání...", + "LoadingData": "Načítání dat...", + "LoadingPopover": "Načítání %s...", + "LoadingPopoverFor": "Načítání %s pro", "Locale": "cs_CZ.UTF-8", - "Logout": "Odhlásit se", + "Logout": "Odhlásit", "MainMetrics": "Hlavní měření", "Matches": "Shody", "MediumToHighTrafficItIsRecommendedTo": "Pro weby se středním nebo velkým provozem doporučujeme zpracovat dnešní hlášení každou půlhodinu (%s vteřin), nebo každou hodinu (%s vteřin)", @@ -222,12 +226,12 @@ "More": "Více", "MoreDetails": "Více podrobností", "MoreLowerCase": "více", - "MultiSitesSummary": "všechny weby", + "MultiSitesSummary": "Všechny weby", "Name": "Jméno", "NbActions": "Počet akcí", "NbSearches": "Počet interních vyhledávání", "Never": "Nikdy", - "NewReportsWillBeProcessedByCron": "Pokud není archivování spouštěno web prohlížečem, budou nová hlášení zpracovávána cronem", + "NewReportsWillBeProcessedByCron": "Pokud není archivování spouštěno webovým prohlížečem, budou nová hlášení zpracovávána cronem.", "NewUpdatePiwikX": "Nová aktualizace: Piwik %s", "NewVisitor": "Nový návštěvník", "NewVisits": "Nové návštěvy", @@ -248,7 +252,7 @@ "OneVisit": "1 návštěva", "OnlyEnterIfRequired": "Uživatelské jméno zadejte pouze v případě, že jej váš SMTP server vyžaduje", "OnlyEnterIfRequiredPassword": "Heslo zadejte pouze v případě, že jej váš SMTP server vyžaduje", - "OnlyUsedIfUserPwdIsSet": "Použito pouze v případě, že je nastaveno uživatelské jméno\/heslo. V případě že si nejste jisti jakou metodu použít, zeptejte se vašeho poskytovatele", + "OnlyUsedIfUserPwdIsSet": "Použito pouze v případě, že je nastaveno uživatelské jméno\/heslo. V případě že si nejste jisti jakou metodu použít, zeptejte se svého poskytovatele", "OpenSourceWebAnalytics": "Open Source Web Analytics", "OperationAtLeast": "Aspoň", "OperationAtMost": "Nejvíce", @@ -260,8 +264,11 @@ "OperationIsNot": "Není", "OperationLessThan": "Menší než", "OperationNotEquals": "Není rovno", + "OperationStartsWith": "Začíná na", + "OperationEndsWith": "Končí na", "OptionalSmtpPort": "Volitelné. Pro nešifrovaná a TLS SMTP spojení je výchozí 25, pro SSL SMTP spojení je výchozí 465.", "Options": "Nastavení", + "Or": "nebo", "OrCancel": "nebo %s Zrušit %s", "Others": "Ostatní", "Outlink": "Odchozí odkaz", @@ -280,7 +287,7 @@ "PiwikXIsAvailablePleaseNotifyPiwikAdmin": "%1$s je dostupný. Prosím, upozorněte %2$sadministrátora Piwiku%3$s.", "PiwikXIsAvailablePleaseUpdateNow": "Je k dispozici Piwik %1$s. %2$s Prosím aktualizujte jej!%3$s (viz %4$s změny%5$s).", "PleaseSpecifyValue": "Prosím zapiště hodnotu pro '%s'.", - "PleaseUpdatePiwik": "Prosím aktualizujte Piwik", + "PleaseUpdatePiwik": "Aktualizujte prosím svůj Piwik", "Plugin": "Plugin", "Plugins": "Zásuvné moduly", "PoweredBy": "Běží na", @@ -327,7 +334,7 @@ "SeeTheOfficialDocumentationForMoreInformation": "Pro více informací navštivte %soficiální dokumentaci%s.", "SeeThisFaq": "Podívejte se na %1$stento často kladený dotaz%2$s.", "Segment": "Část", - "SelectYesIfYouWantToSendEmailsViaServer": "Zvolte \"Ano\" pokud chcete e-mail posílat pomocí uvedeného serveru místo lokální funkce PHP mail", + "SelectYesIfYouWantToSendEmailsViaServer": "Zvolte \"Ano\" pokud chcete email posílat pomocí uvedeného serveru místo lokální funkce PHP mail", "Settings": "Nastavení", "Shipping": "Doprava", "Show": "zobrazit", @@ -339,18 +346,22 @@ "SmtpServerAddress": "Adresa SMTP serveru", "SmtpUsername": "Uživatelské jméno SMTP", "Source": "Zdroj", - "StatisticsAreNotRecorded": "Zaznamenávání statistik návštěvníků Pywikem je aktuálně zakázáno. Povolte ho nastavením record_statistics = 1 ve vašem souboru config\/config.ini.php.", + "StatisticsAreNotRecorded": "Zaznamenávání statistik návštěvníků Piwikem je aktuálně zakázáno. Povolte ho nastavením record_statistics = 1 ve vašem souboru config\/config.ini.php.", "Subtotal": "Mezisoučet", "Summary": "Souhrn", "Table": "Tabulka", "TagCloud": "Oblak tagů", "Tax": "Daň", "TimeAgo": "Před %s", + "TimeFormat": "Formát času", "TimeOnPage": "Čas na stránce", "Total": "Celkem", "TotalRatioTooltip": "Toto je %1$s ze všech %2$s %3$s.", "TotalRevenue": "Celková hodnota", "TotalVisitsPageviewsActionsRevenue": "(Celkem: %s návštěv, %s zobrazení, %s akcí, %s příjem)", + "TrackingScopeAction": "Akce", + "TrackingScopePage": "Stránka", + "TrackingScopeVisit": "Návštěva", "TransitionsRowActionTooltip": "Podívejte se, co dělali návštěvníci před a po návštěvě této stránky", "TransitionsRowActionTooltipTitle": "Otevřít přechody", "TranslatorName": "Filip Bartmann, Jakub Baláš, Michal Čihař", @@ -360,7 +371,7 @@ "UsePlusMinusIconsDocumentation": "Použijte ikonu plus a mínus vlevo od navigace.", "UserId": "ID uživatele", "Username": "Uživatelské jméno", - "UseSMTPServerForEmail": "Pro e-mail použít SMTP server", + "UseSMTPServerForEmail": "Pro email použít SMTP server", "Value": "Hodnota", "VBarGraph": "Svislý sloupcový graf", "View": "Zobrazit", @@ -368,7 +379,7 @@ "Visit": "Návštěva", "VisitConvertedGoal": "Návštěva, která provedla alespoň jednu konverzi Cíle.", "VisitConvertedGoalId": "Návštěva, která provedla konverzi konkrétního ID Cíle.", - "VisitConvertedNGoals": "Návštěva provedla %s konverzí Cílůb.", + "VisitConvertedNGoals": "Návštěva proměnila %s cílů.", "VisitDuration": "Průměrná doba trvání návštěv (v sekundách)", "Visitor": "Návštěvník", "VisitorID": "ID návštěvníka", @@ -380,7 +391,7 @@ "VisitTypeExample": "Například pro výběr všech návštěvníků, kteří se vrátili na stránky včetně těch, co si v předcchozích návštěvách něco koupili, by API požadavek obsahoval %s", "Warning": "Varování", "WarningPhpVersionXIsTooOld": "PHP verze %s, kterou používáte již není podporována (EOL). Důrazně doporučujeme aktualizovat na novější verzi, protože stávající může obsahovat bezpečnostní a další problémy, které byly opraveny v novějších verzích PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik tuto verzi PHP přestane podporovat za %s. Aktualizujte, dokud je čas!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik přestane podporavat PHP %1$s v dalším vydání. Aktualizujte PHP alespoň na %2$s, než bude pozdě.", "WarningFileIntegrityNoManifest": "Test integrity nemůže být proveden z důvodů chybějícího souboru manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Pokud nasazujete Piwik z Gitu, pak je tato zpráva normální.", "WarningFileIntegrityNoMd5file": "Test integrity nemůže být dokončen z důvodů chybějící funkce md5_file().", @@ -400,6 +411,7 @@ "YearsDays": "%1$s roků %2$s dní", "Yes": "Ano", "YouAreCurrentlyUsing": "Používáte Piwik verze %s.", + "YouAreViewingDemoShortMessage": "Díváte se na demo Piwiku", "YouMustBeLoggedIn": "Abyste mohli využívat tyto funkce musíte být přihlášeni", "YourChangesHaveBeenSaved": "Vaše změny byly uloženy" }, @@ -454,9 +466,9 @@ "PossibleSslErrorExplanation": "Došlo k chybě, která může být způsobena neplatným, nebo sám sebou podepsaným SSL certifikátem: \"%s\". Přihlášení fungovat může, ale nebude to tak bezpečné.", "IgnoreSslError": "Ignorovat chybu SSL", "RatingDontRemindMe": "Neupozorňovat mě", - "RatingNotNow": "Ne nyní", + "RatingNotNow": "Nyní ne", "RatingNow": "Dobrá, teď to ohodnotím", - "RatingPleaseRateUs": "Piwik mobile je volně šiřitelný software. Proto bychom ocenili, kdybyste si našli mnutu k jeho ohodnocení v %s. Pokud máte návrh na nějakou novou vlastnost nebo chcete nahlásit chybu, kontaktujte %s", + "RatingPleaseRateUs": "Piwik mobile je volně šiřitelný software. Proto bychom ocenili, kdybyste si našli minutu k jeho ohodnocení v %s. Pokud máte návrh na nějakou novou vlastnost nebo chcete nahlásit chybu, kontaktujte %s", "ReleaseToRefresh": "Uvolněte pro obnovení...", "Reloading": "Znovu nahrávám...", "RequestTimedOutShort": "Chyba časového limitu sítě", @@ -480,7 +492,7 @@ "CompareDocumentation": "Klikněte na odkaz níže a otevřete toto vyskakovací okno pro jiný řádek ze stejné tabulky kvůli srovnání více záznamů. <br\/> Použijte shift + kliknutí pro označení řádku k porovnání bez otevření tohoto vyskakovacího okna.", "CompareRows": "Porovnat řádky", "ComparingRecords": "Porovnávání %s řádků", - "Documentation": "Klikněte na měření, pokud je chcete zobrazit ve velkém grafu vývoje. Použijte shoft + kliknutí pro zobrazení více měření najednou.", + "Documentation": "Klikněte na měření, pokud je chcete zobrazit ve velkém grafu vývoje. Použijte shift + kliknutí pro zobrazení více měření najednou.", "MetricBetweenText": "mezi %s a %s", "MetricChangeText": "%s za interval", "MetricMinMax": "Během tohoto období %1$s se pohyboval(a) mezi %2$s a %3$s", diff --git a/lang/da.json b/lang/da.json index e16b0cfd8b..050ae1a0c7 100644 --- a/lang/da.json +++ b/lang/da.json @@ -368,7 +368,6 @@ "VisitTypeExample": "F. eks., for at vælge alle besøgende, som er vendt tilbage til hjemmesiden, herunder dem, der har købt noget i deres tidligere besøg, vil API-anmodningen indeholde %s", "Warning": "Advarsel", "WarningPhpVersionXIsTooOld": "PHP version %s har nået slutningen af sin levetid (EOL). Du opfordres kraftigt til at opgradere til den aktuelle version, fordi brug af denne version kan udsætte dig for sikkerhedshuller og fejl, som er blevet rettet i nyere versioner af PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik vil ophøre med at understøtte PHP-version i %s. Opgrader PHP versionen, før det er for sent!", "WarningFileIntegrityNoManifest": "Fil integritetstjek kunne ikke udføres på grund af manglende manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Hvis du implementerer Piwik fra Git, er meddelelsen normal.", "WarningFileIntegrityNoMd5file": "Fil integritetstjek kunne ikke gennemføres pga. manglende md5_file () funktion.", diff --git a/lang/de.json b/lang/de.json index 25650ebf00..0f975ee065 100644 --- a/lang/de.json +++ b/lang/de.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "12-Stunden Uhr", + "24HourClock": "24-Stunden Uhr", "AbandonedCarts": "Verlassene Warenkörbe", "AboutPiwikX": "Über Piwik %s", "Action": "Aktion", @@ -262,8 +264,11 @@ "OperationIsNot": "Entspricht nicht", "OperationLessThan": "Weniger als", "OperationNotEquals": "Entspricht nicht", + "OperationStartsWith": "Startet mit", + "OperationEndsWith": "Endet mit", "OptionalSmtpPort": "Optional. Standard ist 25 für unverschlüsseltes SMTP sowie SMTP mit TLS, und 465 für SMTP mit SSL.", "Options": "Optionen", + "Or": "oder", "OrCancel": "oder %s abbrechen %s", "Others": "Andere", "Outlink": "Ausgehender Verweis", @@ -348,11 +353,15 @@ "TagCloud": "Schlagwortwolke", "Tax": "Steuer", "TimeAgo": "vor %s", + "TimeFormat": "Zeitformat", "TimeOnPage": "Zeit auf Seite", "Total": "Gesamt", "TotalRatioTooltip": "Das entspricht %1$s von %2$s %3$s.", "TotalRevenue": "Gesamteinnahmen", "TotalVisitsPageviewsActionsRevenue": "(Gesamt: %s Besuche, %s Seitenansichten, %s Aktionen, %s Einnahmen)", + "TrackingScopeAction": "Aktion", + "TrackingScopePage": "Seite", + "TrackingScopeVisit": "Besuch", "TransitionsRowActionTooltip": "Die Aktionen vor und nach dieser Seite auswerten", "TransitionsRowActionTooltipTitle": "Transitions öffnen", "TranslatorName": "Frank Bueltge, Thorsten Taube, Arthur W. Borens, Marco Ziesing, Andreas Just, Fabian Becker, Henry Müller, Pascal Herbert, Christian W. Schneider, Michael Stenz, Itransition, Timo Besenreuther, Stefan Giehl", @@ -382,7 +391,7 @@ "VisitTypeExample": "Um beispielsweise alle wiederkehrenden Besucher abzufragen, die Bestellungen getätigt haben, muss der API Aufruf folgendes enthalten: %s", "Warning": "Warnung", "WarningPhpVersionXIsTooOld": "Die von Ihnen eingesetzte PHP Version %s hat das Ende der Lebensdauer (EOL) erreicht. Es wird dringend angeraten, ein Update auf eine aktuelle Version durchzuführen, da der Einsatz dieser Version zu Sicherheitsrisiken und Fehlern führen kann, welche in neueren PHP Versionen korrigiert wurden.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik wird die Unterstützung für diese PHP Version im %s einstellen. Aktualisieren Sie ihre PHP Version, bevor es zu spät ist!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik wird die Unterstützung für PHP %1$s in der nächsten Hauptversion einstellen. Aktualisieren Sie ihre PHP Version auf mindestens %2$s, bevor es zu spät ist!", "WarningFileIntegrityNoManifest": "Aufgrund der fehlenden Datei manifest.inc.php konnte die Integritätsprüfung nicht durchgeführt werden.", "WarningFileIntegrityNoManifestDeployingFromGit": "Wenn Sie Piwik von Git deployen ist diese Nachricht normal.", "WarningFileIntegrityNoMd5file": "Durch die fehlende md5_file() Funktion konnte die Integritätsprüfung nicht durchgeführt werden.", diff --git a/lang/el.json b/lang/el.json index 8da0175a43..c8a479aaef 100644 --- a/lang/el.json +++ b/lang/el.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "Ρολόι σε μορφή 12 ωρών", + "24HourClock": "Ρολόι σε μορφή 24 ωρών", "AbandonedCarts": "Εγκαταλειμμένα Καλάθια Αγορών", "AboutPiwikX": "Σχετικά με το Piwik %s", "Action": "Δραστηριότητα", @@ -262,8 +264,11 @@ "OperationIsNot": "Δεν είναι", "OperationLessThan": "Μικρότερο από", "OperationNotEquals": "Δεν ισούται", + "OperationStartsWith": "Αρχίζει με", + "OperationEndsWith": "Τελειώνει σε", "OptionalSmtpPort": "Προαιρετικό: Η 25 είναι η προεπιλεγμένη θύρα για μη κρυπτογραφημένο και TLS SMTP. Η 465 για SSL SMTP.", "Options": "Επιλογές", + "Or": "ή", "OrCancel": "ή %s Άκυρο %s", "Others": "Άλλα", "Outlink": "Εξωτερικός σύνδεσμος", @@ -348,11 +353,15 @@ "TagCloud": "Σύννεφο λέξεων", "Tax": "Φόρος", "TimeAgo": "πριν από %s", + "TimeFormat": "Μορφή ώρας", "TimeOnPage": "Χρόνος στη σελίδα", "Total": "Σύνολο", "TotalRatioTooltip": "Αυτό είναι το %1$s από όλες τις %2$s %3$s.", "TotalRevenue": "Συνολική Πρόσοδος", "TotalVisitsPageviewsActionsRevenue": "(Σύνολα: %s επισκέψεις, %s προβολές σελίδων, %s ενέργειες, %s κέρδος)", + "TrackingScopeAction": "Ενέργεια", + "TrackingScopePage": "Σελίδα", + "TrackingScopeVisit": "Επίσκεψη", "TransitionsRowActionTooltip": "Δείτε τι έκαναν οι επισκέπτες πριν και μετά την προβολή αυτής της σελίδας", "TransitionsRowActionTooltipTitle": "Άνοιγμα Μεταβάσεων", "TranslatorName": "Jim Black www.jblack.info, Γεώργιος Τέλλος OnSite.Net VoIP & IT Solutions, Παναγιώτης Παπάζογλου Δρ. Δασολόγος-Περιβαλλοντολόγος, <a href=\"http:\/\/www.lourdas.name\">Λούρδας Βασίλειος<\/a>", @@ -382,7 +391,7 @@ "VisitTypeExample": "Για παράδειγμα, για να επιλέξετε όλους τους επισκέπτες που έχουν επιστρέψει στην ιστοσελίδα και να περιλάβετε αυτούς που έχουν αγοράσει κάτι στις προηγούμενες επισκέψεις, το αίτημα API θα περιέχει %s", "Warning": "Προειδοποίηση", "WarningPhpVersionXIsTooOld": "Η έκδοση PHP %s που χρησιμοποιείτε έχει φτάσει στο Τέλος Ζωής της (EOL). Συνιστάται να αναβαθμίσετε στην τρέχουσα έκδοση, καθώς η χρήση αυτή της έκδοσης μπορεί να σας εκθέσει σε προβλήματα ασφαλείας και σφάλματα που έχουν διορθωθεί στις πρόσφατες εκδόσεις της PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Το Piwik θα σταματήσει να υποστηρίζει αυτή την έκδοση στο %s. Αναβαθμίστε την έκδοση της PHP σας προτού είναι αργά!", + "WarningPiwikWillStopSupportingPHPVersion": "Το Piwik θα σταματήσει να υποστηρίζει την PHP %1$s στην επόμενη σημαντική έκδοσή του. Αναβαθμίστε την PHP τουλάχιστον στην έκδοση %2$s, προτού είναι αργά!", "WarningFileIntegrityNoManifest": "Ο έλεγχος ακεραιότητας αρχείου δεν μπορεί να πραγματοποιηθεί επειδή λείπει το αρχείο manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Αν παίρνετε το Piwik από το Git, το μήνυμα αυτό είναι φυσιολογικό.", "WarningFileIntegrityNoMd5file": "Ο έλεγχος ακεραιότητας αρχείου δεν μπορεί να ολοκληρωθεί γιατί είναι ανενεργή η συνάρτηση md5_file().", diff --git a/lang/en.json b/lang/en.json index a3da13491c..6581f7fc47 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "12-hour clock", + "24HourClock": "24-hour clock", "AbandonedCarts": "Abandoned Carts", "AboutPiwikX": "About Piwik %s", "Action": "Action", @@ -263,8 +265,11 @@ "OperationIsNot": "Is not", "OperationLessThan": "Less than", "OperationNotEquals": "Not Equals", + "OperationStartsWith": "Starts with", + "OperationEndsWith": "Ends with", "OptionalSmtpPort": "Optional. Defaults to 25 for unencrypted and TLS SMTP, and 465 for SSL SMTP.", "Options": "Options", + "Or": "or", "OrCancel": "or %s Cancel %s", "Others": "Others", "Outlink": "Outlink", @@ -349,11 +354,15 @@ "TagCloud": "Tag Cloud", "Tax": "Tax", "TimeAgo": "%s ago", + "TimeFormat": "Time format", "TimeOnPage": "Time on page", "Total": "Total", "TotalRatioTooltip": "This is %1$s of all %2$s %3$s.", "TotalRevenue": "Total Revenue", "TotalVisitsPageviewsActionsRevenue": "(Total: %s visits, %s pageviews, %s actions, %s revenue)", + "TrackingScopeAction": "Action", + "TrackingScopePage": "Page", + "TrackingScopeVisit": "Visit", "TransitionsRowActionTooltip": "See what visitors did before and after viewing this page", "TransitionsRowActionTooltipTitle": "Open Transitions", "TranslatorName": "-", @@ -383,7 +392,7 @@ "VisitTypeExample": "For example, to select all visitors who have returned to the website, including those who have bought something in their previous visits, the API request would contain %s", "Warning": "Warning", "WarningPhpVersionXIsTooOld": "The PHP version %s you are using has reached its End of Life (EOL). You are strongly urged to upgrade to a current version, as using this version may expose you to security vulnerabilities and bugs that have been fixed in more recent versions of PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik will stop supporting this PHP version in %s. Upgrade your PHP version before it's too late!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik will stop supporting PHP %1$s in the next major version. Upgrade your PHP to at least PHP %2$s, before it's too late!", "WarningFileIntegrityNoManifest": "File integrity check could not be performed due to missing manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "If you are deploying Piwik from Git, this message is normal.", "WarningFileIntegrityNoMd5file": "File integrity check could not be completed due to missing md5_file() function.", diff --git a/lang/es.json b/lang/es.json index 869b0500fe..5c2a97a67d 100644 --- a/lang/es.json +++ b/lang/es.json @@ -378,7 +378,6 @@ "VisitTypeExample": "Por ejemplo, para seleccionar todos los visitantes que han vuelto al sitio, incluyendo a aquellos que han comprado algo en sus anteriores visitas, la solicitud de la API debe contener %s", "Warning": "Advertencia", "WarningPhpVersionXIsTooOld": "La versión %s de PHP que está utilizando ha alcanzado su End of LIfe (EOL). Necesitas urgentemente actualizar a una versión más reciente, ya que usando esta versión puede exponerlo a vulnerabilidades de seguridad y errores que han sido arreglados en versiones más recientes de PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik dejará de dar asistencia a esta versión de PHP en la versión %s. ¡Actualice su versión de PHP antes que sea tarde!", "WarningFileIntegrityNoManifest": "La verificación de integridad de archivos no se pudo realizar debido a que falta manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Si está desplegando Piwik con Git, este mensaje es normal.", "WarningFileIntegrityNoMd5file": "La verificación de integridad no pudo ser completada debido a que falta la función md5_file().", diff --git a/lang/fi.json b/lang/fi.json index ba4ffb0b0a..702aced309 100644 --- a/lang/fi.json +++ b/lang/fi.json @@ -369,7 +369,6 @@ "VisitTypeExample": "Voit esimerkiksi valita kaikki kävijät jotka palasivat sivulle ja ostivat edellisellä käynnillä. Esimerkki pyynnöstä on %s", "Warning": "Varoitus", "WarningPhpVersionXIsTooOld": "Käyttämäsi PHP versio %s on ohittanut EOL (End of Life) ajankohdan. Suosittelemme vakavasti ohjelmiston päivittämistä uudempaan versioon, sillä käyttämäsi versio voi altistaa sivuston tietoturva-aukoille ja virheille, jotka ovat korjattu PHP:n uudemmissa versioissa.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik ei tue tätä versiota PHP:stä %s. Päivitä PHP:n versio ennen kuin se on liian myöhäistä!", "WarningFileIntegrityNoManifest": "Tiedostojen eheystarkistusta ei voida suorittaa, tiedosto manifest.inc.php puuttuu", "WarningFileIntegrityNoManifestDeployingFromGit": "Jos olet ottanut Piwikin käyttöön Git-järjestelmän kautta, tämä viesti on normaali.", "WarningFileIntegrityNoMd5file": "Tiedostojen eheystarkistusta ei voi suorittaa, funktio md5_file() puuttuu.", diff --git a/lang/fr.json b/lang/fr.json index 0a20b7cb5a..c43d588f4b 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "Système horaire sur 12 heures", + "24HourClock": "Système horaire sur 24 heures", "AbandonedCarts": "Paniers abandonnés", "AboutPiwikX": "À propos de Piwik %s", "Action": "Action", @@ -11,6 +13,7 @@ "AllWebsitesDashboard": "Tableau de bord de tous les sites", "And": "et", "API": "API", + "Apply": "Appliquer", "ArchivingInlineHelp": "Pour les sites à trafic moyen et fort, il est recommandé de désactiver l'archivage de Piwik depuis le navigateur. Nous vous recommandons de mettre en place une tâche planifiée (cron job) qui génère les rapports toutes les heures.", "ArchivingTriggerDescription": "Recommandé pour les installations importantes de Piwik, vous devriez %s mettre en place une tâche planifiée (cron) %s pour générer les rapports automatiquement.", "AuthenticationMethodSmtp": "Méthode d'authentification pour le serveur SMTP", @@ -98,6 +101,7 @@ "DateRange": "Période :", "DateRangeFrom": "Du", "DateRangeFromTo": "De %s à %s", + "DateRangeInPeriodList": "période", "DateRangeTo": "Au", "DaysHours": "%1$s jours %2$s heures", "DaysSinceFirstVisit": "Jours depuis la première visite", @@ -346,6 +350,7 @@ "TagCloud": "Nuage de tags", "Tax": "Taxes", "TimeAgo": "il y a %s", + "TimeFormat": "Format horaire", "TimeOnPage": "Temps sur la page", "Total": "Total", "TotalRatioTooltip": "C'est %1$s de %2$s %3$s.", @@ -380,7 +385,7 @@ "VisitTypeExample": "Par exemple, pour sélectionner tous les visiteurs qui sont retournés sur le site web, en incluant ceux qui ont acheté quelque chose lors de leur dernière visite, la requête à l'API contiendrait %s", "Warning": "Attention", "WarningPhpVersionXIsTooOld": "La version de PHP que vous utilisez (%s) est en fin de support. Nous vous incitons vivement à upgrader vers une version plus récente. Votre version actuelle vous expose à des failles de sécurité et des bugs qui ont été corrigés dans les versions les plus récentes de PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik va arrêter le support de cette version de PHP en %s. Mettez à jour votre version de PHP avant qu'il ne soit trop tard !", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik ne prendra plus en charge PHP %1$s dans la prochaine version majeure. Mettez à jour votre PHP au minimum avec une version %2$s avant qu'il ne soit trop tard !", "WarningFileIntegrityNoManifest": "Le contrôle d'intégrité n'a pu être effectué car le fichier manifest.inc.php est manquant.", "WarningFileIntegrityNoManifestDeployingFromGit": "Si vous déployez Piwik depuis Git, ce message est normal.", "WarningFileIntegrityNoMd5file": "Le contrôle d'intégrité n'a pu être effectué car la fonction md5_file() est manquante.", @@ -400,6 +405,7 @@ "YearsDays": "%1$s années %2$s jours", "Yes": "Oui", "YouAreCurrentlyUsing": "Vous utilisez actuellement Piwik %s", + "YouAreViewingDemoShortMessage": "Vous visionnez la démonstration de Piwik", "YouMustBeLoggedIn": "Vous devez être connecté(e) pour accéder à cette fonctionnalité.", "YourChangesHaveBeenSaved": "Vos modifications ont été enregistrées." }, diff --git a/lang/id.json b/lang/id.json index 76f5d61f42..6245ae969f 100644 --- a/lang/id.json +++ b/lang/id.json @@ -1,16 +1,19 @@ { "General": { + "12HourClock": "Waktu 12-jam", + "24HourClock": "Waktu 24-jam", "AbandonedCarts": "Keranjang Terabaikan", "AboutPiwikX": "Tentang Piwik %s", "Action": "Tindakan", "Actions": "Tindakan", "Add": "Tambah", - "AfterEntry": "after entereing here", + "AfterEntry": "setelah memasuki di sini", "All": "Semua", "AllowPiwikArchivingToTriggerBrowser": "Arsipkan laporan ketikan meniliknya dari peramban", "AllWebsitesDashboard": "Panel Kendali semua Situs", "And": "dan", "API": "API", + "Apply": "Terapkan", "ArchivingInlineHelp": "Untuk kunjungan situs menengah hingga tinggi, disarankan untuk mematikan pengarsipan Piwik ketika laporan ditampilkan. Kami lebih menyarankan untuk mengatur tugas Cron untuk mengolah laporan pada tiap jam.", "ArchivingTriggerDescription": "Disarankan untuk instalasi Piwik besar untuk %smengatur tugas Cron%s untuk mengolah laporan otomatis.", "AuthenticationMethodSmtp": "Metode otentikasi untuk SMTP", @@ -62,6 +65,7 @@ "ColumnNbUniqVisitors": "Pengunjung unik", "ColumnNbUniqVisitorsDocumentation": "Jumlah pengunjung unik yang mengunjungi situs Anda. Setiap pengguna hanya terhitung satu kali, walaupun mengunjungi situs beberapa kali.", "ColumnNbUsers": "Pengguna", + "ColumnNbUsersDocumentation": "Jumlah pengguna yang masuk-log ke situs Anda. Ini adalah jumlah pengguna aktif dengan ID Pengguna yang diatur (melalui fungsi Pelacakan 'setUserId').", "ColumnNbVisits": "Kunjungan", "ColumnNbVisitsDocumentation": "Bila pengunjung mengunjungi situs Anda untuk pertama kali atau bila mengunjungi sebuah halaman lebih dari 30 menit setelah halaman terakhir tampil, ini akan tercatat sebagai kunjungan baru.", "ColumnPageBounceRateDocumentation": "Persentase kunjungan yang berawal pada halaman ini dan meninggalkan situs langsung.", @@ -97,6 +101,7 @@ "DateRange": "Rentang tanggal:", "DateRangeFrom": "Dari", "DateRangeFromTo": "Dari %s ke %s", + "DateRangeInPeriodList": "rentang tanggal", "DateRangeTo": "Kepada", "DaysHours": "%1$s hari %2$s jam", "DaysSinceFirstVisit": "Hari sejak kujungan pertama", @@ -112,6 +117,7 @@ "DisplaySimpleTable": "Tampilan tabel sederhana", "DisplayTableWithGoalMetrics": "Menampilkan tabel dengan pengukuran Tujuan", "DisplayTableWithMoreMetrics": "Menampilkan tabel dengan pengukuran lebih banyak", + "Documentation": "Dokumentasi", "Donate": "Menyumbang", "Done": "Selesai", "Download": "Unduh", @@ -127,9 +133,13 @@ "Edit": "Sunting", "EncryptedSmtpTransport": "Masukkan penyandian lapisan transpor yang dibutuhkan peladen SMTP.", "Error": "Galat", + "ErrorRequest": "Ups.. terdapat kesalahan selama melakukan permintaan. Mungkin peladen mendapat masalah sementara, atau mungkin Anda melakukan permintaan dengan data yang terlalu besar. Silakan coba kembali. Bila galat ini terjadi berulang, silakan %shubungi pengelola Piwik Anda%s untuk bantuan.", "EvolutionOverPeriod": "Perkembangan selama periode", "EvolutionSummaryGeneric": "%1$s di %2$s dibandingkan dengan %3$s di %4$s. Perubahan: %5$s", + "ExceptionContactSupportGeneric": "Bila Anda tetap mengalami kendala, silakan %shubungi pengelola Piwik Anda%s untuk bantuan.", + "ExceptionCheckUserHasSuperUserAccessOrIsTheUser": "Pengguna harus menjadi Pengguna Super atau pengguna '%s' itu sendiri.", "ExceptionConfigurationFileNotFound": "Berkas konfigurasi {%s} tak ditemukan.", + "ExceptionConfigurationFileNotFound2": "Bila berkas tersebut ada, harap memeriksa bahwa berkas %s dapat dibaca oleh pengguna '%s'.", "ExceptionDatabaseVersion": "Versi %1$s Anda adalah %2$s, tetapi Piwik membtuhkan setidaknya versi %3$s.", "ExceptionFileIntegrity": "Pemerikasaa intergritas gagal: %s", "ExceptionFilesizeMismatch": "Ukuran berkas tak sesuai: %1$s (diharapkan panjang: %2$s, ditemukan: %3$s)", @@ -342,12 +352,16 @@ "Weekly": "Mingguan", "WeeklyReport": "mingguan", "WeeklyReports": "Laporan mingguan", + "WellDone": "Selamat!", "Widgets": "Gawit", + "Widget": "Gawit", + "XFromY": "%1$s dari %2$s", "YearlyReport": "tahunan", "YearlyReports": "Laporan tahunan", "YearsDays": "%1$s tahun %2$s hari", "Yes": "Ya", "YouAreCurrentlyUsing": "Anda saat ini menggunakan Piwik %s.", + "YouAreViewingDemoShortMessage": "Anda sedang melihat demostrasi Piwik", "YouMustBeLoggedIn": "Anda harus masuk untuk mengakses fungsi ini.", "YourChangesHaveBeenSaved": "Perubahan Anda telah tersimpan." }, @@ -415,8 +429,11 @@ "ShowAll": "Tampilkan semua", "ShowLess": "Tampilkan sedikit", "StaticGraph": "Grafik Iktisar", + "TopVisitedWebsites": "Situs kunjungan tertinggi", + "TryIt": "Cobalah!", "UseSearchBarHint": "Hanya situs %s pertama ditampilkan di sini. Harap menggunakan batang pencarian untuk mengakses situs Anda yang lain.", "VerifyAccount": "Memverifikasi Akun", + "ValidateSslCertificate": "Pengesahan Sertifikat SSL", "VerifyLoginData": "Pastikan bahwa nama-id dan sandi Anda sesuai.", "YouAreOffline": "Maaf, Anda sekarang luring" }, diff --git a/lang/it.json b/lang/it.json index e6d25b85b5..a7c540aead 100644 --- a/lang/it.json +++ b/lang/it.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "Formato 12 ore", + "24HourClock": "Formato 24 ore", "AbandonedCarts": "Carrelli abbandonati", "AboutPiwikX": "Informazioni su Piwik %s", "Action": "Azione", @@ -88,8 +90,8 @@ "CurrentMonth": "Mese corrente", "CurrentWeek": "Settimana corrente", "CurrentYear": "Anno corrente", - "Daily": "Giornalmente", - "DailyReport": "giornalmente", + "Daily": "Giornaliero", + "DailyReport": "giornaliero", "DailyReports": "Reports giornalieri", "DailySum": "somma giornaliera", "DashboardForASpecificWebsite": "Dashboard per un sito specifico", @@ -262,8 +264,11 @@ "OperationIsNot": "Non è", "OperationLessThan": "Minore di", "OperationNotEquals": "Diversi", + "OperationStartsWith": "Inizia con", + "OperationEndsWith": "Finisce con", "OptionalSmtpPort": "Opzionale. Default a 25 per non cifrato e TLS SMTP, e 465 per SSL SMTP.", "Options": "Opzioni", + "Or": "o", "OrCancel": "oppure %s Annulla %s", "Others": "Altri", "Outlink": "Outlink (link esterno)", @@ -348,11 +353,15 @@ "TagCloud": "Tag Cloud", "Tax": "Tasse", "TimeAgo": "%s fa", + "TimeFormat": "Formato ora", "TimeOnPage": "Tempo nella pagina", "Total": "Totale", "TotalRatioTooltip": "Questo è %1$s di tuttti i %2$s %3$s.", "TotalRevenue": "Totale guadagni", "TotalVisitsPageviewsActionsRevenue": "(Totale: %s visite, %s pagine, %s azioni, %s ricavi)", + "TrackingScopeAction": "Azione", + "TrackingScopePage": "Pagina", + "TrackingScopeVisit": "Visita", "TransitionsRowActionTooltip": "Guarda cosa hanno fatto i visitatori prima e dopo aver visto questa pagina", "TransitionsRowActionTooltipTitle": "Apri Transizioni", "TranslatorName": "Alessandro Coscia, Giovdi, Yusef Maali, Andrea Marchitelli (CILEA), Fabrizio Rocca, Ted Mosby, Alfio E. Fresta, Giovanni Matina, Blau", @@ -382,7 +391,7 @@ "VisitTypeExample": "Ad esempio, per selezionare tutti i visitatori che sono ritornati al sito Web, compresi quelli che hanno comprato qualcosa nelle loro precedenti visite, la richiesta di API dovrebbe contenere %s", "Warning": "Attenzione", "WarningPhpVersionXIsTooOld": "La versione %s di PHP che stai utilizzando ha raggiunto il fine vita (EOL). Si invita caldamente ad aggiornare con urgenza all'ultima versione, perché l'uso di questa versione può esporti a vulnerabilità nella sicurezza e ai bug che sono stati corretti nelle versioni più recenti di PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik cesserà di supportare questa versione del PHP nel %s. Aggiorna la tua versione del PHP prima che sia troppo tardi!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik cesserà di supportare PHP %1$s a partire dalla prossima versione maggiore. Aggiorna il tuo PHP almeno alla versione %2$s prima che sia troppo tardi!", "WarningFileIntegrityNoManifest": "Il controllo dell'integrità dei file non può essere eseguito per la mancanza del file manifest.inc.php", "WarningFileIntegrityNoManifestDeployingFromGit": "Se stai sviluppando Piwik da Git, questo messaggio è normale.", "WarningFileIntegrityNoMd5file": "Il controllo d'integrità dei file non ha potuto essere completato per la mancanza della funzione md5_file().", @@ -397,7 +406,7 @@ "Widget": "Widget", "XComparedToY": "%1$s confrontato con %2$s", "XFromY": "%1$s da %2$s", - "YearlyReport": "annualmente", + "YearlyReport": "annuale", "YearlyReports": "Report annuali", "YearsDays": "%1$s anni %2$s giorni", "Yes": "Sì", diff --git a/lang/ja.json b/lang/ja.json index ec59bfa6d3..57afecc7e4 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "12 時間時計", + "24HourClock": "24 時間時計", "AbandonedCarts": "放棄された買い物かご", "AboutPiwikX": "Piwik %s について", "Action": "アクション", @@ -7,9 +9,11 @@ "Add": "追加", "AfterEntry": "ここに入った後", "All": "すべて", + "AllowPiwikArchivingToTriggerBrowser": "アーカイブレポートがブラウザで表示された時", "AllWebsitesDashboard": "全ウェブサイトのダッシュボード", "And": "そして", "API": "API", + "Apply": "適用", "ArchivingInlineHelp": "比較的トラフィックの高いウェブサイトでは、ブラウザでの表示をトリガーとする Piwik アーカイブ処理を無効にすることをお勧めします。 代わりに、1時間ごとに Piwik リポートを処理する cron ジョブをセットアップすることをお勧めします。", "ArchivingTriggerDescription": "比較的トラフィックの高いウェブサイトに推奨します。 自動リポート処理には %scron ジョブのセットアップ%sが必要となります。", "AuthenticationMethodSmtp": "SMTP の認証方法", @@ -30,6 +34,7 @@ "ClickHere": "詳細はこちらをクリック", "ClickToChangePeriod": "期間を変更するには、もう一度クリックしてください", "Close": "閉じる", + "ClickToSearch": "クリックして検索", "ColumnActionsPerVisit": "ビジット単位のアクション数", "ColumnActionsPerVisitDocumentation": "訪問中に実行された平均アクション数(ページビュー、ダウンロード、外部リンク)", "ColumnAverageGenerationTime": "生成時間の平均", @@ -96,6 +101,7 @@ "DateRange": "期間:", "DateRangeFrom": "From", "DateRangeFromTo": "%s から %s まで", + "DateRangeInPeriodList": "期間", "DateRangeTo": "To", "DaysHours": "%1$s 日 %2$s 時間", "DaysSinceFirstVisit": "最初の訪問からの日数", @@ -219,7 +225,7 @@ "MonthlyReports": "月次リポート", "More": "以上", "MoreDetails": "詳細", - "MoreLowerCase": "もっと", + "MoreLowerCase": "詳しく見る", "MultiSitesSummary": "全ウェブサイト", "Name": "名前", "NbActions": "アクション数", @@ -236,6 +242,7 @@ "NotDefined": "%s は定義されていません", "Note": "注意", "NotInstalled": "インストールされていません", + "NotRecommended": "非推奨", "NotValid": "%s は有効ではありません", "NumberOfVisits": "訪問数", "NUsers": "%s ユーザー", @@ -259,6 +266,7 @@ "OperationNotEquals": "等しくない", "OptionalSmtpPort": "オプションです。 非暗号化と TLS SMTP では 25 が、SSL SMTP では 465 がデフォルトになります。", "Options": "オプション", + "Or": "または", "OrCancel": "または %s キャンセル %s", "Others": "その他", "Outlink": "外部リンク", @@ -287,10 +295,13 @@ "Price": "価格", "ProductConversionRate": "製品のコンバージョン率", "ProductRevenue": "製品の収益", + "Measurable": "測定可能", + "Measurables": "測定可能", "PurchasedProducts": "購入された製品", "Quantity": "数量", "RangeReports": "カスタム期間", "ReadThisToLearnMore": "%1$sRead this to learn more.%2$s", + "Recommended": "推奨", "RecordsToPlot": "プロットするレコード", "Refresh": "リフレッシュ", "RefreshPage": "ページの再読み込み", @@ -301,6 +312,7 @@ "ReportGeneratedFrom": "このレポートは、%s のデータを使用して生成しました。", "ReportRatioTooltip": "'%1$s' は %5$s で %3$s %4$s の %2$s を表しています。", "Reports": "リポート", + "RearchiveTimeIntervalOnlyForTodayReports": "これは今日 ( あるいは今日を含む日付範囲 ) のレポートにのみ影響します。", "ReportsWillBeProcessedAtMostEveryHour": "従って、リポートは1時間ごとに処理されます。", "RequestTimedOut": "%s へのデータリクエストがタイムアウトしました。 再試行してください。", "Required": "%s は必須です", @@ -338,10 +350,15 @@ "TagCloud": "タグクラウド", "Tax": "税", "TimeAgo": "%s 前", + "TimeFormat": "時刻の形式", "TimeOnPage": "このページの滞在時間", "Total": "合計", "TotalRatioTooltip": "これは、全ての %2$s %3$s の %1$sです。", "TotalRevenue": "総収益", + "TotalVisitsPageviewsActionsRevenue": "( 合計 : ビジット数 %s 、ページビュー %s 、アクション %s 、利益 %s )", + "TrackingScopeAction": "アクション", + "TrackingScopePage": "ページ", + "TrackingScopeVisit": "ビジット", "TransitionsRowActionTooltip": "訪問者が、このページの表示前後に何をしていたか見てください", "TransitionsRowActionTooltipTitle": "トランジションを開く", "TranslatorName": "Takafumi\/Drupal Japan - http:\/\/drupal.jp Takeshi Ueda\/Piwik Japan Team - http:\/\/piwikjapan.org", @@ -367,10 +384,10 @@ "Visitors": "ビジター", "VisitsWith": "%s のビジット", "VisitorSettings": "ビジターの環境", + "VisitType": "ビジットタイプ", "VisitTypeExample": "例えば、前回のビジットで何かを購入した訪問者を含む、全てのリピーターを選択するためのAPIリクエストは %s を含むものとなります。", "Warning": "警告", "WarningPhpVersionXIsTooOld": "ご利用の PHP バージョン %s は、利用期間が終了( EOL )しています。セキュリティの脆弱性と、最新の PHP バージョンで既に修正されたバグに対するリスクを避けるため、必ず最新バージョンへアップグレードしてください。", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik はこの PHP バージョン %s でサポートを停止します。手遅れになる前に PHP のバージョンをアップグレード しましょう!", "WarningFileIntegrityNoManifest": "manifest.inc.php が不足しているため、ファイルの整合性チェックを実行することができませんでした。", "WarningFileIntegrityNoManifestDeployingFromGit": "Git から Piwik を開発している場合、このメッセージは正常です。", "WarningFileIntegrityNoMd5file": "md5_file() 関数が存在しないため、ファイルの整合性チェックを完了することができませんでした。", @@ -389,6 +406,7 @@ "YearsDays": "%1$s 年 %2$s 日", "Yes": "はい", "YouAreCurrentlyUsing": "現在Piwik %s を利用しています", + "YouAreViewingDemoShortMessage": "Piwik のデモを表示します。", "YouMustBeLoggedIn": "この機能にアクセスするにはログインする必要があります。", "YourChangesHaveBeenSaved": "変更は保存されました。" }, @@ -440,6 +458,7 @@ "NoWebsitesShort": "ウェブサイトなし", "PullDownToRefresh": "プルダウンして更新 …", "PossibleSslError": "SSL 証明エラーの可能性があります。", + "PossibleSslErrorExplanation": "証明書が無効もしくは自己署名であるためにエラーが発生しました : \" %s \"。SSL バリデーションを無視すればログインできるかもしれませんが、安全性が低いです。設定はいつでも変更できます。", "IgnoreSslError": "SSL エラーを無視", "RatingDontRemindMe": "リマインドしないでください。", "RatingNotNow": "今ではありません。", diff --git a/lang/ko.json b/lang/ko.json index 01234f4231..29cbaff91b 100644 --- a/lang/ko.json +++ b/lang/ko.json @@ -1,9 +1,11 @@ { "General": { + "12HourClock": "12 시간 형태", + "24HourClock": "24 시간 형태", "AbandonedCarts": "버려진 장바구니", "AboutPiwikX": "Piwik %s은...", - "Action": "동작", - "Actions": "활동", + "Action": "활동", + "Actions": "활동들", "Add": "추가", "AfterEntry": "여기에 들어간 후", "All": "모두", @@ -11,6 +13,7 @@ "AllWebsitesDashboard": "전체 웹사이트의 대시보드", "And": "그리고", "API": "API", + "Apply": "적용", "ArchivingInlineHelp": "비교적 트래픽이 높은 웹사이트는 브라우저 트리거를 Piwik에 보관하는 것을 비활성화하는 것이 좋습니다. 대신, 1 시간마다 Piwik 보고서를 처리하는 cron 작업을 설정하는 것이 좋습니다.", "ArchivingTriggerDescription": "비교적 트래픽이 높은 웹사이트에 권장합니다. 자동 보고서 처리는 %scron 작업 설정%s이 필요합니다.", "AuthenticationMethodSmtp": "SMTP 인증 방법", @@ -31,8 +34,10 @@ "ClickHere": "자세한 내용은 이곳을 클릭하세요.", "ClickToChangePeriod": "기간을 변경하려면 다시 클릭하세요.", "Close": "닫기", + "ClickToSearch": "검색하기 위해서 클릭해주세요.", "ColumnActionsPerVisit": "방문 당 활동 수", "ColumnActionsPerVisitDocumentation": "방문하는 동안 실행된 평균 작업 수 (페이지 뷰, 다운로드, 외부 링크)", + "ColumnAverageGenerationTime": "평균 페이지 생성 시간", "ColumnAverageTimeOnPage": "페이지에 머문 시간 평균", "ColumnAverageTimeOnPageDocumentation": "이 페이지를 방문한 방문자의 머문 시간의 평균 (페이지만 해당, 전체 웹사이트 아님).", "ColumnAvgTimeOnSite": "웹사이트 평균 시간", @@ -50,6 +55,7 @@ "ColumnExitRateDocumentation": "이 페이지를 본 후 사이트에서 나간 방문 비율입니다.", "ColumnExits": "이탈 수", "ColumnExitsDocumentation": "이 페이지에서 검색을 종료한 방문 수입니다.", + "ColumnGenerationTime": "생성 시간", "ColumnKeyword": "검색어", "ColumnLabel": "라벨", "ColumnMaxActions": "한번 방문에 최대 활동 수", @@ -83,6 +89,7 @@ "CurrentWeek": "이번주", "CurrentYear": "올해", "Daily": "일간", + "DailyReport": "일간", "DailyReports": "일간 보고서", "DailySum": "일별", "DashboardForASpecificWebsite": "특정 웹사이트의 대시보드", @@ -92,6 +99,7 @@ "DateRange": "기간:", "DateRangeFrom": "From", "DateRangeFromTo": "%s에서 %s까지", + "DateRangeInPeriodList": "기간", "DateRangeTo": "To", "DaysHours": "%1$s일 %2$s 시간", "DaysSinceFirstVisit": "첫 방문일", @@ -125,6 +133,7 @@ "Error": "에러", "EvolutionOverPeriod": "기간동안 진행 과정", "EvolutionSummaryGeneric": "%2$s의 %1$s와 %4$s의 %3$s 비교. 변화 추이: %5$s", + "ExceptionContactSupportGeneric": "만약 해당 문제가 계속 발생한다면 %sPiwik 관리자에게 연락하여 도움%s을 구하세요.", "ExceptionConfigurationFileNotFound": "설정파일 {%s}을 찾을 수 없습니다.", "ExceptionDatabaseVersion": "현재 %1$s 버전 %2$s 이지만 Piwik에는 적어도 %3$s가 필요합니다.", "ExceptionFileIntegrity": "무결성 검사 실패: %s", @@ -197,6 +206,7 @@ "MinutesSeconds": "%1$s분 %2$s초", "Mobile": "모바일", "Monthly": "월간", + "MonthlyReport": "월간", "MonthlyReports": "월간 보고서", "MoreDetails": "자세히", "MultiSitesSummary": "전체 웹사이트", @@ -220,11 +230,16 @@ "NumberOfVisits": "방문수", "NVisits": "%s회 방문", "Ok": "확인", + "OneAction": "1회 활동", "OneVisit": "1회 방문", "OnlyEnterIfRequired": "SMTP 서버가 사용자 이름을 필요로하는 경우에만 입력합니다.", "OnlyEnterIfRequiredPassword": "SMTP 서버가 암호를 필요로하는 경우에만 입력합니다.", "OnlyUsedIfUserPwdIsSet": "사용자 이름\/암호가 설정되어있는 경우에만 사용됩니다. 어떤 방법을 사용해야하는지 잘 모르는 경우, 공급자에게 문의하세요.", "OpenSourceWebAnalytics": "오픈 소스 웹 분석기", + "OperationAtLeast": "적어도", + "OperationAtMost": "많아야", + "OperationContains": "포함하기", + "OperationDoesNotContain": "포함하지 않기", "OptionalSmtpPort": "옵션입니다. 비 암호화 및 TLS SMTP는 25가 SSL SMTP는 465이 기본입니다.", "Options": "설정", "OrCancel": "또는 %s 취소 %s", @@ -235,11 +250,14 @@ "OverlayRowActionTooltipTitle": "페이지 중첩 열기", "Overview": "개요", "Pages": "페이지", + "Pagination": "%s - %s 의 %s", + "PaginationWithoutTotal": "%s - %s", "ParameterMustIntegerBetween": "%s 매개변수는 %s에서 %s까지의 정수로 값을 지정하세요.", "Password": "비밀번호", "Period": "기간", "Piechart": "파이차트", - "PiwikXIsAvailablePleaseUpdateNow": "Piwik %1$s 이 사용 가능합니다. %2$s 지금 업데이트하세요!%3$s (보기: %4$s 변경%5$s).", + "PiwikXIsAvailablePleaseNotifyPiwikAdmin": "%1$s가 가능합니다. %2$sPiwik 관리자%3$s에게 알려주세요.", + "PiwikXIsAvailablePleaseUpdateNow": "Piwik %1$s 이 사용 가능합니다. %2$s 지금 업데이트하세요!%3$s (보기: %4$s 변경사항%5$s).", "PleaseSpecifyValue": "'%s'의 값을 지정하세요.", "PleaseUpdatePiwik": "Piwik을 업데이트하세요", "Plugin": "플러그인", @@ -254,6 +272,7 @@ "PurchasedProducts": "구입한 제품", "Quantity": "수량", "RangeReports": "기간 지정", + "ReadThisToLearnMore": "%1$s자세히 알아보기%2$s", "Recommended": "권장", "RecordsToPlot": "구분된 레코드", "Refresh": "새로고침", @@ -265,6 +284,7 @@ "ReportGeneratedFrom": "이 보고서는 %s의 데이터를 사용하여 생성되었습니다.", "Reports": "보고서", "ReportsContainingTodayWillBeProcessedAtMostEvery": "보고서 아카이브를 몇 초에 한 번씩 수행", + "RearchiveTimeIntervalOnlyForTodayReports": "이것은 오늘의 보고서(혹은 오늘이 포함된 기간)에만 영향을 줍니다.", "ReportsWillBeProcessedAtMostEveryHour": "따라서, 보고서는 1시간마다 처리됩니다.", "RequestTimedOut": "%s에 데이터 요청이 초과되었습니다. 다시 시도하세요.", "Required": "%s 필요함", @@ -277,6 +297,7 @@ "Save": "저장", "SaveImageOnYourComputer": "이미지를 저장하려면 이미지를 오른쪽으로 클릭하여 \"다른 이름으로 그림 저장...\"을 선택합니다", "Search": "검색", + "SeeAll": "다 보기", "SeeTheOfficialDocumentationForMoreInformation": "자세한 내용은 %s 공식 문서 %s를 참조하세요.", "SelectYesIfYouWantToSendEmailsViaServer": "로컬 mail() 함수 대신 메일서버를 통해 이메일을 보낼 경우, \"예\"를 선택합니다", "Settings": "설정", @@ -292,9 +313,12 @@ "Source": "소스", "StatisticsAreNotRecorded": "Piwik의 방문자 추적이 현재 비활성화 되어 있습니다! 다시 추적을 활성화 하려면 config\/config.ini.php 파일을 열고 record_statistics = 1로 설정하세요.", "Subtotal": "소계", + "Summary": "요약", "Table": "테이블", "TagCloud": "태그 클라우드", "Tax": "세금", + "TimeAgo": "%s 전", + "TimeFormat": "시간 포멧", "TimeOnPage": "페이지에 머문 시간", "Total": "총", "TotalRevenue": "총 수익", @@ -305,11 +329,13 @@ "Unknown": "알수없음", "Upload": "업로드", "UsePlusMinusIconsDocumentation": "왼쪽의 더하기와 빼기 아이콘을 클릭하여 작업합니다.", + "UserId": "사용자 ID", "Username": "사용자 이름", "UseSMTPServerForEmail": "메일을 보낼 때 SMTP 서버 사용", "Value": "값", "VBarGraph": "수직 막대 그래프", "View": "보기", + "ViewDocumentationFor": "%1$s 관련 문서 보기", "Visit": "방문", "VisitConvertedGoal": "하나의 이상 목표에 도달한 방문", "VisitConvertedGoalId": "특정 목표ID에 도달한 방문", @@ -321,19 +347,26 @@ "Visitors": "방문", "VisitsWith": "%s의 방문", "VisitorSettings": "방문자 설정", + "VisitType": "방문 타입", "VisitTypeExample": "예를 들어, 이전 방문에서 무언가를 구입한 방문자를 포함한 모든 리피터를 선택하기 위한 API 요청 %s를 포함합니다.", "Warning": "경고", + "WarningPhpVersionXIsTooOld": "현재 사용하고 있는 PHP 버전 %s은 지원이 끝났습니다(End of Life, EOL). 최근에 업그레이드 된 PHP 버전에서 해결한 보안 문제나 버그 등이 사용하고 계신 버전에서 남아 있을 수 있으니 업그레이드 하시길 강력히 권장합니다.", "WarningFileIntegrityNoManifest": "manifest.inc.php가 없기 때문에, 파일의 무결성 검사를 수행할 수 없습니다.", + "WarningFileIntegrityNoManifestDeployingFromGit": "만약 Git으로부터 Piwik를 사용하고 있다면, 이 메세지는 정상입니다.", "WarningFileIntegrityNoMd5file": "md5_file() 함수가 존재하지 않기 때문에, 파일 무결성 검사를 완료할 수 없습니다.", "WarningPasswordStored": "%s 경고: %s 암호는 설정 파일에 저장됩니다. 이 설정 파일에 액세스 할 수있는 사람이라면, 패스워드를 볼 수 있을 가능성이 있다는 점에 유의하세요.", "Website": "웹사이트", "Weekly": "주간", + "WeeklyReport": "매주", "WeeklyReports": "주간 보고서", "Widgets": "위젯", + "Widget": "위젯", + "YearlyReport": "매년", "YearlyReports": "연간 보고서", "YearsDays": "%1$s년 %2$s일", "Yes": "예", "YouAreCurrentlyUsing": "현재 Piwik %s를 이용하고 있습니다.", + "YouAreViewingDemoShortMessage": "Piwik의 데모를 보고 있습니다.", "YouMustBeLoggedIn": "이 기능에 접근하려면 로그인해야합니다.", "YourChangesHaveBeenSaved": "변경 사항이 저장되었습니다." }, @@ -351,22 +384,30 @@ "ChooseHttpTimeout": "HTTP 시간 초과 값 선택", "ChooseMetric": "측정 항목 선택", "ChooseReport": "보고서 선택", + "ChooseSegment": "세그멘트 선택", "ConfirmRemoveAccount": "현재 계정을 삭제 하시겠습니까?", "DefaultReportDate": "보고서 날짜", + "EmailUs": "이메일 보내기", "EnableGraphsLabel": "그래프 표시", "EvolutionGraph": "이력 그래프", "HelpUsToImprovePiwikMobile": "Piwik 모바일에 익명의 사용 추적을 사용하시겠습니까?", + "HowtoDeleteAnAccount": "계정을 제거하기 위해서 길게 눌러주세요.", "HowtoDeleteAnAccountOniOS": "왼쪽에 오른쪽으로 끌어서 계정 삭제", + "HowtoLoginAnonymous": "익명 접속을 위해 사용자 이름과 비밀번호란을 비워두세요.", "HttpIsNotSecureWarning": "Piwik 승인을 위한 토큰 (token_auth) 발급시 'HTTP'를 사용하면 일반 텍스트로 전송됩니다. 그렇기 때문에 인터넷을 통한 데이터의 보안 전송을 위해 HTTPS를 사용하는 것을 추천합니다. 계속 진행 하시겠습니까?", "HttpTimeout": "HTTP 시간초과", + "IncompatiblePiwikVersion": "현재 사용하고 있는 Piwik 버전은 Piwik 모바일 2와 호환되지 않습니다. Piwik을 업데이트 후 다시 시도해주시거나 Piwik 모바일 1을 설치해주세요.", "LastUpdated": "최종 업데이트: %s", + "LoadingReport": "%s 로딩중", "LoginCredentials": "자격", "LoginUseHttps": "HTTPS 사용", "MultiChartLabel": "스파크라인 표시", "NavigationBack": "뒤로", "NetworkError": "네트워크 에러", "NetworkErrorWithStatusCode": "\"%s\" 오류가 발생했습니다. 요청에서 \"%s\" 상태가 반환되었습니다. URL은 \"%s\" 입니다. 입력한 URL을 확인해 보고 서버의 오류 로그에서 더 자세한 내용을 분석하여 이 문제를 해결하세요.", + "NetworkErrorWithStatusCodeShort": "네트워크 에러 %s", "NetworkNotReachable": "네트워크에 연결할 수 없음", + "NoAccountIsSelected": "하나의 계정을 선택하셔야 합니다. 만약 환경설정을 하지 않았다면 새로운 계정을 추가하세요.", "NoDataShort": "데이터 없음", "NoPiwikAccount": "Piwik 계정이 없나요?", "NoReportsShort": "보고서 없음", @@ -383,20 +424,23 @@ "RatingPleaseRateUs": "Piwik 모바일 앱은 무료 소프트웨어입니다. 여러분의 소중한 1분으로 %s에서 우리의 애플리케이션을 평가해 주신다면 정말 감사하겠습니다. 새로운 기능 제안이나 버그를 발견하셨다면, %s로 연락 부탁드려요", "ReleaseToRefresh": "놓아서 새로고침...", "Reloading": "새로 고침 중...", + "RequestTimedOutShort": "네트워크 타임 아웃 오류", "SaveSuccessError": "Piwik URL 또는 사용자이름과 비밀번호 조합이 잘못되었습니다.", "SearchWebsite": "검색 웹사이트", "ShowAll": "모두 보기", "ShowLess": "작게 보기", "StaticGraph": "그래프 개요", + "TopVisitedWebsites": "상위 방문된 웹사이트들", "TryIt": "시도해 보세요!", "UseSearchBarHint": "첫 번째 %s의 웹사이트가 여기에 표시됩니다. 다른 웹사이트에 액세스 할 수있는 검색막대를 사용하세요.", "VerifyAccount": "계정 확인", + "ValidateSslCertificate": "SSL 증서 확인", "VerifyLoginData": "사용자 이름과 비밀번호 조합이 올바른지 확인하세요.", "YouAreOffline": "현재 오프라인 상태" }, "RowEvolution": { "AvailableMetrics": "사용가능한 통계", - "CompareDocumentation": "아래 링크를 클릭하고 동일한 테일블내에 있는 다른 행의 팝업을 열어 기록을 비교할 수 있습ㄴ다.<br \/>팝업을 열지 않고 행을 비교 표시하려면 시프트-클릭하세요.", + "CompareDocumentation": "아래 링크를 클릭하고 동일한 테이블내에 있는 다른 행의 팝업을 열어 기록을 비교할 수 있습니다.<br \/>팝업을 열지 않고 행을 비교 표시하려면 쉬프트-클릭하세요.", "CompareRows": "기록 비교", "ComparingRecords": "%s 행 비교", "Documentation": "큰 진화 그래프에서 표시할 통계를 클릭합니다. 쉬프트-클릭을 사용하여 한번에 여러개의 측정 항목을 표시합니다.", diff --git a/lang/nb.json b/lang/nb.json index b07c46616f..11f1f9e3cb 100644 --- a/lang/nb.json +++ b/lang/nb.json @@ -1,17 +1,20 @@ { "General": { + "12HourClock": "12-timersklokke", + "24HourClock": "24-timersklokke", "AbandonedCarts": "Forlatte handlevogner", "AboutPiwikX": "Om Piwik %s", "Action": "Handling", "Actions": "Handlinger", "Add": "Legg til", - "AfterEntry": "etter at man har skrevet inn her", + "AfterEntry": "etter å ha skrevet her", "All": "Alle", - "AllowPiwikArchivingToTriggerBrowser": "Arkiver rapporter når vist fra nettleseren", + "AllowPiwikArchivingToTriggerBrowser": "Arkiver rapporter når de vises i nettleseren", "AllWebsitesDashboard": "Oversiktspanel for alle nettsteder", "And": "og", "API": "API", - "ArchivingInlineHelp": "For nettsteder med medium til høy trafikk, er det anbefalt å slå av at nettleseren kan utløse Piwik-arkivering. I stedet anbefaler vi at du setter opp en cron-jobb som prosesserer Piwik-rapporter hver time.", + "Apply": "Bruk", + "ArchivingInlineHelp": "For nettsteder med medium til høy trafikk, er det anbefalt å arkivering av rapporter når de vises i nettleseren. I stedet anbefaler vi at du setter opp en cron-jobb som prosesserer Piwik-rapporter hver time.", "ArchivingTriggerDescription": "For større Piwik-installasjoner anbefales det at du setter opp en %scron-jobb%s for å behandle rapporter automatisk.", "AuthenticationMethodSmtp": "Autentiseringsmetode for SMTP", "AverageOrderValue": "Gjennomsnittlig ordrepris", @@ -19,11 +22,11 @@ "AverageQuantity": "Gjennomsnittlig antall", "BackToPiwik": "Tilbake til Piwik", "Broken": "Oppdelt", - "BrokenDownReportDocumentation": "Den er delt opp i forskjellige rapporter som er vist i sparklines på bunnen av side. Du kan forstørre grafene ved å klikke på den rapporten du ønsker å se.", + "BrokenDownReportDocumentation": "Den er delt opp i forskjellige rapporter som vises i minigrafer (sparklines) på bunnen av siden. Du kan forstørre grafene ved å klikke på den rapporten du ønsker å se.", "Cancel": "Avbryt", "CannotUnzipFile": "Kan ikke pakke opp filen %1$s: %2$s", "ChangePassword": "Endre passord", - "ChangeTagCloudView": "Legg merke til at du kan se på rapporten på andre måter enn som en merkelappsky. Bruk kontrollene på bunnen av rapporten for å gjøre det.", + "ChangeTagCloudView": "Legg merke til at du kan se på rapporten på andre måter enn som en stikkordsky. Bruk kontrollene på bunnen av rapporten for å gjøre det.", "ChooseDate": "Velg dato", "ChooseLanguage": "Velg språk", "ChoosePeriod": "Velg periode", @@ -32,23 +35,23 @@ "ClickToChangePeriod": "Klikk igjen for å endre periode.", "Close": "Lukk", "ClickToSearch": "Klikk for å søke", - "ColumnActionsPerVisit": "Handlinger pr. besøk", + "ColumnActionsPerVisit": "Handlinger per besøk", "ColumnActionsPerVisitDocumentation": "Det gjennomsnittlige antallet handlinger (sidevisninger, nettstedsøk, nedlastinger eller utlenker) som ble gjennomført i løpet av besøkene.", "ColumnAverageGenerationTime": "Gj.snitt. genereringstid", - "ColumnAverageGenerationTimeDocumentation": "Den gjennomsnittlige tiden det tok å generere siden. Dette måltallet inkludere tiden det tok tjeneren å generere siden, pluss tiden det den besøkende å laste ned svar fra tjeneren. Et lavere gjennomsnitt betyr et raskere nettsted for dine besøkende!", - "ColumnAverageTimeOnPage": "Gj. tid på side", + "ColumnAverageGenerationTimeDocumentation": "Den gjennomsnittlige tiden det tok å generere siden. Dette måltallet inkluderer tiden det tok tjeneren å generere siden, pluss tiden det den besøkende å laste ned svar fra tjeneren. Et lavere gjennomsnitt betyr et raskere nettsted for dine besøkende!", + "ColumnAverageTimeOnPage": "Gj.snitt. tid på side", "ColumnAverageTimeOnPageDocumentation": "Den gjennomsnittlige tiden besøkende tilbrakte på denne siden (bare siden, ikke hele nettstedet).", - "ColumnAvgTimeOnSite": "Gj. tid på nettstedet", + "ColumnAvgTimeOnSite": "Gj.snitt. tid på nettstedet", "ColumnAvgTimeOnSiteDocumentation": "Gjennomsnittlig varighet for et besøk.", "ColumnBounceRate": "Sprettfrekvens", - "ColumnBounceRateDocumentation": "Prosentandelen av besøk som hadde bare en sidevisning. Dette betyr at den besøkende forlot nettstedet direkte fra inngangssiden.", + "ColumnBounceRateDocumentation": "Prosentandelen av besøk som bare hadde én sidevisning. Dette betyr at den besøkende forlot nettstedet direkte fra inngangssiden.", "ColumnBounces": "Sprett", "ColumnBouncesDocumentation": "Prosentandel av besøk som startet og sluttet på denne siden. Dette betyr at den besøkende forlot nettstedet etter kun å ha sett denne siden.", - "ColumnConversionRate": "Konvertingsrate", + "ColumnConversionRate": "Konverteringsrate", "ColumnConversionRateDocumentation": "Prosentandelen av besøk som utløste målkonvertering.", "ColumnDestinationPage": "Destinasjonsside", "ColumnEntrances": "Innganger", - "ColumnEntrancesDocumentation": "Antallet besøk som startet på denne siden.", + "ColumnEntrancesDocumentation": "Antall besøk som startet på denne siden.", "ColumnExitRate": "Utgangsfrekvens", "ColumnExitRateDocumentation": "Prosentandelen av besøk som forlot nettstedet etter å ha sett på denne siden.", "ColumnExits": "Utganger", @@ -62,7 +65,7 @@ "ColumnNbUniqVisitors": "Unike besøkende", "ColumnNbUniqVisitorsDocumentation": "Antallet unike besøkende som kommer til nettstedet ditt. Hver bruker blir telt bare en gang selv om han besøker nettstedet flere ganger om dagen.", "ColumnNbUsers": "Brukere", - "ColumnNbUsersDocumentation": "Antallet bruker logget inn på nettstedet ditt. Det er antallet unike aktive brukere som har en bruker-ID satt (via sporsingskodefunksjonen «setUserId»).", + "ColumnNbUsersDocumentation": "Antall brukere som er logget inn på nettstedet ditt. Det er antallet unike aktive brukere som har en bruker-ID satt (via sporsingskodefunksjonen «setUserId»).", "ColumnNbVisits": "Besøk", "ColumnNbVisitsDocumentation": "Hvis en besøkende kommer til nettstedet ditt for første gang eller han besøker nettstedet mer enn 30 minutter etter hans siste sidevisning, så vil det bli registrert som et nytt besøk.", "ColumnPageBounceRateDocumentation": "Prosentandelen av besøk som startet på denne siden og forlot nettstedet med en gang.", @@ -75,7 +78,7 @@ "ColumnUniqueEntrances": "Unike innganger", "ColumnUniqueExits": "Unike utganger", "ColumnUniquePageviews": "Unike sidevisninger", - "ColumnUniquePageviewsDocumentation": "Antallet besøk som inkluderte denne sida. Hvis ei side ble vist flere ganger i løpet av et besøk, telles det bare som en gang.", + "ColumnUniquePageviewsDocumentation": "Antall besøk som inkluderte denne siden. Hvis en side ble vist flere ganger i løpet av et besøk, telles det bare som en gang.", "ColumnValuePerVisit": "Verdi per besøk", "ColumnViewedAfterSearch": "Klikket i søkeresultat", "ColumnViewedAfterSearchDocumentation": "Antallet ganger denne siden var besøkt etter at en besøkende gjorde et søk på nettstedet ditt og klikket på denne siden i søkeresultatene.", @@ -93,11 +96,12 @@ "DailySum": "daglig sum", "DashboardForASpecificWebsite": "Oversiktspanel for et bestemt nettsted", "DataForThisGraphHasBeenPurged": "Dataene for denne grafen er mer enn %s måneder gamle og har blitt fjernet.", - "DataForThisTagCloudHasBeenPurged": "Dataene for denne merkelappskyen er mer enn %s måneder gamle og har blitt fjernet.", + "DataForThisTagCloudHasBeenPurged": "Dataene for denne stikkordskyen er mer enn %s måneder gamle og har blitt fjernet.", "Date": "Dato", "DateRange": "Datointervall:", "DateRangeFrom": "Fra", "DateRangeFromTo": "Fra %s til %s", + "DateRangeInPeriodList": "datointervall", "DateRangeTo": "Til", "DaysHours": "%1$s dager %2$s timer", "DaysSinceFirstVisit": "Dager siden første besøk", @@ -112,30 +116,30 @@ "Discount": "Rabatt", "DisplaySimpleTable": "Vis enkel tabell", "DisplayTableWithGoalMetrics": "Vis en tabell med måltall for mål", - "DisplayTableWithMoreMetrics": "Vis en tabell mer flere måltall", + "DisplayTableWithMoreMetrics": "Vis en tabell med måltall for besøkeres engasjement", "Documentation": "Dokumentasjon", "Donate": "Doner", "Done": "Ferdig", "Download": "Last ned", "DownloadFail_FileExists": "Filen %s finnes allerede!", - "DownloadFail_FileExistsContinue": "Prøver å fortsette nedlastingen for %s, men en komplett nedlastet fil eksisterer allerede.", - "DownloadFail_HttpRequestFail": "Klarte ikke laste ned fila. Noe kan være galt med nettstedet du laster ned fra. Du kan prøve igjen seinere eller laste ned fila på egenhånd.", + "DownloadFail_FileExistsContinue": "Prøver å fortsette nedlastingen for %s, men en komplett nedlastet fil eksisterer allerede!", + "DownloadFail_HttpRequestFail": "Klarte ikke laste ned fila. Noe kan være galt med nettstedet du laster ned fra. Du kan prøve igjen senere eller laste ned filen på egenhånd.", "DownloadFullVersion": "%1$sLast ned%2$s den fullstendige utgaven! Prøv %3$s", "DownloadPleaseRemoveExisting": "Hvis du vil at den skal erstattes, må du fjerne den eksisterende filen.", "Downloads": "Nedlastinger", "EcommerceOrders": "E-handelordre", "EcommerceVisitStatusDesc": "Besøk e-handelstatus etter slutten av besøket", - "EcommerceVisitStatusEg": "For eksempel, for å velge alle besøk som har lagt inn en e-handelordre, så vil API-forespørselen inneholde %s.", - "Edit": "Endre", + "EcommerceVisitStatusEg": "For eksempel: for å velge alle besøk som har lagt inn en e-handelordre, så vil API-forespørselen inneholde %s.", + "Edit": "Rediger", "EncryptedSmtpTransport": "Fyll inn transportlagkrypteringen som kreves av din SMTP-tjener.", "Error": "Feil", - "ErrorRequest": "Obs ... Det oppstod et problem under forespørselen. Kanskje tjeneren hadde en midlertidig feil eller kanskje du ba om en rapport med for mye data. Prøv igjen. Hvis feilen oppstår gjentatte ganger, %skontakt din Piwik-administrator%s for assistanse.", - "EvolutionOverPeriod": "Utvikling over perioden", + "ErrorRequest": "Oi... Det oppstod et problem under forespørselen. Kanskje tjeneren hadde en midlertidig feil eller kanskje du ba om en rapport med for mye data. Prøv igjen. Hvis feilen oppstår gjentatte ganger, %skontakt din Piwik-administrator%s for assistanse.", + "EvolutionOverPeriod": "Utvikling i perioden", "EvolutionSummaryGeneric": "%1$s i %2$s sammenlignet med %3$s i %4$s. Utvikling: %5$s", "ExceptionContactSupportGeneric": "Hvis du fortsatt har dette problemet, %skontakt din Piwik-administrator%s for assistanse.", "ExceptionCheckUserHasSuperUserAccessOrIsTheUser": "Bruker må enten være en superbruker eller brukeren «%s» selv.", - "ExceptionConfigurationFileNotFound": "Klarte ikke finne konfigurasjonsfila {%s}.", - "ExceptionConfigurationFileNotFound2": "Hvis filen eksisterer, vennligst sjekk at %s er lesbar for brukeren '%s'.", + "ExceptionConfigurationFileNotFound": "Klarte ikke finne konfigurasjonsfilen {%s}.", + "ExceptionConfigurationFileNotFound2": "Hvis filen eksisterer, vennligst sjekk at %s er lesbar for brukeren «%s».", "ExceptionDatabaseVersion": "Din %1$s versjon er %2$s, men Piwik krever minst %3$s.", "ExceptionDatabaseVersionNewerThanCodebase": "Din Piwik-kodebase kjører den gamle versjonen %1$s, og vi har oppdaget at din Piwik-database allerede har blitt oppgradert til den nyere versjonen %2$s.", "ExceptionDatabaseVersionNewerThanCodebaseWait": "Kanskje dine Piwik-administratorer holder på med å fullføre oppgraderingsprosessen. Prøv igjen om noen få minutter.", @@ -143,6 +147,7 @@ "ExceptionFilesizeMismatch": "Filstørrelse stemmer ikke: %1$s (forventet lengde: %2$s, fant: %3$s)", "ExceptionIncompatibleClientServerVersions": "Din %1$s klientversjon er %2$s som ikke passer med tjenerversjonen %3$s.", "ExceptionInvalidAggregateReportsFormat": "Format «%s» for aggregerte rapporter er ikke gyldig. Prøv en av følgende istedenfor: %s.", + "ExceptionInvalidArchiveTimeToLive": "Arkiveringsintervallet for i dag må være et antall sekunder som er større enn null", "ExceptionInvalidDateFormat": "Datoformatet må være %s eller et nøkkelord støttet av %s-funksjonen (se %s for mer informasjon).", "ExceptionInvalidDateRange": "Datoen «%s» er ikke et gyldig datointervall. Riktig format er: %s", "ExceptionInvalidPeriod": "Perioden «%s» er ikke støttet. Prøv en av følgende istedenfor: %s", @@ -150,24 +155,27 @@ "ExceptionInvalidReportRendererFormat": "Rapportformat «%s» er ikke gyldig. Prøv en av følgende istedenfor: %s.", "ExceptionInvalidStaticGraphType": "Statisk graftype «%s» er ikke gyldig. Prøv en av følgende istedenfor: %s.", "ExceptionInvalidToken": "Token er ikke gyldig.", - "ExceptionLanguageFileNotFound": "Fant ikke språkfila «%s».", + "ExceptionLanguageFileNotFound": "Fant ikke språkfilen «%s».", "ExceptionMethodNotFound": "Metoden «%s» eksisterer ikke eller er ikke tilgjengelig i modulen «%s».", "ExceptionMissingFile": "Mangler fil: %s", - "ExceptionNonceMismatch": "Klarte ikke verifiserer sikkerhetstoken i dette skjemaet.", + "ExceptionNonceMismatch": "Klarte ikke å verifisere sikkerhetstoken i dette skjemaet.", "ExceptionPrivilege": "Du har ikke tilgang til denne ressursen siden den krever «%s»-tilgang.", "ExceptionPrivilegeAccessWebsite": "Du har ikke tilgang til denne ressursen siden den krever «%s»-tilgang for nettstedet med id = %d.", "ExceptionPrivilegeAtLeastOneWebsite": "Du har ikke tilgang til denne ressursen siden den krever «%s»-tilgang for minst ett av nettstedene.", - "ExceptionUnableToStartSession": "Klarte ikke starte økt.", - "ExceptionUndeletableFile": "Klarte ikke slette %s", - "ExceptionUnreadableFileDisabledMethod": "Klarte ikke lese konfigurasjonsfila {%s}. Verten din kan ha deaktivert %s.", - "ExceptionReportNotFound": "Den etterspurte rapporten fins ikke.", - "ExceptionWidgetNotFound": "Det etterspurte elementet fins ikke.", + "ExceptionUnableToStartSession": "Klarte ikke å starte økt.", + "ExceptionUndeletableFile": "Klarte ikke å slette %s", + "ExceptionUnreadableFileDisabledMethod": "Klarte ikke å lese konfigurasjonsfilen {%s}. Verten din kan ha deaktivert %s.", + "ExceptionReportNotFound": "Den etterspurte rapporten finnes ikke.", + "ExceptionWidgetNotFound": "Den etterspurte widgeten finnes ikke.", + "ExceptionReportNotEnabled": "Den etterspurte rapporten er ikke aktivert. Dette betyr vanligvis at enten er utvidelsen som definerer rapporten deaktivert, eller så har du ikke nok rettigheter til å vise rapporten.", + "ExpandDataTableFooter": "Endre visualiseringen eller konfigurer rapporten", "Export": "Eksporter", "ExportAsImage": "Eksporter som bilde", - "ExportThisReport": "Eksporter dette datasett i andre format", + "ExportThisReport": "Eksporter dette datasettet i andre formater", "Faq": "FAQ", "FileIntegrityWarningExplanation": "Integritetskontrollen av filer oppdaget noen feil. Dette skyldes mest sannsynlig feil under opplasting av Piwik sine filer. Du bør laste opp alle Piwik sine filer på nytt i BINARY-modus og oppfriske siden inntil ingen feil vises.", "First": "Først", + "Flatten": "Gjør flat", "ForExampleShort": "f.eks.", "Forums": "Forum", "FromReferrer": "fra", @@ -180,9 +188,10 @@ "GraphHelp": "Mer informasjon om visning av grafikk i Piwik", "HelloUser": "Hallo, %s!", "Help": "Hjelp", + "HelpTranslatePiwik": "Kanskje du vil %1$shjelpe oss å forbedre Piwik-oversettelser%2$s?", "Hide": "skjul", "HoursMinutes": "%1$s timer %2$s min", - "Id": "Id", + "Id": "ID", "IfArchivingIsFastYouCanSetupCronRunMoreOften": "Forutsatt arkivering er raskt for ditt oppsett, kan du sette opp crontab til å kjøre oftere.", "InfoFor": "Informasjon for %s", "Installed": "Installert", @@ -195,20 +204,20 @@ "LastDaysShort": "Siste %s dager", "LearnMore": "%1$slær mer%2$s", "Live": "Live", - "Loading": "Laster ...", - "LoadingData": "Laster data ...", + "Loading": "Laster...", + "LoadingData": "Laster data...", "LoadingPopover": "Laster %s...", "LoadingPopoverFor": "Laster %s for", "Locale": "nb_NO.UTF-8", "Logout": "Logg ut", - "MainMetrics": "Hoved måltall", + "MainMetrics": "Hovedmåltall", "Matches": "Treff", - "MediumToHighTrafficItIsRecommendedTo": "For nettsteder med medium til høy trafikk, er det anbefalt å prosesserer dagens rapporter ikke oftere enn hver halvtime (%s sekund) eller hver time (%s sekund).", + "MediumToHighTrafficItIsRecommendedTo": "For nettsteder med medium til høy trafikk, er det anbefalt å ikke prosessere dagens rapporter oftere enn hver halvtime (%s sekunder) eller hver time (%s sekunder).", "Metadata": "Metadata", "Metric": "Måltall", "Metrics": "Måltall", - "MetricsToPlot": "Måltall å tegne", - "MetricToPlot": "Måltall å tegne", + "MetricsToPlot": "Måltall som skal vises", + "MetricToPlot": "Måltall som skal vises", "MinutesSeconds": "%1$s min %2$ss", "Mobile": "Mobil", "Monthly": "Månedlig", @@ -224,13 +233,13 @@ "Never": "Aldri", "NewReportsWillBeProcessedByCron": "Hvis Piwik-arkivering ikke blir utløst av nettleseren, så vil nye rapporter bli prosessert ved hjelp av crontab.", "NewUpdatePiwikX": "Ny utgave: Piwik %s", - "NewVisitor": "Ny besøk", + "NewVisitor": "Ny besøker", "NewVisits": "Nye besøk", "Next": "Neste", "No": "Nei", "NoDataForGraph": "Ingen data for denne grafen.", - "NoDataForTagCloud": "Ingen data for denne merkelappskyen.", - "NotDefined": "%s ikke definert", + "NoDataForTagCloud": "Ingen data for denne stikkordskyen.", + "NotDefined": "%s er ikke definert", "Note": "Notat", "NotInstalled": "Ikke installert", "NotRecommended": "ikke anbefalt", @@ -243,8 +252,8 @@ "OneVisit": "1 besøk", "OnlyEnterIfRequired": "Skriv bare inn et brukernavn hvis din SMTP-tjener krever det", "OnlyEnterIfRequiredPassword": "Skriv bare inn et passord hvis din SMTP-tjener krever det", - "OnlyUsedIfUserPwdIsSet": "Brukes kun hvis et brukernavn\/passord er satt, spør din leverandør om du er usikker på hvilken metode som skal brukes.", - "OpenSourceWebAnalytics": "Åpen kildekode nettstatistikk", + "OnlyUsedIfUserPwdIsSet": "Brukes kun hvis et brukernavn\/passord er satt. Spør din leverandør om du er usikker på hvilken metode som skal brukes.", + "OpenSourceWebAnalytics": "Nettstatistikk med åpen kildekode", "OperationAtLeast": "Minst", "OperationAtMost": "Høyst", "OperationContains": "Inneholder", @@ -259,30 +268,40 @@ "Options": "Alternativer", "OrCancel": "eller %s avbryt %s", "Others": "Andre", + "Outlink": "Utlenke", "Outlinks": "Utlenker", + "OverlayRowActionTooltip": "Se analysedata direkte på ditt nettsted (åpner en ny fane)", + "OverlayRowActionTooltipTitle": "Åpne sideoverlegg", "Overview": "Oversikt", "Pages": "Sider", - "Pagination": "%s - %s av %s", - "PaginationWithoutTotal": "%s - %s", + "Pagination": "%s – %s av %s", + "PaginationWithoutTotal": "%s – %s", "ParameterMustIntegerBetween": "Parameteret %s må være et heltall mellom %s og %s.", "Password": "Passord", "Period": "Periode", "Piechart": "Kakediagram", + "PiwikIsACollaborativeProjectYouCanContributeAndDonate": "%1$sPiwik%2$s er et samarbeidsprosjekt mellom %7$sPiwik-teamets%8$s medlemmer og mange andre bidragsytere over hele verden. Hvis du liker Piwik, kan du hjelpe oss: finn ut %3$shvordan delta i Piwik%4$s eller %5$sdoner nå%6$s for å støtte utviklingen av Piwik 3.0!", "PiwikXIsAvailablePleaseNotifyPiwikAdmin": "%1$s er tilgjengelig. Vennligst gi beskjed til %2$sPiwik administrator%3$s.", "PiwikXIsAvailablePleaseUpdateNow": "Piwik %1$s er tilgjengelig. %2$s Oppdater nå!%3$s (se %4$s endringer%5$s).", "PleaseSpecifyValue": "Oppgi en verdi for «%s».", - "PleaseUpdatePiwik": "Oppdatering din Piwik", - "Plugin": "Tillegg", - "Plugins": "Tillegg", + "PleaseUpdatePiwik": "Vennligst oppdater din Piwik", + "Plugin": "Utvidelse", + "Plugins": "Utvidelser", "PoweredBy": "Drevet av", "Previous": "Forrige", - "PreviousDays": "Tidligere %s dager (ikke inkludert i dag)", + "PreviousDays": "Forrige %s dager (ikke inkludert i dag)", "PreviousDaysShort": "Forrige %s dager", "Price": "Pris", + "ProductConversionRate": "Produktets konverteringsrate", + "ProductRevenue": "Produktets inntjening", + "Measurable": "Målbart", + "Measurables": "Målbare", "PurchasedProducts": "Kjøpte produkter", "Quantity": "Antall", + "RangeReports": "Tilpassede datointervaller", "ReadThisToLearnMore": "%1$sLes dette for å lære mer.%2$s", "Recommended": "Anbefalt", + "RecordsToPlot": "Oppføringer som skal vises", "Refresh": "Oppdater", "RefreshPage": "Last siden på nytt", "RelatedReport": "Relatert rapport", @@ -290,48 +309,60 @@ "Remove": "Fjern", "Report": "Rapport", "ReportGeneratedFrom": "Denne rapporten ble generert ved hjelp av data fra %s.", - "ReportRatioTooltip": "'%1$s' representerer %2$s av %3$s %4$s med %5$s.", + "ReportRatioTooltip": "«%1$s» representerer %2$s av %3$s %4$s med %5$s.", "Reports": "Rapporter", + "ReportsContainingTodayWillBeProcessedAtMostEvery": "Arkiver rapporter som mest hvert X sekund", + "RearchiveTimeIntervalOnlyForTodayReports": "Dette påvirker kun rapporter for i dag (eller andre datointervaller som inkluderer i dag)", + "ReportsWillBeProcessedAtMostEveryHour": "Rapporter vil derfor prosesseres som mest hver time.", "RequestTimedOut": "En dataforespørsel til %s fikk tidsavbrudd. Prøv igjen.", "Required": "%s påkrevd", "ReturningVisitor": "Tilbakevendende besøkende", "ReturningVisitorAllVisits": "Vis alle besøk", + "RowEvolutionRowActionTooltip": "Se hvordan måltallet for raden endret seg over tid", + "RowEvolutionRowActionTooltipTitle": "Åpne radutvikling", "Rows": "Rader", - "RowsToDisplay": "Rader for visning", + "RowsToDisplay": "Rader som skal vises", "Save": "Lagre", - "SaveImageOnYourComputer": "For å lagre bildet på din datamaskin, høyreklikk på bildet og velg \"Lagre bilde som ...\"", + "SaveImageOnYourComputer": "For å lagre bildet på din datamaskin, høyreklikk på bildet og velg «Lagre bilde som...»", "Search": "Søk", "Clear": "Tøm", "SearchNoResults": "Ingen resultater", "SeeAll": "se alle", "SeeTheOfficialDocumentationForMoreInformation": "Se %sden offisielle dokumentasjonen%s for mer informasjon.", - "SeeThisFaq": "Se %1$sdenne FAQ'en%2$s.", + "SeeThisFaq": "Se %1$sdenne FAQ-en%2$s.", "Segment": "Segment", "SelectYesIfYouWantToSendEmailsViaServer": "Velg «Ja» hvis du vil sende e-post med en navngitt tjener istedenfor den lokale mail-funksjonen", "Settings": "Innstillinger", "Shipping": "Frakt", "Show": "vis", - "SmallTrafficYouCanLeaveDefault": "For nettsteder med liten trafikk, så kan du beholde standardvalget %s sekund og ha tilgang til alle rapporter i sanntid.", + "SingleWebsitesDashboard": "Oversiktspanel for ett nettsted", + "SmallTrafficYouCanLeaveDefault": "På nettsteder med lite trafikk kan du beholde standardvalget %s sekunder og ha tilgang til alle rapporter i sanntid.", "SmtpEncryption": "SMTP-kryptering", "SmtpPassword": "SMTP-passord", "SmtpPort": "SMTP-port", "SmtpServerAddress": "SMTP-tjeneradresse", "SmtpUsername": "SMTP-brukernavn", "Source": "Kilde", + "StatisticsAreNotRecorded": "Piwik besøkssporing er deaktivert! Aktiver sporing igjen ved å sette record_statistics = 1 i din config\/config.ini.php-fil.", "Subtotal": "Delsum", "Summary": "Sammendrag", "Table": "Tabell", - "TagCloud": "Merkelappsky", + "TagCloud": "Stikkordsky", "Tax": "Skatt", "TimeAgo": "%s siden", + "TimeFormat": "Tidsformat", "TimeOnPage": "Tid på side", "Total": "Totalt", "TotalRatioTooltip": "Dette er %1$s av alle %2$s %3$s.", + "TotalRevenue": "Total inntjening", "TotalVisitsPageviewsActionsRevenue": "(Totalt: %s besøk, %s sidevisninger, %s handlinger, %s inntekter)", + "TransitionsRowActionTooltip": "Se hva besøkende gjorde før og etter å ha sett denne siden", + "TransitionsRowActionTooltipTitle": "Åpne overganger", "TranslatorName": "Hans Fredrik Nordhaug", "UniquePurchases": "Unike kjøp", "Unknown": "Ukjent", "Upload": "Last opp", + "UsePlusMinusIconsDocumentation": "Bruk pluss- og minus-ikonene til venstre for å navigere.", "UserId": "Bruker-ID", "Username": "Brukernavn", "UseSMTPServerForEmail": "Bruk SMTP-tjener for e-post", @@ -340,25 +371,32 @@ "View": "Vis", "ViewDocumentationFor": "Vis dokumentasjon for %1$s", "Visit": "Besøk", + "VisitConvertedGoal": "Besøk som er konvertert til minst ett mål", + "VisitConvertedGoalId": "Besøk konvertert til et spesifikt mål-ID", "VisitConvertedNGoals": "Besøk konverterte %s mål", - "VisitDuration": "Gj. besøksvarighet (i sekunder)", - "VisitorID": "Besøks-ID", - "VisitorIP": "IP for besøkende", - "Visitors": "Besøkende", + "VisitDuration": "Gj.snitt. besøksvarighet (i sekunder)", + "Visitor": "Besøker", + "VisitorID": "Besøker-ID", + "VisitorIP": "IP for besøker", + "Visitors": "Besøkere", "VisitsWith": "Besøk med %s", - "VisitorSettings": "Besøkendes innstillinger", + "VisitorSettings": "Besøkers innstillinger", "VisitType": "Besøkstype", + "VisitTypeExample": "For eksempel: for å velge alle besøkere som har returnert til nettstedet, inkludert de som har kjøpt noe i deres tidligere besøk, må API-forespørselen inneholde %s", "Warning": "Advarsel", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik kutter støtte for denne PHP-versjonen i %s. Oppgrader din PHP-versjon før det er for sent.", - "WarningFileIntegrityNoManifest": "Klarte ikke utføre integritetskontroll av filer fordi manifest.inc.php mangler.", + "WarningPhpVersionXIsTooOld": "PHP-versjonen du bruker (%s) er utdatert. Du bør oppgradere til en støttet versjon, siden den versjonen du bruker kan eksponere deg for sårbarheter og feil som har blitt fikset i nyere versjoner av PHP.", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik vil slutte å støtte PHP %1$s i neste versjon. Oppgrader din PHP til minst PHP %2$s, før det er for sent!", + "WarningFileIntegrityNoManifest": "Klarte ikke å utføre integritetskontroll av filer fordi manifest.inc.php mangler.", + "WarningFileIntegrityNoManifestDeployingFromGit": "Hvis du installerer Piwik fra Git, er denne meldingen normal.", "WarningFileIntegrityNoMd5file": "Klarte ikke utføre integritetskontroll av filer fordi funksjonen md5_file mangler.", "WarningPasswordStored": "%sAdvarsel:%s Dette passordet lagres i klartekst i konfigurasjonsfilen slik at alle med tilgang kan lese det.", + "WarningDebugOnDemandEnabled": "Sporingsmodus %s er aktivert. På grunn av sikkerheten bør dette kun være aktivert i en kort periode. For å deaktivere, sett %s til %s i %s", "Website": "Nettsted", "Weekly": "Ukentlig", "WeeklyReport": "ukentlig", "WeeklyReports": "Ukentlige rapporter", "WellDone": "Bra jobba!", - "Widgets": "Element", + "Widgets": "Widgeter", "Widget": "Widget", "XComparedToY": "%1$s sammenlignet med %2$s", "XFromY": "%1$s fra %2$s", @@ -367,8 +405,9 @@ "YearsDays": "%1$s år %2$s dager", "Yes": "Ja", "YouAreCurrentlyUsing": "Du bruker for øyeblikket Piwik %s.", + "YouAreViewingDemoShortMessage": "Du ser demoen av Piwik", "YouMustBeLoggedIn": "Du må være innlogget for å bruke denne funksjonaliteten.", - "YourChangesHaveBeenSaved": "Din endringer er lagret." + "YourChangesHaveBeenSaved": "Dine endringer er lagret." }, "Mobile": { "AboutPiwikMobile": "Om Piwik Mobile", @@ -380,18 +419,19 @@ "Advanced": "Avansert", "AnonymousAccess": "Anonym tilgang", "AnonymousTracking": "Anonym sporing", - "ChooseHttpTimeout": "Velg HTTP-tidsavbrudd verdi", + "AskForAnonymousTrackingPermission": "Når aktivert vil Piwik Mobile sende anonym bruksdata til piwik.org. Hensikten er å bruke dataen til å hjelpe Piwik Mobile-utviklere å bedre skjønne hvordan appen brukes. Informasjon som blir sendt er: menyer og innstillinger som klikkes på, navn og versjon på OS, og feilmeldinger som vises i Piwik Mobile. Vi vil IKKE spore dine analysedata. Dataen som sendes vil ikke bli offentliggjort. Du kan deaktivere og aktivere anonym sporing i innstillingene når som helst.", + "ChooseHttpTimeout": "Velg verdi for HTTP-tidsavbrudd", "ChooseMetric": "Velg måltall", "ChooseReport": "Velg en rapport", "ChooseSegment": "Velg segment", "ConfirmRemoveAccount": "Vil du fjerne denne kontoen?", "DefaultReportDate": "Rapportdato", - "EmailUs": "Send epost til oss", + "EmailUs": "Send e-post til oss", "EnableGraphsLabel": "Vis grafer", "EvolutionGraph": "Historisk graf", - "HelpUsToImprovePiwikMobile": "Vil du slå på anonym sporing av bruk i Piwik Mobile?", + "HelpUsToImprovePiwikMobile": "Vil du aktivere anonym sporing av bruk i Piwik Mobile?", "HowtoDeleteAnAccount": "Trykk lenge for å fjerne en konto.", - "HowtoDeleteAnAccountOniOS": "Sveip høyre til venstre for å slette en konto", + "HowtoDeleteAnAccountOniOS": "Sveip fra høyre til venstre for å slette en konto", "HowtoLoginAnonymous": "La brukernavn og passord være tomt for anonym innlogging.", "HttpIsNotSecureWarning": "Din Piwik autorisasjonstoken (token_auth) blir sendt i klartekst hvis du bruker HTTP. Av den grunn anbefaler vi HTTPS for sikker transport av data over internett. Vil du fortsette?", "HttpTimeout": "HTTP-tidsavbrudd", @@ -401,7 +441,7 @@ "LoginCredentials": "Påloggingsinformasjon", "LoginToPiwikToChangeSettings": "Logg inn på din Piwik-tjener for å opprette og oppdatere nettsted og brukere eller for å endre generelle innstillinger som «Rapport som skal lastes som standard».", "LoginUseHttps": "Bruk HTTPS", - "MultiChartLabel": "Vis sparklines", + "MultiChartLabel": "Vis minigrafer", "NavigationBack": "Tilbake", "NetworkError": "Nettverksfeil", "NetworkErrorWithStatusCode": "Det oppstod en «%s»-feil. Forespørselen returnerte statusen «%s». URL-en var «%s». Sjekk URL-en du skrev inn og feilloggene på denne tjeneren for mer informasjon om feilen og hvordan du kan løse problemet.", @@ -419,17 +459,17 @@ "PossibleSslError": "Mulig feil med SSL-sertifikat", "PossibleSslErrorExplanation": "En feil oppstod som kan være forårsaket av et ugyldig eller selvsignert sertifikat: «%s» Innlogging kan virke for deg hvis du ignorerer SSL-validering, men det er mindre sikkert. Du kan endre SSL-validering når som helst i innstillingene.", "IgnoreSslError": "Ignorer SSL-feil", - "RatingDontRemindMe": "Ikke minn meg på", + "RatingDontRemindMe": "Ikke minn meg på dette", "RatingNotNow": "Ikke nå", - "RatingNow": "OK, jeg vil vurdere det nå", + "RatingNow": "OK, jeg vil vurdere den nå", "RatingPleaseRateUs": "Appen Piwik Mobil er fri programvare. Vi ville virkelig sette pris på om du brukte 1 minutt på å vurdere denne appen i %s. Hvis du har forslag til nye funksjoner eller feilrapporter, kontakt %s", "ReleaseToRefresh": "Slipp for å oppdatere...", "Reloading": "Laster inn på nytt ...", - "RequestTimedOutShort": "Nettverksfeil - tidsavbrudd", + "RequestTimedOutShort": "Nettverksfeil – tidsavbrudd", "RestrictedCompatibility": "Begrenset kompatibilitet", - "RestrictedCompatibilityExplanation": "Piwik-versjonen %s som du bruker er ikke fullt støttet av Piwik Mobile 2. Du kan oppleve noen feil. Vi anbefaler at du enten oppdatere Piwik til siste utgave eller bruker Piwik Mobile 1.", + "RestrictedCompatibilityExplanation": "Piwik-versjonen %s som du bruker er ikke fullt støttet av Piwik Mobile 2. Du kan oppleve noen feil. Vi anbefaler at du enten oppdaterer Piwik til siste utgave eller bruker Piwik Mobile 1.", "SaveSuccessError": "Piwik-URL eller kombinasjonen av brukernavn og passord er feil.", - "SearchWebsite": "Søk nettsteder", + "SearchWebsite": "Søk i nettsteder", "ShowAll": "Vis alle", "ShowLess": "Vis mindre", "StaticGraph": "Oversiktsgraf", @@ -443,15 +483,16 @@ }, "RowEvolution": { "AvailableMetrics": "Tilgjengelige måltall", + "CompareDocumentation": "Klikk lenken nedenfor og åpne dette popup-vunduet for en annen rad i samme tabell for å sammenlikne flere oppføringer.<br \/>Bruk SHIFT-klikk for å markere raden for sammenlikning uten å åpne dette popup-vinduet.", "CompareRows": "Sammenlign oppføringer", "ComparingRecords": "Sammenligner %s rader", "Documentation": "Klikk måltallet for å vise det i den store utviklingsgrafen. Bruk SHIFT-klikk for å vise flere måltall på en gang.", "MetricBetweenText": "mellom %s og %s", - "MetricChangeText": "%s endring over perioden", - "MetricMinMax": "%1$s rangert mellom %2$s og %3$s over perioden", + "MetricChangeText": "%s endring i perioden", + "MetricMinMax": "%1$s rangert mellom %2$s og %3$s i perioden", "MetricsFor": "Måltall for %s", "MultiRowEvolutionTitle": "Utvikling av flere rader", - "PickAnotherRow": "Velg en annen rad å sammenligne", - "PickARow": "Velg en rad å sammenlign" + "PickAnotherRow": "Velg en annen rad å sammenlikne med", + "PickARow": "Velg en rad å sammenlikne med" } }
\ No newline at end of file diff --git a/lang/nl.json b/lang/nl.json index dfefa26737..c8b0818e18 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -379,7 +379,6 @@ "VisitTypeExample": "Om bijvoorbeeld het aantal terugkerende bezoekers (inclusief die bezoekers die iets gekocht hebben) te selecteren moet de API %s bevatten", "Warning": "Let op", "WarningPhpVersionXIsTooOld": "Je gebruikt PHP versie %s. Deze heeft de status 'End of Life (OEL)'. We adviseren je ten zeerste om te upgraden naar de recente versie van PHP, omdat de versie die nu gebruikt beveiligingslekken en bugs kan bevatten, welke in meer recente versies van PHP opgelost zijn.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik stopt de ondersteuning van deze PHP versie in %s. Upgrade je PHP versie voor het te laat is!", "WarningFileIntegrityNoManifest": "Bestand integriteits controle kon niet worden uitgevoerd vanwege ontbrekend manifest.inc.php", "WarningFileIntegrityNoManifestDeployingFromGit": "Als je Piwik vanuit Git deployed, dan is deze melding normaal.", "WarningFileIntegrityNoMd5file": "Bestand integriteit controle kon niet worden voltooid vanwege ontbrekende md5_file() functie.", diff --git a/lang/pl.json b/lang/pl.json index 2edf0aecbf..c68085a88f 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -344,7 +344,6 @@ "VisitorSettings": "Konfiguracje użytkownika", "VisitTypeExample": "Przykładowo, aby wybrać wszystkich odwiedzających, którzy powrócili na serwis, włączając tych, którzy zakupili coś podczas poprzednich wizyt, zapytanie API będzie zawierać %s", "Warning": "Ostrzeżenie", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik przestanie wspierać tą wersję PHP w %s. Zauktualizuj wersję PHP zanim będzie za późno!", "WarningFileIntegrityNoManifest": "Weryfikacja integralności plików nie mogła zostać przeprowadzona z powodu braku pliku manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "W przypadku wdrażania Piwik z Git, komunikat ten jest prawidłowy.", "WarningFileIntegrityNoMd5file": "Weryfikacja integralności plików nie mogła zostać zakończona z powodu braku funkcji md5_file().", diff --git a/lang/pt-br.json b/lang/pt-br.json index 9b16a71640..2bed8292e5 100644 --- a/lang/pt-br.json +++ b/lang/pt-br.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "Relógio de 12 horas", + "24HourClock": "Relógio de 24 horas", "AbandonedCarts": "Compras abandonadas", "AboutPiwikX": "Sobre o Piwik %s", "Action": "Ação", @@ -171,7 +173,7 @@ "ExportAsImage": "Exportar como imagem", "ExportThisReport": "Exportar este conjunto de dados noutros formatos", "Faq": "FAQ", - "FileIntegrityWarningExplanation": "O check de integridade do arquivo falhou e reportou alguns erros. Isso é mais provável devido a uma parcial ou upload falho de algum arquivo Piwik. Você deveria refazer o upload de todos os arquivos Piwik e recarregar a página até que nenhum erro seja exibido.", + "FileIntegrityWarningExplanation": "A verificação de integridade do arquivo falhou e reportou alguns erros. Isso é mais provável devido a um carregamento parcial ou falho de algum arquivo Piwik. Você deve recarregar todos os arquivos Piwik em modo BINÁRIO e também recarregar a página até que nenhum erro seja exibido.", "First": "Primeiro", "Flatten": "Achatar", "ForExampleShort": "ex.", @@ -262,8 +264,11 @@ "OperationIsNot": "não é", "OperationLessThan": "menos que", "OperationNotEquals": "não é igual a", + "OperationStartsWith": "Inicia com", + "OperationEndsWith": "Finaliza com", "OptionalSmtpPort": "Opcional. O padrão é 25 para não criptografadas e TLS SMTP, e 465 para SMTP SSL.", "Options": "Opções", + "Or": "ou", "OrCancel": "ou %s Cancele %s", "Others": "Outros", "Outlink": "Saída", @@ -348,17 +353,21 @@ "TagCloud": "Núvem de Tag", "Tax": "Taxas", "TimeAgo": "%s atrás", + "TimeFormat": "Formato do horário", "TimeOnPage": "Tempo na página", "Total": "Total", "TotalRatioTooltip": "Este é %1$s de todos os %2$s %3$s.", "TotalRevenue": "Total de Revendas", "TotalVisitsPageviewsActionsRevenue": "(Total: %s visitas , %s exibições de página, %s actions, %s receita)", + "TrackingScopeAction": "Ação", + "TrackingScopePage": "Página", + "TrackingScopeVisit": "Visita", "TransitionsRowActionTooltip": "Veja o que os visitantes fizeram antes e depois de verem este página", "TransitionsRowActionTooltipTitle": "Transições abertas", "TranslatorName": "Marcos Napier, Marcela Ferraro, Zob, Raphael Milani, Fernando Fraga Rodrigues", "UniquePurchases": "Pedidos únicos", "Unknown": "Desconhecido", - "Upload": "Enviar", + "Upload": "Carregar", "UsePlusMinusIconsDocumentation": "Use os ícones de mais e menos à esquerda para navegar.", "UserId": "ID do usuário", "Username": "Nome de Usuário", @@ -382,7 +391,7 @@ "VisitTypeExample": "Por exemplo, para selecionar todos os visitantes que voltaram para o site, incluindo aqueles que compraram algo em suas visitas anteriores, a requisição a API deve conter %s", "Warning": "Aviso", "WarningPhpVersionXIsTooOld": "A versão %s do PHP que você está usando atingiu seu limite de vida (EOL). Encorajamos fortemente a atualizar para a versão mais atual, podendo a utilização desta versão expô-lo a vulnerabilidades de segurança e bugs que foram corrigidos em versões mais recentes do PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik vai deixar de suportar esta versão PHP em %s. Atualize a sua versão do PHP, antes que seja tarde demais!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik vai parar de suportar PHP %1$s na próxima versão principal. Atualize o seu PHP para, pelo menos, PHP %2$s, antes que seja tarde demais!", "WarningFileIntegrityNoManifest": "O check de integridade do arquivo não pode ser feita enquanto estiver faltando manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Se você estiver implantando Piwik a partir do repositório Git, esta mensagem é normal.", "WarningFileIntegrityNoMd5file": "O check de integridade do arquivo não pode ser completado enquanto estiver faltando a função md5_file().", diff --git a/lang/ru.json b/lang/ru.json index e11ba77b41..18ba18e860 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -7,9 +7,11 @@ "Add": "Добавить", "AfterEntry": "после захода сюда", "All": "Все", + "AllowPiwikArchivingToTriggerBrowser": "Архив отчетов при просмотре в браузере", "AllWebsitesDashboard": "Статистика всех сайтов", "And": "и", "API": "API-функции", + "Apply": "Применить", "ArchivingInlineHelp": "Для сайтов со средней или высокой нагрузкой рекомендуется отменить архивирование данных при входе в веб-аналитику через браузер. Вместо этого лучше назначить cron-задачу, чтобы Piwik автоматически формировал отчеты каждый час.", "ArchivingTriggerDescription": "Для сайтов с высокой нагрузкой настоятельно рекомендуется %sназначить cron-задачу%s, которая будет формировать отчеты автоматически.", "AuthenticationMethodSmtp": "Метод аутентификации SMTP", @@ -97,6 +99,7 @@ "DateRange": "Временной период:", "DateRangeFrom": "От", "DateRangeFromTo": "С %s по %s", + "DateRangeInPeriodList": "диапазон дат:", "DateRangeTo": "До", "DaysHours": "%1$s дн. %2$s час", "DaysSinceFirstVisit": "Дней прошло с момента первого посещения", @@ -236,6 +239,7 @@ "NotDefined": "%s - не определено", "Note": "Заметка", "NotInstalled": "Не установлено", + "NotRecommended": "не рекомендуется", "NotValid": "%s неверный", "NumberOfVisits": "Количество посещений", "NUsers": "%s пользователей", @@ -291,6 +295,7 @@ "Quantity": "Количество", "RangeReports": "Другие периоды", "ReadThisToLearnMore": "%1$sПрочтите это, чтобы узнать больше.%2$s", + "Recommended": "Рекомендуется", "RecordsToPlot": "Записи для построения графика", "Refresh": "Обновить", "RefreshPage": "Обновить страницу", @@ -301,6 +306,8 @@ "ReportGeneratedFrom": "Этот отчет был сформирован с использованием данных: %s.", "ReportRatioTooltip": "'%1$s' представляет %2$s из %3$s %4$s с %5$s.", "Reports": "Отчеты", + "ReportsContainingTodayWillBeProcessedAtMostEvery": "Архив отчетов на каждые Х секунд", + "RearchiveTimeIntervalOnlyForTodayReports": "Это влияет только на отчеты (или другие Диапазоны Дат включая сегодня)", "ReportsWillBeProcessedAtMostEveryHour": "Следовательно, отчеты обрабатываются каждый час.", "RequestTimedOut": "Время ожидания при запросе данных к %s истекло. Пожалуйста, попробуйте снова.", "Required": "%s необходимо", @@ -338,10 +345,12 @@ "TagCloud": "Теги", "Tax": "Налог (комиссия)", "TimeAgo": "%s назад", + "TimeFormat": "Формат времени", "TimeOnPage": "Время, проведенное на странице", "Total": "Всего", "TotalRatioTooltip": "Это %1$s из всех %2$s %3$s.", "TotalRevenue": "Общая прибыль", + "TotalVisitsPageviewsActionsRevenue": "(Всего: %s визиты, %s просмотры страниц, %s действия, %s выручка)", "TransitionsRowActionTooltip": "Посмотрите, что посетители делали до и после просмотра этой страницы", "TransitionsRowActionTooltipTitle": "Открыть переходы", "TranslatorName": "Ademaro, <a href=\"http:\/\/jokerintertactive.ru\/\">Joker Interactive<\/a>, <a href=\"http:\/\/codax.ru\/\">Важенин Илья (компания Codax)<\/a>, Nelde Maxim, Andrey, Vadim Nekhai", @@ -367,10 +376,11 @@ "Visitors": "Посетители", "VisitsWith": "Посещения с %s", "VisitorSettings": "Настройки посетителей", + "VisitType": "Тип визита", "VisitTypeExample": "Например, чтобы выбрать всех посетителей, которые вернулисть на сайт, включая тех, кто уже купил что-то в свои предыдущие визиты, API-запрос будет содержать: %s", "Warning": "Внимание", "WarningPhpVersionXIsTooOld": "Версия PHP %s, которую вы используете, завершила свой жизненый цикл (EOL). Настоятельно рекомендуем обновиться до текущей версии, т. к. использование устаревшей версии подвергает вас уязвимостям в безопасности и ошибкам, которые устранены в более свежей версии PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik будет прекращать поддержку этой версии PHP в %s. Обновите версию PHP пока не слишком поздно!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik прекратит поддержку PHP %1$s в следующей версии. Обновите ваш PHP, по крайней мере, до версии PHP %2$s !", "WarningFileIntegrityNoManifest": "Проверка целостности не может быть проведена из-за отсутствия manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Если вы делаете деплой Piwik из Git, это сообщение является нормальным.", "WarningFileIntegrityNoMd5file": "Проверка целостности не может быть проведена из-за отсутствия функции md5_file().", @@ -389,6 +399,7 @@ "YearsDays": "%1$s г. %2$s дн.", "Yes": "Да", "YouAreCurrentlyUsing": "Вы используете версию Piwik %s.", + "YouAreViewingDemoShortMessage": "Вы просматриваете Piwik в демо режиме", "YouMustBeLoggedIn": "Вы должны зайти на сайт, чтобы получить доступ к этому функционалу.", "YourChangesHaveBeenSaved": "Изменения сохранены." }, diff --git a/lang/sk.json b/lang/sk.json index 574122d94c..6593349188 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "12 hodinový čas", + "24HourClock": "24 hodinový čas", "AbandonedCarts": "Opustené košíky", "AboutPiwikX": "O Piwiku %s", "Action": "Akcia", @@ -7,9 +9,11 @@ "Add": "Pridať", "AfterEntry": "po zadaní sem", "All": "Všetko", + "AllowPiwikArchivingToTriggerBrowser": "Archivovať reporty, keď sú prezerané v prehliadači", "AllWebsitesDashboard": "Nástenka pre všetky weby", "And": "a", "API": "API", + "Apply": "Použiť", "ArchivingInlineHelp": "Pre stredné až vysoké návštevnosti webových stránok, sa odporúča vypnúť v Piwiku archiváciu spúšťanú prezeraním z prehliadača. Namiesto toho odporúčame nastaviť cron úlohu na spracovanie Piwik reportov každú hodinu.", "ArchivingTriggerDescription": "Odporúča sa pre väčšie inštalácie Piwiku, musíte %s nastaviť cron %s aby spracovával reporty automaticky.", "AuthenticationMethodSmtp": "Metóda overovania pre SMTP", @@ -97,6 +101,7 @@ "DateRange": "Časové obdobie:", "DateRangeFrom": "Od", "DateRangeFromTo": "Od %s do %s", + "DateRangeInPeriodList": "dátumový rozsah", "DateRangeTo": "Do", "DaysHours": "%1$s dní %2$s hodín", "DaysSinceFirstVisit": "Dní od prvej návštevy", @@ -306,6 +311,8 @@ "ReportGeneratedFrom": "Tento report bol vytvorený s použitím dát z %s.", "ReportRatioTooltip": "'%1$s' reprezentuje %2$s z %3$s %4$s s %5$s.", "Reports": "Správy", + "ReportsContainingTodayWillBeProcessedAtMostEvery": "Archivovať reporty každých X sekúnd", + "RearchiveTimeIntervalOnlyForTodayReports": "Toto ovplyvní len dnešné reporty (alebo akýkoľvek iný dátumový rozsah ktorý bude obsahovať dnešný deň)", "ReportsWillBeProcessedAtMostEveryHour": "Reporty budú budú preto spracované nanajvýš každú hodinu.", "RequestTimedOut": "Čas %sS vypršal. Prosím skúste to znova.", "Required": "%s požadované", @@ -343,10 +350,12 @@ "TagCloud": "Oblak značiek", "Tax": "Daň", "TimeAgo": "pred %s", + "TimeFormat": "Časový formát", "TimeOnPage": "Doba na stránke", "Total": "Celkom", "TotalRatioTooltip": "Toto je %1$s z celkového %2$s %3$s.", "TotalRevenue": "Celkový príjem", + "TotalVisitsPageviewsActionsRevenue": "(Celkom: %s návštev, %s zobrazení, %s akcií, tržby: %s)", "TransitionsRowActionTooltip": "Pozrite si čo robili návštevníci pred a po prezretí si tejto stránky.", "TransitionsRowActionTooltipTitle": "Otvorené zmeny", "TranslatorName": "Miroslav Habara, Zdenko Podobný, Juraj \"Lup0\" Viktorín, Ivanka", @@ -372,14 +381,16 @@ "Visitors": "Návštevníci", "VisitsWith": "Návštevy s %s", "VisitorSettings": "Nastavenia návštevníkov", + "VisitType": "Typ návštevy", "VisitTypeExample": "Napríklad: Pre výber všetkých návštevníkov, ktorí sa vrátili na web, vrátane tých, ktorí niečo nakúpili počas prechádzajúcej návštevy by API požiadavka obsahovala %s", "Warning": "Varovanie", "WarningPhpVersionXIsTooOld": "PHP verzia %s, ktorú používate, práve dospela do svojho konca (EOL). Nutne ju potrebujete aktualizovať na najnovšiu verziu , pretože používanie tejto verzie Vás môže vystaviť bezpečnostnému riziku a chybám, ktoré boli odstránené v novších verziách PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik prestane podporovať túto verziu PHP v %s. Aktualizujte PHP skôr ako bude príliš neskoro!", + "WarningPiwikWillStopSupportingPHPVersion": "Piwik ukončí podporu PHP %1$s pri nasledujúcej hlavnej verzií. Aktualizujte svoje PHP aspoň na verziu %2$s skôr než bude neskoro!", "WarningFileIntegrityNoManifest": "Súbor kontrola integrity nemohla byť vykonaná z dôvodu chýbajúcich manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Ak nasadzujete Piwik z Git-u, tak toto je normálna správa.", "WarningFileIntegrityNoMd5file": "Súbor kontrola integrity nemožno dokončiť z dôvodu chýbajúcich md5_file () funkcie.", "WarningPasswordStored": "%sVarovanie:%s Toto heslo bude uložené v konfiguračnom súbore viditeľné pre všetkých, ako prístup.", + "WarningDebugOnDemandEnabled": "Je povolený režim sledovača (tracker) %s. Z bezpečnostných dôvodov by mal byť povolený len veľmi krátky čas. Na jeho ukončenie nastavte %s na %s v %s", "Website": "Web", "Weekly": "Týždenne", "WeeklyReport": "týždenne", @@ -394,6 +405,7 @@ "YearsDays": "%1$s rokov %2$s dní", "Yes": "Áno", "YouAreCurrentlyUsing": "Aktuálne používate Piwik %s.", + "YouAreViewingDemoShortMessage": "Prezeráte si Piwik demo", "YouMustBeLoggedIn": "Musíte byť prihlásený pre prístup k tejto functionnality.", "YourChangesHaveBeenSaved": "Vaše zmeny boli uložené." }, diff --git a/lang/sl.json b/lang/sl.json index 1c2a4fc259..52d6a42ee0 100644 --- a/lang/sl.json +++ b/lang/sl.json @@ -1,5 +1,7 @@ { "General": { + "12HourClock": "12-urni prikaz časa", + "24HourClock": "24-urni prikaz časa", "AbandonedCarts": "Opuščeni Vozički", "AboutPiwikX": "O Piwik %s", "Action": "Dejanje", @@ -11,6 +13,7 @@ "AllWebsitesDashboard": "Vse nadzorne plošče spletnih strani", "And": "in", "API": "API", + "Apply": "Uveljavi", "ArchivingInlineHelp": "Za srednje velike in velike spletne strani izklopite arhiviranje preko spletnega brskalnika. Namesto tega vam priporočamo, da nastavite \"cron job\" tako, da bodo poročila ustvarjena vsako uro.", "ArchivingTriggerDescription": "Priporočamo za večje namestitve. %sNastavite \"cron job\"%s za avtomatsko procesiranje.", "AuthenticationMethodSmtp": "Metoda potrditve verodostojnosti za SMTP", @@ -370,7 +373,6 @@ "VisitTypeExample": "Na primer, če želite zbrati vse obiskovalce, ki so se vrnili na spletno stran, vključno s tistimi, ki so v prejšnjih obiskih kaj kupili, bi API zahteva vsebovala %s", "Warning": "Opozorilo", "WarningPhpVersionXIsTooOld": "Različica PHP %s, ki jo uporabljate, ni več podprta! Priporočamo čim prejšnjo nadgradnjo na zadnjo stabilno različico, saj ste z nadaljnjo uporabo trenutne različice lahko izpostavljeni varnostnim ranljivostim in napakam, ki so odpravljene v novejših različicah PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Ta različica PHP ne bo več podprta v Piwiku %s. Čim prej nadgradite PHP!", "WarningFileIntegrityNoManifest": "Pregleda integritete datotek ni bilo mogoče izvesti, saj manjka manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Če nameščate Piwik iz Git repozitorija, je to sporočilo normalno.", "WarningFileIntegrityNoMd5file": "Ne morem preveriti integritete datoteke, ker manjka funkcija md5_file().", @@ -436,6 +438,7 @@ "NoWebsitesShort": "Ni spletnih mest", "PullDownToRefresh": "Potegnite navzdol za osvežitev...", "PossibleSslError": "Možna napaka SSL certifikata", + "PossibleSslErrorExplanation": "Prišlo je do napake. Vzrok zanjo je lahko neveljavno ali samopodpisano digitalno potrdilo: \"%s\". Prijava je vseeno mogoča, če prezrete SSL preverjanje, vendar je manj varna. SSL preverjanje lahko vključite\/izključite kadarkoli v nastavitvah vašega brskalnika.", "IgnoreSslError": "Prezri SSL napako", "RatingDontRemindMe": "Ne spomni me", "RatingNotNow": "Ne zdaj", diff --git a/lang/sq.json b/lang/sq.json index 80902db077..acf1e9822e 100644 --- a/lang/sq.json +++ b/lang/sq.json @@ -379,7 +379,6 @@ "VisitTypeExample": "Për shembull, për përzgjedhjen e krejt vizitorëve që janë rikthyer te sajti, përfshi ata që kanë blerë diçka gjatë vizitash të mëparshme, kërkesa API do të duhej të përmbante %s", "Warning": "Sinjalizim", "WarningPhpVersionXIsTooOld": "Versioni PHP %s që po përdorni ka mbërritur në fund të ciklit të vet (EOL). Ftoheni me forcë ta përmirësoni me një version të tanishëm, ngaqë përdorimi i këtij versioni mund t’ju lërë zbuluar kundrejt cenimesh sigurie dhe të metash që janë ndrequr në versione më të rinj të PHP-së.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik-u do të reshtë mbulimin e këtij versioni të PHP-së në %s. Përmirësoni versionin e PHP-së tuaj, para se të jetë tepër vonë!", "WarningFileIntegrityNoManifest": "Kontrolli i pacenueshmërisë së kartelës s’u krye dot, për shkak të mungesës së manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Nëse po krijoni një instalim të Piwik-ut prej Git-i, ky mesazh është normal.", "WarningFileIntegrityNoMd5file": "Kontrolli i pacenueshmërisë së kartelës s’u plotësua dot, për shkak të mungesës së funksionit md5_file().", diff --git a/lang/sr.json b/lang/sr.json index f50bda8460..acea21bac6 100644 --- a/lang/sr.json +++ b/lang/sr.json @@ -379,7 +379,6 @@ "VisitTypeExample": "Na primer, kako biste obeležili sve posetioce koji su se vratili na sajt, uključujući i one koji su već nešto kupili, API zahtev bi sadržao %s", "Warning": "Upozorenje", "WarningPhpVersionXIsTooOld": "PHP verzija %s koju koristite je zastarela. Molimo vas da hitno nadogradite PHP na najnoviju verziju jer ukoliko nastavite da koristite postojeću, izlažete se sigurnosnim rizicima i bagovima koji su već ispravljeni u novijim verzijama PHP-a.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik će prestati sa podrškom ove PHP verzije %s. Nadogradite vaš PHP pre nego što postane kasno!", "WarningFileIntegrityNoManifest": "Proveru integriteta datoteka nije moguće sprovesti zato što nedostaje datoteka manifest.inc.php.", "WarningFileIntegrityNoManifestDeployingFromGit": "Ukoliko podižete Piwik iz Git-a, ova poruka je sasvim prirodna.", "WarningFileIntegrityNoMd5file": "Proveru integriteta datoteka nije moguće okončati zato što nedostaje funkcija md5_file().", diff --git a/lang/sv.json b/lang/sv.json index 4c38a0691b..25326597ab 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -379,7 +379,6 @@ "VisitTypeExample": "Till exempel, för att markera alla besökare som har återvänt till webbplatsen, inklusive de som har köpt något i sina tidigare besök, så skulle API-begäran innehålla %s", "Warning": "Varning", "WarningPhpVersionXIsTooOld": "Den version av PHP du använder, %s, har nått End of Life (EOL). Du bör uppgradera till en aktuell version eftersom den nuvarande versionen kan innehålla säkerhetsluckor och buggar som har fixats i en senare version av PHP.", - "WarningPiwikWillStopSupportingPHPVersion": "Piwik kommer inte längre stödja den här versionen av PHP om %s. Uppgradera din PHP-version innan det är för sent.", "WarningFileIntegrityNoManifest": "Filintegriteten kunde inte kontrolleras eftersom att filen manifest.inc.php saknades.", "WarningFileIntegrityNoManifestDeployingFromGit": "Om du utvecklar Piwik från Git, är det här meddelandet normalt.", "WarningFileIntegrityNoMd5file": "Filintegriteten kunde inte slutföras eftersom att funktionen md5_file() saknas.", diff --git a/lang/tr.json b/lang/tr.json index ff8ee3fbc2..93fdace4ae 100644 --- a/lang/tr.json +++ b/lang/tr.json @@ -10,6 +10,7 @@ "AllWebsitesDashboard": "Tüm Websitelerin Panosu", "And": "ve", "API": "API", + "Apply": "Uygula", "AuthenticationMethodSmtp": "SMTP için kimlik doğrulama yöntemi", "AverageOrderValue": "Ortalama Sipariş Değeri", "AveragePrice": "Ortalama Fiyat", @@ -258,9 +259,11 @@ "TagCloud": "Etiket bulutu", "Tax": "Vergi", "TimeAgo": "%s önce", + "TimeFormat": "Zaman formatı", "TimeOnPage": "Sayfadaki süre", "Total": "Toplam", "TotalRevenue": "Toplam Kazanç", + "TotalVisitsPageviewsActionsRevenue": "(Toplam: %s ziyaretçiler, %s sayfa görüntülemeler, %s aksiyonlar, %s geliri)", "TransitionsRowActionTooltipTitle": "Açık Geçişler", "TranslatorName": "Fabian Becker, Emre Yazici, Emre Saraçoğlu, <a href=\"http:\/\/www.ugureskici.com\">Uğur Eskici<\/a>", "UniquePurchases": "Tekil Satın Alımlar", @@ -308,18 +311,23 @@ "AnonymousAccess": "İsimsiz erişim", "AnonymousTracking": "İsimsiz izleme", "ChooseHttpTimeout": "HTTP zamanaşımı değerini seçin", + "ChooseReport": "Rapor seçin", + "ConfirmRemoveAccount": "Bu hesabı kaldırmak istediğinizden emin misiniz?", "DefaultReportDate": "Rapor tarihi", "HowtoDeleteAnAccount": "Bir hesabı silmek için uzun basın.", "HowtoDeleteAnAccountOniOS": "Bir hesabı silmek için sağdan sola kaydırın.", "HttpTimeout": "HTTP Zamanaşımı", "LastUpdated": "Son Güncelleme: %s", + "LoadingReport": "Yükleniyor %s", "LoginUseHttps": "https kullan", "NavigationBack": "Geri", "NetworkError": "Ağ Hatası", + "NetworkErrorWithStatusCodeShort": "Ağ Hatası %s", "NetworkNotReachable": "Ağa erişilemiyor", "NoDataShort": "Veri Yok", "NoPiwikAccount": "Piwik Hesabınız yok mu?", "NoVisitorFound": "Hiç ziyaretçi bulunamadı", + "PullDownToRefresh": "Yenilemek için aşağı çekin...", "PossibleSslError": "Muhtemel SSL sertifika hatası", "IgnoreSslError": "SSL Hatasını yoksay", "RatingDontRemindMe": "Tekrar hatırlatma", diff --git a/libs/PiwikTracker b/libs/PiwikTracker -Subproject 9a70fe37a1ce23cbd64b7cde00a6030e30942b7 +Subproject ac3e26bb3e2c8a428ccbf6ca663c2ef37fa47a5 diff --git a/misc/log-analytics b/misc/log-analytics -Subproject 11957e29646d348ae8ab238ef84cbe23d728cef +Subproject aa600c9a20049db05443c1ebd69f68190870247 @@ -8,7 +8,7 @@ * @license http://piwik.org/free-software/bsd/ BSD-3 Clause (also in js/LICENSE.txt) * @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause */ -if(typeof JSON2!=="object"&&window.JSON){JSON2=window.JSON}else{(function(){var a={}; +if(typeof JSON2!=="object"&&typeof window.JSON==="object"&&window.JSON.stringify&&window.JSON.parse){JSON2=window.JSON}else{(function(){var a={}; /*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ (function(){var c=typeof define==="function"&&define.amd;var e={"function":true,object:true};var h=e[typeof a]&&a&&!a.nodeType&&a;var i=e[typeof window]&&window||this,b=h&&e[typeof module]&&module&&!module.nodeType&&typeof global=="object"&&global;if(b&&(b.global===b||b.window===b||b.self===b)){i=b}function j(ab,V){ab||(ab=i.Object());V||(V=i.Object());var K=ab.Number||i.Number,R=ab.String||i.String,x=ab.Object||i.Object,S=ab.Date||i.Date,T=ab.SyntaxError||i.SyntaxError,aa=ab.TypeError||i.TypeError,J=ab.Math||i.Math,Y=ab.JSON||i.JSON; if(typeof Y=="object"&&Y){V.stringify=Y.stringify;V.parse=Y.parse}var n=x.prototype,u=n.toString,r,m,L;var B=new S(-3509827334573292);try{B=B.getUTCFullYear()==-109252&&B.getUTCMonth()===0&&B.getUTCDate()===1&&B.getUTCHours()==10&&B.getUTCMinutes()==37&&B.getUTCSeconds()==6&&B.getUTCMilliseconds()==708}catch(v){}function o(ac){if(o[ac]!==L){return o[ac]}var ad;if(ac=="bug-string-char-index"){ad="a"[0]!="a"}else{if(ac=="json"){ad=o("json-stringify")&&o("json-parse")}else{var ak,ah='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if(ac=="json-stringify"){var ai=V.stringify,aj=typeof ai=="function"&&B;if(aj){(ak=function(){return 1}).toJSON=ak;try{aj=ai(0)==="0"&&ai(new K())==="0"&&ai(new R())=='""'&&ai(u)===L&&ai(L)===L&&ai()===L&&ai(ak)==="1"&&ai([ak])=="[1]"&&ai([L])=="[null]"&&ai(null)=="null"&&ai([L,u,null])=="[null,null,null]"&&ai({a:[ak,true,false,null,"\x00\b\n\f\r\t"]})==ah&&ai(null,ak)==="1"&&ai([1,2],null,1)=="[\n 1,\n 2\n]"&&ai(new S(-8640000000000000))=='"-271821-04-20T00:00:00.000Z"'&&ai(new S(8640000000000000))=='"+275760-09-13T00:00:00.000Z"'&&ai(new S(-62198755200000))=='"-000001-01-01T00:00:00.000Z"'&&ai(new S(-1))=='"1969-12-31T23:59:59.999Z"' @@ -18,46 +18,49 @@ var I="000000";var t=function(ac,ad){return(I+(ad||0)).slice(-ac)};var z="\\u00" }else{at=null}}else{if(typeof at.toJSON=="function"&&((ae!=N&&ae!=O&&ae!=E)||r.call(at,"toJSON"))){at=at.toJSON(ai)}}}if(ag){at=ag.call(aA,ai,at)}if(at===null){return"null"}ae=u.call(at);if(ae==A){return""+at}else{if(ae==N){return at>-1/0&&at<1/0?""+at:"null"}else{if(ae==O){return C(""+at)}}}if(typeof at=="object"){for(af=aj.length;af--;){if(aj[af]===at){throw aa()}}aj.push(at);ar=[];av=ac;ac+=ax;if(ae==E){for(ah=0,af=at.length;ah<af;ah++){ad=p(ah,at,ag,al,ax,ac,aj);ar.push(ad===L?"null":ad)}ao=ar.length?(ax?"[\n"+ac+ar.join(",\n"+ac)+"\n"+av+"]":("["+ar.join(",")+"]")):"[]"}else{m(al||at,function(aC){var aB=p(aC,at,ag,al,ax,ac,aj);if(aB!==L){ar.push(C(aC)+":"+(ax?" ":"")+aB)}});ao=ar.length?(ax?"{\n"+ac+ar.join(",\n"+ac)+"\n"+av+"}":("{"+ar.join(",")+"}")):"{}"}aj.pop();return ao}};V.stringify=function(ac,ae,af){var ad,al,aj,ai;if(e[typeof ae]&&ae){if((ai=u.call(ae))==U){al=ae}else{if(ai==E){aj={};for(var ah=0,ag=ae.length,ak;ah<ag;ak=ae[ah++],((ai=u.call(ak)),ai==O||ai==N)&&(aj[ak]=1)){}}}}if(af){if((ai=u.call(af))==N){if((af-=af%1)>0){for(ad="",af>10&&(af=10); ad.length<af;ad+=" "){}}}else{if(ai==O){ad=af.length<=10?af:af.slice(0,10)}}}return p("",(ak={},ak[""]=ac,ak),al,aj,ad,"",[])}}if(!o("json-parse")){var M=R.fromCharCode;var l={92:"\\",34:'"',47:"/",98:"\b",116:"\t",110:"\n",102:"\f",114:"\r"};var G,X;var H=function(){G=X=null;throw T()};var y=function(){var ah=X,af=ah.length,ag,ae,ac,ai,ad;while(G<af){ad=ah.charCodeAt(G);switch(ad){case 9:case 10:case 13:case 32:G++;break;case 123:case 125:case 91:case 93:case 58:case 44:ag=F?ah.charAt(G):ah[G];G++;return ag;case 34:for(ag="@",G++;G<af;){ad=ah.charCodeAt(G);if(ad<32){H()}else{if(ad==92){ad=ah.charCodeAt(++G);switch(ad){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:ag+=l[ad];G++;break;case 117:ae=++G;for(ac=G+4;G<ac;G++){ad=ah.charCodeAt(G);if(!(ad>=48&&ad<=57||ad>=97&&ad<=102||ad>=65&&ad<=70)){H()}}ag+=M("0x"+ah.slice(ae,G));break;default:H()}}else{if(ad==34){break}ad=ah.charCodeAt(G);ae=G;while(ad>=32&&ad!=92&&ad!=34){ad=ah.charCodeAt(++G)}ag+=ah.slice(ae,G)}}}if(ah.charCodeAt(G)==34){G++; return ag}H();default:ae=G;if(ad==45){ai=true;ad=ah.charCodeAt(++G)}if(ad>=48&&ad<=57){if(ad==48&&((ad=ah.charCodeAt(G+1)),ad>=48&&ad<=57)){H()}ai=false;for(;G<af&&((ad=ah.charCodeAt(G)),ad>=48&&ad<=57);G++){}if(ah.charCodeAt(G)==46){ac=++G;for(;ac<af&&((ad=ah.charCodeAt(ac)),ad>=48&&ad<=57);ac++){}if(ac==G){H()}G=ac}ad=ah.charCodeAt(G);if(ad==101||ad==69){ad=ah.charCodeAt(++G);if(ad==43||ad==45){G++}for(ac=G;ac<af&&((ad=ah.charCodeAt(ac)),ad>=48&&ad<=57);ac++){}if(ac==G){H()}G=ac}return +ah.slice(ae,G)}if(ai){H()}if(ah.slice(G,G+4)=="true"){G+=4;return true}else{if(ah.slice(G,G+5)=="false"){G+=5;return false}else{if(ah.slice(G,G+4)=="null"){G+=4;return null}}}H()}}return"$"};var W=function(ad){var ac,ae;if(ad=="$"){H()}if(typeof ad=="string"){if((F?ad.charAt(0):ad[0])=="@"){return ad.slice(1)}if(ad=="["){ac=[];for(;;ae||(ae=true)){ad=y();if(ad=="]"){break}if(ae){if(ad==","){ad=y();if(ad=="]"){H()}}else{H()}}if(ad==","){H()}ac.push(W(ad))}return ac}else{if(ad=="{"){ac={};for(;;ae||(ae=true)){ad=y(); -if(ad=="}"){break}if(ae){if(ad==","){ad=y();if(ad=="}"){H()}}else{H()}}if(ad==","||typeof ad!="string"||(F?ad.charAt(0):ad[0])!="@"||y()!=":"){H()}ac[ad.slice(1)]=W(y())}return ac}}H()}return ad};var P=function(ae,ad,af){var ac=w(ae,ad,af);if(ac===L){delete ae[ad]}else{ae[ad]=ac}};var w=function(af,ae,ag){var ad=af[ae],ac;if(typeof ad=="object"&&ad){if(u.call(ad)==E){for(ac=ad.length;ac--;){P(ad,ac,ag)}}else{m(ad,function(ah){P(ad,ah,ag)})}}return ag.call(af,ae,ad)};V.parse=function(ae,af){var ac,ad;G=0;X=""+ae;ac=W(y());if(y()!="$"){H()}G=X=null;return af&&u.call(af)==U?w((ad={},ad[""]=ac,ad),"",af):ac}}}V.runInContext=j;return V}if(h&&!c){j(i,h)}else{var f=i.JSON,k=i.JSON3,d=false;var g=j(i,(i.JSON3={noConflict:function(){if(!d){d=true;i.JSON=f;i.JSON3=k;f=k=null}return g}}));i.JSON={parse:g.parse,stringify:g.stringify}}if(c){define(function(){return g})}}).call(this);JSON2=a})()}if(typeof _paq!=="object"){_paq=[]}if(typeof Piwik!=="object"){Piwik=(function(){var k,a={},v=document,e=navigator,L=screen,H=window,f=H.performance||H.mozPerformance||H.msPerformance||H.webkitPerformance,q=false,F=[],m=H.encodeURIComponent,G=H.decodeURIComponent,h=unescape,M,u,d; -function j(X){try{return G(X)}catch(Y){return unescape(X)}}function x(Y){var X=typeof Y;return X!=="undefined"}function r(X){return typeof X==="function"}function K(X){return typeof X==="object"}function o(X){return typeof X==="string"||X instanceof String}function S(){var X,Z,Y;for(X=0;X<arguments.length;X+=1){Y=arguments[X];Z=Y.shift();if(o(Z)){M[Z].apply(M,Y)}else{Z.apply(M,Y)}}}function W(aa,Z,Y,X){if(aa.addEventListener){aa.addEventListener(Z,Y,X);return true}if(aa.attachEvent){return aa.attachEvent("on"+Z,Y)}aa["on"+Z]=Y}function P(Y,ab){var X="",aa,Z;for(aa in a){if(Object.prototype.hasOwnProperty.call(a,aa)){Z=a[aa][Y];if(r(Z)){X+=Z(ab)}}}return X}function T(){var X;P("unload");if(k){do{X=new Date()}while(X.getTimeAlias()<k)}}function Q(){var X;if(!q){q=true;P("load");for(X=0;X<F.length;X++){F[X]()}}return true}function p(){var Y;if(v.addEventListener){W(v,"DOMContentLoaded",function X(){v.removeEventListener("DOMContentLoaded",X,false);Q()})}else{if(v.attachEvent){v.attachEvent("onreadystatechange",function X(){if(v.readyState==="complete"){v.detachEvent("onreadystatechange",X); -Q()}});if(v.documentElement.doScroll&&H===H.top){(function X(){if(!q){try{v.documentElement.doScroll("left")}catch(Z){setTimeout(X,0);return}Q()}}())}}}if((new RegExp("WebKit")).test(e.userAgent)){Y=setInterval(function(){if(q||/loaded|complete/.test(v.readyState)){clearInterval(Y);Q()}},10)}W(H,"load",Q,false)}function i(Z,Y){var X=v.createElement("script");X.type="text/javascript";X.src=Z;if(X.readyState){X.onreadystatechange=function(){var aa=this.readyState;if(aa==="loaded"||aa==="complete"){X.onreadystatechange=null;Y()}}}else{X.onload=Y}v.getElementsByTagName("head")[0].appendChild(X)}function y(){var X="";try{X=H.top.document.referrer}catch(Z){if(H.parent){try{X=H.parent.document.referrer}catch(Y){X=""}}}if(X===""){X=v.referrer}return X}function l(X){var Z=new RegExp("^([a-z]+):"),Y=Z.exec(X);return Y?Y[1]:null}function c(X){var Z=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),Y=Z.exec(X);return Y?Y[1]:X}function J(Z,Y){var X="[\\?&#]"+Y+"=([^&#]*)";var ab=new RegExp(X); -var aa=ab.exec(Z);return aa?G(aa[1]):""}function t(X){return unescape(m(X))}function V(am){var Z=function(at,ar){return(at<<ar)|(at>>>(32-ar))},an=function(av){var at="",au,ar;for(au=7;au>=0;au--){ar=(av>>>(au*4))&15;at+=ar.toString(16)}return at},ac,ap,ao,Y=[],ag=1732584193,ae=4023233417,ad=2562383102,ab=271733878,aa=3285377520,al,ak,aj,ai,ah,aq,X,af=[];am=t(am);X=am.length;for(ap=0;ap<X-3;ap+=4){ao=am.charCodeAt(ap)<<24|am.charCodeAt(ap+1)<<16|am.charCodeAt(ap+2)<<8|am.charCodeAt(ap+3);af.push(ao)}switch(X&3){case 0:ap=2147483648;break;case 1:ap=am.charCodeAt(X-1)<<24|8388608;break;case 2:ap=am.charCodeAt(X-2)<<24|am.charCodeAt(X-1)<<16|32768;break;case 3:ap=am.charCodeAt(X-3)<<24|am.charCodeAt(X-2)<<16|am.charCodeAt(X-1)<<8|128;break}af.push(ap);while((af.length&15)!==14){af.push(0)}af.push(X>>>29);af.push((X<<3)&4294967295);for(ac=0;ac<af.length;ac+=16){for(ap=0;ap<16;ap++){Y[ap]=af[ac+ap]}for(ap=16;ap<=79;ap++){Y[ap]=Z(Y[ap-3]^Y[ap-8]^Y[ap-14]^Y[ap-16],1)}al=ag;ak=ae;aj=ad;ai=ab;ah=aa; -for(ap=0;ap<=19;ap++){aq=(Z(al,5)+((ak&aj)|(~ak&ai))+ah+Y[ap]+1518500249)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}for(ap=20;ap<=39;ap++){aq=(Z(al,5)+(ak^aj^ai)+ah+Y[ap]+1859775393)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}for(ap=40;ap<=59;ap++){aq=(Z(al,5)+((ak&aj)|(ak&ai)|(aj&ai))+ah+Y[ap]+2400959708)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}for(ap=60;ap<=79;ap++){aq=(Z(al,5)+(ak^aj^ai)+ah+Y[ap]+3395469782)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}ag=(ag+al)&4294967295;ae=(ae+ak)&4294967295;ad=(ad+aj)&4294967295;ab=(ab+ai)&4294967295;aa=(aa+ah)&4294967295}aq=an(ag)+an(ae)+an(ad)+an(ab)+an(aa);return aq.toLowerCase()}function O(Z,X,Y){if(Z==="translate.googleusercontent.com"){if(Y===""){Y=X}X=J(X,"u");Z=c(X)}else{if(Z==="cc.bingj.com"||Z==="webcache.googleusercontent.com"||Z.slice(0,5)==="74.6."){X=v.links[0].href;Z=c(X)}}return[Z,X,Y]}function z(Y){var X=Y.length;if(Y.charAt(--X)==="."){Y=Y.slice(0,X)}if(Y.slice(0,2)==="*."){Y=Y.slice(1)}return Y}function U(Y){Y=Y&&Y.text?Y.text:Y; -if(!o(Y)){var X=v.getElementsByTagName("title");if(X&&x(X[0])){Y=X[0].text}}return Y}function D(X){if(!X){return[]}if(!x(X.children)&&x(X.childNodes)){return X.children}if(x(X.children)){return X.children}return[]}function I(Y,X){if(!Y||!X){return false}if(Y.contains){return Y.contains(X)}if(Y===X){return true}if(Y.compareDocumentPosition){return !!(Y.compareDocumentPosition(X)&16)}return false}function A(Z,aa){if(Z&&Z.indexOf){return Z.indexOf(aa)}if(!x(Z)||Z===null){return -1}if(!Z.length){return -1}var X=Z.length;if(X===0){return -1}var Y=0;while(Y<X){if(Z[Y]===aa){return Y}Y++}return -1}function g(Z){if(!Z){return false}function X(ab,ac){if(H.getComputedStyle){return v.defaultView.getComputedStyle(ab,null)[ac]}if(ab.currentStyle){return ab.currentStyle[ac]}}function aa(ab){ab=ab.parentNode;while(ab){if(ab===v){return true}ab=ab.parentNode}return false}function Y(ad,aj,ab,ag,ae,ah,af){var ac=ad.parentNode,ai=1;if(!aa(ad)){return false}if(9===ac.nodeType){return true}if("0"===X(ad,"opacity")||"none"===X(ad,"display")||"hidden"===X(ad,"visibility")){return false -}if(!x(aj)||!x(ab)||!x(ag)||!x(ae)||!x(ah)||!x(af)){aj=ad.offsetTop;ae=ad.offsetLeft;ag=aj+ad.offsetHeight;ab=ae+ad.offsetWidth;ah=ad.offsetWidth;af=ad.offsetHeight}if(Z===ad&&(0===af||0===ah)&&"hidden"===X(ad,"overflow")){return false}if(ac){if(("hidden"===X(ac,"overflow")||"scroll"===X(ac,"overflow"))){if(ae+ai>ac.offsetWidth+ac.scrollLeft||ae+ah-ai<ac.scrollLeft||aj+ai>ac.offsetHeight+ac.scrollTop||aj+af-ai<ac.scrollTop){return false}}if(ad.offsetParent===ac){ae+=ac.offsetLeft;aj+=ac.offsetTop}return Y(ac,aj,ab,ag,ae,ah,af)}return true}return Y(Z)}var R={htmlCollectionToArray:function(Z){var X=[],Y;if(!Z||!Z.length){return X}for(Y=0;Y<Z.length;Y++){X.push(Z[Y])}return X},find:function(X){if(!document.querySelectorAll||!X){return[]}var Y=document.querySelectorAll(X);return this.htmlCollectionToArray(Y)},findMultiple:function(Z){if(!Z||!Z.length){return[]}var Y,aa;var X=[];for(Y=0;Y<Z.length;Y++){aa=this.find(Z[Y]);X=X.concat(aa)}X=this.makeNodesUnique(X);return X},findNodesByTagName:function(Y,X){if(!Y||!X||!Y.getElementsByTagName){return[] -}var Z=Y.getElementsByTagName(X);return this.htmlCollectionToArray(Z)},makeNodesUnique:function(X){var ac=[].concat(X);X.sort(function(ae,ad){if(ae===ad){return 0}var ag=A(ac,ae);var af=A(ac,ad);if(ag===af){return 0}return ag>af?-1:1});if(X.length<=1){return X}var Y=0;var aa=0;var ab=[];var Z;Z=X[Y++];while(Z){if(Z===X[Y]){aa=ab.push(Y)}Z=X[Y++]||null}while(aa--){X.splice(ab[aa],1)}return X},getAttributeValueFromNode:function(ab,Z){if(!this.hasNodeAttribute(ab,Z)){return}if(ab&&ab.getAttribute){return ab.getAttribute(Z)}if(!ab||!ab.attributes){return}var aa=(typeof ab.attributes[Z]);if("undefined"===aa){return}if(ab.attributes[Z].value){return ab.attributes[Z].value}if(ab.attributes[Z].nodeValue){return ab.attributes[Z].nodeValue}var Y;var X=ab.attributes;if(!X){return}for(Y=0;Y<X.length;Y++){if(X[Y].nodeName===Z){return X[Y].nodeValue}}return null},hasNodeAttributeWithValue:function(Y,X){var Z=this.getAttributeValueFromNode(Y,X);return !!Z},hasNodeAttribute:function(Z,X){if(Z&&Z.hasAttribute){return Z.hasAttribute(X) -}if(Z&&Z.attributes){var Y=(typeof Z.attributes[X]);return"undefined"!==Y}return false},hasNodeCssClass:function(Z,X){if(Z&&X&&Z.className){var Y=typeof Z.className==="string"?Z.className.split(" "):[];if(-1!==A(Y,X)){return true}}return false},findNodesHavingAttribute:function(ab,Z,X){if(!X){X=[]}if(!ab||!Z){return X}var aa=D(ab);if(!aa||!aa.length){return X}var Y,ac;for(Y=0;Y<aa.length;Y++){ac=aa[Y];if(this.hasNodeAttribute(ac,Z)){X.push(ac)}X=this.findNodesHavingAttribute(ac,Z,X)}return X},findFirstNodeHavingAttribute:function(Z,Y){if(!Z||!Y){return}if(this.hasNodeAttribute(Z,Y)){return Z}var X=this.findNodesHavingAttribute(Z,Y);if(X&&X.length){return X[0]}},findFirstNodeHavingAttributeWithValue:function(aa,Z){if(!aa||!Z){return}if(this.hasNodeAttributeWithValue(aa,Z)){return aa}var X=this.findNodesHavingAttribute(aa,Z);if(!X||!X.length){return}var Y;for(Y=0;Y<X.length;Y++){if(this.getAttributeValueFromNode(X[Y],Z)){return X[Y]}}},findNodesHavingCssClass:function(ab,aa,X){if(!X){X=[] -}if(!ab||!aa){return X}if(ab.getElementsByClassName){var ac=ab.getElementsByClassName(aa);return this.htmlCollectionToArray(ac)}var Z=D(ab);if(!Z||!Z.length){return[]}var Y,ad;for(Y=0;Y<Z.length;Y++){ad=Z[Y];if(this.hasNodeCssClass(ad,aa)){X.push(ad)}X=this.findNodesHavingCssClass(ad,aa,X)}return X},findFirstNodeHavingClass:function(Z,Y){if(!Z||!Y){return}if(this.hasNodeCssClass(Z,Y)){return Z}var X=this.findNodesHavingCssClass(Z,Y);if(X&&X.length){return X[0]}},isLinkElement:function(Y){if(!Y){return false}var X=String(Y.nodeName).toLowerCase();var aa=["a","area"];var Z=A(aa,X);return Z!==-1},setAnyAttribute:function(Y,X,Z){if(!Y||!X){return}if(Y.setAttribute){Y.setAttribute(X,Z)}else{Y[X]=Z}}};var n={CONTENT_ATTR:"data-track-content",CONTENT_CLASS:"piwikTrackContent",CONTENT_NAME_ATTR:"data-content-name",CONTENT_PIECE_ATTR:"data-content-piece",CONTENT_PIECE_CLASS:"piwikContentPiece",CONTENT_TARGET_ATTR:"data-content-target",CONTENT_TARGET_CLASS:"piwikContentTarget",CONTENT_IGNOREINTERACTION_ATTR:"data-content-ignoreinteraction",CONTENT_IGNOREINTERACTION_CLASS:"piwikContentIgnoreInteraction",location:undefined,findContentNodes:function(){var Y="."+this.CONTENT_CLASS; -var X="["+this.CONTENT_ATTR+"]";var Z=R.findMultiple([Y,X]);return Z},findContentNodesWithinNode:function(aa){if(!aa){return[]}var Y=R.findNodesHavingCssClass(aa,this.CONTENT_CLASS);var X=R.findNodesHavingAttribute(aa,this.CONTENT_ATTR);if(X&&X.length){var Z;for(Z=0;Z<X.length;Z++){Y.push(X[Z])}}if(R.hasNodeAttribute(aa,this.CONTENT_ATTR)){Y.push(aa)}else{if(R.hasNodeCssClass(aa,this.CONTENT_CLASS)){Y.push(aa)}}Y=R.makeNodesUnique(Y);return Y},findParentContentNode:function(Y){if(!Y){return}var Z=Y;var X=0;while(Z&&Z!==v&&Z.parentNode){if(R.hasNodeAttribute(Z,this.CONTENT_ATTR)){return Z}if(R.hasNodeCssClass(Z,this.CONTENT_CLASS)){return Z}Z=Z.parentNode;if(X>1000){break}X++}},findPieceNode:function(Y){var X;X=R.findFirstNodeHavingAttribute(Y,this.CONTENT_PIECE_ATTR);if(!X){X=R.findFirstNodeHavingClass(Y,this.CONTENT_PIECE_CLASS)}if(X){return X}return Y},findTargetNodeNoDefault:function(X){if(!X){return}var Y=R.findFirstNodeHavingAttributeWithValue(X,this.CONTENT_TARGET_ATTR);if(Y){return Y -}Y=R.findFirstNodeHavingAttribute(X,this.CONTENT_TARGET_ATTR);if(Y){return Y}Y=R.findFirstNodeHavingClass(X,this.CONTENT_TARGET_CLASS);if(Y){return Y}},findTargetNode:function(X){var Y=this.findTargetNodeNoDefault(X);if(Y){return Y}return X},findContentName:function(Y){if(!Y){return}var ab=R.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_NAME_ATTR);if(ab){return R.getAttributeValueFromNode(ab,this.CONTENT_NAME_ATTR)}var X=this.findContentPiece(Y);if(X){return this.removeDomainIfIsInLink(X)}if(R.hasNodeAttributeWithValue(Y,"title")){return R.getAttributeValueFromNode(Y,"title")}var Z=this.findPieceNode(Y);if(R.hasNodeAttributeWithValue(Z,"title")){return R.getAttributeValueFromNode(Z,"title")}var aa=this.findTargetNode(Y);if(R.hasNodeAttributeWithValue(aa,"title")){return R.getAttributeValueFromNode(aa,"title")}},findContentPiece:function(Y){if(!Y){return}var aa=R.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_PIECE_ATTR);if(aa){return R.getAttributeValueFromNode(aa,this.CONTENT_PIECE_ATTR) -}var X=this.findPieceNode(Y);var Z=this.findMediaUrlInNode(X);if(Z){return this.toAbsoluteUrl(Z)}},findContentTarget:function(Z){if(!Z){return}var aa=this.findTargetNode(Z);if(R.hasNodeAttributeWithValue(aa,this.CONTENT_TARGET_ATTR)){return R.getAttributeValueFromNode(aa,this.CONTENT_TARGET_ATTR)}var Y;if(R.hasNodeAttributeWithValue(aa,"href")){Y=R.getAttributeValueFromNode(aa,"href");return this.toAbsoluteUrl(Y)}var X=this.findPieceNode(Z);if(R.hasNodeAttributeWithValue(X,"href")){Y=R.getAttributeValueFromNode(X,"href");return this.toAbsoluteUrl(Y)}},isSameDomain:function(X){if(!X||!X.indexOf){return false}if(0===X.indexOf(this.getLocation().origin)){return true}var Y=X.indexOf(this.getLocation().host);if(8>=Y&&0<=Y){return true}return false},removeDomainIfIsInLink:function(Z){var Y="^https?://[^/]+";var X="^.*//[^/]+";if(Z&&Z.search&&-1!==Z.search(new RegExp(Y))&&this.isSameDomain(Z)){Z=Z.replace(new RegExp(X),"");if(!Z){Z="/"}}return Z},findMediaUrlInNode:function(ab){if(!ab){return}var Z=["img","embed","video","audio"]; -var X=ab.nodeName.toLowerCase();if(-1!==A(Z,X)&&R.findFirstNodeHavingAttributeWithValue(ab,"src")){var aa=R.findFirstNodeHavingAttributeWithValue(ab,"src");return R.getAttributeValueFromNode(aa,"src")}if(X==="object"&&R.hasNodeAttributeWithValue(ab,"data")){return R.getAttributeValueFromNode(ab,"data")}if(X==="object"){var ac=R.findNodesByTagName(ab,"param");if(ac&&ac.length){var Y;for(Y=0;Y<ac.length;Y++){if("movie"===R.getAttributeValueFromNode(ac[Y],"name")&&R.hasNodeAttributeWithValue(ac[Y],"value")){return R.getAttributeValueFromNode(ac[Y],"value")}}}var ad=R.findNodesByTagName(ab,"embed");if(ad&&ad.length){return this.findMediaUrlInNode(ad[0])}}},trim:function(X){if(X&&String(X)===X){return X.replace(/^\s+|\s+$/g,"")}return X},isOrWasNodeInViewport:function(ac){if(!ac||!ac.getBoundingClientRect||ac.nodeType!==1){return true}var ab=ac.getBoundingClientRect();var aa=v.documentElement||{};var Z=ab.top<0;if(Z&&ac.offsetTop){Z=(ac.offsetTop+ab.height)>0}var Y=aa.clientWidth;if(H.innerWidth&&Y>H.innerWidth){Y=H.innerWidth -}var X=aa.clientHeight;if(H.innerHeight&&X>H.innerHeight){X=H.innerHeight}return((ab.bottom>0||Z)&&ab.right>0&&ab.left<Y&&((ab.top<X)||Z))},isNodeVisible:function(Y){var X=g(Y);var Z=this.isOrWasNodeInViewport(Y);return X&&Z},buildInteractionRequestParams:function(X,Y,Z,aa){var ab="";if(X){ab+="c_i="+m(X)}if(Y){if(ab){ab+="&"}ab+="c_n="+m(Y)}if(Z){if(ab){ab+="&"}ab+="c_p="+m(Z)}if(aa){if(ab){ab+="&"}ab+="c_t="+m(aa)}return ab},buildImpressionRequestParams:function(X,Y,Z){var aa="c_n="+m(X)+"&c_p="+m(Y);if(Z){aa+="&c_t="+m(Z)}return aa},buildContentBlock:function(Z){if(!Z){return}var X=this.findContentName(Z);var Y=this.findContentPiece(Z);var aa=this.findContentTarget(Z);X=this.trim(X);Y=this.trim(Y);aa=this.trim(aa);return{name:X||"Unknown",piece:Y||"Unknown",target:aa||""}},collectContent:function(aa){if(!aa||!aa.length){return[]}var Z=[];var X,Y;for(X=0;X<aa.length;X++){Y=this.buildContentBlock(aa[X]);if(x(Y)){Z.push(Y)}}return Z},setLocation:function(X){this.location=X},getLocation:function(){var X=this.location||H.location; -if(!X.origin){X.origin=X.protocol+"//"+X.hostname+(X.port?":"+X.port:"")}return X},toAbsoluteUrl:function(Y){if((!Y||String(Y)!==Y)&&Y!==""){return Y}if(""===Y){return this.getLocation().href}if(Y.search(/^\/\//)!==-1){return this.getLocation().protocol+Y}if(Y.search(/:\/\//)!==-1){return Y}if(0===Y.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+Y}if(0===Y.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+Y}if(0===Y.search("^[a-zA-Z]{2,11}:")){return Y}if(Y.search(/^\//)!==-1){return this.getLocation().origin+Y}var X="(.*/)";var Z=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(X))[0];return Z+Y},isUrlToCurrentDomain:function(Y){var Z=this.toAbsoluteUrl(Y);if(!Z){return false}var X=this.getLocation().origin;if(X===Z){return true}if(0===String(Z).indexOf(X)){if(":"===String(Z).substr(X.length,1)){return false}return true}return false},setHrefAttribute:function(Y,X){if(!Y||!X){return}R.setAnyAttribute(Y,"href",X)},shouldIgnoreInteraction:function(Z){var Y=R.hasNodeAttribute(Z,this.CONTENT_IGNOREINTERACTION_ATTR); -var X=R.hasNodeCssClass(Z,this.CONTENT_IGNOREINTERACTION_CLASS);return Y||X}};function C(X,Y){if(Y){return Y}if(X.slice(-9)==="piwik.php"){X=X.slice(0,X.length-9)}return X}function B(ab){var X="Piwik_Overlay";var ae=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)$");var Z=ae.exec(v.referrer);if(Z){var aa=Z[1];if(aa!==String(ab)){return false}var ad=Z[2],Y=Z[3];H.name=X+"###"+ad+"###"+Y}var ac=H.name.split("###");return ac.length===3&&ac[0]===X}function N(Y,ad,aa){var ac=H.name.split("###"),ab=ac[1],X=ac[2],Z=C(Y,ad);i(Z+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(Z,aa,ab,X)})}function E(aJ,by){var ae=O(v.domain,H.location.href,y()),bY=z(ae[0]),cj=j(ae[1]),bG=j(ae[2]),cn=false,bC="GET",bE=bC,bj="application/x-www-form-urlencoded; charset=UTF-8",aP=bj,ab=aJ||"",az="",bA="",b5=by||"",aO="",a7="",bd,aX=v.title,aZ=["7z","aac","apk","arc","arj","asf","asx","avi","azw3","bin","csv","deb","dmg","doc","docx","epub","exe","flv","gif","gz","gzip","hqx","ibooks","jar","jpg","jpeg","js","mobi","mp2","mp3","mp4","mpg","mpeg","mov","movie","msi","msp","odb","odf","odg","ods","odt","ogg","ogv","pdf","phps","png","ppt","pptx","qt","qtm","ra","ram","rar","rpm","sea","sit","tar","tbz","tbz2","bz","bz2","tgz","torrent","txt","wav","wma","wmv","wpd","xls","xlsx","xml","z","zip"],bB=[bY],aj=[],bp=[],aH=[],bz=500,ak,b3,br,al,ao,a3=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],aU=["pk_kwd","piwik_kwd","utm_term"],ch="_pk_",ar,ci,ap=false,b9,a5,ba,ay=33955200000,aE=1800000,bh=15768000000,a6=true,aM=0,bc=false,ah=false,av,bq={},ac={},ca=200,bR={},b6={},ai=[],aB=false,bg=false,bK=false,b7=false,bN=false,bo=null,bb,bu,au,a2=V,bJ; -function bT(cw,ct,cs,cv,cr,cu){if(ap){return}var cq;if(cs){cq=new Date();cq.setTime(cq.getTime()+cs)}v.cookie=cw+"="+m(ct)+(cs?";expires="+cq.toGMTString():"")+";path="+(cv||"/")+(cr?";domain="+cr:"")+(cu?";secure":"")}function aw(cs){if(ap){return 0}var cq=new RegExp("(^|;)[ ]*"+cs+"=([^;]*)"),cr=cq.exec(v.cookie);return cr?G(cr[2]):0}function cd(cq){var cr;if(al){cr=new RegExp("#.*");return cq.replace(cr,"")}return cq}function bX(cs,cq){var ct=l(cq),cr;if(ct){return cq}if(cq.slice(0,1)==="/"){return l(cs)+"://"+c(cs)+cq}cs=cd(cs);cr=cs.indexOf("?");if(cr>=0){cs=cs.slice(0,cr)}cr=cs.lastIndexOf("/");if(cr!==cs.length-1){cs=cs.slice(0,cr+1)}return cs+cq}function bD(ct){var cr,cq,cs;for(cr=0;cr<bB.length;cr++){cq=z(bB[cr].toLowerCase());if(ct===cq){return true}if(cq.slice(0,1)==="."){if(ct===cq.slice(1)){return true}cs=ct.length-cq.length;if((cs>0)&&(ct.slice(cs)===cq)){return true}}}return false}function cp(cq,cs){var cr=new Image(1,1);cr.onload=function(){u=0;if(typeof cs==="function"){cs() -}};cr.src=ab+(ab.indexOf("?")<0?"?":"&")+cq}function bU(cr,cu,cq){if(!x(cq)||null===cq){cq=true}try{var ct=H.XMLHttpRequest?new H.XMLHttpRequest():H.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;ct.open("POST",ab,true);ct.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cq){cp(cr,cu)}else{if(typeof cu==="function"){cu()}}};ct.setRequestHeader("Content-Type",aP);ct.send(cr)}catch(cs){if(cq){cp(cr,cu)}}}function cc(cr){var cq=new Date();var cs=cq.getTime()+cr;if(!k||cs>k){k=cs}}function aC(cq){if(bb||!b3){return}bb=setTimeout(function cr(){bb=null;if(br()){return}var cs=new Date(),ct=b3-(cs.getTime()-bo);ct=Math.min(b3,ct);aC(ct)},cq||b3)}function be(){if(!bb){return}clearTimeout(bb);bb=null}function ax(){if(br()){return}aC()}function bm(){be()}function bF(){if(bN||!b3){return}bN=true;W(H,"focus",ax);W(H,"blur",bm);aC()}function aI(cu){var cr=new Date();var cq=cr.getTime();bo=cq;if(bg&&cq<bg){var cs=bg-cq;setTimeout(cu,cs);cc(cs+50);bg+=50; -return}if(bg===false){var ct=800;bg=cq+ct}cu()}function a4(cr,cq,cs){if(!b9&&cr){aI(function(){if(bE==="POST"){bU(cr,cs)}else{cp(cr,cs)}cc(cq)})}if(!bN){bF()}else{aC()}}function bi(cq){if(b9){return false}return(cq&&cq.length)}function aq(cs,cq){if(!bi(cs)){return}var cr='{"requests":["?'+cs.join('","?')+'"]}';aI(function(){bU(cr,null,false);cc(cq)})}function bS(cq){return ch+cq+"."+b5+"."+bJ}function af(){if(ap){return"0"}if(!x(e.cookieEnabled)){var cq=bS("testcookie");bT(cq,"1");return aw(cq)==="1"?"1":"0"}return e.cookieEnabled?"1":"0"}function bv(){bJ=a2((ar||bY)+(ci||"/")).slice(0,4)}function at(){var cr=bS("cvar"),cq=aw(cr);if(cq.length){cq=JSON2.parse(cq);if(K(cq)){return cq}}return{}}function aa(){if(ah===false){ah=at()}}function cf(){return a2((e.userAgent||"")+(e.platform||"")+JSON2.stringify(b6)+(new Date()).getTime()+Math.random()).slice(0,16)}function Z(){var cs=new Date(),cq=Math.round(cs.getTime()/1000),cr=bS("id"),cv=aw(cr),cu,ct;if(cv){cu=cv.split(".");cu.unshift("0");if(a7.length){cu[1]=a7 -}return cu}if(a7.length){ct=a7}else{if("0"===af()){ct=""}else{ct=cf()}}cu=["1",ct,cq,0,cq,"",""];return cu}function bM(){var cx=Z(),ct=cx[0],cu=cx[1],cr=cx[2],cq=cx[3],cv=cx[4],cs=cx[5];if(!x(cx[6])){cx[6]=""}var cw=cx[6];return{newVisitor:ct,uuid:cu,createTs:cr,visitCount:cq,currentVisitTs:cv,lastVisitTs:cs,lastEcommerceOrderTs:cw}}function aQ(){var ct=new Date(),cr=ct.getTime(),cu=bM().createTs;var cq=parseInt(cu,10);var cs=(cq*1000)+ay-cr;return cs}function an(cq){if(!b5){return}var cs=new Date(),cr=Math.round(cs.getTime()/1000);if(!x(cq)){cq=bM()}var ct=cq.uuid+"."+cq.createTs+"."+cq.visitCount+"."+cr+"."+cq.lastVisitTs+"."+cq.lastEcommerceOrderTs;bT(bS("id"),ct,aQ(),ci,ar)}function Y(){var cq=aw(bS("ref"));if(cq.length){try{cq=JSON2.parse(cq);if(K(cq)){return cq}}catch(cr){}}return["","",0,""]}function b4(cs,cr,cq){bT(cs,"",-86400,cr,cq)}function bn(cr){var cq="testvalue";bT("test",cq,10000,null,cr);if(aw("test")===cq){b4("test",null,cr);return true}return false}function X(){var cs=ap; -ap=false;var cq=["id","ses","cvar","ref"];var cr,ct;for(cr=0;cr<cq.length;cr++){ct=bS(cq[cr]);if(0!==aw(ct)){b4(ct,ci,ar)}}ap=cs}function cm(cq){b5=cq;an()}function b2(cu){if(!cu||!K(cu)){return}var ct=[];var cs;for(cs in cu){if(Object.prototype.hasOwnProperty.call(cu,cs)){ct.push(cs)}}var cv={};ct.sort();var cq=ct.length;var cr;for(cr=0;cr<cq;cr++){cv[ct[cr]]=cu[ct[cr]]}return cv}function a9(){bT(bS("ses"),"*",aE,ci,ar)}function aY(cs,cL,cM,ct){var cK,cr=new Date(),cz=Math.round(cr.getTime()/1000),cw,cJ,cu=1024,cQ,cA,cH=ah,cv=bS("ses"),cF=bS("ref"),cC=bS("cvar"),cD=aw(cv),cI=Y(),cO=bd||cj,cx,cq;if(ap){X()}if(b9){return""}var cE=bM();if(!x(ct)){ct=""}var cB=v.characterSet||v.charset;if(!cB||cB.toLowerCase()==="utf-8"){cB=null}cx=cI[0];cq=cI[1];cw=cI[2];cJ=cI[3];if(!cD){var cN=aE/1000;if(!cE.lastVisitTs||(cz-cE.lastVisitTs)>cN){cE.visitCount++;cE.lastVisitTs=cE.currentVisitTs}if(!ba||!cx.length){for(cK in a3){if(Object.prototype.hasOwnProperty.call(a3,cK)){cx=J(cO,a3[cK]);if(cx.length){break -}}}for(cK in aU){if(Object.prototype.hasOwnProperty.call(aU,cK)){cq=J(cO,aU[cK]);if(cq.length){break}}}}cQ=c(bG);cA=cJ.length?c(cJ):"";if(cQ.length&&!bD(cQ)&&(!ba||!cA.length||bD(cA))){cJ=bG}if(cJ.length||cx.length){cw=cz;cI=[cx,cq,cw,cd(cJ.slice(0,cu))];bT(cF,JSON2.stringify(cI),bh,ci,ar)}}cs+="&idsite="+b5+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cr.getHours()+"&m="+cr.getMinutes()+"&s="+cr.getSeconds()+"&url="+m(cd(cO))+(bG.length?"&urlref="+m(cd(bG)):"")+((aO&&aO.length)?"&uid="+m(aO):"")+"&_id="+cE.uuid+"&_idts="+cE.createTs+"&_idvc="+cE.visitCount+"&_idn="+cE.newVisitor+(cx.length?"&_rcn="+m(cx):"")+(cq.length?"&_rck="+m(cq):"")+"&_refts="+cw+"&_viewts="+cE.lastVisitTs+(String(cE.lastEcommerceOrderTs).length?"&_ects="+cE.lastEcommerceOrderTs:"")+(String(cJ).length?"&_ref="+m(cd(cJ.slice(0,cu))):"")+(cB?"&cs="+m(cB):"")+"&send_image=0";for(cK in b6){if(Object.prototype.hasOwnProperty.call(b6,cK)){cs+="&"+cK+"="+b6[cK]}}if(cL){cs+="&data="+m(JSON2.stringify(cL))}else{if(ao){cs+="&data="+m(JSON2.stringify(ao)) -}}function cy(cR,cS){var cT=JSON2.stringify(cR);if(cT.length>2){return"&"+cS+"="+m(cT)}return""}var cP=b2(bq);var cG=b2(ac);cs+=cy(cP,"cvar");cs+=cy(cG,"e_cvar");if(ah){cs+=cy(ah,"_cvar");for(cK in cH){if(Object.prototype.hasOwnProperty.call(cH,cK)){if(ah[cK][0]===""||ah[cK][1]===""){delete ah[cK]}}}if(bc){bT(cC,JSON2.stringify(ah),aE,ci,ar)}}if(a6){if(aM){cs+=">_ms="+aM}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cs+=">_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cE.lastEcommerceOrderTs=x(ct)&&String(ct).length?ct:cE.lastEcommerceOrderTs;an(cE);a9();cs+=P(cM);if(bA.length){cs+="&"+bA}if(r(av)){cs=av(cs)}return cs}br=function bw(){var cq=new Date();if(bo+b3<=cq.getTime()){var cr=aY("ping=1",null,"ping");a4(cr,bz);return true}return false};function bW(ct,cs,cx,cu,cq,cA){var cv="idgoal=0",cw,cr=new Date(),cy=[],cz;if(String(ct).length){cv+="&ec_id="+m(ct);cw=Math.round(cr.getTime()/1000)}cv+="&revenue="+cs;if(String(cx).length){cv+="&ec_st="+cx}if(String(cu).length){cv+="&ec_tx="+cu -}if(String(cq).length){cv+="&ec_sh="+cq}if(String(cA).length){cv+="&ec_dt="+cA}if(bR){for(cz in bR){if(Object.prototype.hasOwnProperty.call(bR,cz)){if(!x(bR[cz][1])){bR[cz][1]=""}if(!x(bR[cz][2])){bR[cz][2]=""}if(!x(bR[cz][3])||String(bR[cz][3]).length===0){bR[cz][3]=0}if(!x(bR[cz][4])||String(bR[cz][4]).length===0){bR[cz][4]=1}cy.push(bR[cz])}}cv+="&ec_items="+m(JSON2.stringify(cy))}cv=aY(cv,ao,"ecommerce",cw);a4(cv,bz)}function bV(cq,cu,ct,cs,cr,cv){if(String(cq).length&&x(cu)){bW(cq,cu,ct,cs,cr,cv)}}function cg(cq){if(x(cq)){bW("",cq,"","","","")}}function bl(cs,ct){var cq=new Date(),cr=aY("action_name="+m(U(cs||aX)),ct,"log");a4(cr,bz)}function aL(cs,cr){var ct,cq="(^| )(piwik[_-]"+cr;if(cs){for(ct=0;ct<cs.length;ct++){cq+="|"+cs[ct]}}cq+=")( |$)";return new RegExp(cq)}function bP(cq){return(ab&&cq&&0===String(cq).indexOf(ab))}function b1(cu,cq,cv,cr){if(bP(cq)){return 0}var ct=aL(bp,"download"),cs=aL(aH,"link"),cw=new RegExp("\\.("+aZ.join("|")+")([?&#]|$)","i");if(cs.test(cu)){return"link" -}if(cr||ct.test(cu)||cw.test(cq)){return"download"}if(cv){return 0}return"link"}function bf(cr){var cq;cq=cr.parentNode;while(cq!==null&&x(cq)){if(R.isLinkElement(cr)){break}cr=cq;cq=cr.parentNode}return cr}function bt(cu){cu=bf(cu);if(!R.hasNodeAttribute(cu,"href")){return}if(!x(cu.href)){return}var ct=R.getAttributeValueFromNode(cu,"href");if(bP(ct)){return}var cv=cu.hostname||c(cu.href);var cw=cv.toLowerCase();var cr=cu.href.replace(cv,cw);var cs=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i");if(!cs.test(cr)){var cq=b1(cu.className,cr,bD(cw),R.hasNodeAttribute(cu,"download"));if(cq){return{type:cq,href:cr}}}}function cl(cq,cr,cs,ct){var cu=n.buildInteractionRequestParams(cq,cr,cs,ct);if(!cu){return}return aY(cu,null,"contentInteraction")}function ck(cs,ct,cx,cq,cr){if(!x(cs)){return}if(bP(cs)){return cs}var cv=n.toAbsoluteUrl(cs);var cu="redirecturl="+m(cv)+"&";cu+=cl(ct,cx,cq,(cr||cs));var cw="&";if(ab.indexOf("?")<0){cw="?"}return ab+cw+cu}function a8(cq,cr){if(!cq||!cr){return false -}var cs=n.findTargetNode(cq);if(n.shouldIgnoreInteraction(cs)){return false}cs=n.findTargetNodeNoDefault(cq);if(cs&&!I(cs,cr)){return false}return true}function aW(cs,cr,cu){if(!cs){return}var cq=n.findParentContentNode(cs);if(!cq){return}if(!a8(cq,cs)){return}var ct=n.buildContentBlock(cq);if(!ct){return}if(!ct.target&&cu){ct.target=cu}return n.buildInteractionRequestParams(cr,ct.name,ct.piece,ct.target)}function aT(cr){if(!ai||!ai.length){return false}var cq,cs;for(cq=0;cq<ai.length;cq++){cs=ai[cq];if(cs&&cs.name===cr.name&&cs.piece===cr.piece&&cs.target===cr.target){return true}}return false}function ad(ct){if(!ct){return false}var cw=n.findTargetNode(ct);if(!cw||n.shouldIgnoreInteraction(cw)){return false}var cx=bt(cw);if(b7&&cx&&cx.type){return false}if(R.isLinkElement(cw)&&R.hasNodeAttributeWithValue(cw,"href")){var cq=String(R.getAttributeValueFromNode(cw,"href"));if(0===cq.indexOf("#")){return false}if(bP(cq)){return true}if(!n.isUrlToCurrentDomain(cq)){return false}var cu=n.buildContentBlock(ct); -if(!cu){return}var cs=cu.name;var cy=cu.piece;var cv=cu.target;if(!R.hasNodeAttributeWithValue(cw,n.CONTENT_TARGET_ATTR)||cw.wasContentTargetAttrReplaced){cw.wasContentTargetAttrReplaced=true;cv=n.toAbsoluteUrl(cq);R.setAnyAttribute(cw,n.CONTENT_TARGET_ATTR,cv)}var cr=ck(cq,"click",cs,cy,cv);n.setHrefAttribute(cw,cr);return true}return false}function ag(cr){if(!cr||!cr.length){return}var cq;for(cq=0;cq<cr.length;cq++){ad(cr[cq])}}function bs(cq){return function(cr){if(!cq){return}var cu=n.findParentContentNode(cq);var cv;if(cr){cv=cr.target||cr.srcElement}if(!cv){cv=cq}if(!a8(cu,cv)){return}cc(bz);if(R.isLinkElement(cq)&&R.hasNodeAttributeWithValue(cq,"href")&&R.hasNodeAttributeWithValue(cq,n.CONTENT_TARGET_ATTR)){var cs=R.getAttributeValueFromNode(cq,"href");if(!bP(cs)&&cq.wasContentTargetAttrReplaced){R.setAnyAttribute(cq,n.CONTENT_TARGET_ATTR,"")}}var cz=bt(cq);if(bK&&cz&&cz.type){return cz.type}if(ad(cu)){return"href"}var cw=n.buildContentBlock(cu);if(!cw){return}var ct=cw.name;var cA=cw.piece; -var cy=cw.target;var cx=cl("click",ct,cA,cy);a4(cx,bz);return cx}}function aK(cs){if(!cs||!cs.length){return}var cq,cr;for(cq=0;cq<cs.length;cq++){cr=n.findTargetNode(cs[cq]);if(cr&&!cr.contentInteractionTrackingSetupDone){cr.contentInteractionTrackingSetupDone=true;W(cr,"click",bs(cr))}}}function aG(cs,ct){if(!cs||!cs.length){return[]}var cq,cr;for(cq=0;cq<cs.length;cq++){if(aT(cs[cq])){cs.splice(cq,1);cq--}else{ai.push(cs[cq])}}if(!cs||!cs.length){return[]}ag(ct);aK(ct);var cu=[];for(cq=0;cq<cs.length;cq++){cr=aY(n.buildImpressionRequestParams(cs[cq].name,cs[cq].piece,cs[cq].target),undefined,"contentImpressions");cu.push(cr)}return cu}function a1(cr){var cq=n.collectContent(cr);return aG(cq,cr)}function bO(cr){if(!cr||!cr.length){return[]}var cq;for(cq=0;cq<cr.length;cq++){if(!n.isNodeVisible(cr[cq])){cr.splice(cq,1);cq--}}if(!cr||!cr.length){return[]}return a1(cr)}function bZ(cs,cq,cr){var ct=n.buildImpressionRequestParams(cs,cq,cr);return aY(ct,null,"contentImpression")}function a0(ct,cr){if(!ct){return -}var cq=n.findParentContentNode(ct);var cs=n.buildContentBlock(cq);if(!cs){return}if(!cr){cr="Unknown"}return cl(cr,cs.name,cs.piece,cs.target)}function bI(cr,ct,cq,cs){return"e_c="+m(cr)+"&e_a="+m(ct)+(x(cq)?"&e_n="+m(cq):"")+(x(cs)?"&e_v="+m(cs):"")}function am(cs,cu,cq,ct,cv){if(String(cs).length===0||String(cu).length===0){return false}var cr=aY(bI(cs,cu,cq,ct),cv,"event");a4(cr,bz)}function aS(cq,ct,cr,cu){var cs=aY("search="+m(cq)+(ct?"&search_cat="+m(ct):"")+(x(cr)?"&search_count="+cr:""),cu,"sitesearch");a4(cs,bz)}function bx(cq,ct,cs){var cr=aY("idgoal="+cq+(ct?"&revenue="+ct:""),cs,"goal");a4(cr,bz)}function b0(ct,cq,cx,cw,cs){var cv=cq+"="+m(cd(ct));var cr=aW(cs,"click",ct);if(cr){cv+="&"+cr}var cu=aY(cv,cx,"link");a4(cu,(cw?0:bz),cw)}function b8(cr,cq){if(cr!==""){return cr+cq.charAt(0).toUpperCase()+cq.slice(1)}return cq}function aR(cv){var cu,cq,ct=["","webkit","ms","moz"],cs;if(!a5){for(cq=0;cq<ct.length;cq++){cs=ct[cq];if(Object.prototype.hasOwnProperty.call(v,b8(cs,"hidden"))){if(v[b8(cs,"visibilityState")]==="prerender"){cu=true -}break}}}if(cu){W(v,cs+"visibilitychange",function cr(){v.removeEventListener(cs+"visibilitychange",cr,false);cv()});return}cv()}function aV(cq){if(v.readyState==="complete"){cq()}else{if(H.addEventListener){H.addEventListener("load",cq)}else{if(H.attachEvent){H.attachEvent("onLoad",cq)}}}}function aF(cr){var cq=false;if(v.attachEvent){cq=v.readyState==="complete"}else{cq=v.readyState!=="loading"}if(cq){cr()}else{if(v.addEventListener){v.addEventListener("DOMContentLoaded",cr)}else{if(v.attachEvent){v.attachEvent("onreadystatechange",cr)}}}}function bQ(cq){var cr=bt(cq);if(cr&&cr.type){cr.href=j(cr.href);b0(cr.href,cr.type,undefined,null,cq)}}function aN(){return v.all&&!v.addEventListener}function aD(cq){var cs=cq.which;var cr=(typeof cq.button);if(!cs&&cr!=="undefined"){if(aN()){if(cq.button&1){cs=1}else{if(cq.button&2){cs=3}else{if(cq.button&4){cs=2}}}}else{if(cq.button===0||cq.button==="0"){cs=1}else{if(cq.button&1){cs=2}else{if(cq.button&2){cs=3}}}}}return cs}function aA(cq){switch(aD(cq)){case 1:return"left"; -case 2:return"middle";case 3:return"right"}}function cb(cq){return cq.target||cq.srcElement}function co(cq){return function(ct){ct=ct||H.event;var cs=aA(ct);var cu=cb(ct);if(ct.type==="click"){var cr=false;if(cq&&cs==="middle"){cr=true}if(cu&&!cr){bQ(cu)}}else{if(ct.type==="mousedown"){if(cs==="middle"&&cu){bu=cs;au=cu}else{bu=au=null}}else{if(ct.type==="mouseup"){if(cs===bu&&cu===au){bQ(cu)}bu=au=null}else{if(ct.type==="contextmenu"){bQ(cu)}}}}}}function bL(cr,cq){W(cr,"click",co(cq),false);if(cq){W(cr,"mouseup",co(cq),false);W(cr,"mousedown",co(cq),false);W(cr,"contextmenu",co(cq),false)}}function bk(cr){if(!bK){bK=true;var cs,cq=aL(aj,"ignore"),ct=v.links;if(ct){for(cs=0;cs<ct.length;cs++){if(!cq.test(ct[cs].className)){bL(ct[cs],cr)}}}}}function bH(cs,cu,cv){if(aB){return true}aB=true;var cw=false;var ct,cr;function cq(){cw=true}aV(function(){function cx(cz){setTimeout(function(){if(!aB){return}cw=false;cv.trackVisibleContentImpressions();cx(cz)},cz)}function cy(cz){setTimeout(function(){if(!aB){return -}if(cw){cw=false;cv.trackVisibleContentImpressions()}cy(cz)},cz)}if(cs){ct=["scroll","resize"];for(cr=0;cr<ct.length;cr++){if(v.addEventListener){v.addEventListener(ct[cr],cq)}else{H.attachEvent("on"+ct[cr],cq)}}cy(100)}if(cu&&cu>0){cu=parseInt(cu,10);cx(cu)}})}function ce(){var cr,cs,ct={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},cq=H.devicePixelRatio||1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(cr in ct){if(Object.prototype.hasOwnProperty.call(ct,cr)){cs=e.mimeTypes[ct[cr]];b6[cr]=(cs&&cs.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&x(e.javaEnabled)&&e.javaEnabled()){b6.java="1"}if(r(H.GearsFactory)){b6.gears="1"}b6.cookie=af()}b6.res=L.width*cq+"x"+L.height*cq}ce();bv();an();return{getVisitorId:function(){return bM().uuid -},getVisitorInfo:function(){return Z()},getAttributionInfo:function(){return Y()},getAttributionCampaignName:function(){return Y()[0]},getAttributionCampaignKeyword:function(){return Y()[1]},getAttributionReferrerTimestamp:function(){return Y()[2]},getAttributionReferrerUrl:function(){return Y()[3]},setTrackerUrl:function(cq){ab=cq},getTrackerUrl:function(){return ab},getSiteId:function(){return b5},setSiteId:function(cq){cm(cq)},setUserId:function(cq){if(!x(cq)||!cq.length){return}aO=cq;a7=a2(aO).substr(0,16)},getUserId:function(){return aO},setCustomData:function(cq,cr){if(K(cq)){ao=cq}else{if(!ao){ao={}}ao[cq]=cr}},getCustomData:function(){return ao},setCustomRequestProcessing:function(cq){av=cq},appendToTrackingUrl:function(cq){bA=cq},getRequest:function(cq){return aY(cq)},addPlugin:function(cq,cr){a[cq]=cr},setCustomVariable:function(cr,cq,cu,cs){var ct;if(!x(cs)){cs="visit"}if(!x(cq)){return}if(!x(cu)){cu=""}if(cr>0){cq=!o(cq)?String(cq):cq;cu=!o(cu)?String(cu):cu;ct=[cq.slice(0,ca),cu.slice(0,ca)]; -if(cs==="visit"||cs===2){aa();ah[cr]=ct}else{if(cs==="page"||cs===3){bq[cr]=ct}else{if(cs==="event"){ac[cr]=ct}}}}},getCustomVariable:function(cr,cs){var cq;if(!x(cs)){cs="visit"}if(cs==="page"||cs===3){cq=bq[cr]}else{if(cs==="event"){cq=ac[cr]}else{if(cs==="visit"||cs===2){aa();cq=ah[cr]}}}if(!x(cq)||(cq&&cq[0]==="")){return false}return cq},deleteCustomVariable:function(cq,cr){if(this.getCustomVariable(cq,cr)){this.setCustomVariable(cq,"","",cr)}},storeCustomVariablesInCookie:function(){bc=true},setLinkTrackingTimer:function(cq){bz=cq},setDownloadExtensions:function(cq){if(o(cq)){cq=cq.split("|")}aZ=cq},addDownloadExtensions:function(cr){var cq;if(o(cr)){cr=cr.split("|")}for(cq=0;cq<cr.length;cq++){aZ.push(cr[cq])}},removeDownloadExtensions:function(cs){var cr,cq=[];if(o(cs)){cs=cs.split("|")}for(cr=0;cr<aZ.length;cr++){if(A(cs,aZ[cr])===-1){cq.push(aZ[cr])}}aZ=cq},setDomains:function(cq){bB=o(cq)?[cq]:cq;bB.push(bY)},setIgnoreClasses:function(cq){aj=o(cq)?[cq]:cq},setRequestMethod:function(cq){bE=cq||bC -},setRequestContentType:function(cq){aP=cq||bj},setReferrerUrl:function(cq){bG=cq},setCustomUrl:function(cq){bd=bX(cj,cq)},setDocumentTitle:function(cq){aX=cq},setAPIUrl:function(cq){az=cq},setDownloadClasses:function(cq){bp=o(cq)?[cq]:cq},setLinkClasses:function(cq){aH=o(cq)?[cq]:cq},setCampaignNameKey:function(cq){a3=o(cq)?[cq]:cq},setCampaignKeywordKey:function(cq){aU=o(cq)?[cq]:cq},discardHashTag:function(cq){al=cq},setCookieNamePrefix:function(cq){ch=cq;ah=at()},setCookieDomain:function(cq){var cr=z(cq);if(bn(cr)){ar=cr;bv()}},setCookiePath:function(cq){ci=cq;bv()},setVisitorCookieTimeout:function(cq){ay=cq*1000},setSessionCookieTimeout:function(cq){aE=cq*1000},setReferralCookieTimeout:function(cq){bh=cq*1000},setConversionAttributionFirstReferrer:function(cq){ba=cq},disableCookies:function(){ap=true;b6.cookie="0";if(b5){X()}},deleteCookies:function(){X()},setDoNotTrack:function(cr){var cq=e.doNotTrack||e.msDoNotTrack;b9=cr&&(cq==="yes"||cq==="1");if(b9){this.disableCookies()}},addListener:function(cr,cq){bL(cr,cq) -},enableLinkTracking:function(cq){b7=true;if(q){bk(cq)}else{F.push(function(){bk(cq)})}},enableJSErrorTracking:function(){if(cn){return}cn=true;var cq=H.onerror;H.onerror=function(cv,ct,cs,cu,cr){aR(function(){var cw="JavaScript Errors";var cx=ct+":"+cs;if(cu){cx+=":"+cu}am(cw,cx,cv)});if(cq){return cq(cv,ct,cs,cu,cr)}return false}},disablePerformanceTracking:function(){a6=false},setGenerationTimeMs:function(cq){aM=parseInt(cq,10)},enableHeartBeatTimer:function(cq){cq=Math.max(cq,1);b3=(cq||15)*1000;if(bo!==null){bF()}},killFrame:function(){if(H.location!==H.top.location){H.top.location=H.location}},redirectFile:function(cq){if(H.location.protocol==="file:"){H.location=cq}},setCountPreRendered:function(cq){a5=cq},trackGoal:function(cq,cs,cr){aR(function(){bx(cq,cs,cr)})},trackLink:function(cr,cq,ct,cs){aR(function(){b0(cr,cq,ct,cs)})},trackPageView:function(cq,cr){ai=[];if(B(b5)){aR(function(){N(ab,az,b5)})}else{aR(function(){bl(cq,cr)})}},trackAllContentImpressions:function(){if(B(b5)){return -}aR(function(){aF(function(){var cq=n.findContentNodes();var cr=a1(cq);aq(cr,bz)})})},trackVisibleContentImpressions:function(cq,cr){if(B(b5)){return}if(!x(cq)){cq=true}if(!x(cr)){cr=750}bH(cq,cr,this);aR(function(){aV(function(){var cs=n.findContentNodes();var ct=bO(cs);aq(ct,bz)})})},trackContentImpression:function(cs,cq,cr){if(B(b5)){return}if(!cs){return}cq=cq||"Unknown";aR(function(){var ct=bZ(cs,cq,cr);a4(ct,bz)})},trackContentImpressionsWithinNode:function(cq){if(B(b5)||!cq){return}aR(function(){if(aB){aV(function(){var cr=n.findContentNodesWithinNode(cq);var cs=bO(cr);aq(cs,bz)})}else{aF(function(){var cr=n.findContentNodesWithinNode(cq);var cs=a1(cr);aq(cs,bz)})}})},trackContentInteraction:function(cs,ct,cq,cr){if(B(b5)){return}if(!cs||!ct){return}cq=cq||"Unknown";aR(function(){var cu=cl(cs,ct,cq,cr);a4(cu,bz)})},trackContentInteractionNode:function(cr,cq){if(B(b5)||!cr){return}aR(function(){var cs=a0(cr,cq);a4(cs,bz)})},logAllContentBlocksOnPage:function(){var cr=n.findContentNodes(); -var cq=n.collectContent(cr);if(console!==undefined&&console&&console.log){console.log(cq)}},trackEvent:function(cr,ct,cq,cs){aR(function(){am(cr,ct,cq,cs)})},trackSiteSearch:function(cq,cs,cr){aR(function(){aS(cq,cs,cr)})},setEcommerceView:function(ct,cq,cs,cr){if(!x(cs)||!cs.length){cs=""}else{if(cs instanceof Array){cs=JSON2.stringify(cs)}}bq[5]=["_pkc",cs];if(x(cr)&&String(cr).length){bq[2]=["_pkp",cr]}if((!x(ct)||!ct.length)&&(!x(cq)||!cq.length)){return}if(x(ct)&&ct.length){bq[3]=["_pks",ct]}if(!x(cq)||!cq.length){cq=""}bq[4]=["_pkn",cq]},addEcommerceItem:function(cu,cq,cs,cr,ct){if(cu.length){bR[cu]=[cu,cq,cs,cr,ct]}},trackEcommerceOrder:function(cq,cu,ct,cs,cr,cv){bV(cq,cu,ct,cs,cr,cv)},trackEcommerceCartUpdate:function(cq){cg(cq)}}}function w(){return{push:S}}function b(ac,ab){var ad={};var Z,aa;for(Z=0;Z<ab.length;Z++){var X=ab[Z];ad[X]=1;for(aa=0;aa<ac.length;aa++){if(ac[aa]&&ac[aa][0]){var Y=ac[aa][0];if(X===Y){S(ac[aa]);delete ac[aa];if(ad[Y]>1){if(console!==undefined&&console&&console.error){console.error("The method "+Y+' is registered more than once in "paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers') -}}ad[Y]++}}}}return ac}W(H,"beforeunload",T,false);p();Date.prototype.getTimeAlias=Date.prototype.getTime;M=new E();var s=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setUserId","setSiteId","enableLinkTracking"];_paq=b(_paq,s);for(u=0;u<_paq.length;u++){if(_paq[u]){S(_paq[u])}}_paq=new w();d={addPlugin:function(X,Y){a[X]=Y},getTracker:function(X,Y){if(!x(Y)){Y=this.getAsyncTracker().getSiteId()}if(!x(X)){X=this.getAsyncTracker().getTrackerUrl()}return new E(X,Y)},getAsyncTracker:function(){return M}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g); +if(ad=="}"){break}if(ae){if(ad==","){ad=y();if(ad=="}"){H()}}else{H()}}if(ad==","||typeof ad!="string"||(F?ad.charAt(0):ad[0])!="@"||y()!=":"){H()}ac[ad.slice(1)]=W(y())}return ac}}H()}return ad};var P=function(ae,ad,af){var ac=w(ae,ad,af);if(ac===L){delete ae[ad]}else{ae[ad]=ac}};var w=function(af,ae,ag){var ad=af[ae],ac;if(typeof ad=="object"&&ad){if(u.call(ad)==E){for(ac=ad.length;ac--;){P(ad,ac,ag)}}else{m(ad,function(ah){P(ad,ah,ag)})}}return ag.call(af,ae,ad)};V.parse=function(ae,af){var ac,ad;G=0;X=""+ae;ac=W(y());if(y()!="$"){H()}G=X=null;return af&&u.call(af)==U?w((ad={},ad[""]=ac,ad),"",af):ac}}}V.runInContext=j;return V}if(h&&!c){j(i,h)}else{var f=i.JSON,k=i.JSON3,d=false;var g=j(i,(i.JSON3={noConflict:function(){if(!d){d=true;i.JSON=f;i.JSON3=k;f=k=null}return g}}));i.JSON={parse:g.parse,stringify:g.stringify}}if(c){define(function(){return g})}}).call(this);JSON2=a})()}if(typeof _paq!=="object"){_paq=[]}if(typeof Piwik!=="object"){Piwik=(function(){var k,a={},w=document,e=navigator,M=screen,I=window,f=I.performance||I.mozPerformance||I.msPerformance||I.webkitPerformance,q=false,G=[],m=I.encodeURIComponent,H=I.decodeURIComponent,h=unescape,N,v,d; +function j(Y){try{return H(Y)}catch(Z){return unescape(Y)}}function y(Z){var Y=typeof Z;return Y!=="undefined"}function r(Y){return typeof Y==="function"}function L(Y){return typeof Y==="object"}function o(Y){return typeof Y==="string"||Y instanceof String}function s(Z){if(!Z){return true}var Y;var aa=true;for(Y in Z){if(Object.prototype.hasOwnProperty.call(Z,Y)){aa=false}}return aa}function T(){var Y,aa,Z;for(Y=0;Y<arguments.length;Y+=1){Z=arguments[Y];aa=Z.shift();if(o(aa)){N[aa].apply(N,Z)}else{aa.apply(N,Z)}}}function X(ab,aa,Z,Y){if(ab.addEventListener){ab.addEventListener(aa,Z,Y);return true}if(ab.attachEvent){return ab.attachEvent("on"+aa,Z)}ab["on"+aa]=Z}function Q(Z,ac){var Y="",ab,aa;for(ab in a){if(Object.prototype.hasOwnProperty.call(a,ab)){aa=a[ab][Z];if(r(aa)){Y+=aa(ac)}}}return Y}function U(){var Y;Q("unload");if(k){do{Y=new Date()}while(Y.getTimeAlias()<k)}}function R(){var Y;if(!q){q=true;Q("load");for(Y=0;Y<G.length;Y++){G[Y]()}}return true}function p(){var Z;if(w.addEventListener){X(w,"DOMContentLoaded",function Y(){w.removeEventListener("DOMContentLoaded",Y,false); +R()})}else{if(w.attachEvent){w.attachEvent("onreadystatechange",function Y(){if(w.readyState==="complete"){w.detachEvent("onreadystatechange",Y);R()}});if(w.documentElement.doScroll&&I===I.top){(function Y(){if(!q){try{w.documentElement.doScroll("left")}catch(aa){setTimeout(Y,0);return}R()}}())}}}if((new RegExp("WebKit")).test(e.userAgent)){Z=setInterval(function(){if(q||/loaded|complete/.test(w.readyState)){clearInterval(Z);R()}},10)}X(I,"load",R,false)}function i(aa,Z){var Y=w.createElement("script");Y.type="text/javascript";Y.src=aa;if(Y.readyState){Y.onreadystatechange=function(){var ab=this.readyState;if(ab==="loaded"||ab==="complete"){Y.onreadystatechange=null;Z()}}}else{Y.onload=Z}w.getElementsByTagName("head")[0].appendChild(Y)}function z(){var Y="";try{Y=I.top.document.referrer}catch(aa){if(I.parent){try{Y=I.parent.document.referrer}catch(Z){Y=""}}}if(Y===""){Y=w.referrer}return Y}function l(Y){var aa=new RegExp("^([a-z]+):"),Z=aa.exec(Y);return Z?Z[1]:null}function c(Y){var aa=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),Z=aa.exec(Y); +return Z?Z[1]:Y}function K(aa,Z){var Y="[\\?&#]"+Z+"=([^&#]*)";var ac=new RegExp(Y);var ab=ac.exec(aa);return ab?H(ab[1]):""}function u(Y){return unescape(m(Y))}function W(an){var aa=function(au,at){return(au<<at)|(au>>>(32-at))},ao=function(aw){var au="",av,at;for(av=7;av>=0;av--){at=(aw>>>(av*4))&15;au+=at.toString(16)}return au},ad,aq,ap,Z=[],ah=1732584193,af=4023233417,ae=2562383102,ac=271733878,ab=3285377520,am,al,ak,aj,ai,ar,Y,ag=[];an=u(an);Y=an.length;for(aq=0;aq<Y-3;aq+=4){ap=an.charCodeAt(aq)<<24|an.charCodeAt(aq+1)<<16|an.charCodeAt(aq+2)<<8|an.charCodeAt(aq+3);ag.push(ap)}switch(Y&3){case 0:aq=2147483648;break;case 1:aq=an.charCodeAt(Y-1)<<24|8388608;break;case 2:aq=an.charCodeAt(Y-2)<<24|an.charCodeAt(Y-1)<<16|32768;break;case 3:aq=an.charCodeAt(Y-3)<<24|an.charCodeAt(Y-2)<<16|an.charCodeAt(Y-1)<<8|128;break}ag.push(aq);while((ag.length&15)!==14){ag.push(0)}ag.push(Y>>>29);ag.push((Y<<3)&4294967295);for(ad=0;ad<ag.length;ad+=16){for(aq=0;aq<16;aq++){Z[aq]=ag[ad+aq]}for(aq=16; +aq<=79;aq++){Z[aq]=aa(Z[aq-3]^Z[aq-8]^Z[aq-14]^Z[aq-16],1)}am=ah;al=af;ak=ae;aj=ac;ai=ab;for(aq=0;aq<=19;aq++){ar=(aa(am,5)+((al&ak)|(~al&aj))+ai+Z[aq]+1518500249)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}for(aq=20;aq<=39;aq++){ar=(aa(am,5)+(al^ak^aj)+ai+Z[aq]+1859775393)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}for(aq=40;aq<=59;aq++){ar=(aa(am,5)+((al&ak)|(al&aj)|(ak&aj))+ai+Z[aq]+2400959708)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}for(aq=60;aq<=79;aq++){ar=(aa(am,5)+(al^ak^aj)+ai+Z[aq]+3395469782)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}ah=(ah+am)&4294967295;af=(af+al)&4294967295;ae=(ae+ak)&4294967295;ac=(ac+aj)&4294967295;ab=(ab+ai)&4294967295}ar=ao(ah)+ao(af)+ao(ae)+ao(ac)+ao(ab);return ar.toLowerCase()}function P(aa,Y,Z){if(!aa){aa=""}if(!Y){Y=""}if(aa==="translate.googleusercontent.com"){if(Z===""){Z=Y}Y=K(Y,"u");aa=c(Y)}else{if(aa==="cc.bingj.com"||aa==="webcache.googleusercontent.com"||aa.slice(0,5)==="74.6."){Y=w.links[0].href;aa=c(Y)}}return[aa,Y,Z] +}function A(Z){var Y=Z.length;if(Z.charAt(--Y)==="."){Z=Z.slice(0,Y)}if(Z.slice(0,2)==="*."){Z=Z.slice(1)}if(Z.indexOf("/")!==-1){Z=Z.substr(0,Z.indexOf("/"))}return Z}function V(Z){Z=Z&&Z.text?Z.text:Z;if(!o(Z)){var Y=w.getElementsByTagName("title");if(Y&&y(Y[0])){Z=Y[0].text}}return Z}function E(Y){if(!Y){return[]}if(!y(Y.children)&&y(Y.childNodes)){return Y.children}if(y(Y.children)){return Y.children}return[]}function J(Z,Y){if(!Z||!Y){return false}if(Z.contains){return Z.contains(Y)}if(Z===Y){return true}if(Z.compareDocumentPosition){return !!(Z.compareDocumentPosition(Y)&16)}return false}function B(aa,ab){if(aa&&aa.indexOf){return aa.indexOf(ab)}if(!y(aa)||aa===null){return -1}if(!aa.length){return -1}var Y=aa.length;if(Y===0){return -1}var Z=0;while(Z<Y){if(aa[Z]===ab){return Z}Z++}return -1}function g(aa){if(!aa){return false}function Y(ac,ad){if(I.getComputedStyle){return w.defaultView.getComputedStyle(ac,null)[ad]}if(ac.currentStyle){return ac.currentStyle[ad]}}function ab(ac){ac=ac.parentNode; +while(ac){if(ac===w){return true}ac=ac.parentNode}return false}function Z(ae,ak,ac,ah,af,ai,ag){var ad=ae.parentNode,aj=1;if(!ab(ae)){return false}if(9===ad.nodeType){return true}if("0"===Y(ae,"opacity")||"none"===Y(ae,"display")||"hidden"===Y(ae,"visibility")){return false}if(!y(ak)||!y(ac)||!y(ah)||!y(af)||!y(ai)||!y(ag)){ak=ae.offsetTop;af=ae.offsetLeft;ah=ak+ae.offsetHeight;ac=af+ae.offsetWidth;ai=ae.offsetWidth;ag=ae.offsetHeight}if(aa===ae&&(0===ag||0===ai)&&"hidden"===Y(ae,"overflow")){return false}if(ad){if(("hidden"===Y(ad,"overflow")||"scroll"===Y(ad,"overflow"))){if(af+aj>ad.offsetWidth+ad.scrollLeft||af+ai-aj<ad.scrollLeft||ak+aj>ad.offsetHeight+ad.scrollTop||ak+ag-aj<ad.scrollTop){return false}}if(ae.offsetParent===ad){af+=ad.offsetLeft;ak+=ad.offsetTop}return Z(ad,ak,ac,ah,af,ai,ag)}return true}return Z(aa)}var S={htmlCollectionToArray:function(aa){var Y=[],Z;if(!aa||!aa.length){return Y}for(Z=0;Z<aa.length;Z++){Y.push(aa[Z])}return Y},find:function(Y){if(!document.querySelectorAll||!Y){return[] +}var Z=document.querySelectorAll(Y);return this.htmlCollectionToArray(Z)},findMultiple:function(aa){if(!aa||!aa.length){return[]}var Z,ab;var Y=[];for(Z=0;Z<aa.length;Z++){ab=this.find(aa[Z]);Y=Y.concat(ab)}Y=this.makeNodesUnique(Y);return Y},findNodesByTagName:function(Z,Y){if(!Z||!Y||!Z.getElementsByTagName){return[]}var aa=Z.getElementsByTagName(Y);return this.htmlCollectionToArray(aa)},makeNodesUnique:function(Y){var ad=[].concat(Y);Y.sort(function(af,ae){if(af===ae){return 0}var ah=B(ad,af);var ag=B(ad,ae);if(ah===ag){return 0}return ah>ag?-1:1});if(Y.length<=1){return Y}var Z=0;var ab=0;var ac=[];var aa;aa=Y[Z++];while(aa){if(aa===Y[Z]){ab=ac.push(Z)}aa=Y[Z++]||null}while(ab--){Y.splice(ac[ab],1)}return Y},getAttributeValueFromNode:function(ac,aa){if(!this.hasNodeAttribute(ac,aa)){return}if(ac&&ac.getAttribute){return ac.getAttribute(aa)}if(!ac||!ac.attributes){return}var ab=(typeof ac.attributes[aa]);if("undefined"===ab){return}if(ac.attributes[aa].value){return ac.attributes[aa].value +}if(ac.attributes[aa].nodeValue){return ac.attributes[aa].nodeValue}var Z;var Y=ac.attributes;if(!Y){return}for(Z=0;Z<Y.length;Z++){if(Y[Z].nodeName===aa){return Y[Z].nodeValue}}return null},hasNodeAttributeWithValue:function(Z,Y){var aa=this.getAttributeValueFromNode(Z,Y);return !!aa},hasNodeAttribute:function(aa,Y){if(aa&&aa.hasAttribute){return aa.hasAttribute(Y)}if(aa&&aa.attributes){var Z=(typeof aa.attributes[Y]);return"undefined"!==Z}return false},hasNodeCssClass:function(aa,Y){if(aa&&Y&&aa.className){var Z=typeof aa.className==="string"?aa.className.split(" "):[];if(-1!==B(Z,Y)){return true}}return false},findNodesHavingAttribute:function(ac,aa,Y){if(!Y){Y=[]}if(!ac||!aa){return Y}var ab=E(ac);if(!ab||!ab.length){return Y}var Z,ad;for(Z=0;Z<ab.length;Z++){ad=ab[Z];if(this.hasNodeAttribute(ad,aa)){Y.push(ad)}Y=this.findNodesHavingAttribute(ad,aa,Y)}return Y},findFirstNodeHavingAttribute:function(aa,Z){if(!aa||!Z){return}if(this.hasNodeAttribute(aa,Z)){return aa}var Y=this.findNodesHavingAttribute(aa,Z); +if(Y&&Y.length){return Y[0]}},findFirstNodeHavingAttributeWithValue:function(ab,aa){if(!ab||!aa){return}if(this.hasNodeAttributeWithValue(ab,aa)){return ab}var Y=this.findNodesHavingAttribute(ab,aa);if(!Y||!Y.length){return}var Z;for(Z=0;Z<Y.length;Z++){if(this.getAttributeValueFromNode(Y[Z],aa)){return Y[Z]}}},findNodesHavingCssClass:function(ac,ab,Y){if(!Y){Y=[]}if(!ac||!ab){return Y}if(ac.getElementsByClassName){var ad=ac.getElementsByClassName(ab);return this.htmlCollectionToArray(ad)}var aa=E(ac);if(!aa||!aa.length){return[]}var Z,ae;for(Z=0;Z<aa.length;Z++){ae=aa[Z];if(this.hasNodeCssClass(ae,ab)){Y.push(ae)}Y=this.findNodesHavingCssClass(ae,ab,Y)}return Y},findFirstNodeHavingClass:function(aa,Z){if(!aa||!Z){return}if(this.hasNodeCssClass(aa,Z)){return aa}var Y=this.findNodesHavingCssClass(aa,Z);if(Y&&Y.length){return Y[0]}},isLinkElement:function(Z){if(!Z){return false}var Y=String(Z.nodeName).toLowerCase();var ab=["a","area"];var aa=B(ab,Y);return aa!==-1},setAnyAttribute:function(Z,Y,aa){if(!Z||!Y){return +}if(Z.setAttribute){Z.setAttribute(Y,aa)}else{Z[Y]=aa}}};var n={CONTENT_ATTR:"data-track-content",CONTENT_CLASS:"piwikTrackContent",CONTENT_NAME_ATTR:"data-content-name",CONTENT_PIECE_ATTR:"data-content-piece",CONTENT_PIECE_CLASS:"piwikContentPiece",CONTENT_TARGET_ATTR:"data-content-target",CONTENT_TARGET_CLASS:"piwikContentTarget",CONTENT_IGNOREINTERACTION_ATTR:"data-content-ignoreinteraction",CONTENT_IGNOREINTERACTION_CLASS:"piwikContentIgnoreInteraction",location:undefined,findContentNodes:function(){var Z="."+this.CONTENT_CLASS;var Y="["+this.CONTENT_ATTR+"]";var aa=S.findMultiple([Z,Y]);return aa},findContentNodesWithinNode:function(ab){if(!ab){return[]}var Z=S.findNodesHavingCssClass(ab,this.CONTENT_CLASS);var Y=S.findNodesHavingAttribute(ab,this.CONTENT_ATTR);if(Y&&Y.length){var aa;for(aa=0;aa<Y.length;aa++){Z.push(Y[aa])}}if(S.hasNodeAttribute(ab,this.CONTENT_ATTR)){Z.push(ab)}else{if(S.hasNodeCssClass(ab,this.CONTENT_CLASS)){Z.push(ab)}}Z=S.makeNodesUnique(Z);return Z},findParentContentNode:function(Z){if(!Z){return +}var aa=Z;var Y=0;while(aa&&aa!==w&&aa.parentNode){if(S.hasNodeAttribute(aa,this.CONTENT_ATTR)){return aa}if(S.hasNodeCssClass(aa,this.CONTENT_CLASS)){return aa}aa=aa.parentNode;if(Y>1000){break}Y++}},findPieceNode:function(Z){var Y;Y=S.findFirstNodeHavingAttribute(Z,this.CONTENT_PIECE_ATTR);if(!Y){Y=S.findFirstNodeHavingClass(Z,this.CONTENT_PIECE_CLASS)}if(Y){return Y}return Z},findTargetNodeNoDefault:function(Y){if(!Y){return}var Z=S.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_TARGET_ATTR);if(Z){return Z}Z=S.findFirstNodeHavingAttribute(Y,this.CONTENT_TARGET_ATTR);if(Z){return Z}Z=S.findFirstNodeHavingClass(Y,this.CONTENT_TARGET_CLASS);if(Z){return Z}},findTargetNode:function(Y){var Z=this.findTargetNodeNoDefault(Y);if(Z){return Z}return Y},findContentName:function(Z){if(!Z){return}var ac=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_NAME_ATTR);if(ac){return S.getAttributeValueFromNode(ac,this.CONTENT_NAME_ATTR)}var Y=this.findContentPiece(Z);if(Y){return this.removeDomainIfIsInLink(Y) +}if(S.hasNodeAttributeWithValue(Z,"title")){return S.getAttributeValueFromNode(Z,"title")}var aa=this.findPieceNode(Z);if(S.hasNodeAttributeWithValue(aa,"title")){return S.getAttributeValueFromNode(aa,"title")}var ab=this.findTargetNode(Z);if(S.hasNodeAttributeWithValue(ab,"title")){return S.getAttributeValueFromNode(ab,"title")}},findContentPiece:function(Z){if(!Z){return}var ab=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_PIECE_ATTR);if(ab){return S.getAttributeValueFromNode(ab,this.CONTENT_PIECE_ATTR)}var Y=this.findPieceNode(Z);var aa=this.findMediaUrlInNode(Y);if(aa){return this.toAbsoluteUrl(aa)}},findContentTarget:function(aa){if(!aa){return}var ab=this.findTargetNode(aa);if(S.hasNodeAttributeWithValue(ab,this.CONTENT_TARGET_ATTR)){return S.getAttributeValueFromNode(ab,this.CONTENT_TARGET_ATTR)}var Z;if(S.hasNodeAttributeWithValue(ab,"href")){Z=S.getAttributeValueFromNode(ab,"href");return this.toAbsoluteUrl(Z)}var Y=this.findPieceNode(aa);if(S.hasNodeAttributeWithValue(Y,"href")){Z=S.getAttributeValueFromNode(Y,"href"); +return this.toAbsoluteUrl(Z)}},isSameDomain:function(Y){if(!Y||!Y.indexOf){return false}if(0===Y.indexOf(this.getLocation().origin)){return true}var Z=Y.indexOf(this.getLocation().host);if(8>=Z&&0<=Z){return true}return false},removeDomainIfIsInLink:function(aa){var Z="^https?://[^/]+";var Y="^.*//[^/]+";if(aa&&aa.search&&-1!==aa.search(new RegExp(Z))&&this.isSameDomain(aa)){aa=aa.replace(new RegExp(Y),"");if(!aa){aa="/"}}return aa},findMediaUrlInNode:function(ac){if(!ac){return}var aa=["img","embed","video","audio"];var Y=ac.nodeName.toLowerCase();if(-1!==B(aa,Y)&&S.findFirstNodeHavingAttributeWithValue(ac,"src")){var ab=S.findFirstNodeHavingAttributeWithValue(ac,"src");return S.getAttributeValueFromNode(ab,"src")}if(Y==="object"&&S.hasNodeAttributeWithValue(ac,"data")){return S.getAttributeValueFromNode(ac,"data")}if(Y==="object"){var ad=S.findNodesByTagName(ac,"param");if(ad&&ad.length){var Z;for(Z=0;Z<ad.length;Z++){if("movie"===S.getAttributeValueFromNode(ad[Z],"name")&&S.hasNodeAttributeWithValue(ad[Z],"value")){return S.getAttributeValueFromNode(ad[Z],"value") +}}}var ae=S.findNodesByTagName(ac,"embed");if(ae&&ae.length){return this.findMediaUrlInNode(ae[0])}}},trim:function(Y){if(Y&&String(Y)===Y){return Y.replace(/^\s+|\s+$/g,"")}return Y},isOrWasNodeInViewport:function(ad){if(!ad||!ad.getBoundingClientRect||ad.nodeType!==1){return true}var ac=ad.getBoundingClientRect();var ab=w.documentElement||{};var aa=ac.top<0;if(aa&&ad.offsetTop){aa=(ad.offsetTop+ac.height)>0}var Z=ab.clientWidth;if(I.innerWidth&&Z>I.innerWidth){Z=I.innerWidth}var Y=ab.clientHeight;if(I.innerHeight&&Y>I.innerHeight){Y=I.innerHeight}return((ac.bottom>0||aa)&&ac.right>0&&ac.left<Z&&((ac.top<Y)||aa))},isNodeVisible:function(Z){var Y=g(Z);var aa=this.isOrWasNodeInViewport(Z);return Y&&aa},buildInteractionRequestParams:function(Y,Z,aa,ab){var ac="";if(Y){ac+="c_i="+m(Y)}if(Z){if(ac){ac+="&"}ac+="c_n="+m(Z)}if(aa){if(ac){ac+="&"}ac+="c_p="+m(aa)}if(ab){if(ac){ac+="&"}ac+="c_t="+m(ab)}return ac},buildImpressionRequestParams:function(Y,Z,aa){var ab="c_n="+m(Y)+"&c_p="+m(Z);if(aa){ab+="&c_t="+m(aa) +}return ab},buildContentBlock:function(aa){if(!aa){return}var Y=this.findContentName(aa);var Z=this.findContentPiece(aa);var ab=this.findContentTarget(aa);Y=this.trim(Y);Z=this.trim(Z);ab=this.trim(ab);return{name:Y||"Unknown",piece:Z||"Unknown",target:ab||""}},collectContent:function(ab){if(!ab||!ab.length){return[]}var aa=[];var Y,Z;for(Y=0;Y<ab.length;Y++){Z=this.buildContentBlock(ab[Y]);if(y(Z)){aa.push(Z)}}return aa},setLocation:function(Y){this.location=Y},getLocation:function(){var Y=this.location||I.location;if(!Y.origin){Y.origin=Y.protocol+"//"+Y.hostname+(Y.port?":"+Y.port:"")}return Y},toAbsoluteUrl:function(Z){if((!Z||String(Z)!==Z)&&Z!==""){return Z}if(""===Z){return this.getLocation().href}if(Z.search(/^\/\//)!==-1){return this.getLocation().protocol+Z}if(Z.search(/:\/\//)!==-1){return Z}if(0===Z.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+Z}if(0===Z.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+Z}if(0===Z.search("^[a-zA-Z]{2,11}:")){return Z +}if(Z.search(/^\//)!==-1){return this.getLocation().origin+Z}var Y="(.*/)";var aa=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(Y))[0];return aa+Z},isUrlToCurrentDomain:function(Z){var aa=this.toAbsoluteUrl(Z);if(!aa){return false}var Y=this.getLocation().origin;if(Y===aa){return true}if(0===String(aa).indexOf(Y)){if(":"===String(aa).substr(Y.length,1)){return false}return true}return false},setHrefAttribute:function(Z,Y){if(!Z||!Y){return}S.setAnyAttribute(Z,"href",Y)},shouldIgnoreInteraction:function(aa){var Z=S.hasNodeAttribute(aa,this.CONTENT_IGNOREINTERACTION_ATTR);var Y=S.hasNodeCssClass(aa,this.CONTENT_IGNOREINTERACTION_CLASS);return Z||Y}};function D(Y,Z){if(Z){return Z}if(Y.slice(-9)==="piwik.php"){Y=Y.slice(0,Y.length-9)}return Y}function C(ae){var ag="Piwik_Overlay";var Z=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$");var aa=Z.exec(w.referrer);if(aa){var ac=aa[1];if(ac!==String(ae)){return false +}var ad=aa[2],Y=aa[3],ab=aa[4];if(!ab){ab=""}else{if(ab.indexOf("&segment=")===0){ab=ab.substr("&segment=".length)}}I.name=ag+"###"+ad+"###"+Y+"###"+ab}var af=I.name.split("###");return af.length===4&&af[0]===ag}function O(Z,af,ab){var ae=I.name.split("###"),ad=ae[1],Y=ae[2],ac=ae[3],aa=D(Z,af);i(aa+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(aa,ab,ad,Y,ac)})}function F(bH,bB){var bx=P(w.domain,I.location.href,z()),cf=A(bx[0]),bh=j(bx[1]),aW=j(bx[2]),cd=false,bL="GET",cs=bL,am="application/x-www-form-urlencoded; charset=UTF-8",bX=am,ai=bH||"",bc="",cj="",bz=bB||"",a5="",bi="",aG,aS=w.title,cp=["7z","aac","apk","arc","arj","asf","asx","avi","azw3","bin","csv","deb","dmg","doc","docx","epub","exe","flv","gif","gz","gzip","hqx","ibooks","jar","jpg","jpeg","js","mobi","mp2","mp3","mp4","mpg","mpeg","mov","movie","msi","msp","odb","odf","odg","ods","odt","ogg","ogv","pdf","phps","png","ppt","pptx","qt","qtm","ra","ram","rar","rpm","sea","sit","tar","tbz","tbz2","bz","bz2","tgz","torrent","txt","wav","wma","wmv","wpd","xls","xlsx","xml","z","zip"],ae=[cf],a6=[],bf=[],aJ=[],bd=500,b6,aH,bl,bj,Y,bT=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],bb=["pk_kwd","piwik_kwd","utm_term"],aT="_pk_",ch,aY,aU=false,cb,aQ,a2,b7=33955200000,bR=1800000,co=15768000000,aE=true,bP=0,bk=false,at=false,bE,bp={},bO={},aV={},a1=200,ck={},cq={},bD=[],bI=false,b0=false,Z=false,cr=false,aq=false,ci=null,bF,au,a7,bA=W,aX; +function cv(cF,cC,cB,cE,cA,cD){if(aU){return}var cz;if(cB){cz=new Date();cz.setTime(cz.getTime()+cB)}w.cookie=cF+"="+m(cC)+(cB?";expires="+cz.toGMTString():"")+";path="+(cE||"/")+(cA?";domain="+cA:"")+(cD?";secure":"")}function ah(cB){if(aU){return 0}var cz=new RegExp("(^|;)[ ]*"+cB+"=([^;]*)"),cA=cz.exec(w.cookie);return cA?H(cA[2]):0}function bv(cz){var cA;if(bj){cA=new RegExp("#.*");return cz.replace(cA,"")}return cz}function bo(cB,cz){var cC=l(cz),cA;if(cC){return cz}if(cz.slice(0,1)==="/"){return l(cB)+"://"+c(cB)+cz}cB=bv(cB);cA=cB.indexOf("?");if(cA>=0){cB=cB.slice(0,cA)}cA=cB.lastIndexOf("/");if(cA!==cB.length-1){cB=cB.slice(0,cA+1)}return cB+cz}function b4(cB,cz){var cA;cB=String(cB).toLowerCase();cz=String(cz).toLowerCase();if(cB===cz){return true}if(cz.slice(0,1)==="."){if(cB===cz.slice(1)){return true}cA=cB.length-cz.length;if((cA>0)&&(cB.slice(cA)===cz)){return true}}return false}function cm(cA,cz){cA=String(cA);return cA.indexOf(cz,cA.length-cz.length)!==-1}function aP(cA,cz){cA=String(cA); +return cA.substr(0,cA.length-cz)}function bN(cz){var cA=document.createElement("a");if(cz.indexOf("//")!==0&&cz.indexOf("http")!==0){cz="http://"+cz}cA.href=n.toAbsoluteUrl(cz);if(cA.pathname){return cA.pathname}return""}function aF(cA,cz){var cB=(!cz||cz==="/");if(cB){return true}if(cA===cz){return true}if(!cA){return false}cz=String(cz).toLowerCase();cA=String(cA).toLowerCase();if(!cm(cA,"/")){cA+="/"}if(!cm(cz,"/")){cz+="/"}return cA.indexOf(cz)===0}function ab(cD,cF){var cA,cz,cB,cC,cE;for(cA=0;cA<ae.length;cA++){cC=A(ae[cA]);cE=bN(ae[cA]);if(b4(cD,cC)&&aF(cF,cE)){return true}}return false}function ay(cC){var cA,cz,cB;for(cA=0;cA<ae.length;cA++){cz=A(ae[cA].toLowerCase());if(cC===cz){return true}if(cz.slice(0,1)==="."){if(cC===cz.slice(1)){return true}cB=cC.length-cz.length;if((cB>0)&&(cC.slice(cB)===cz)){return true}}}return false}function bS(cz,cB){var cA=new Image(1,1);cA.onload=function(){v=0;if(typeof cB==="function"){cB()}};cA.src=ai+(ai.indexOf("?")<0?"?":"&")+cz}function cn(cA,cD,cz){if(!y(cz)||null===cz){cz=true +}try{var cC=I.XMLHttpRequest?new I.XMLHttpRequest():I.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cC.open("POST",ai,true);cC.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cz){bS(cA,cD)}else{if(typeof cD==="function"){cD()}}};cC.setRequestHeader("Content-Type",bX);cC.send(cA)}catch(cB){if(cz){bS(cA,cD)}}}function bJ(cA){var cz=new Date();var cB=cz.getTime()+cA;if(!k||cB>k){k=cB}}function bQ(cz){if(bF||!aH){return}bF=setTimeout(function cA(){bF=null;if(bl()){return}var cB=new Date(),cC=aH-(cB.getTime()-ci);cC=Math.min(aH,cC);bQ(cC)},cz||aH)}function be(){if(!bF){return}clearTimeout(bF);bF=null}function aM(){if(bl()){return}bQ()}function af(){be()}function cx(){if(aq||!aH){return}aq=true;X(I,"focus",aM);X(I,"blur",af);bQ()}function b1(cD){var cA=new Date();var cz=cA.getTime();ci=cz;if(b0&&cz<b0){var cB=b0-cz;setTimeout(cD,cB);bJ(cB+50);b0+=50;return}if(b0===false){var cC=800;b0=cz+cC}cD()}function ba(cA,cz,cB){if(!cb&&cA){b1(function(){if(cs==="POST"){cn(cA,cB) +}else{bS(cA,cB)}bJ(cz)})}if(!aq){cx()}else{bQ()}}function bM(cz){if(cb){return false}return(cz&&cz.length)}function cw(cB,cz){if(!bM(cB)){return}var cA='{"requests":["?'+cB.join('","?')+'"]}';b1(function(){cn(cA,null,false);bJ(cz)})}function aw(cz){return aT+cz+"."+bz+"."+aX}function by(){if(aU){return"0"}if(!y(e.cookieEnabled)){var cz=aw("testcookie");cv(cz,"1");return ah(cz)==="1"?"1":"0"}return e.cookieEnabled?"1":"0"}function aR(){aX=bA((ch||cf)+(aY||"/")).slice(0,4)}function bq(){var cA=aw("cvar"),cz=ah(cA);if(cz.length){cz=JSON2.parse(cz);if(L(cz)){return cz}}return{}}function b2(){if(at===false){at=bq()}}function cc(){return bA((e.userAgent||"")+(e.platform||"")+JSON2.stringify(cq)+(new Date()).getTime()+Math.random()).slice(0,16)}function b9(){var cB=new Date(),cz=Math.round(cB.getTime()/1000),cA=aw("id"),cE=ah(cA),cD,cC;if(cE){cD=cE.split(".");cD.unshift("0");if(bi.length){cD[1]=bi}return cD}if(bi.length){cC=bi}else{if("0"===by()){cC=""}else{cC=cc()}}cD=["1",cC,cz,0,cz,"",""];return cD +}function aA(){var cG=b9(),cC=cG[0],cD=cG[1],cA=cG[2],cz=cG[3],cE=cG[4],cB=cG[5];if(!y(cG[6])){cG[6]=""}var cF=cG[6];return{newVisitor:cC,uuid:cD,createTs:cA,visitCount:cz,currentVisitTs:cE,lastVisitTs:cB,lastEcommerceOrderTs:cF}}function al(){var cC=new Date(),cA=cC.getTime(),cD=aA().createTs;var cz=parseInt(cD,10);var cB=(cz*1000)+b7-cA;return cB}function ao(cz){if(!bz){return}var cB=new Date(),cA=Math.round(cB.getTime()/1000);if(!y(cz)){cz=aA()}var cC=cz.uuid+"."+cz.createTs+"."+cz.visitCount+"."+cA+"."+cz.lastVisitTs+"."+cz.lastEcommerceOrderTs;cv(aw("id"),cC,al(),aY,ch)}function bg(){var cz=ah(aw("ref"));if(cz.length){try{cz=JSON2.parse(cz);if(L(cz)){return cz}}catch(cA){}}return["","",0,""]}function br(cB,cA,cz){cv(cB,"",-86400,cA,cz)}function a3(cA){var cz="testvalue";cv("test",cz,10000,null,cA);if(ah("test")===cz){br("test",null,cA);return true}return false}function aj(){var cB=aU;aU=false;var cz=["id","ses","cvar","ref"];var cA,cC;for(cA=0;cA<cz.length;cA++){cC=aw(cz[cA]);if(0!==ah(cC)){br(cC,aY,ch) +}}aU=cB}function bw(cz){bz=cz;ao()}function cy(cD){if(!cD||!L(cD)){return}var cC=[];var cB;for(cB in cD){if(Object.prototype.hasOwnProperty.call(cD,cB)){cC.push(cB)}}var cE={};cC.sort();var cz=cC.length;var cA;for(cA=0;cA<cz;cA++){cE[cC[cA]]=cD[cC[cA]]}return cE}function bG(){cv(aw("ses"),"*",bR,aY,ch)}function bU(cB,cW,cX,cC){var cV,cA=new Date(),cJ=Math.round(cA.getTime()/1000),cG,cU,cD=1024,c2,cK,cS=at,cE=aw("ses"),cQ=aw("ref"),cN=aw("cvar"),cO=ah(cE),cT=bg(),cZ=aG||bh,cH,cz;if(aU){aj()}if(cb){return""}var cP=aA();if(!y(cC)){cC=""}var cM=w.characterSet||w.charset;if(!cM||cM.toLowerCase()==="utf-8"){cM=null}cH=cT[0];cz=cT[1];cG=cT[2];cU=cT[3];if(!cO){var cY=bR/1000;if(!cP.lastVisitTs||(cJ-cP.lastVisitTs)>cY){cP.visitCount++;cP.lastVisitTs=cP.currentVisitTs}if(!a2||!cH.length){for(cV in bT){if(Object.prototype.hasOwnProperty.call(bT,cV)){cH=K(cZ,bT[cV]);if(cH.length){break}}}for(cV in bb){if(Object.prototype.hasOwnProperty.call(bb,cV)){cz=K(cZ,bb[cV]);if(cz.length){break}}}}c2=c(aW);cK=cU.length?c(cU):""; +if(c2.length&&!ay(c2)&&(!a2||!cK.length||ay(cK))){cU=aW}if(cU.length||cH.length){cG=cJ;cT=[cH,cz,cG,bv(cU.slice(0,cD))];cv(cQ,JSON2.stringify(cT),co,aY,ch)}}cB+="&idsite="+bz+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cA.getHours()+"&m="+cA.getMinutes()+"&s="+cA.getSeconds()+"&url="+m(bv(cZ))+(aW.length?"&urlref="+m(bv(aW)):"")+((a5&&a5.length)?"&uid="+m(a5):"")+"&_id="+cP.uuid+"&_idts="+cP.createTs+"&_idvc="+cP.visitCount+"&_idn="+cP.newVisitor+(cH.length?"&_rcn="+m(cH):"")+(cz.length?"&_rck="+m(cz):"")+"&_refts="+cG+"&_viewts="+cP.lastVisitTs+(String(cP.lastEcommerceOrderTs).length?"&_ects="+cP.lastEcommerceOrderTs:"")+(String(cU).length?"&_ref="+m(bv(cU.slice(0,cD))):"")+(cM?"&cs="+m(cM):"")+"&send_image=0";for(cV in cq){if(Object.prototype.hasOwnProperty.call(cq,cV)){cB+="&"+cV+"="+cq[cV]}}var c1=[];if(cW){for(cV in cW){if(Object.prototype.hasOwnProperty.call(cW,cV)&&/^dimension\d+$/.test(cV)){var cF=cV.replace("dimension","");c1.push(parseInt(cF,10));c1.push(String(cF));cB+="&"+cV+"="+cW[cV]; +delete cW[cV]}}}if(cW&&s(cW)){cW=null}for(cV in aV){if(Object.prototype.hasOwnProperty.call(aV,cV)){var cL=(-1===c1.indexOf(cV));if(cL){cB+="&dimension"+cV+"="+aV[cV]}}}if(cW){cB+="&data="+m(JSON2.stringify(cW))}else{if(Y){cB+="&data="+m(JSON2.stringify(Y))}}function cI(c3,c4){var c5=JSON2.stringify(c3);if(c5.length>2){return"&"+c4+"="+m(c5)}return""}var c0=cy(bp);var cR=cy(bO);cB+=cI(c0,"cvar");cB+=cI(cR,"e_cvar");if(at){cB+=cI(at,"_cvar");for(cV in cS){if(Object.prototype.hasOwnProperty.call(cS,cV)){if(at[cV][0]===""||at[cV][1]===""){delete at[cV]}}}if(bk){cv(cN,JSON2.stringify(at),bR,aY,ch)}}if(aE){if(bP){cB+=">_ms="+bP}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cB+=">_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cP.lastEcommerceOrderTs=y(cC)&&String(cC).length?cC:cP.lastEcommerceOrderTs;ao(cP);bG();cB+=Q(cX);if(cj.length){cB+="&"+cj}if(r(bE)){cB=bE(cB)}return cB}bl=function aI(){var cz=new Date();if(ci+aH<=cz.getTime()){var cA=bU("ping=1",null,"ping"); +ba(cA,bd);return true}return false};function aZ(cC,cB,cG,cD,cz,cJ){var cE="idgoal=0",cF,cA=new Date(),cH=[],cI;if(String(cC).length){cE+="&ec_id="+m(cC);cF=Math.round(cA.getTime()/1000)}cE+="&revenue="+cB;if(String(cG).length){cE+="&ec_st="+cG}if(String(cD).length){cE+="&ec_tx="+cD}if(String(cz).length){cE+="&ec_sh="+cz}if(String(cJ).length){cE+="&ec_dt="+cJ}if(ck){for(cI in ck){if(Object.prototype.hasOwnProperty.call(ck,cI)){if(!y(ck[cI][1])){ck[cI][1]=""}if(!y(ck[cI][2])){ck[cI][2]=""}if(!y(ck[cI][3])||String(ck[cI][3]).length===0){ck[cI][3]=0}if(!y(ck[cI][4])||String(ck[cI][4]).length===0){ck[cI][4]=1}cH.push(ck[cI])}}cE+="&ec_items="+m(JSON2.stringify(cH))}cE=bU(cE,Y,"ecommerce",cF);ba(cE,bd)}function bs(cz,cD,cC,cB,cA,cE){if(String(cz).length&&y(cD)){aZ(cz,cD,cC,cB,cA,cE)}}function a0(cz){if(y(cz)){aZ("",cz,"","","","")}}function bt(cB,cC){var cz=new Date(),cA=bU("action_name="+m(V(cB||aS)),cC,"log");ba(cA,bd)}function aC(cB,cA){var cC,cz="(^| )(piwik[_-]"+cA;if(cB){for(cC=0;cC<cB.length; +cC++){cz+="|"+cB[cC]}}cz+=")( |$)";return new RegExp(cz)}function ax(cz){return(ai&&cz&&0===String(cz).indexOf(ai))}function bV(cD,cz,cE,cA){if(ax(cz)){return 0}var cC=aC(bf,"download"),cB=aC(aJ,"link"),cF=new RegExp("\\.("+cp.join("|")+")([?&#]|$)","i");if(cB.test(cD)){return"link"}if(cA||cC.test(cD)||cF.test(cz)){return"download"}if(cE){return 0}return"link"}function ac(cA){var cz;cz=cA.parentNode;while(cz!==null&&y(cz)){if(S.isLinkElement(cA)){break}cA=cz;cz=cA.parentNode}return cA}function ct(cE){cE=ac(cE);if(!S.hasNodeAttribute(cE,"href")){return}if(!y(cE.href)){return}var cD=S.getAttributeValueFromNode(cE,"href");if(ax(cD)){return}var cA=cE.pathname||bN(cE.href);var cF=cE.hostname||c(cE.href);var cG=cF.toLowerCase();var cB=cE.href.replace(cF,cG);var cC=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i");if(!cC.test(cB)){var cz=bV(cE.className,cB,ab(cG,cA),S.hasNodeAttribute(cE,"download"));if(cz){return{type:cz,href:cB}}}}function ar(cz,cA,cB,cC){var cD=n.buildInteractionRequestParams(cz,cA,cB,cC); +if(!cD){return}return bU(cD,null,"contentInteraction")}function b8(cB,cC,cG,cz,cA){if(!y(cB)){return}if(ax(cB)){return cB}var cE=n.toAbsoluteUrl(cB);var cD="redirecturl="+m(cE)+"&";cD+=ar(cC,cG,cz,(cA||cB));var cF="&";if(ai.indexOf("?")<0){cF="?"}return ai+cF+cD}function aN(cz,cA){if(!cz||!cA){return false}var cB=n.findTargetNode(cz);if(n.shouldIgnoreInteraction(cB)){return false}cB=n.findTargetNodeNoDefault(cz);if(cB&&!J(cB,cA)){return false}return true}function bW(cB,cA,cD){if(!cB){return}var cz=n.findParentContentNode(cB);if(!cz){return}if(!aN(cz,cB)){return}var cC=n.buildContentBlock(cz);if(!cC){return}if(!cC.target&&cD){cC.target=cD}return n.buildInteractionRequestParams(cA,cC.name,cC.piece,cC.target)}function az(cA){if(!bD||!bD.length){return false}var cz,cB;for(cz=0;cz<bD.length;cz++){cB=bD[cz];if(cB&&cB.name===cA.name&&cB.piece===cA.piece&&cB.target===cA.target){return true}}return false}function a9(cC){if(!cC){return false}var cF=n.findTargetNode(cC);if(!cF||n.shouldIgnoreInteraction(cF)){return false +}var cG=ct(cF);if(cr&&cG&&cG.type){return false}if(S.isLinkElement(cF)&&S.hasNodeAttributeWithValue(cF,"href")){var cz=String(S.getAttributeValueFromNode(cF,"href"));if(0===cz.indexOf("#")){return false}if(ax(cz)){return true}if(!n.isUrlToCurrentDomain(cz)){return false}var cD=n.buildContentBlock(cC);if(!cD){return}var cB=cD.name;var cH=cD.piece;var cE=cD.target;if(!S.hasNodeAttributeWithValue(cF,n.CONTENT_TARGET_ATTR)||cF.wasContentTargetAttrReplaced){cF.wasContentTargetAttrReplaced=true;cE=n.toAbsoluteUrl(cz);S.setAnyAttribute(cF,n.CONTENT_TARGET_ATTR,cE)}var cA=b8(cz,"click",cB,cH,cE);n.setHrefAttribute(cF,cA);return true}return false}function ap(cA){if(!cA||!cA.length){return}var cz;for(cz=0;cz<cA.length;cz++){a9(cA[cz])}}function aB(cz){return function(cA){if(!cz){return}var cD=n.findParentContentNode(cz);var cE;if(cA){cE=cA.target||cA.srcElement}if(!cE){cE=cz}if(!aN(cD,cE)){return}bJ(bd);if(S.isLinkElement(cz)&&S.hasNodeAttributeWithValue(cz,"href")&&S.hasNodeAttributeWithValue(cz,n.CONTENT_TARGET_ATTR)){var cB=S.getAttributeValueFromNode(cz,"href"); +if(!ax(cB)&&cz.wasContentTargetAttrReplaced){S.setAnyAttribute(cz,n.CONTENT_TARGET_ATTR,"")}}var cI=ct(cz);if(Z&&cI&&cI.type){return cI.type}if(a9(cD)){return"href"}var cF=n.buildContentBlock(cD);if(!cF){return}var cC=cF.name;var cJ=cF.piece;var cH=cF.target;var cG=ar("click",cC,cJ,cH);ba(cG,bd);return cG}}function bu(cB){if(!cB||!cB.length){return}var cz,cA;for(cz=0;cz<cB.length;cz++){cA=n.findTargetNode(cB[cz]);if(cA&&!cA.contentInteractionTrackingSetupDone){cA.contentInteractionTrackingSetupDone=true;X(cA,"click",aB(cA))}}}function a4(cB,cC){if(!cB||!cB.length){return[]}var cz,cA;for(cz=0;cz<cB.length;cz++){if(az(cB[cz])){cB.splice(cz,1);cz--}else{bD.push(cB[cz])}}if(!cB||!cB.length){return[]}ap(cC);bu(cC);var cD=[];for(cz=0;cz<cB.length;cz++){cA=bU(n.buildImpressionRequestParams(cB[cz].name,cB[cz].piece,cB[cz].target),undefined,"contentImpressions");cD.push(cA)}return cD}function bZ(cA){var cz=n.collectContent(cA);return a4(cz,cA)}function aL(cA){if(!cA||!cA.length){return[]}var cz; +for(cz=0;cz<cA.length;cz++){if(!n.isNodeVisible(cA[cz])){cA.splice(cz,1);cz--}}if(!cA||!cA.length){return[]}return bZ(cA)}function ak(cB,cz,cA){var cC=n.buildImpressionRequestParams(cB,cz,cA);return bU(cC,null,"contentImpression")}function cu(cC,cA){if(!cC){return}var cz=n.findParentContentNode(cC);var cB=n.buildContentBlock(cz);if(!cB){return}if(!cA){cA="Unknown"}return ar(cA,cB.name,cB.piece,cB.target)}function ca(cA,cC,cz,cB){return"e_c="+m(cA)+"&e_a="+m(cC)+(y(cz)?"&e_n="+m(cz):"")+(y(cB)?"&e_v="+m(cB):"")}function ad(cB,cD,cz,cC,cE){if(String(cB).length===0||String(cD).length===0){return false}var cA=bU(ca(cB,cD,cz,cC),cE,"event");ba(cA,bd)}function bC(cz,cC,cA,cD){var cB=bU("search="+m(cz)+(cC?"&search_cat="+m(cC):"")+(y(cA)?"&search_count="+cA:""),cD,"sitesearch");ba(cB,bd)}function ce(cz,cC,cB){var cA=bU("idgoal="+cz+(cC?"&revenue="+cC:""),cB,"goal");ba(cA,bd)}function cl(cC,cz,cG,cF,cB){var cE=cz+"="+m(bv(cC));var cA=bW(cB,"click",cC);if(cA){cE+="&"+cA}var cD=bU(cE,cG,"link");ba(cD,(cF?0:bd),cF) +}function bm(cA,cz){if(cA!==""){return cA+cz.charAt(0).toUpperCase()+cz.slice(1)}return cz}function bK(cE){var cD,cz,cC=["","webkit","ms","moz"],cB;if(!aQ){for(cz=0;cz<cC.length;cz++){cB=cC[cz];if(Object.prototype.hasOwnProperty.call(w,bm(cB,"hidden"))){if(w[bm(cB,"visibilityState")]==="prerender"){cD=true}break}}}if(cD){X(w,cB+"visibilitychange",function cA(){w.removeEventListener(cB+"visibilitychange",cA,false);cE()});return}cE()}function an(cz){if(w.readyState==="complete"){cz()}else{if(I.addEventListener){I.addEventListener("load",cz)}else{if(I.attachEvent){I.attachEvent("onLoad",cz)}}}}function aO(cA){var cz=false;if(w.attachEvent){cz=w.readyState==="complete"}else{cz=w.readyState!=="loading"}if(cz){cA()}else{if(w.addEventListener){w.addEventListener("DOMContentLoaded",cA)}else{if(w.attachEvent){w.attachEvent("onreadystatechange",cA)}}}}function b5(cz){var cA=ct(cz);if(cA&&cA.type){cA.href=j(cA.href);cl(cA.href,cA.type,undefined,null,cz)}}function bY(){return w.all&&!w.addEventListener +}function cg(cz){var cB=cz.which;var cA=(typeof cz.button);if(!cB&&cA!=="undefined"){if(bY()){if(cz.button&1){cB=1}else{if(cz.button&2){cB=3}else{if(cz.button&4){cB=2}}}}else{if(cz.button===0||cz.button==="0"){cB=1}else{if(cz.button&1){cB=2}else{if(cz.button&2){cB=3}}}}}return cB}function bn(cz){switch(cg(cz)){case 1:return"left";case 2:return"middle";case 3:return"right"}}function aD(cz){return cz.target||cz.srcElement}function ag(cz){return function(cC){cC=cC||I.event;var cB=bn(cC);var cD=aD(cC);if(cC.type==="click"){var cA=false;if(cz&&cB==="middle"){cA=true}if(cD&&!cA){b5(cD)}}else{if(cC.type==="mousedown"){if(cB==="middle"&&cD){au=cB;a7=cD}else{au=a7=null}}else{if(cC.type==="mouseup"){if(cB===au&&cD===a7){b5(cD)}au=a7=null}else{if(cC.type==="contextmenu"){b5(cD)}}}}}}function aa(cA,cz){X(cA,"click",ag(cz),false);if(cz){X(cA,"mouseup",ag(cz),false);X(cA,"mousedown",ag(cz),false);X(cA,"contextmenu",ag(cz),false)}}function a8(cA){if(!Z){Z=true;var cB,cz=aC(a6,"ignore"),cC=w.links;if(cC){for(cB=0; +cB<cC.length;cB++){if(!cz.test(cC[cB].className)){aa(cC[cB],cA)}}}}}function av(cB,cD,cE){if(bI){return true}bI=true;var cF=false;var cC,cA;function cz(){cF=true}an(function(){function cG(cI){setTimeout(function(){if(!bI){return}cF=false;cE.trackVisibleContentImpressions();cG(cI)},cI)}function cH(cI){setTimeout(function(){if(!bI){return}if(cF){cF=false;cE.trackVisibleContentImpressions()}cH(cI)},cI)}if(cB){cC=["scroll","resize"];for(cA=0;cA<cC.length;cA++){if(w.addEventListener){w.addEventListener(cC[cA],cz)}else{I.attachEvent("on"+cC[cA],cz)}}cH(100)}if(cD&&cD>0){cD=parseInt(cD,10);cG(cD)}})}function aK(cD,cF){var cE=bN(cD);var cC=bN(cF);if(!cE||cE==="/"||!cC||cC==="/"){return}var cB=A(cD);if(ab(cB,"/")){return}if(cm(cE,"/")){cE=aP(cE,1)}var cG=cE.split("/");var cA;for(cA=2;cA<cG.length;cA++){var cz=cG.slice(0,cA).join("/");if(ab(cB,cz)){cE=cz;break}}if(!aF(cC,cE)){return}return cE}function b3(){var cA,cB,cC={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},cz=I.devicePixelRatio||1; +if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(cA in cC){if(Object.prototype.hasOwnProperty.call(cC,cA)){cB=e.mimeTypes[cC[cA]];cq[cA]=(cB&&cB.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&y(e.javaEnabled)&&e.javaEnabled()){cq.java="1"}if(r(I.GearsFactory)){cq.gears="1"}cq.cookie=by()}cq.res=M.width*cz+"x"+M.height*cz}b3();aR();ao();return{getVisitorId:function(){return aA().uuid},getVisitorInfo:function(){return b9()},getAttributionInfo:function(){return bg()},getAttributionCampaignName:function(){return bg()[0]},getAttributionCampaignKeyword:function(){return bg()[1]},getAttributionReferrerTimestamp:function(){return bg()[2]},getAttributionReferrerUrl:function(){return bg()[3]},setTrackerUrl:function(cz){ai=cz},getTrackerUrl:function(){return ai},getSiteId:function(){return bz},setSiteId:function(cz){bw(cz)},setUserId:function(cz){if(!y(cz)||!cz.length){return}a5=cz;bi=bA(a5).substr(0,16)},getUserId:function(){return a5},setCustomData:function(cz,cA){if(L(cz)){Y=cz +}else{if(!Y){Y={}}Y[cz]=cA}},getCustomData:function(){return Y},setCustomRequestProcessing:function(cz){bE=cz},appendToTrackingUrl:function(cz){cj=cz},getRequest:function(cz){return bU(cz)},addPlugin:function(cz,cA){a[cz]=cA},setCustomDimension:function(cz,cA){cz=parseInt(cz,10);if(cz>0){if(!y(cA)){cA=""}if(!o(cA)){cA=String(cA)}aV[cz]=cA}},getCustomDimension:function(cz){cz=parseInt(cz,10);if(cz>0&&Object.prototype.hasOwnProperty.call(aV,cz)){return aV[cz]}},deleteCustomDimension:function(cz){cz=parseInt(cz,10);if(cz>0){delete aV[cz]}},setCustomVariable:function(cA,cz,cD,cB){var cC;if(!y(cB)){cB="visit"}if(!y(cz)){return}if(!y(cD)){cD=""}if(cA>0){cz=!o(cz)?String(cz):cz;cD=!o(cD)?String(cD):cD;cC=[cz.slice(0,a1),cD.slice(0,a1)];if(cB==="visit"||cB===2){b2();at[cA]=cC}else{if(cB==="page"||cB===3){bp[cA]=cC}else{if(cB==="event"){bO[cA]=cC}}}}},getCustomVariable:function(cA,cB){var cz;if(!y(cB)){cB="visit"}if(cB==="page"||cB===3){cz=bp[cA]}else{if(cB==="event"){cz=bO[cA]}else{if(cB==="visit"||cB===2){b2(); +cz=at[cA]}}}if(!y(cz)||(cz&&cz[0]==="")){return false}return cz},deleteCustomVariable:function(cz,cA){if(this.getCustomVariable(cz,cA)){this.setCustomVariable(cz,"","",cA)}},storeCustomVariablesInCookie:function(){bk=true},setLinkTrackingTimer:function(cz){bd=cz},setDownloadExtensions:function(cz){if(o(cz)){cz=cz.split("|")}cp=cz},addDownloadExtensions:function(cA){var cz;if(o(cA)){cA=cA.split("|")}for(cz=0;cz<cA.length;cz++){cp.push(cA[cz])}},removeDownloadExtensions:function(cB){var cA,cz=[];if(o(cB)){cB=cB.split("|")}for(cA=0;cA<cp.length;cA++){if(B(cB,cp[cA])===-1){cz.push(cp[cA])}}cp=cz},setDomains:function(cz){ae=o(cz)?[cz]:cz;var cB=false,cA;for(cA in ae){if(Object.prototype.hasOwnProperty.call(ae,cA)&&b4(cf,A(String(ae[cA])))){cB=true;if(!aY){var cC=aK(ae[cA],bh);if(cC){this.setCookiePath(cC)}break}}}if(!cB){ae.push(cf)}},setIgnoreClasses:function(cz){a6=o(cz)?[cz]:cz},setRequestMethod:function(cz){cs=cz||bL},setRequestContentType:function(cz){bX=cz||am},setReferrerUrl:function(cz){aW=cz +},setCustomUrl:function(cz){aG=bo(bh,cz)},setDocumentTitle:function(cz){aS=cz},setAPIUrl:function(cz){bc=cz},setDownloadClasses:function(cz){bf=o(cz)?[cz]:cz},setLinkClasses:function(cz){aJ=o(cz)?[cz]:cz},setCampaignNameKey:function(cz){bT=o(cz)?[cz]:cz},setCampaignKeywordKey:function(cz){bb=o(cz)?[cz]:cz},discardHashTag:function(cz){bj=cz},setCookieNamePrefix:function(cz){aT=cz;at=bq()},setCookieDomain:function(cz){var cA=A(cz);if(a3(cA)){ch=cA;aR()}},setCookiePath:function(cz){aY=cz;aR()},setVisitorCookieTimeout:function(cz){b7=cz*1000},setSessionCookieTimeout:function(cz){bR=cz*1000},setReferralCookieTimeout:function(cz){co=cz*1000},setConversionAttributionFirstReferrer:function(cz){a2=cz},disableCookies:function(){aU=true;cq.cookie="0";if(bz){aj()}},deleteCookies:function(){aj()},setDoNotTrack:function(cA){var cz=e.doNotTrack||e.msDoNotTrack;cb=cA&&(cz==="yes"||cz==="1");if(cb){this.disableCookies()}},addListener:function(cA,cz){aa(cA,cz)},enableLinkTracking:function(cz){cr=true;if(q){a8(cz) +}else{G.push(function(){a8(cz)})}},enableJSErrorTracking:function(){if(cd){return}cd=true;var cz=I.onerror;I.onerror=function(cE,cC,cB,cD,cA){bK(function(){var cF="JavaScript Errors";var cG=cC+":"+cB;if(cD){cG+=":"+cD}ad(cF,cG,cE)});if(cz){return cz(cE,cC,cB,cD,cA)}return false}},disablePerformanceTracking:function(){aE=false},setGenerationTimeMs:function(cz){bP=parseInt(cz,10)},enableHeartBeatTimer:function(cz){cz=Math.max(cz,1);aH=(cz||15)*1000;if(ci!==null){cx()}},killFrame:function(){if(I.location!==I.top.location){I.top.location=I.location}},redirectFile:function(cz){if(I.location.protocol==="file:"){I.location=cz}},setCountPreRendered:function(cz){aQ=cz},trackGoal:function(cz,cB,cA){bK(function(){ce(cz,cB,cA)})},trackLink:function(cA,cz,cC,cB){bK(function(){cl(cA,cz,cC,cB)})},trackPageView:function(cz,cA){bD=[];if(C(bz)){bK(function(){O(ai,bc,bz)})}else{bK(function(){bt(cz,cA)})}},trackAllContentImpressions:function(){if(C(bz)){return}bK(function(){aO(function(){var cz=n.findContentNodes(); +var cA=bZ(cz);cw(cA,bd)})})},trackVisibleContentImpressions:function(cz,cA){if(C(bz)){return}if(!y(cz)){cz=true}if(!y(cA)){cA=750}av(cz,cA,this);bK(function(){an(function(){var cB=n.findContentNodes();var cC=aL(cB);cw(cC,bd)})})},trackContentImpression:function(cB,cz,cA){if(C(bz)){return}if(!cB){return}cz=cz||"Unknown";bK(function(){var cC=ak(cB,cz,cA);ba(cC,bd)})},trackContentImpressionsWithinNode:function(cz){if(C(bz)||!cz){return}bK(function(){if(bI){an(function(){var cA=n.findContentNodesWithinNode(cz);var cB=aL(cA);cw(cB,bd)})}else{aO(function(){var cA=n.findContentNodesWithinNode(cz);var cB=bZ(cA);cw(cB,bd)})}})},trackContentInteraction:function(cB,cC,cz,cA){if(C(bz)){return}if(!cB||!cC){return}cz=cz||"Unknown";bK(function(){var cD=ar(cB,cC,cz,cA);ba(cD,bd)})},trackContentInteractionNode:function(cA,cz){if(C(bz)||!cA){return}bK(function(){var cB=cu(cA,cz);ba(cB,bd)})},logAllContentBlocksOnPage:function(){var cA=n.findContentNodes();var cz=n.collectContent(cA);if(console!==undefined&&console&&console.log){console.log(cz) +}},trackEvent:function(cA,cC,cz,cB,cD){bK(function(){ad(cA,cC,cz,cB,cD)})},trackSiteSearch:function(cz,cB,cA,cC){bK(function(){bC(cz,cB,cA,cC)})},setEcommerceView:function(cC,cz,cB,cA){if(!y(cB)||!cB.length){cB=""}else{if(cB instanceof Array){cB=JSON2.stringify(cB)}}bp[5]=["_pkc",cB];if(y(cA)&&String(cA).length){bp[2]=["_pkp",cA]}if((!y(cC)||!cC.length)&&(!y(cz)||!cz.length)){return}if(y(cC)&&cC.length){bp[3]=["_pks",cC]}if(!y(cz)||!cz.length){cz=""}bp[4]=["_pkn",cz]},addEcommerceItem:function(cD,cz,cB,cA,cC){if(cD.length){ck[cD]=[cD,cz,cB,cA,cC]}},trackEcommerceOrder:function(cz,cD,cC,cB,cA,cE){bs(cz,cD,cC,cB,cA,cE)},trackEcommerceCartUpdate:function(cz){a0(cz)}}}function x(){return{push:T}}function b(ad,ac){var ae={};var aa,ab;for(aa=0;aa<ac.length;aa++){var Y=ac[aa];ae[Y]=1;for(ab=0;ab<ad.length;ab++){if(ad[ab]&&ad[ab][0]){var Z=ad[ab][0];if(Y===Z){T(ad[ab]);delete ad[ab];if(ae[Z]>1){if(console!==undefined&&console&&console.error){console.error("The method "+Z+' is registered more than once in "paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers') +}}ae[Z]++}}}}return ad}X(I,"beforeunload",U,false);p();Date.prototype.getTimeAlias=Date.prototype.getTime;N=new F();var t=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","enableLinkTracking"];_paq=b(_paq,t);for(v=0;v<_paq.length;v++){if(_paq[v]){T(_paq[v])}}_paq=new x();d={addPlugin:function(Y,Z){a[Y]=Z},getTracker:function(Y,Z){if(!y(Z)){Z=this.getAsyncTracker().getSiteId()}if(!y(Y)){Y=this.getAsyncTracker().getTrackerUrl()}return new F(Y,Z)},getAsyncTracker:function(){return N}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g); c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; /*! @license-end */ };
\ No newline at end of file diff --git a/plugins/API/API.php b/plugins/API/API.php index 8dcaf0186b..4eab2c63fd 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -91,8 +91,9 @@ class API extends \Piwik\Plugin\API * are not visible in the UI and not present in the API meta data. These columns are * translated here. * @return array + * @deprecated since Piwik 2.15.1 */ - public static function getDefaultMetricTranslations() + public function getDefaultMetricTranslations() { return Metrics::getDefaultMetricTranslations(); } @@ -105,6 +106,8 @@ class API extends \Piwik\Plugin\API */ public function getAvailableMeasurableTypes() { + Piwik::checkUserHasSomeViewAccess(); + $typeManager = new TypeManager(); $types = $typeManager->getAllTypes(); @@ -123,11 +126,17 @@ class API extends \Piwik\Plugin\API public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true) { - $isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous(); + if (empty($idSites)) { + Piwik::checkUserHasSomeViewAccess(); + } else { + Piwik::checkUserHasViewAccess($idSites); + } + + $isNotAnonymous = !Piwik::isUserIsAnonymous(); $sites = (is_array($idSites) ? implode('.', $idSites) : (int) $idSites); $cache = Cache::getTransientCache(); - $cachKey = 'API.getSegmentsMetadata' . $sites . '_' . (int) $_hideImplementationData . '_' . (int) $isAuthenticatedWithViewAccess; + $cachKey = 'API.getSegmentsMetadata' . $sites . '_' . (int) $_hideImplementationData . '_' . (int) $isNotAnonymous; $cachKey = CacheId::pluginAware($cachKey); if ($cache->contains($cachKey)) { @@ -135,7 +144,7 @@ class API extends \Piwik\Plugin\API } $metadata = new SegmentMetadata(); - $segments = $metadata->getSegmentsMetadata($idSites, $_hideImplementationData, $isAuthenticatedWithViewAccess); + $segments = $metadata->getSegmentsMetadata($idSites, $_hideImplementationData, $isNotAnonymous); $cache->save($cachKey, $segments); @@ -164,6 +173,7 @@ class API extends \Piwik\Plugin\API * * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL. * @return string + * @deprecated since Piwik 2.15.1 */ public function getLogoUrl($pathOnly = false) { @@ -176,6 +186,7 @@ class API extends \Piwik\Plugin\API * * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL. * @return string + * @deprecated since Piwik 2.15.1 */ public function getHeaderLogoUrl($pathOnly = false) { @@ -214,6 +225,8 @@ class API extends \Piwik\Plugin\API public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false, $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false) { + Piwik::checkUserHasViewAccess($idSite); + if ($language) { /** @var Translator $translator */ $translator = StaticContainer::get('Piwik\Translation\Translator'); @@ -239,6 +252,8 @@ class API extends \Piwik\Plugin\API public function getReportMetadata($idSites = '', $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false) { + Piwik::checkUserHasViewAccess($idSites); + $reporter = new ProcessedReport(); $metadata = $reporter->getReportMetadata($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports); return $metadata; @@ -247,11 +262,13 @@ class API extends \Piwik\Plugin\API public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, $apiParameters = false, $idGoal = false, $language = false, $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false, - $format_metrics = null) + $format_metrics = null, $idDimension = false) { + Piwik::checkUserHasViewAccess($idSite); + $reporter = new ProcessedReport(); $processed = $reporter->getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment, - $apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics); + $apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics, $idDimension); return $processed; } @@ -296,6 +313,8 @@ class API extends \Piwik\Plugin\API */ public function get($idSite, $period, $date, $segment = false, $columns = false) { + Piwik::checkUserHasViewAccess($idSite); + $columns = Piwik::getArrayFromApiParameter($columns); // build columns map for faster checks later on @@ -376,13 +395,16 @@ class API extends \Piwik\Plugin\API * @param bool|int $idGoal * @param bool|string $legendAppendMetric * @param bool|string $labelUseAbsoluteUrl + * @param bool|int $idDimension * @return array */ - public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true) + public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false) { + Piwik::checkUserHasViewAccess($idSite); + $rowEvolution = new RowEvolution(); return $rowEvolution->getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $column, - $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl); + $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl, $idDimension); } /** @@ -442,30 +464,20 @@ class API extends \Piwik\Plugin\API if (empty(Config::getInstance()->General['enable_segment_suggested_values'])) { return array(); } + Piwik::checkUserHasViewAccess($idSite); $maxSuggestionsToReturn = 30; - $segmentsMetadata = $this->getSegmentsMetadata($idSite, $_hideImplementationData = false); - - $segmentFound = false; - foreach ($segmentsMetadata as $segmentMetadata) { - if ($segmentMetadata['segment'] == $segmentName) { - $segmentFound = $segmentMetadata; - break; - } - } - if (empty($segmentFound)) { - throw new \Exception("Requested segment not found."); - } + $segment = $this->findSegment($segmentName, $idSite); // if segment has suggested values callback then return result from it instead $suggestedValuesCallbackRequiresTable = false; - if (isset($segmentFound['suggestedValuesCallback'])) { + if (isset($segment['suggestedValuesCallback'])) { $suggestedValuesCallbackRequiresTable = $this->doesSuggestedValuesCallbackNeedData( - $segmentFound['suggestedValuesCallback']); + $segment['suggestedValuesCallback']); if (!$suggestedValuesCallbackRequiresTable) { - return call_user_func($segmentFound['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn); + return call_user_func($segment['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn); } } @@ -474,6 +486,57 @@ class API extends \Piwik\Plugin\API return array(); } + if (!empty($segment['unionOfSegments'])) { + $values = array(); + foreach ($segment['unionOfSegments'] as $unionSegmentName) { + $unionSegment = $this->findSegment($unionSegmentName, $idSite); + + try { + $result = $this->getSuggestedValuesForSegmentName($idSite, $unionSegment, $maxSuggestionsToReturn); + if (!empty($result)) { + $values = array_merge($result, $values); + } + } catch (\Exception $e) { + // we ignore if there was no data found for $unionSegmentName + } + } + + if (empty($values)) { + throw new \Exception("There was no data to suggest for $segmentName"); + } + + } else { + $values = $this->getSuggestedValuesForSegmentName($idSite, $segment, $maxSuggestionsToReturn); + } + + $values = $this->getMostFrequentValues($values); + $values = array_slice($values, 0, $maxSuggestionsToReturn); + $values = array_map(array('Piwik\Common', 'unsanitizeInputValue'), $values); + + return $values; + } + + private function findSegment($segmentName, $idSite) + { + $segmentsMetadata = $this->getSegmentsMetadata($idSite, $_hideImplementationData = false); + + $segmentFound = false; + foreach ($segmentsMetadata as $segmentMetadata) { + if ($segmentMetadata['segment'] == $segmentName) { + $segmentFound = $segmentMetadata; + break; + } + } + + if (empty($segmentFound)) { + throw new \Exception("Requested segment $segmentName not found."); + } + + return $segmentFound; + } + + private function getSuggestedValuesForSegmentName($idSite, $segment, $maxSuggestionsToReturn) + { $startDate = Date::now()->subDay(60)->toString(); $requestLastVisits = "method=Live.getLastVisitsDetails &idSite=$idSite @@ -483,6 +546,8 @@ class API extends \Piwik\Plugin\API &serialize=0 &flat=1"; + $segmentName = $segment['segment']; + // Select non empty fields only // Note: this optimization has only a very minor impact $requestLastVisits .= "&segment=$segmentName" . urlencode('!='); @@ -497,22 +562,18 @@ class API extends \Piwik\Plugin\API $request = new Request($requestLastVisits); $table = $request->process(); + if (empty($table)) { throw new \Exception("There was no data to suggest for $segmentName"); } - if ($suggestedValuesCallbackRequiresTable) { - $values = call_user_func($segmentFound['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table); + if (isset($segment['suggestedValuesCallback']) && + $this->doesSuggestedValuesCallbackNeedData($segment['suggestedValuesCallback'])) { + $values = call_user_func($segment['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table); } else { $values = $this->getSegmentValuesFromVisitorLog($segmentName, $table); } - $values = $this->getMostFrequentValues($values); - - $values = array_slice($values, 0, $maxSuggestionsToReturn); - - $values = array_map(array('Piwik\Common', 'unsanitizeInputValue'), $values); - return $values; } diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php index 65b59a2051..8a9a96b537 100644 --- a/plugins/API/ProcessedReport.php +++ b/plugins/API/ProcessedReport.php @@ -298,17 +298,25 @@ class ProcessedReport public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, $apiParameters = false, $idGoal = false, $language = false, $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false, - $formatMetrics = null) + $formatMetrics = null, $idDimension = false) { $timer = new Timer(); if (empty($apiParameters)) { $apiParameters = array(); } + if (!empty($idGoal) && empty($apiParameters['idGoal']) ) { $apiParameters['idGoal'] = $idGoal; } + + if (!empty($idDimension) + && empty($apiParameters['idDimension']) + ) { + $apiParameters['idDimension'] = (int) $idDimension; + } + // Is this report found in the Metadata available reports? $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc, $showSubtableReports = true); diff --git a/plugins/API/RowEvolution.php b/plugins/API/RowEvolution.php index c708608089..711a48316c 100644 --- a/plugins/API/RowEvolution.php +++ b/plugins/API/RowEvolution.php @@ -37,7 +37,7 @@ class RowEvolution 'getPageUrl' ); - public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true) + public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false) { // validation of requested $period & $date if ($period == 'range') { @@ -52,9 +52,9 @@ class RowEvolution $label = DataTablePostProcessor::unsanitizeLabelParameter($label); $labels = Piwik::getArrayFromApiParameter($label); - $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal); + $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal, $idDimension); - $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal); + $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal, $idDimension); if (empty($labels)) { $labels = $this->getLabelsFromDataTable($dataTable, $labels); @@ -249,7 +249,7 @@ class RowEvolution * @throws Exception * @return DataTable\Map|DataTable */ - private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false) + private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false, $idDimension = false) { if (!is_array($label)) { $label = array($label); @@ -266,6 +266,7 @@ class RowEvolution 'serialize' => '0', 'segment' => $segment, 'idGoal' => $idGoal, + 'idDimension' => $idDimension, // data for row evolution should NOT be limited 'filter_limit' => -1, @@ -310,12 +311,15 @@ class RowEvolution * @throws Exception * @return array */ - private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false) + private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false, $idDimension = false) { $apiParameters = array(); if (!empty($idGoal) && $idGoal > 0) { $apiParameters = array('idGoal' => $idGoal); } + if (!empty($idDimension) && $idDimension > 0) { + $apiParameters = array('idDimension' => (int) $idDimension); + } $reportMetadata = API::getInstance()->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc = false, $showSubtableReports = true); diff --git a/plugins/API/lang/cs.json b/plugins/API/lang/cs.json index 68c93a8e58..32d7dcba79 100644 --- a/plugins/API/lang/cs.json +++ b/plugins/API/lang/cs.json @@ -4,9 +4,9 @@ "KeepTokenSecret": "Tento token_auth je tajný jako vaše uživatelské jméno a heslo, %s neříkejte jej nikomu jinému %s!", "LoadedAPIs": "Úspěšně načteno %s API", "MoreInformation": "Pro více informací o API Piwiku se podívejte na %s Úvod do API Piwiku %s a %s Referenci API Piwiku %s", - "PluginDescription": "Všechna data v Piwiku jsou dostupná pomocí jednoduchých API. Tento zásuvný modul je vstupním bodem těchto webových služeb, který vám umožňuje získat vaše analitická data jako XML, JSON, CSV, PHP atd.", + "PluginDescription": "Všechna data v Piwiku jsou dostupná pomocí jednoduchých API. Tento zásuvný modul je vstupním bodem těchto webových služeb, který vám umožňuje získat vaše analytická data jako XML, JSON, CSV, PHP atd.", "ReportingApiReference": "Reference API hlášení", - "TopLinkTooltip": "Zpřístupněte Vaše Webové analýzy programově skrze jednoduché API pomocí json, xml a dalších.", + "TopLinkTooltip": "Zpřístupněte svoje Webové analýzy programově skrze jednoduché API pomocí json, xml a dalších.", "UserAuthentication": "Autentifikace uživatele", "UsingTokenAuth": "Pokud chcete %s načíst data ze skriptu, cronu, atd. %s Potřebujete přidat parametr %s k voláním API, které vyžadují přihlášení", "Glossary": "Glosář", diff --git a/plugins/API/lang/ja.json b/plugins/API/lang/ja.json index 7e65064104..f32bf15992 100644 --- a/plugins/API/lang/ja.json +++ b/plugins/API/lang/ja.json @@ -5,8 +5,11 @@ "LoadedAPIs": "%s API が正常に読み込まれました", "MoreInformation": "Piwik API の詳細については、%sIntroduction to Piwik API%s や %sPiwik API Reference%s を参照してください。", "PluginDescription": "Pwik の全てのデータは簡単な API を通して利用することができます。このプラグインはウェブサービスの入り口で、xml , json , php , csv などのあなたのウェブ分析データを入手するために呼び出すことができます。", + "ReportingApiReference": "API リファレンスを報告", "TopLinkTooltip": "jsopn、xml等シンプルなAPIを介して、プログラムで分析データにアクセスできます", "UserAuthentication": "ユーザー認証", - "UsingTokenAuth": "%sスクリプト(crontab 等)でリクエストデータを得たい場合%sは、API をコールする URL(認証が必要)にパラメータ %s を付加する必要があります。" + "UsingTokenAuth": "%sスクリプト(crontab 等)でリクエストデータを得たい場合%sは、API をコールする URL(認証が必要)にパラメータ %s を付加する必要があります。", + "Glossary": "用語集", + "LearnAboutCommonlyUsedTerms": "Piwik Analytics の最もよく使われる用語について: %s と %s" } }
\ No newline at end of file diff --git a/plugins/API/lang/ko.json b/plugins/API/lang/ko.json index c2c8657960..2f04051378 100644 --- a/plugins/API/lang/ko.json +++ b/plugins/API/lang/ko.json @@ -5,7 +5,9 @@ "LoadedAPIs": "성공적으로 %s API를 불러옴", "MoreInformation": "Piwik API에 대한 자세한 내용은 %sIntroduction to Piwik API %s 문서와 %sPiwik API Reference%s 문서를 참조하세요.", "TopLinkTooltip": "JSON, XML 등의 간단한 API를 통해 프로그래밍 방식으로 웹 로그 분석 데이터에 접근할 수 있습니다.", - "UserAuthentication": "용자 인증", - "UsingTokenAuth": "%s 스크립트 (crontab 등)에서 요청 데이터를 얻고 싶다면 %s는 API를 호출하는 URL (인증 필요)에 매개 변수 %s를 추가해야합니다." + "UserAuthentication": "사용자 인증", + "UsingTokenAuth": "%s 스크립트 (crontab 등)에서 요청 데이터를 얻고 싶다면 %s는 API를 호출하는 URL (인증 필요)에 매개 변수 %s를 추가해야합니다.", + "Glossary": "용어", + "LearnAboutCommonlyUsedTerms": "Piwik 분석에 있어 자주 사용되는 용어 %s 와 %s 배우기" } }
\ No newline at end of file diff --git a/plugins/API/lang/nb.json b/plugins/API/lang/nb.json index 7cde5b4d1c..cd56984f85 100644 --- a/plugins/API/lang/nb.json +++ b/plugins/API/lang/nb.json @@ -1,6 +1,7 @@ { "API": { "LoadedAPIs": "Lastet %s API", - "UserAuthentication": "Brukerautentisering" + "UserAuthentication": "Brukerautentisering", + "Glossary": "Ordliste" } }
\ No newline at end of file diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index bca4396271..b42ccf2539 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -106,6 +106,7 @@ class Actions extends \Piwik\Plugin public function getJsFiles(&$jsFiles) { $jsFiles[] = "plugins/Actions/javascripts/actionsDataTable.js"; + $jsFiles[] = "plugins/Actions/javascripts/rowactions.js"; } public function isSiteSearchEnabled($idSites, $idSite) diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php index 5110130b84..7610e973ef 100644 --- a/plugins/Actions/Archiver.php +++ b/plugins/Actions/Archiver.php @@ -149,10 +149,7 @@ class Archiver extends \Piwik\Plugin\Archiver $select = "log_action.name, log_action.type, log_action.idaction, - log_action.url_prefix, - count(distinct log_link_visit_action.idvisit) as `" . PiwikMetrics::INDEX_NB_VISITS . "`, - count(distinct log_link_visit_action.idvisitor) as `" . PiwikMetrics::INDEX_NB_UNIQ_VISITORS . "`, - count(*) as `" . PiwikMetrics::INDEX_PAGE_NB_HITS . "`"; + log_action.url_prefix"; $select = $this->addMetricsToSelect($select, $metricsConfig); @@ -178,8 +175,7 @@ class Archiver extends \Piwik\Plugin\Archiver $rankingQuery = new RankingQuery($rankingQueryLimit); $rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW); $rankingQuery->addLabelColumn(array('idaction', 'name')); - $rankingQuery->addColumn(array('url_prefix', PiwikMetrics::INDEX_NB_UNIQ_VISITORS)); - $rankingQuery->addColumn(array(PiwikMetrics::INDEX_PAGE_NB_HITS, PiwikMetrics::INDEX_NB_VISITS), 'sum'); + $rankingQuery->addColumn('url_prefix'); if ($this->isSiteSearchEnabled()) { $rankingQuery->addColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min'); diff --git a/plugins/Actions/ArchivingHelper.php b/plugins/Actions/ArchivingHelper.php index b5735225b8..192a3089ba 100644 --- a/plugins/Actions/ArchivingHelper.php +++ b/plugins/Actions/ArchivingHelper.php @@ -53,7 +53,7 @@ class ArchivingHelper unset($row[PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT]); } - if ($row['type'] == Action::TYPE_CONTENT) { + if (in_array($row['type'], array(Action::TYPE_CONTENT, Action::TYPE_EVENT))) { continue; } diff --git a/plugins/Actions/Columns/ActionType.php b/plugins/Actions/Columns/ActionType.php new file mode 100644 index 0000000000..3d6e9b599a --- /dev/null +++ b/plugins/Actions/Columns/ActionType.php @@ -0,0 +1,70 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Columns; + +use Piwik\Piwik; +use Piwik\Plugin\Dimension\ActionDimension; +use Piwik\Plugins\Actions\Segment; +use Piwik\Tracker\Action; +use Exception; + +/** + * This example dimension only defines a name and does not track any data. It's supposed to be only used in reports. + * + * See {@link http://developer.piwik.org/api-reference/Piwik/Columns\Dimension} for more information. + */ +class ActionType extends ActionDimension +{ + private $types = array( + Action::TYPE_PAGE_URL => 'pageviews', + Action::TYPE_CONTENT => 'contents', + Action::TYPE_SITE_SEARCH => 'sitesearches', + Action::TYPE_EVENT => 'events', + Action::TYPE_OUTLINK => 'outlinks', + Action::TYPE_DOWNLOAD => 'downloads' + ); + + /** + * The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app. + * @return string + */ + public function getName() + { + return Piwik::translate('Actions_ActionType'); + } + + protected function configureSegments() + { + $types = $this->types; + + $segment = new Segment(); + $segment->setSegment('actionType'); + $segment->setName('Actions_ActionType'); + $segment->setSqlSegment('log_action.type'); + $segment->setType(Segment::TYPE_METRIC); + $segment->setAcceptedValues(sprintf('A type of action, such as: %s', implode(', ', $types))); + $segment->setSqlFilter(function ($type) use ($types) { + if (array_key_exists($type, $types)) { + return $type; + } + + $index = array_search(strtolower(trim(urldecode($type))), $types); + + if ($index === false) { + throw new Exception("actionType must be one of: " . implode(', ', $types)); + } + + return $index; + }); + $segment->setSuggestedValuesCallback(function ($idSite, $maxSuggestionsToReturn) use ($types) { + return array_slice(array_values($types), 0, $maxSuggestionsToReturn); + }); + $this->addSegment($segment); + } +}
\ No newline at end of file diff --git a/plugins/Actions/Columns/ActionUrl.php b/plugins/Actions/Columns/ActionUrl.php new file mode 100644 index 0000000000..5fa80d1efa --- /dev/null +++ b/plugins/Actions/Columns/ActionUrl.php @@ -0,0 +1,32 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Columns; + +use Piwik\Piwik; +use Piwik\Plugin\Dimension\ActionDimension; +use Piwik\Plugins\Actions\Segment; + +class ActionUrl extends ActionDimension +{ + public function getName() + { + return Piwik::translate('Actions_ColumnActionURL'); + } + + protected function configureSegments() + { + $segment = new Segment(); + $segment->setSegment('actionUrl'); + $segment->setName('Actions_ColumnActionURL'); + $segment->setUnionOfSegments(array('pageUrl', 'downloadUrl', 'outlinkUrl')); + + $this->addSegment($segment); + } + +} diff --git a/plugins/Actions/Metrics.php b/plugins/Actions/Metrics.php index 83b1c7370a..006fd6043d 100644 --- a/plugins/Actions/Metrics.php +++ b/plugins/Actions/Metrics.php @@ -50,6 +50,18 @@ class Metrics public static function getActionMetrics() { $metricsConfig = array( + PiwikMetrics::INDEX_NB_VISITS => array( + 'aggregation' => 'sum', + 'query' => "count(distinct log_link_visit_action.idvisit)" + ), + PiwikMetrics::INDEX_NB_UNIQ_VISITORS => array( + 'aggregation' => false, + 'query' => "count(distinct log_link_visit_action.idvisitor)" + ), + PiwikMetrics::INDEX_PAGE_NB_HITS => array( + 'aggregation' => 'sum', + 'query' => "count(*)" + ), PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION => array( 'aggregation' => 'sum', 'query' => "sum( diff --git a/plugins/Actions/Reports/GetPageUrls.php b/plugins/Actions/Reports/GetPageUrls.php index 2c850a5339..004d11ba88 100644 --- a/plugins/Actions/Reports/GetPageUrls.php +++ b/plugins/Actions/Reports/GetPageUrls.php @@ -39,7 +39,6 @@ class GetPageUrls extends Base new AveragePageGenerationTime() ); - $this->segmentSql = 'log_visit.visit_entry_idaction_url'; $this->subcategoryId = 'General_Pages'; } diff --git a/plugins/Actions/config/config.php b/plugins/Actions/config/config.php deleted file mode 100644 index 293acee476..0000000000 --- a/plugins/Actions/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Actions\Tracker\ActionsRequestProcessor'), - )), - -); diff --git a/plugins/Actions/javascripts/rowactions.js b/plugins/Actions/javascripts/rowactions.js new file mode 100644 index 0000000000..33050b90ed --- /dev/null +++ b/plugins/Actions/javascripts/rowactions.js @@ -0,0 +1,65 @@ +$(function () { + + function isActionsModule(params) + { + return params.module == 'Actions'; + } + + function isPageUrlReport(params) { + var action = params.action; + + return isActionsModule(params) && + (action == 'getPageUrls' || action == 'getEntryPageUrls' || action == 'getExitPageUrls' || action == 'getPageUrlsFollowingSiteSearch'); + }; + + function isPageTitleReport(params) { + var action = params.action; + + return isActionsModule(params) && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch'); + }; + + function getLinkForTransitionAndOverlayPopover(tr) + { + var link = tr.find('> td:first > a').attr('href'); + link = $('<textarea>').html(link).val(); // remove html entities + return link; + } + + DataTable_RowActions_Transitions.registerReport({ + isAvailableOnReport: function (dataTableParams) { + return isPageUrlReport(dataTableParams); + }, + isAvailableOnRow: function (dataTableParams, tr) { + return isPageUrlReport(dataTableParams) && tr.find('> td:first span.label').parent().is('a') + }, + trigger: function (tr, e, subTableLabel) { + var link = getLinkForTransitionAndOverlayPopover(tr); + this.openPopover('url:' + link); + } + }); + + DataTable_RowActions_Transitions.registerReport({ + isAvailableOnReport: function (dataTableParams) { + return isPageTitleReport(dataTableParams); + }, + isAvailableOnRow: function (dataTableParams, tr) { + return isPageTitleReport(dataTableParams); + }, + trigger: function (tr, e, subTableLabel) { + DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]); + } + }); + + DataTable_RowActions_Overlay.registerReport({ + isAvailableOnReport: function (dataTableParams) { + return isPageUrlReport(dataTableParams); + }, + onClick: function (actionA, tr, e) { + return { + link: getLinkForTransitionAndOverlayPopover(tr), + segment: null + } + } + }); + +});
\ No newline at end of file diff --git a/plugins/Actions/lang/cs.json b/plugins/Actions/lang/cs.json index 1c68478b02..5b63d05d60 100644 --- a/plugins/Actions/lang/cs.json +++ b/plugins/Actions/lang/cs.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Průměr dle %s přístupů %s mezi %s a %s", "ColumnClickedURL": "URL prokliku", + "ColumnActionURL": "URL akce", "ColumnClicks": "Prokliků", "ColumnClicksDocumentation": "Počet kliknutí na odkaz v závislosti na čase.", "ColumnDownloadURL": "URL stažení", @@ -32,17 +33,17 @@ "ExitPagesReportDocumentation": "Toto hlášení obsahuje informace o výstupních stránkách, které se vyskytly v daném období. Výstupní stránka je poslední stránkou, kterou uživatel zobrazí během své návštěvy. %s URL výstupních stránek jsou zobrazeny jako strom složek.", "ExitPageTitles": "Titulky výstupních stránek", "ExitPageTitlesReportDocumentation": "Toto hlášení obsahuje informace o titulcích výstupních stránek za dané období.", - "LearnMoreAboutSiteSearchLink": "Zjistěte více o sledování toho, jak návštěvníci používaí váš vyhledávač.", + "LearnMoreAboutSiteSearchLink": "Zjistěte více o sledování toho, jak návštěvníci používají váš vyhledávač.", "OneSearch": "1 Vyhledávání", "OutlinkDocumentation": "Externí odkaz je odkaz, který vede z vašich stránek na jinou domén.", "OutlinksReportDocumentation": "Toto hlášení zobrazuje hyerarchický seznam URL externích odkazů, na které vaši návštěvníci klikli.", "PagesReportDocumentation": "Toto hlášení obsahuje informace o URL navštívených. %s Tabulka je organizována hyerarchicky jako strom složek.", "PageTitlesReportDocumentation": "Toto hlášení obsahuje informace o titulcích navštívených stránek. %s Titulek je HTML tag %s, který většina prohlížečů zobrazuje v titulku okna.", "PageUrls": "URL stránky", - "PluginDescription": "Podává hlášení o zobrazení a titulcích stránek. Umožňuje vám měřit váš interní vyhledávač. Automaticky sleduje kliky na externí odkazy a soubory ke stažení.", + "PluginDescription": "Podává hlášení o zobrazení a titulcích stránek. Umožňuje měřit váš interní vyhledávač. Automaticky sleduje kliky na externí odkazy a soubory ke stažení.", "SiteSearchCategories1": "Toto hlášení shrnuje kategorie, které návštěvníci vybrali při vyhledávání na stránkách.", "SiteSearchCategories2": "Například elektronické obchody mívají filtr kategorií, aby mohli uzákazníci zvolit, ve které kategorii bude provedeno hledání.", - "SiteSearchFollowingPagesDoc": "Když návštěvníci hledají na vašich stránkách, snaží se nalézt určitou stránku, obhsa, produkt, nebo službu. Toto hlášení zobrazuje stránky, na které bylo klikáno nejčastěji po interním vyhledávání. Jinak řečeno, jedná se o stránky, které byly nejvíce hledány návštěvníky, kteří už byli na vašich stránkách.", + "SiteSearchFollowingPagesDoc": "Když návštěvníci hledají na vašich stránkách, snaží se nalézt určitou stránku, obsah, produkt, nebo službu. Toto hlášení zobrazuje stránky, na které bylo klikáno nejčastěji po interním vyhledávání. Jinak řečeno, jedná se o stránky, které byly nejvíce hledány návštěvníky, kteří už byli na vašich stránkách.", "SiteSearchIntro": "Sledování vyhledávání na vašich stránkách je velmi efektivní způsob, jak se dozvědět více o tom, co vaši návštěvníci na stránkách hledají, jaký obsah by je mohl zajímat, jaké produkty by si mohli chtít koupit, a jak jim obecně zpříjemnit pobyt na vašich stránkách.", "SiteSearchKeyword": "Klíčová slova (Vyhledávání)", "SiteSearchKeywordsDocumentation": "Toto hlášení shrnuje klíčová slova, která byla použita návštěvníky při hledání pomocí interního vyhledávače.", diff --git a/plugins/Actions/lang/de.json b/plugins/Actions/lang/de.json index f52aaabfbe..ffefdf14be 100644 --- a/plugins/Actions/lang/de.json +++ b/plugins/Actions/lang/de.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Durchschnitt basierend auf %s Aufruf(en) %s zwischen %s und %s", "ColumnClickedURL": "Angeklickte URL", + "ColumnActionURL": "Aktions-URL", "ColumnClicks": "Klicks", "ColumnClicksDocumentation": "Anzahl der Klicks auf diesen Link.", "ColumnDownloadURL": "Download-URL", diff --git a/plugins/Actions/lang/el.json b/plugins/Actions/lang/el.json index edc949a3f3..a495f92138 100644 --- a/plugins/Actions/lang/el.json +++ b/plugins/Actions/lang/el.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Μέσος όρος βάσει των %s επισκέψεων %s μεταξύ %s και %s", "ColumnClickedURL": "URL επιλογής", + "ColumnActionURL": "Διεύθυνση URL ενέργειας", "ColumnClicks": "Κλικ", "ColumnClicksDocumentation": "Οι φορές που πατήθηκε αυτός ο σύνδεσμος.", "ColumnDownloadURL": "URL λήψης", diff --git a/plugins/Actions/lang/en.json b/plugins/Actions/lang/en.json index 5a31efee34..f176db870d 100644 --- a/plugins/Actions/lang/en.json +++ b/plugins/Actions/lang/en.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Average based on %s hit(s) %s between %s and %s", "ColumnClickedURL": "Clicked URL", + "ColumnActionURL": "Action URL", "ColumnClicks": "Clicks", "ColumnClicksDocumentation": "The number of times this link was clicked.", "ColumnDownloadURL": "Download URL", @@ -60,6 +61,7 @@ "WidgetPageUrlsFollowingSearch": "Pages Following a Site Search", "WidgetSearchCategories": "Search Categories", "WidgetSearchKeywords": "Site Search Keywords", - "WidgetSearchNoResultKeywords": "Search Keywords with No Results" + "WidgetSearchNoResultKeywords": "Search Keywords with No Results", + "ActionType": "Action Type" } }
\ No newline at end of file diff --git a/plugins/Actions/lang/it.json b/plugins/Actions/lang/it.json index 013467699f..d727a00bbc 100644 --- a/plugins/Actions/lang/it.json +++ b/plugins/Actions/lang/it.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Media basata su %s hits %s tra %s e %s", "ColumnClickedURL": "URL Cliccate", + "ColumnActionURL": "URL dell'azione", "ColumnClicks": "Click", "ColumnClicksDocumentation": "Numero di volte che questo link è stato cliccato.", "ColumnDownloadURL": "URL Download", diff --git a/plugins/Actions/lang/ja.json b/plugins/Actions/lang/ja.json index 2d9e44354e..c752f0ece4 100644 --- a/plugins/Actions/lang/ja.json +++ b/plugins/Actions/lang/ja.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "%s ~ %s の間の %s のヒット %s に基づく平均", "ColumnClickedURL": "クリックされた URL", + "ColumnActionURL": "アクション URL", "ColumnClicks": "クリック数", "ColumnClicksDocumentation": "このリンクがクリックされた回数", "ColumnDownloadURL": "ダウンロード URL", @@ -39,6 +40,7 @@ "PagesReportDocumentation": "これは訪問されたページURLについてのリポートです。%s 表は階層構造になっており、URLはフォルダーの階層で表示されています。", "PageTitlesReportDocumentation": "これは訪問されたページタイトルについてのリポートです。%s ページタイトルは多くのブラウザでウインドウのタイトルに表示されるHTMLの %s タグです。", "PageUrls": "ページURL", + "PluginDescription": "ページ ビューとページのタイトルについてレポートします。内部のウェブサイトの検索エンジンを測定できます。自動的に外部リンクとファイルのダウンロードのクリックを追跡します。", "SiteSearchCategories1": "このレポートは、訪問者がサイトで検索した時に選択したカテゴリの一覧です。", "SiteSearchCategories2": "例えば、eコマースサイトは一般的に\"カテゴリー\"のセレクトを持っていて、訪問者は製品の検索結果を特定のカテゴリーに絞り込みできます。", "SiteSearchFollowingPagesDoc": "訪問者は、特定のページ、コンテンツ、製品、またはサービスを見つけようとして、サイト内で検索を行います。このレポートは、サイト内で検索してから最も多くクリックされたページの一覧です。言い換えれば、サイト内で訪問者が最も検索したページの一覧ということです。", diff --git a/plugins/Actions/lang/ko.json b/plugins/Actions/lang/ko.json index b5ec63216b..fa9f7d2257 100644 --- a/plugins/Actions/lang/ko.json +++ b/plugins/Actions/lang/ko.json @@ -1,6 +1,7 @@ { "Actions": { "ColumnClickedURL": "조회한 URL", + "ColumnActionURL": "활동 URL", "ColumnClicks": "조회수", "ColumnClicksDocumentation": "이 링크를 클릭 한 횟수입니다.", "ColumnDownloadURL": "다운로드 URL", @@ -32,15 +33,18 @@ "ExitPageTitles": "이탈 페이지 제목", "ExitPageTitlesReportDocumentation": "이 보고서는 기간의 이탈 페이지 제목에 대한 정보입니다.", "LearnMoreAboutSiteSearchLink": "방문자가 검색기능을 사용하는 것을 추적하는 방법에 대해 자세히 알아보세요.", + "OneSearch": "검색 1", "OutlinkDocumentation": "외부 링크는 사이트 내에서 외부 사이트(다른 도메인)로의 링크입니다.", "OutlinksReportDocumentation": "이 보고서는 방문자가 클릭 한 외부 링크의 URL을 계층 구조 목록으로 표시합니다.", "PagesReportDocumentation": "이 보고서는 방문한 페이지 URL에 대한 정보입니다. %s 테이블은 계층 구조로 되어 있으며, URL은 폴더 구조로 표시됩니다.", "PageTitlesReportDocumentation": "이 보고서는 방문한 페이지 제목에 대한 정보입니다. %s 페이지 제목은 대부분의 브라우저 윈도우의 제목에 표시되는 HTML %s 태그입니다.", "PageUrls": "페이지 URL", + "PluginDescription": "페이지 뷰와 제목에 대한 보고서. 내부 웹사이트 검색 엔진을 측정할 수 있습니다. 외부 링크 클릭 및 파일 다운로드에 대해서도 자동으로 추적합니다.", "SiteSearchCategories1": "이 보고서는 웹사이트에서 검색했을 때 방문자가 선택한 카테고리를 나열합니다.", "SiteSearchCategories2": "예를 들면, 일반적인 전자상거래 사이트는 '카테고리'를 제공하는데 방문자는 특정 카테고리의 모든 제품을 검색하는 것으로 범위를 축소할 수 있습니다.", "SiteSearchFollowingPagesDoc": "방문자는 특정 페이지, 콘텐츠, 제품 또는 서비스를 찾으려고 웹사이트에서 검색을 시도합니다. 이 보고서는 사이트 내부에서 검색시도 후 가장 많이 클릭한 페이지 목록입니다. 즉, 이 페이지 목록은 대부분의 방문자가 사이트내 검색에 의해 방문한 것입니다.", "SiteSearchIntro": "방문자의 검색을 추적하는 것은 당신의 웹사이트를 좋게 만드는 매우 효과적인 방법입니다. 잠재 고객이 무엇을 찾고 있는지에 대해 자세히 알 수 있고, 이것으로 새로운 콘텐츠에 대한 아이디어를 찾는 데 도움을 받게 되며, 잠재 고객이 찾는 새로운 전자상거래 제품을 제공할 수 있게 됩니다. 그리고 방문자의 웹사이트 사용 경험을 대폭 향상시킵니다.", + "SiteSearchKeyword": "키워드 (사이트 검색)", "SiteSearchKeywordsDocumentation": "이 보고서에서는 방문자가 내부 검색 엔진에서 검색한 검색어를 나열합니다.", "SiteSearchKeywordsNoResultDocumentation": "이 보고서는 검색 결과를 반환하지 않은 검색어를 나열합니다: 어쩌면 검색 엔진 알고리즘을 향상 시키거나 방문자가 당신의 웹사이트에 (아직)없는 콘텐츠를 찾고 있는 것이겠죠?", "SubmenuPagesEntry": "진입 페이지", diff --git a/plugins/Actions/lang/nb.json b/plugins/Actions/lang/nb.json index bf3f0ea718..18ab7471b9 100644 --- a/plugins/Actions/lang/nb.json +++ b/plugins/Actions/lang/nb.json @@ -1,41 +1,63 @@ { "Actions": { "AvgGenerationTimeTooltip": "Gjennomsnitt basert på %s treff %s mellom %s og %s", - "ColumnClickedURL": "URL for klikk", + "ColumnClickedURL": "Klikket URL", "ColumnClicks": "Klikk", "ColumnClicksDocumentation": "Antall klikk på denne lenken", - "ColumnDownloadURL": "URL for nedlasting", - "ColumnEntryPageTitle": "Tittel for inngangsside", - "ColumnEntryPageURL": "URL for inngangsside", - "ColumnExitPageTitle": "Tittel for utgangsside", - "ColumnExitPageURL": "URL for utgangsside", + "ColumnDownloadURL": "Nedlastings-URL", + "ColumnEntryPageTitle": "Tittel på inngangsside", + "ColumnEntryPageURL": "URL på inngangsside", + "ColumnExitPageTitle": "Tittel på utgangsside", + "ColumnExitPageURL": "URL på utgangsside", "ColumnNoResultKeyword": "Nøkkelord uten søkeresultat", "ColumnPageName": "Sidenavn", - "ColumnPagesPerSearch": "Søkeresultater", - "ColumnPageURL": "URL for side", + "ColumnPagesPerSearch": "Søkeresultatsider", + "ColumnPagesPerSearchDocumentation": "Brukere vil søke på ditt nettsted, og noen ganger klikke «neste» for å se flere resultater. Dette er det gjennomsnittlige antallet søkeresultatsider for dette nøkkelordet.", + "ColumnPageURL": "Side-URL", "ColumnSearchCategory": "Søkekategori", "ColumnSearches": "Søk", - "ColumnSearchesDocumentation": "Antall besøkende som søkte etter dette nøkkelordet i din søkemotor.", + "ColumnSearchesDocumentation": "Antall besøkende som søkte etter dette nøkkelordet i ditt nettsteds søkemotor.", + "ColumnSearchExits": "% Utganger fra søk", "ColumnSearchExitsDocumentation": "Antall besøkende som forlot nettsiden etter å ha søkt etter dette nøkkelordet.", "ColumnSearchResultsCount": "Antall søkeresultater", "ColumnSiteSearchKeywords": "Unike nøkkelord", "ColumnUniqueClicks": "Unike klikk", + "ColumnUniqueClicksDocumentation": "Antall besøk som involverte et klikk på denne lenken. Hvis lenken ble klikket flere ganger i løpet av et besøk, blir det kun talt én gang.", "ColumnUniqueDownloads": "Unike nedlastinger", "ColumnUniqueOutlinks": "Unike utlenker", "DownloadsReportDocumentation": "I denne rapporten kan du se hvilke filer de besøkende har lastet ned. %s Hva Piwik teller som en nedlasting, er klikket på en nedlastingslink. Om nedlastingen ble fullført eller ikke er ikke kjent for Piwik.", + "EntryPagesReportDocumentation": "Denne rapporten inneholder informasjon om inngangssidene som ble brukt i løpet av den angitte perioden. En inngangsside er den første siden en bruker ser i løpet av besøket. %s Inngangs-URLen vises som en mappestruktur.", + "EntryPageTitles": "Inngangssidetitler", + "EntryPageTitlesReportDocumentation": "Denne rapporten inneholder informasjon om titler på inngangssider som ble brukt i løpet av den angitte perioden.", + "ExitPagesReportDocumentation": "Denne rapporten inneholder informasjon om utgangssidene som ble brukt i den angitte perioden. En utgangsside er den siste siden en bruker ser i løpet av besøket. %s Utgangs-URLene vises som en mappestruktur.", + "ExitPageTitles": "Titler på utgangssider", + "ExitPageTitlesReportDocumentation": "Denne rapporten inneholder informasjon om titler på utgangssider som ble brukt i den angitte perioden.", + "LearnMoreAboutSiteSearchLink": "Lær mer om hvordan du kan spore hvordan besøkere bruker din søkemotor.", "OneSearch": "1 søk", + "OutlinkDocumentation": "En utlenke er en lenke som leder besøkeren bort fra ditt nettsted (til et annet domene).", + "OutlinksReportDocumentation": "Denne rapporten viser en hierarkisk liste av utlenkers URL-er som ble klikket av dine besøkere.", + "PagesReportDocumentation": "Denne rapporten inneholder informasjon om side-URLer som har blitt besøkt. %s Tabellen er organisert hierarkisk, URL-ene vises som en mappestruktur.", + "PageTitlesReportDocumentation": "Denne rapporten inneholder informasjon om titler på sider som har blitt besøkt. %s Sidetittelen er HTML-taggen %s som de fleste nettlesere viser i vindustittelen.", + "PageUrls": "Side-URLer", + "PluginDescription": "Rapporter om sidevisninger org sidetitler. Lar deg måle ditt nettsteds interne søkemotor. Sporer klikk på eksterne lenker og nedlastinger automatisk.", + "SiteSearchCategories1": "Denne rapporten viser kategorier som besøkere velger når de søker på ditt nettsted.", + "SiteSearchCategories2": "For eksempel: e-handelsnettsteder har typisk en «kategori»-velger slik at besøkere kan begrense sine søk til kun produkter i en spesifikk kategori.", + "SiteSearchFollowingPagesDoc": "Når besøkere søker på ditt nettsted, leter de etter en bestemt side, innhold, produkt eller tjeneste. Denne rapporten lister opp sider som ble klikket mest etter et internt søk. Med andre ord, det er listen over sider som blir mest søkt etter av besøkere som allerede er på nettstedet.", + "SiteSearchIntro": "Å spore søk som besøkere gjør på dine nettsteder er en effektiv måte å lære mer om hva din målgruppe leter etter. Det kan hjelpe deg å finne ideer til nytt innhold, nye produkter og generelt å øke kvaliteten på dine besøkeres opplevelse av ditt nettsted.", "SiteSearchKeyword": "Nøkkelord (Sidesøk)", + "SiteSearchKeywordsDocumentation": "Denne rapporten lister opp nøkkelord som besøkere har søkt etter i nettstedets interne søkemotor.", + "SiteSearchKeywordsNoResultDocumentation": "Denne rapporten lister opp nøkkelord som ikke ga noen søkeresultater. Kanskje søkealgoritmen kan forbedres, eller kanskje dine besøkere ser etter innhold som ikke er på ditt nettsted (ennå)?", "SubmenuPagesEntry": "Inngangssider", "SubmenuPagesExit": "Utgangssider", "SubmenuPageTitles": "Sidetitler", "SubmenuSitesearch": "Sidesøk", - "WidgetEntryPageTitles": "Inngangssiders tittel", - "WidgetExitPageTitles": "Utgangs Side Tittler", + "WidgetEntryPageTitles": "Titler på inngangssider", + "WidgetExitPageTitles": "Titler på utgangssider", "WidgetPagesEntry": "Inngangssider", "WidgetPagesExit": "Utgangssider", - "WidgetPageTitles": "Side Tittler", - "WidgetPageTitlesFollowingSearch": "Side Tittel fulgt av ett sidesøk", - "WidgetPageUrlsFollowingSearch": "Sider etter et nettstedssøk", + "WidgetPageTitles": "Sidetitler", + "WidgetPageTitlesFollowingSearch": "Sidetittel fulgt av ett nettstedssøk", + "WidgetPageUrlsFollowingSearch": "Sider som følger etter et nettstedssøk", "WidgetSearchCategories": "Søkekategorier", "WidgetSearchKeywords": "Nøkkelord for sidesøk", "WidgetSearchNoResultKeywords": "Nøkkelord uten resultater" diff --git a/plugins/Actions/lang/pt-br.json b/plugins/Actions/lang/pt-br.json index 99baab291c..41d3f88a93 100644 --- a/plugins/Actions/lang/pt-br.json +++ b/plugins/Actions/lang/pt-br.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Média baseada em %s visita(s) %s entre %s e %s", "ColumnClickedURL": "URL clicado", + "ColumnActionURL": "URL Ação", "ColumnClicks": "Cliques", "ColumnClicksDocumentation": "O número de vezes que link foi clicado.", "ColumnDownloadURL": "URL do Download", diff --git a/plugins/AnonymousPiwikUsageMeasurement b/plugins/AnonymousPiwikUsageMeasurement -Subproject 02dfdc4b8e744cf432fdcb91e3613d0f04ccc59 +Subproject 5ba37193bba2be01df462b61c724c212657c449 diff --git a/plugins/Contents/lang/cs.json b/plugins/Contents/lang/cs.json index 596ee07a03..7cbe4e4488 100644 --- a/plugins/Contents/lang/cs.json +++ b/plugins/Contents/lang/cs.json @@ -8,6 +8,9 @@ "ContentName": "Jméno obsahu", "ContentPiece": "Část obsahu", "ContentTarget": "Cíl obsahu", - "Contents": "Obsah" + "Contents": "Obsah", + "InteractionsMetricDocumentation": "Kolikrát bylo s blokem obsahu interagováno (t. j. kliknuto na banner nebo reklamu).", + "ImpressionsMetricDocumentation": "Počet zobrazení banneru nebo reklamy na stránkách.", + "InteractionRateMetricDocumentation": "Poměr impresí obsahu k interakcím." } }
\ No newline at end of file diff --git a/plugins/Contents/lang/de.json b/plugins/Contents/lang/de.json index cc42a8d6bb..61cb4074fa 100644 --- a/plugins/Contents/lang/de.json +++ b/plugins/Contents/lang/de.json @@ -8,6 +8,9 @@ "ContentName": "Inhaltsname", "ContentPiece": "Inhaltsteil", "ContentTarget": "Inhaltsziel", - "Contents": "Inhalte" + "Contents": "Inhalte", + "InteractionsMetricDocumentation": "Die Anzahl, wie häufig mit einem Inhalt interagiert wurde (z.B. durch einen Klick auf ein Banner oder eine Anzeige).", + "ImpressionsMetricDocumentation": "Die Anzahl, wie häufig ein Inhalt, z.b. ein Banner oder eine Anzeige, auf der Seite angezeigt wurden.", + "InteractionRateMetricDocumentation": "Verhältnis zwischen Impressionen des Inhalts und Interaktionen." } }
\ No newline at end of file diff --git a/plugins/Contents/lang/ja.json b/plugins/Contents/lang/ja.json index 56af2f6ea7..a15de7b042 100644 --- a/plugins/Contents/lang/ja.json +++ b/plugins/Contents/lang/ja.json @@ -1,5 +1,6 @@ { "Contents": { + "PluginDescription": "コンテンツやバナーの追跡では、ページのコンテンツの任意の部分(バナー広告、イメージ、任意項目) のパフォーマンス(ビュー、クリック数、CTR )を測定できます。", "Impressions": "インプレッション", "Interactions": "インタラクション", "Interaction": "インタラクション", @@ -7,6 +8,9 @@ "ContentName": "コンテンツ名", "ContentPiece": "コンテンツ要素", "ContentTarget": "コンテンツターゲット", - "Contents": "内容" + "Contents": "内容", + "InteractionsMetricDocumentation": "( 例えば、バナーや広告を「クリック」というような ) 相互作用をコンテンツブロックが受けた回数。", + "ImpressionsMetricDocumentation": "広告やバナーなどのコンテンツブロックがページに表示された回数。", + "InteractionRateMetricDocumentation": "相互作用に対するコンテンツの表示回数の比率。" } }
\ No newline at end of file diff --git a/plugins/Contents/lang/ko.json b/plugins/Contents/lang/ko.json new file mode 100644 index 0000000000..121856bf06 --- /dev/null +++ b/plugins/Contents/lang/ko.json @@ -0,0 +1,11 @@ +{ + "Contents": { + "Interactions": "상호 작용들", + "Interaction": "상호 작용", + "InteractionRate": "상호 작용 비율", + "ContentName": "콘텐츠 이름", + "ContentPiece": "콘텐츠 조각", + "ContentTarget": "콘텐츠 목표", + "Contents": "콘텐츠" + } +}
\ No newline at end of file diff --git a/plugins/CoreAdminHome/Commands/DeleteLogsData.php b/plugins/CoreAdminHome/Commands/DeleteLogsData.php index df4141edbb..a504ed2c9c 100644 --- a/plugins/CoreAdminHome/Commands/DeleteLogsData.php +++ b/plugins/CoreAdminHome/Commands/DeleteLogsData.php @@ -168,6 +168,10 @@ class DeleteLogsData extends ConsoleCommand private function askForDeleteConfirmation(InputInterface $input, OutputInterface $output) { + if ($input->getOption('no-interaction')) { + return true; + } + $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('<comment>You are about to delete log data. This action cannot be undone, are you sure you want to continue? (Y/N)</comment> ', false); @@ -194,4 +198,4 @@ class DeleteLogsData extends ConsoleCommand $this->writeSuccessMessage($output, array("Table optimization finished.")); } -}
\ No newline at end of file +} diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php index 305506038a..0f9bf60702 100644 --- a/plugins/CoreAdminHome/CoreAdminHome.php +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreAdminHome; use Piwik\Db; use Piwik\Piwik; +use Piwik\ProxyHttp; use Piwik\Settings\UserSetting; /** @@ -26,7 +27,9 @@ class CoreAdminHome extends \Piwik\Plugin 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'UsersManager.deleteUser' => 'cleanupUser', - 'API.DocumentationGenerator.@hideExceptForSuperUser' => 'displayOnlyForSuperUser' + 'API.DocumentationGenerator.@hideExceptForSuperUser' => 'displayOnlyForSuperUser', + 'Template.jsGlobalVariables' => 'addJsGlobalVariables', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys' ); } @@ -57,10 +60,28 @@ class CoreAdminHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreAdminHome/javascripts/generalSettings.js"; $jsFiles[] = "plugins/CoreHome/javascripts/donate.js"; $jsFiles[] = "plugins/CoreAdminHome/javascripts/pluginSettings.js"; + $jsFiles[] = "plugins/CoreAdminHome/javascripts/protocolCheck.js"; } public function displayOnlyForSuperUser(&$hide) { $hide = !Piwik::hasUserSuperUserAccess(); } + + public function addJsGlobalVariables(&$out) + { + if (ProxyHttp::isHttps()) { + $isHttps = 'true'; + } else { + $isHttps = 'false'; + } + + $out .= "piwik.hasServerDetectedHttps = $isHttps;\n"; + } + + public function getClientSideTranslationKeys(&$translationKeys) + { + $translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectly'; + $translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectlySolution'; + } } diff --git a/plugins/CoreAdminHome/Menu.php b/plugins/CoreAdminHome/Menu.php index b4621e2f5e..a149baa399 100644 --- a/plugins/CoreAdminHome/Menu.php +++ b/plugins/CoreAdminHome/Menu.php @@ -61,7 +61,7 @@ class Menu extends \Piwik\Plugin\Menu if (!Piwik::isUserIsAnonymous()) { $menu->addManageItem('CoreAdminHome_TrackingCode', $this->urlForAction('trackingCodeGenerator'), - $order = 10); + $order = 20); if (SettingsManager::hasUserPluginsSettingsForCurrentUser()) { $menu->addPersonalItem('CoreAdminHome_PluginSettings', diff --git a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js index 87b053e980..3c983deca8 100644 --- a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js +++ b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js @@ -302,7 +302,7 @@ $('#javascript-text>textarea,#image-tracking-text>textarea').click(function () { $(this).select(); }); - + // initial generation getSiteData( $('#js-tracker-website').attr('siteid'), diff --git a/plugins/CoreAdminHome/javascripts/protocolCheck.js b/plugins/CoreAdminHome/javascripts/protocolCheck.js new file mode 100644 index 0000000000..5bd4148e10 --- /dev/null +++ b/plugins/CoreAdminHome/javascripts/protocolCheck.js @@ -0,0 +1,43 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +$(document).ready(function () { + + if (!piwik || !location.protocol) { + return; + } + + if (!piwik.hasSuperUserAccess) { + // we show a potential notification only to super users + return; + } + + if (piwik.hasServerDetectedHttps) { + // https was detected, not needed to show a message + return; + } + + var isHttpsUsed = 0 === location.protocol.indexOf('https'); + + if (!isHttpsUsed) { + // not using https anyway, we do not show a message + return; + } + + var params = [ + '"config/config.ini.php"', + '"assume_secure_protocol=1"', + '"[General]"', + '<a href="?module=Proxy&action=redirect&url=https://piwik.org/faq/how-to-install/faq_98/" target="_blank">', + '</a>' + ]; + var message = _pk_translate('CoreAdminHome_ProtocolNotDetectedCorrectly') + " " + _pk_translate('CoreAdminHome_ProtocolNotDetectedCorrectlySolution', params); + + var UI = require('piwik/UI'); + var notification = new UI.Notification(); + notification.show(message, {context: 'warning'}); +}); diff --git a/plugins/CoreAdminHome/lang/cs.json b/plugins/CoreAdminHome/lang/cs.json index 8233b15931..136ef9770a 100644 --- a/plugins/CoreAdminHome/lang/cs.json +++ b/plugins/CoreAdminHome/lang/cs.json @@ -8,9 +8,9 @@ "ClickHereToOptIn": "Klikněte zde pro přihlášení.", "ClickHereToOptOut": "Klikněte zde pro vyloučení.", "CustomLogoFeedbackInfo": "Pokud přizpůsobíte logo Piwiku, možná by vás zajímalo, jak skrýt odkaz %s v horním menu, Pokud to chcete provést, zakažte plugin zpětné vazby na stránce %sSpravovat zásuvné moduly%s.", - "CustomLogoHelpText": "Můžete přizpůsobit logo Piwiku, které bude zobrazeno v uživatelském rozhraní a v e-mailových hlášeních.", + "CustomLogoHelpText": "Můžete přizpůsobit logo Piwiku, které bude zobrazeno v uživatelském rozhraní a v emailových hlášeních.", "DevelopmentProcess": "Přestože náš %sproces vývoje%s zahrnuje tisíce automatizovaných testů, beta testeři hrají klíčovou roli v naší politice nevýskytu chyb.", - "EmailServerSettings": "Nastavení e-mailového serveru", + "EmailServerSettings": "Nastavení emailového serveru", "ForBetaTestersOnly": "Pouze pro beta testery", "ImageTracking": "Sledování obrázkem", "ImageTrackingIntro1": "Pokud má návštěvník vypnutý JavaScript nebo nemůže být JavaScript použit, můžete využít obrázku k měření a sledování Vaší návštěvnosti.", @@ -58,7 +58,7 @@ "MenuDevelopment": "Vývoj", "OptOutComplete": "Vyloučení hotovo. Vaše návštěvy nebudou sledovány nástrojem webové analýzy.", "OptOutCompleteBis": "Poznámka: pokud smažete cookie, odstraníte vylučovací cookie nebo zmměníte počítač nebo prohlížeč, budete muset provést proceduru vyloučení znovu.", - "OptOutDntFound": "Nejste sledováni, protože váš prohlížeč hlásí, že si to nepřejete. Jedná se o nastavení prohlížeče, takže se nebudete moct přihlásit, dokud nezakážete funkci nesledovat.", + "OptOutDntFound": "Nejste sledováni, protože váš prohlížeč hlásí, že si to nepřejete. Jedná se o nastavení prohlížeče, takže se nebudete moci přihlásit, dokud nezakážete funkci nesledovat.", "OptOutExplanation": "Piwik se zaměřuje na poskytování soukromí na internetu. Pokud chcete dát svým návštěvníkům možnost, aby byli vyloučeni z webové analýzy Piwikem, můžete na nějakou stránku (třeba stránku o soukromí) umístit následující HTML kód.", "OptOutExplanationBis": "Tento kód zobrazí iframe s odkazem, který nastaví u návštěvníka vynechávací cookie. %s Klikněte zde%s pro zobrazení obsahu iframe.", "OptOutForYourVisitors": "Piwik vyloučení pro Vaše návštěvníky", @@ -70,8 +70,8 @@ "PluginSettingsIntro": "Zde můžete změnit nastavení pro následující zásuvné moduly třetích stran:", "PluginSettingsValueNotAllowed": "Hodnota pro pole \"%s\" zásuvného modulu \"%s\" není povolena", "PluginSettingsSaveFailed": "Nepodařilo se uložit nastavení zásuvného modulu", - "SendPluginUpdateCommunication": "Pokud bude k dispozici aktualizace pluginu, odeslat e-mail.", - "SendPluginUpdateCommunicationHelp": "Super uživatelům bude odeslán e-mail, když bude k dispozici aktualizace zásuvného modulu.", + "SendPluginUpdateCommunication": "Pokud bude k dispozici aktualizace pluginu, odeslat email.", + "SendPluginUpdateCommunicationHelp": "Super uživatelům bude odeslán email, pokud bude k dispozici aktualizace zásuvného modulu.", "StableReleases": "Piwik je důležitý nástroj pro měření, doporučujeme vždy používat nejnovější vydání. Pokud používáte nejnovější beta verzi a našli jste chyby, prosíme o jejich nahlášení %spřímo zde %s.", "LtsReleases": "LTS (verze s dlouhodobou podporou) dostávají pouze bezpečnostní a jiné opravy chyb.", "SystemPluginSettings": "Systémová nastavení zásuvných modulů", @@ -84,9 +84,9 @@ "ValidPiwikHostname": "Platné jméno hostitele Piwiku", "WithOptionalRevenue": "s volitelným příjmem", "YouAreOptedIn": "Aktuálně nejste vyloučen.", - "YouAreOptedOut": "Aktuálně jste vyloučen.", - "YouMayOptOut": "Zde se můžete rozhodnout, zda se smí ve Vašem prohlížeči ukládat jedinečná analytická data “ Cookies” , a zda umožníte provozovateli webové stránky shromažďovat a analyzovat různé statistické údaje.", - "YouMayOptOutBis": "Pokud jste se rozhodli že ne, klikněte na přiložený odkaz pro uložení deaktivačního Cookie ve Vašem prohlížeči.", + "YouAreOptedOut": "Aktuálně jste vyloučeni.", + "YouMayOptOut": "Zde se můžete zakázat uložení cookie s identifikačním číslem přiděleným vašemu počítači a tím zamezit provozovateli této webové stránky shromažďovat a analyzovat statistické údaje.", + "YouMayOptOutBis": "Pokud jste se rozhodli že ne, klikněte na přiložený odkaz pro uložení deaktivačního cookie ve svém prohlížeči.", "OptingYouOut": "Vylučování, prosím čekejte..." } }
\ No newline at end of file diff --git a/plugins/CoreAdminHome/lang/en.json b/plugins/CoreAdminHome/lang/en.json index 5fe5d64d39..25a85f6475 100644 --- a/plugins/CoreAdminHome/lang/en.json +++ b/plugins/CoreAdminHome/lang/en.json @@ -87,6 +87,8 @@ "YouAreOptedOut": "You are currently opted out.", "YouMayOptOut": "You may choose not to have a unique web analytics cookie identification number assigned to your computer to avoid the aggregation and analysis of data collected on this website.", "YouMayOptOutBis": "To make that choice, please click below to receive an opt-out cookie.", - "OptingYouOut": "Opting you out, please wait..." + "OptingYouOut": "Opting you out, please wait...", + "ProtocolNotDetectedCorrectly": "You are currently viewing Piwik over a secure SSL connection (using https), but Piwik could only detect a non secure connection on the server. ", + "ProtocolNotDetectedCorrectlySolution": "To make sure Piwik securely requests and serves your content over HTTPS, you may edit your %s file and either configure your proxy settings, or you may add the line %s below the %s section. %sLearn more%s" } } diff --git a/plugins/CoreAdminHome/lang/it.json b/plugins/CoreAdminHome/lang/it.json index a8692d08a6..d453a6252c 100644 --- a/plugins/CoreAdminHome/lang/it.json +++ b/plugins/CoreAdminHome/lang/it.json @@ -56,7 +56,7 @@ "MenuGeneralSettings": "Impostazioni generali", "MenuManage": "Gestione", "MenuDevelopment": "Sviluppo", - "OptOutComplete": "Opt-out completato; le tue visite a questo sito non verranno registrate dallo strumento di Web Analytics.", + "OptOutComplete": "Opt-out completato. Le tue visite a questo sito non verranno registrate dallo strumento di Web Analytics.", "OptOutCompleteBis": "Nota che se cancelli i tuoi cookie, cancelli anche il cookie di opt-out, e se cambi computer o browser web, devi fare la procedura di opt-out nuovamente.", "OptOutDntFound": "Non vieni tracciato poiché il tuo browser comunica che non lo desideri. Questa è un'impostazione del tuo browser, dunque non potrai effettuare l'opt-in finchè non disabiliti la funzionalità \"Non Tracciare\".", "OptOutExplanation": "Piwik è impegnato ad assicurare la riservatezza su Internet. Per dare ai tuoi ospiti la possibilità di escludersi dalle Statistiche Web Piwik, è possibile aggiungere il seguente codice HTML in una pagina del tuo sito web, ad esempio in una pagina sulla privacy.", diff --git a/plugins/CoreAdminHome/lang/ja.json b/plugins/CoreAdminHome/lang/ja.json index 782ccc1ad7..a40ab8e3b8 100644 --- a/plugins/CoreAdminHome/lang/ja.json +++ b/plugins/CoreAdminHome/lang/ja.json @@ -1,8 +1,10 @@ { "CoreAdminHome": { + "AddNewTrustedHost": "新しい信頼できるホストを追加します。", "Administration": "管理", "ArchivingSettings": "アーカイブ設定", "BrandingSettings": "ブランディング設定", + "ReleaseChannel": "リリース チャネル", "ClickHereToOptIn": "クリックしてオプトイン。", "ClickHereToOptOut": "クリックしてオプトアウト。", "CustomLogoFeedbackInfo": "Piwikのロゴをカスタマイズする場合、トップメニューの %s のリンクも隠したいと思うかもしれません。それには %sプラグインの管理%s のページでフィードバックプラグインを無効にします。", @@ -68,8 +70,10 @@ "PluginSettingsIntro": "ここでは、次のサードパーティのプラグインの設定変更ができます", "PluginSettingsValueNotAllowed": "\"%s\" プラグインの \"%s\" フィールドの値は許可されていません。", "PluginSettingsSaveFailed": "プラグインの設定を保存できませんでした", + "SendPluginUpdateCommunication": "プラグインの更新が利用可能なときにメールを送信します。", "SendPluginUpdateCommunicationHelp": "使用可能な新しいプラグインのバージョンがある場合、管理者ユーザーへメールが通知されます。", "StableReleases": "Piwikがビジネスの重要な一部である場合、最新の安定版を使用することを推奨します。また、最新のベータ版を使用し、バグを見つけたり、提案があれば、%sこちらをご覧ください%s。", + "LtsReleases": "LTS (長期サポート) のバージョンは、セキュリティとバグ修正のみ受信します。", "SystemPluginSettings": "システムプラグイン設定", "TrackAGoal": "目標の追跡", "TrackingCode": "トラッキングコード", diff --git a/plugins/CoreAdminHome/lang/ko.json b/plugins/CoreAdminHome/lang/ko.json index d44b2797a7..d51f4e43be 100644 --- a/plugins/CoreAdminHome/lang/ko.json +++ b/plugins/CoreAdminHome/lang/ko.json @@ -8,22 +8,40 @@ "ClickHereToOptOut": "클릭하여 차단합니다.", "CustomLogoFeedbackInfo": "Piwik 로고를 변경하거나 상단 메뉴의 %s 링크도 숨길 수 있습니다. %s 플러그인 관리 %s 페이지에서 피드백 플러그인을 비활성화세요.", "CustomLogoHelpText": "Piwik 로고를 사용자정의하여 사용자 인터페이스 화면과 이메일 보고서를 볼 수 있습니다.", + "DevelopmentProcess": "Piwik의 %s개발 과정%s안에 수많은 자동 검사를 수행하지만, Piwik의 \"무버그 정책\"을 만족시키기 위해 베타 테스터가 상당히 중요합니다.", "EmailServerSettings": "메일 서버 설정", + "ForBetaTestersOnly": "베타 테스터 전용", "ImageTracking": "이미지 추적", "ImageTrackingLink": "이미지 추적 링크", "ImportingServerLogs": "서버 로그 가져오기", "JavaScriptTracking": "자바스크립트 추적", + "JSTracking_CampaignKwdParam": "캠페인 키워드 파라메터", + "JSTracking_CampaignNameParam": "캠페인 이름 파라메터", + "JSTracking_CodeNote": "아래 코드는 당신의 웹사이트에서 모든 페이지의 %1$s 태그 바로 앞에 위치해야 합니다.", + "JSTracking_CustomCampaignQueryParamDesc": "중요: %1$s Piwik는 자동으로 구글 분석 파라메터를 감지합니다.%2$s", + "JSTracking_EnableDoNotTrack": "클라이언트에서 DoNotTrack 탐지 활성화", + "JSTrackingIntro3": "대부분의 웹사이트, 블로그, CMS 등에서 쉽게 사용할 수 있도록 플러그인 형태로 제공되고 있습니다. (%1$sPiwik 통합 플러그인 리스트%2$s 보기) 만약 플러그인이 제공되지 않는다면, 웹사이트 템플릿을 고쳐 \"footer\" 파일에 해당 코드를 넣어 해결할 수 있습니다.", + "JSTrackingIntro5": "만약 페이지 뷰에 대한 것 이상을 원할 경우, %1$sPiwik 자바스크립트 트래킹 문서%2$s 내 여러 가능한 함수 리스트를 참고하세요. 이 함수들을 통해 목표나 맞춤 변수, 상거래 주문 및 담겨져있기만 한 카트 등을 추적할 수 있습니다.", "LogoUpload": "업로드 할 로고 선택", + "FaviconUpload": "업로드 할 파비콘 선택", "MenuDiagnostic": "진단", "MenuGeneralSettings": "일반 설정", "MenuManage": "관리", + "MenuDevelopment": "개발", "OptOutComplete": "차단 완료; 당신의 방문한 이 웹사이트는 이제 웹 분석 도구에 기록되지 않습니다.", "OptOutCompleteBis": "Cookie를 삭제하여 차단 Cookie를 삭제하거나 컴퓨터 또는 브라우저를 변경 한 경우는 차단 절차를 다시 수행해야 한다는 점 유의하세요.", "OptOutExplanation": "Piwik은 인터넷에서 개인 정보 제공에 최선을 다하고 있습니다. 방문자에 Piwik 웹 분석의 차단 옵션을 제공하기 위해 웹 사이트의 1페이지 (개인 정보 보호 정책 페이지 등)에 다음 HTML 코드를 추가 할 수 있습니다.", "OptOutExplanationBis": "HTML 코드는 방문자의 브라우저에 차단 Cookie를 설정하는 Piwik 차단 링크를 포함 iFrame을 표시합니다. iFrame에서 표시되는 내용을 표시하려면 %s여기를 클릭%s합니다.", "OptOutForYourVisitors": "방문자의 Piwik 차단", "PiwikIsInstalledAt": "Piwik가 설치되어 있습니다", + "PersonalPluginSettings": "나만의 플러그인 설정", "PluginSettings": "플러그인 설정", + "PluginSettingsIntro": "다음 나열된 서드 파티 플러그인의 설정을 바꿀 수 있다:", + "PluginSettingsSaveFailed": "플러그인 설정 저장 실패", + "SendPluginUpdateCommunication": "플러그인 업데이트가 가능할 때 이메일 알림", + "SendPluginUpdateCommunicationHelp": "플러그인의 새로운 버전이 나타날 경우 수퍼 유저에게 메일로 알려집니다.", + "StableReleases": "만약 Piwik가 당신의 비지니스에 상당히 중요한 요소라면 최근 안정 버전을 사용하시는 것을 추천합니다. 만약 최근 베타 버전을 사용 중 버그를 찾으셨거나 제안하실 사항이 있으시다면, %s여기를 봐주세요%s.", + "LtsReleases": "LTS (Long Term Support, 오랜 기간동안 지원됨) 버전은 보안 및 버그 해결만 받습니다.", "SystemPluginSettings": "시스템 플러그인 설정", "TrackAGoal": "목표 추적", "TrackingCode": "추적 코드", diff --git a/plugins/CoreAdminHome/lang/nb.json b/plugins/CoreAdminHome/lang/nb.json index d0fa1a793a..cb4c8f07e1 100644 --- a/plugins/CoreAdminHome/lang/nb.json +++ b/plugins/CoreAdminHome/lang/nb.json @@ -1,15 +1,26 @@ { "CoreAdminHome": { + "AddNewTrustedHost": "Legg til en ny trygg tjener", "Administration": "Administrasjon", "ArchivingSettings": "Arkiveringsinnstillinger", "BrandingSettings": "Innstillinger for profilering", + "ReleaseChannel": "Versjonskanal", "ClickHereToOptIn": "Trykk her for å delta.", "ClickHereToOptOut": "Trykk her for å ikke delta.", + "CustomLogoFeedbackInfo": "Hvis du tilpasser Piwik-logoen vil du kanskje også skjule %s-lenken i toppmenyen. For å gjøre det må du deaktivere Feedback-utvidelsen i %sAdministrasjon av utvidelser%s-siden.", + "CustomLogoHelpText": "Du kan tilpasse Piwik-logoen som vises i brukergrensesnittet og i e-postrapporter.", + "DevelopmentProcess": "Selv om vår %sutviklingsprosess%s inkluderer tusener av automatiske testser, spiller betatestere en viktig rolle for å at vi skal kunne etterleve vårt mål om å ikke ha noen feil.", "EmailServerSettings": "Innstillinger for e-posttjener", "ForBetaTestersOnly": "Kun for beta testere", "ImageTracking": "Bildesporing", - "ImageTrackingLink": "Sporings lenke for bilde", + "ImageTrackingIntro1": "Når en bruker har deaktivert JavaScript, eller når JavaScript ikke kan brukes, kan du bruke en bildesporingslenke for å spore besøkere.", + "ImageTrackingIntro2": "Generer lenken under og kopier den genererte HTML-koden til ditt nettsted. Hvis du bruker dette som en fallback for JavaScript-sporing, kan du bruke %1$s-tagger rundt koden.", + "ImageTrackingIntro3": "For hele listen med valg du kan bruke med en bildesporingslenke, se %1$sDokumentasjonen for sporings-API%2$s.", + "ImageTrackingLink": "Bildesporingslenke", "ImportingServerLogs": "Importerer serverlogger", + "ImportingServerLogsDesc": "Som et alternativ til å spore besøkere gjennom nettleseren (enten via JavaScript eller bildesporingslenke), kan du importere serverlogger regelmessig. %1$sLær mer serverlogganalyse her.%2$s", + "InvalidPluginsWarning": "De følgende utvidelsene er ikke kompatible med %1$s og kunne ikke lastes inn: %2$s.", + "InvalidPluginsYouCanUninstall": "Du kan oppdatere eller avinstallere disse utvidelsene på %1$sAdministrer utvidelser%2$s-siden.", "JavaScriptTracking": "JavaScript-sporing", "JSTracking_EnableDoNotTrack": "Aktiver klientside DoNotTrack-gjenkjenning", "LogoUpload": "Velg en logo for å laste opp", diff --git a/plugins/CoreAdminHome/lang/pt-br.json b/plugins/CoreAdminHome/lang/pt-br.json index bda01821fc..dd8298be31 100644 --- a/plugins/CoreAdminHome/lang/pt-br.json +++ b/plugins/CoreAdminHome/lang/pt-br.json @@ -48,10 +48,10 @@ "JSTrackingIntro4": "Se você não quiser usar JavaScript para monitorar visitantes, %1$sgere abaixo, um link para rastreamento por imagem%2$s.", "JSTrackingIntro5": "Se você deseja mais do que apenas monitorar exibições de página, porfavor verifique a %1$sdocumentação do rastreamento por javascript do Piwik%2$s para uma lista de funcões disponíveis. Usando essas funções você pode acompanhar objetivos, variáveis personalizadas, ordens de comércio eletrônico, compras abandonadas e muito mais.", "LogoNotWriteableInstruction": "Para usar um logotipo cutomizado ao invés do logo padrão Piwiki, dê permissão de escrita para esse diretório: %1$s Piwiki precisa de acesso de escrita para seus logotipos armazenados no arquivo %2$s.", - "FileUploadDisabled": "O envio de arquivos não está habilitado na sua configuração do PHP. Para enviar uma logomarca customizada defina %s no php.ini e reinicie seu servidor web.", + "FileUploadDisabled": "O carregamento de arquivos não está habilitado na sua configuração do PHP. Para carregar uma logomarca customizada defina %s no php.ini e reinicie seu servidor web.", "LogoUpload": "Selecione um logotipo para carregar", - "FaviconUpload": "Selecione um Favicon para fazer upload", - "LogoUploadHelp": "Por favor faça o upload de um arquivo nos formatos %s com uma altura mínima de %s pixels.", + "FaviconUpload": "Selecione um Favicon para carregar", + "LogoUploadHelp": "Por favor carregue um arquivo nos formatos %s com uma altura mínima de %s pixels.", "MenuDiagnostic": "Diagnostico", "MenuGeneralSettings": "Configurações Gerais", "MenuManage": "Gerenciar", diff --git a/plugins/CoreAdminHome/lang/tr.json b/plugins/CoreAdminHome/lang/tr.json index 1a4116f46e..8a412db6af 100644 --- a/plugins/CoreAdminHome/lang/tr.json +++ b/plugins/CoreAdminHome/lang/tr.json @@ -50,8 +50,12 @@ "OptOutForYourVisitors": "ziyaretciniz için Piwiki devre dışı bırakmak", "PiwikIsInstalledAt": "Piwikde yüklenir", "PluginSettingChangeNotAllowed": "Bu ayarların \"%s\" değerlerini değiştirmek için \"%s\" eklentisinde gerekli izniniz yok.", + "PluginSettings": "Eklenti Ayarları", "PluginSettingsIntro": "Buradan aşağıdaki 3. parti eklentiler için ayarları değiştirebilirsiniz:", + "PluginSettingsSaveFailed": "Eklenti ayarları kaydedilemedi", + "SendPluginUpdateCommunication": "Eklenti güncellemesi olduğunda e-posta gönder", "StableReleases": "Eğer Piwik işiniz için kritik bir parçaysa, son ve stabil olan versiyonu kullanmanızı öneririz. Eğer en son güncel betayı kullanıyorsanız ve bir hata bulur ya da öneriniz olursa, lütfen şuraya%s bakın%s.", + "SystemPluginSettings": "Sistem Eklenti Ayarları", "TrackAGoal": "Bir hedef izleme", "TrackingCode": "İzleme Kodu", "TrustedHostConfirm": "Güvenilir alan adınızı değiştirmek istediğinizden emin misiniz?", diff --git a/plugins/CoreAdminHome/stylesheets/generalSettings.less b/plugins/CoreAdminHome/stylesheets/generalSettings.less index 9d232f789b..dde1da48ed 100644 --- a/plugins/CoreAdminHome/stylesheets/generalSettings.less +++ b/plugins/CoreAdminHome/stylesheets/generalSettings.less @@ -8,7 +8,7 @@ } // hide PHP is deprecated notification in UI test -.uiTest [notification-id="PHP53VersionCheck"] { +.uiTest [notification-id="DeprecatedPHPVersionCheck"] { display: none !important; } diff --git a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig index 25d0352733..6d66695552 100644 --- a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig +++ b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig @@ -75,7 +75,7 @@ {{ 'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate("<span class='current-site-alias'>"~defaultReportSiteAlias~"</span>")|raw }} </div> <label class="checkbox"> - <input type="checkbox" id="javascript-tracking-all-aliases"/> + <input type="checkbox" checked="checked" id="javascript-tracking-all-aliases"/> {{ 'CoreAdminHome_JSTracking_MergeAliases'|translate }} <span class='current-site-name'>{{ defaultReportSiteName|raw }}</span> </label> diff --git a/plugins/CoreConsole/Commands/GenerateArchiver.php b/plugins/CoreConsole/Commands/GenerateArchiver.php index bbbdb85dfc..29bcee7bb6 100644 --- a/plugins/CoreConsole/Commands/GenerateArchiver.php +++ b/plugins/CoreConsole/Commands/GenerateArchiver.php @@ -30,7 +30,7 @@ class GenerateArchiver extends GeneratePluginBase $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; - $replace = array('ExamplePlugin' => ucfirst($pluginName), 'EXAMPLEPLUGIN' => strtoupper($pluginName)); + $replace = array('ExamplePlugin' => $pluginName, 'EXAMPLEPLUGIN' => strtoupper($pluginName)); $whitelistFiles = array('/Archiver.php'); $this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles); diff --git a/plugins/CoreConsole/Commands/GeneratePlugin.php b/plugins/CoreConsole/Commands/GeneratePlugin.php index c42d7a619b..bf83889305 100644 --- a/plugins/CoreConsole/Commands/GeneratePlugin.php +++ b/plugins/CoreConsole/Commands/GeneratePlugin.php @@ -12,6 +12,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\Filesystem; use Piwik\Plugins\ExamplePlugin\ExamplePlugin; use Piwik\Version; +use Piwik\Plugin; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -28,7 +29,8 @@ class GeneratePlugin extends GeneratePluginBase ->setDescription('Generates a new plugin/theme including all needed files') ->addOption('name', null, InputOption::VALUE_REQUIRED, 'Plugin name ([a-Z0-9_-])') ->addOption('description', null, InputOption::VALUE_REQUIRED, 'Plugin description, max 150 characters') - ->addOption('pluginversion', null, InputOption::VALUE_OPTIONAL, 'Plugin version'); + ->addOption('pluginversion', null, InputOption::VALUE_OPTIONAL, 'Plugin version') + ->addOption('overwrite', null, InputOption::VALUE_NONE, 'Generate even if plugin directory already exists.'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -115,20 +117,24 @@ class GeneratePlugin extends GeneratePluginBase */ protected function getPluginName(InputInterface $input, OutputInterface $output) { + $overwrite = $input->getOption('overwrite'); + $self = $this; - $validate = function ($pluginName) use ($self) { + $validate = function ($pluginName) use ($self, $overwrite) { if (empty($pluginName)) { throw new \RuntimeException('You have to enter a plugin name'); } - if (!Filesystem::isValidFilename($pluginName)) { - throw new \RuntimeException(sprintf('The plugin name %s is not valid', $pluginName)); + if (!Plugin\Manager::getInstance()->isValidPluginName($pluginName)) { + throw new \RuntimeException(sprintf('The plugin name %s is not valid. The name must start with a letter and is only allowed to contain numbers and letters.', $pluginName)); } $pluginPath = $self->getPluginPath($pluginName); - if (file_exists($pluginPath)) { + if (file_exists($pluginPath) + && !$overwrite + ) { throw new \RuntimeException('A plugin with this name already exists'); } diff --git a/plugins/CoreConsole/Commands/GeneratePluginBase.php b/plugins/CoreConsole/Commands/GeneratePluginBase.php index c3fce493dc..9012839943 100644 --- a/plugins/CoreConsole/Commands/GeneratePluginBase.php +++ b/plugins/CoreConsole/Commands/GeneratePluginBase.php @@ -31,7 +31,7 @@ abstract class GeneratePluginBase extends ConsoleCommand private function getRelativePluginPath($pluginName) { - return '/plugins/' . ucfirst($pluginName); + return '/plugins/' . $pluginName; } private function createFolderWithinPluginIfNotExists($pluginNameOrCore, $folder) @@ -325,8 +325,6 @@ abstract class GeneratePluginBase extends ConsoleCommand $validate($pluginName); } - $pluginName = ucfirst($pluginName); - return $pluginName; } diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php index bbe7e44c8f..a611ca8def 100644 --- a/plugins/CoreHome/Columns/UserId.php +++ b/plugins/CoreHome/Columns/UserId.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\CoreHome\Columns; use Piwik\Cache; use Piwik\DataTable; use Piwik\DataTable\Map; +use Piwik\Metrics; use Piwik\Period\Range; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; @@ -124,7 +125,14 @@ class UserId extends VisitDimension return false; } - $numUsers = $result->getColumn('nb_users'); + $firstRow = $result->getFirstRow(); + if ($firstRow instanceof DataTable\Row && $firstRow->hasColumn(Metrics::INDEX_NB_USERS)) { + $metric = Metrics::INDEX_NB_USERS; + } else { + $metric = 'nb_users'; + } + + $numUsers = $result->getColumn($metric); $numUsers = array_sum($numUsers); return !empty($numUsers); diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index 519891acb0..c1f88e035c 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -143,6 +143,8 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/trim.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/pretty-url.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/escape.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/htmldecode.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/ucfirst.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js"; @@ -207,7 +209,6 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'General_Show'; $translationKeys[] = 'General_Hide'; $translationKeys[] = 'General_Website'; - $translationKeys[] = 'General_ChooseWebsite'; $translationKeys[] = 'Intl_Year_Short'; $translationKeys[] = 'General_MultiSitesSummary'; $translationKeys[] = 'General_SearchNoResults'; @@ -278,6 +279,7 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'Intl_Day_Min_StandAlone_6'; $translationKeys[] = 'Intl_Day_Min_StandAlone_7'; $translationKeys[] = 'General_And'; + $translationKeys[] = 'General_All'; $translationKeys[] = 'General_Search'; $translationKeys[] = 'General_Clear'; $translationKeys[] = 'General_MoreDetails'; @@ -304,5 +306,6 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'CoreHome_Segments'; $translationKeys[] = 'CoreHome_MenuEntries'; $translationKeys[] = 'SitesManager_Sites'; + $translationKeys[] = 'CoreHome_ChangeCurrentWebsite'; } } diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php index d73c5c2158..106685edf7 100644 --- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php +++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php @@ -91,7 +91,7 @@ class RowEvolution if (!is_array($this->label)) { throw new Exception("Expected label to be an array, got instead: " . $this->label); } - $this->label = $this->label[0]; + $this->label = Common::unsanitizeInputValue($this->label[0]); if ($this->label === '') throw new Exception("Parameter label not set."); diff --git a/plugins/CoreHome/Menu.php b/plugins/CoreHome/Menu.php index 20d9ac8eb8..c9331e4470 100644 --- a/plugins/CoreHome/Menu.php +++ b/plugins/CoreHome/Menu.php @@ -35,12 +35,13 @@ class Menu extends \Piwik\Plugin\Menu if (Piwik::isUserIsAnonymous()) { if (Plugin\Manager::getInstance()->isPluginActivated('ScheduledReports')) { - $menu->addItem($login, null, array('module' => 'ScheduledReports', 'action' => 'index'), 970, $login); + $menu->addItem($login, null, array('module' => 'ScheduledReports', 'action' => 'index'), 970, Piwik::translate('ScheduledReports_PersonalEmailReports')); } else { - $menu->addItem($login, null, array('module' => 'API', 'action' => 'listAllAPI'), 970, $login); + $menu->addItem($login, null, array('module' => 'API', 'action' => 'listAllAPI'), 970, Piwik::translate('API_ReportingApiReference')); } } else { - $menu->addItem($login, null, array('module' => 'UsersManager', 'action' => 'userSettings'), 970, $login); + $tooltip = sprintf('%s: %s', Piwik::translate('UsersManager_PersonalSettings'), $login); + $menu->addItem($login, null, array('module' => 'UsersManager', 'action' => 'userSettings'), 970, $tooltip); } $module = $this->getLoginModule(); diff --git a/plugins/CoreHome/angularjs/common/filters/htmldecode.js b/plugins/CoreHome/angularjs/common/filters/htmldecode.js new file mode 100644 index 0000000000..c3e8107931 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/filters/htmldecode.js @@ -0,0 +1,26 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.filter').filter('htmldecode', htmldecode); + + htmldecode.$inject = ['piwik']; + + /** + * Be aware that this filter can cause XSS so only use it when you're sure it is safe. + * Eg it should be safe when it is afterwards escaped by angular sanitize again. + */ + function htmldecode(piwik) { + + return function(text) { + if (text && text.length) { + return piwik.helper.htmlDecode(text); + } + + return text; + }; + } +})(); diff --git a/plugins/CoreHome/angularjs/common/filters/ucfirst.js b/plugins/CoreHome/angularjs/common/filters/ucfirst.js new file mode 100644 index 0000000000..34b3460be8 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/filters/ucfirst.js @@ -0,0 +1,21 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.filter').filter('ucfirst', ucfirst); + + function ucfirst() { + + return function(value) { + if (!value) { + return value; + } + + var firstLetter = (value + '').charAt(0).toUpperCase(); + return firstLetter + value.substr(1); + }; + } +})(); diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html index 8cf6a9a67b..93371a03bf 100644 --- a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html @@ -1,6 +1,7 @@ -<ul class="navbar"> +<ul class="navbar"role="menu"> <li ng-repeat="category in menuModel.menu" class="menuTab" + role="menuitem" ng-class="{'active': category.active}"> <a class="item" href="" @@ -10,8 +11,9 @@ {{ 'CoreHome_Menu'|translate }} </span> </a> - <ul> + <ul role="menu"> <li ng-repeat="subcategory in category.subcategories" + role="menuitem" ng-class="{'active': subcategory.active}"> <div ng-if="subcategory.isGroup" piwik-menudropdown show-search="true" menu-title="{{ subcategory.name|escape }}"> <a class="item" diff --git a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html index 9ee4932163..e4e46a676a 100644 --- a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html +++ b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html @@ -16,7 +16,7 @@ <a ng-click="view.showSitesList=!view.showSitesList; view.showSitesList && !model.isLoading && model.loadInitialSites();" piwik-onenter="view.showSitesList=!view.showSitesList; view.showSitesList && !model.isLoading && model.loadInitialSites();" href="javascript:void(0)" - title="{{ 'General_ChooseWebsite'|translate }}" + title="{{ 'CoreHome_ChangeCurrentWebsite'|translate:((selectedSite.name || model.firstSiteName)|htmldecode) }}" ng-class="{'loading': model.isLoading}" class="title"> <span class="icon icon-arrow-bottom" @@ -37,6 +37,7 @@ ng-hide="!showSelectedSite && activeSiteId==site.idsite"> <a piwik-ignore-click href="{{ getUrlForSiteId(site.idsite) }}" piwik-autocomplete-matched="view.searchTerm" + title="{{ site.name|htmldecode }}" ng-bind-html="site.name"></a> </li> </ul> diff --git a/plugins/CoreHome/config/config.php b/plugins/CoreHome/config/config.php index 3c646cbd88..39236b62c5 100644 --- a/plugins/CoreHome/config/config.php +++ b/plugins/CoreHome/config/config.php @@ -3,10 +3,6 @@ return array( 'Piwik\Plugins\CoreHome\Tracker\VisitRequestProcessor' => DI\object() - ->constructorParameter('visitStandardLength', DI\get('ini.Tracker.visit_standard_length')), - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\CoreHome\Tracker\VisitRequestProcessor'), - )), + ->constructorParameter('visitStandardLength', DI\get('ini.Tracker.visit_standard_length')) ); diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js index 3d5fb0432c..30b21f46af 100644 --- a/plugins/CoreHome/javascripts/broadcast.js +++ b/plugins/CoreHome/javascripts/broadcast.js @@ -214,6 +214,10 @@ var broadcast = { currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr); } + if (module != 'CustomDimensions') { + currentHashStr = broadcast.updateParamValue('idDimension=', currentHashStr); + } + if (disableHistory) { var newLocation = window.location.href.split('#')[0] + '#?' + currentHashStr; // window.location.replace changes the current url without pushing it on the browser's history stack @@ -451,7 +455,8 @@ var broadcast = { broadcast.getParamValue('action', urlAjax), { idGoal: broadcast.getParamValue('idGoal', urlAjax), - idDashboard: broadcast.getParamValue('idDashboard', urlAjax) + idDashboard: broadcast.getParamValue('idDashboard', urlAjax), + idDimension: broadcast.getParamValue('idDimension', urlAjax) } ); }); diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js index 9a7f34cd4c..2ec3fb16d8 100644 --- a/plugins/CoreHome/javascripts/dataTable.js +++ b/plugins/CoreHome/javascripts/dataTable.js @@ -508,13 +508,20 @@ $.extend(DataTable.prototype, UIControl.prototype, { }; } + function getFilterLimitAsString(limit) { + if (limit == '-1') { + return _pk_translate('General_All').toLowerCase(); + } + return limit; + } + // setup limit control - $('.limitSelection', domElem).append('<div><span>' + self.param[limitParamName] + '</span></div><ul></ul>'); + $('.limitSelection', domElem).append('<div><span value="'+ self.param[limitParamName] +'">' + getFilterLimitAsString(self.param[limitParamName]) + '</span></div><ul></ul>'); if (self.props.show_limit_control) { $('.limitSelection ul', domElem).hide(); for (var i = 0; i < numbers.length; i++) { - $('.limitSelection ul', domElem).append('<li value="' + numbers[i] + '"><span>' + numbers[i] + '</span></li>'); + $('.limitSelection ul', domElem).append('<li value="' + numbers[i] + '"><span>' + getFilterLimitAsString(numbers[i]) + '</span></li>'); } $('.limitSelection ul li:last', domElem).addClass('last'); @@ -537,12 +544,12 @@ $.extend(DataTable.prototype, UIControl.prototype, { $('.limitSelection', domElem).is('.visible') ? hide() : show(); }); $('.limitSelection ul li', domElem).on('click', function (event) { - var limit = parseInt($(event.target).text()); + var limit = parseInt($(event.target).closest('li').attr('value')); hide(); if (limit != self.param[limitParamName]) { setLimitValue(self.param, limit); - $('.limitSelection>div>span', domElem).text(limit); + $('.limitSelection>div>span', domElem).text( getFilterLimitAsString(limit)).attr('value', limit); self.reloadAjaxDataTable(); var data = {}; @@ -1047,7 +1054,7 @@ $.extend(DataTable.prototype, UIControl.prototype, { $(this).attr('href', function () { var url = $(this).attr('href') + '&token_auth=' + piwik.token_auth; - var limit = $('.limitSelection>div>span', domElem).text(); + var limit = $('.limitSelection>div>span', domElem).attr('value'); var defaultLimit = $(this).attr('filter_limit'); if (!limit || 'undefined' === limit || defaultLimit == -1) { limit = defaultLimit; @@ -1071,6 +1078,7 @@ $.extend(DataTable.prototype, UIControl.prototype, { var segment = self.param.segment; var label = self.param.label; var idGoal = self.param.idGoal; + var idDimension = self.param.idDimension; var param_date = self.param.date; var date = $(this).attr('date'); if (typeof date != 'undefined') { @@ -1139,6 +1147,11 @@ $.extend(DataTable.prototype, UIControl.prototype, { && idGoal != '-1') { str += '&idGoal=' + idGoal; } + // Export Dimension specific reports + if (typeof idDimension != 'undefined' + && idDimension != '-1') { + str += '&idDimension=' + idDimension; + } if (label) { label = label.split(','); diff --git a/plugins/CoreHome/javascripts/dataTable_rowactions.js b/plugins/CoreHome/javascripts/dataTable_rowactions.js index 004e27e3b6..e7308fb93f 100644 --- a/plugins/CoreHome/javascripts/dataTable_rowactions.js +++ b/plugins/CoreHome/javascripts/dataTable_rowactions.js @@ -386,6 +386,17 @@ DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMeth requestParams.action = 'getRowEvolutionPopover'; requestParams.colors = JSON.stringify(piwik.getSparklineColors()); + var idDimension; + if (broadcast.getValueFromUrl('module') === 'Widgetize') { + idDimension = broadcast.getValueFromUrl('idDimension'); + } else { + idDimension = broadcast.getValueFromHash('idDimension'); + } + + if (idDimension) { + requestParams.idDimension = parseInt(idDimension, 10); + } + $.extend(requestParams, extraParams); var ajaxRequest = new ajaxHelper(); diff --git a/plugins/CoreHome/javascripts/notification.js b/plugins/CoreHome/javascripts/notification.js index e6c3f017f2..527c845d87 100644 --- a/plugins/CoreHome/javascripts/notification.js +++ b/plugins/CoreHome/javascripts/notification.js @@ -54,6 +54,16 @@ this.$node = placeNotification(template, options); }; + /** + * Removes a previously shown notification having the given notification id. + * + * + * @param {string} notificationId The id of a notification that was previously registered. + */ + Notification.prototype.remove = function (notificationId) { + $('[piwik-notification][notification-id=' + notificationId + ']').remove(); + }; + Notification.prototype.scrollToNotification = function () { if (this.$node) { piwikHelper.lazyScrollTo(this.$node, 250); diff --git a/plugins/CoreHome/javascripts/popover.js b/plugins/CoreHome/javascripts/popover.js index aff7ed7ff9..f694769821 100644 --- a/plugins/CoreHome/javascripts/popover.js +++ b/plugins/CoreHome/javascripts/popover.js @@ -142,7 +142,17 @@ var Piwik_Popover = (function () { /** Set the title of the popover */ setTitle: function (titleHtml) { + var titleText = piwikHelper.htmlDecode(titleHtml); + if (titleText.length > 60) { + titleHtml = $('<span>').attr('class', 'tooltip').attr('title', titleText).html(titleHtml); + } container.dialog('option', 'title', titleHtml); + try { + $('.tooltip', container.parentNode).tooltip('destroy'); + } catch (e) {} + if (titleText.length > 60) { + $('.tooltip', container.parentNode).tooltip({track: true, items: '.tooltip'}); + } }, /** Set inner HTML of the popover */ diff --git a/plugins/CoreHome/lang/cs.json b/plugins/CoreHome/lang/cs.json index b2a02bb644..aba489871a 100644 --- a/plugins/CoreHome/lang/cs.json +++ b/plugins/CoreHome/lang/cs.json @@ -20,7 +20,7 @@ "IncludeRowsWithLowPopulation": "Řádky s nízkou populací jsou skryty %s Zobrazit všechny řádky", "InjectedHostEmailBody": "Ahoj. Dnes jsem se snažil dostat do \\Piwiku a dostal jsem varování o neznámém ménu hostitele.", "InjectedHostEmailSubject": "K piwiku bylo přistupováno s neznámým jménem hostitele %s", - "InjectedHostNonSuperUserWarning": "%1$sKlikněte zde pro bezpečný přístup k Piwiku%2$s a odstranění tohoto varování. Pokud chcete upozornit administrátora na tento problém, %3$sklikněte zde pro odeslání e-mailu%4$s.", + "InjectedHostNonSuperUserWarning": "%1$sKlikněte zde pro bezpečný přístup k Piwiku%2$s a odstranění tohoto varování. Pokud chcete upozornit administrátora na tento problém, %3$sklikněte zde pro odeslání emailu%4$s.", "InjectedHostSuperUserWarning": "Piwik může být špatně nakonfigurován, například pokud byl přesunut na nový server nebo URL. Můžete %1$skliknout zde a přidat %2$s jako platné jméno hostitele Piwiku (pokud mu důvěřujete)%3$s, nebo %4$s klikněte zde%5$s pro bezpečný přístup k Piwiku%6$s.", "InjectedHostWarningIntro": "Nyní přistupujete k Piwiku z %1$s, ale byl nakonfigurován, aby naslouchal na následující adrese: %2$s.", "JavascriptDisabled": "Musíte mít zapnutý JavaScript, jinak Piwik nezobrazíte.<br \/> Nebo jen není Váš prohlížeč mezi podporovanými.<br \/>Pro běžné zobrazení zapněte JavaScript ve svém prohlížeči, poté %1$szkuste znovu%2$s.<br \/>", @@ -48,6 +48,11 @@ "YouAreUsingTheLatestVersion": "Používáte nejnovější verzi Piwiku.", "ClickRowToExpandOrContract": "Klikněte na tento řádek pro rozbalení nebo zbalení podtabulky.", "UndoPivotBySubtable": "Toto hlášení bylo zaměřeno %s Vrátit zpět", - "PivotBySubtable": "Toto hlášení není zaměřené %s Zaměřit na %s" + "PivotBySubtable": "Toto hlášení není zaměřené %s Zaměřit na %s", + "QuickAccessTitle": "Najít zástupce %s: Pro hledání stiskněte 'f'.", + "MenuEntries": "Položky menu", + "Segments": "Segmenty", + "AdblockIsMaybeUsed": "Pokud používáte blokovač reklam, zakažte ho pro tyto stránky, aby Piwik správně fungoval.", + "ChangeCurrentWebsite": "Vybrat webovou stránku, aktuálně vybraná stránka: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/de.json b/plugins/CoreHome/lang/de.json index 4c744998eb..a80015d88d 100644 --- a/plugins/CoreHome/lang/de.json +++ b/plugins/CoreHome/lang/de.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Suche nach %s. Tastenkürzel: Drücken Sie 'f' um zu suchen.", "MenuEntries": "Menüeinträge", "Segments": "Segmente", - "AdblockIsMaybeUsed": "Für den Fall, dass Sie einen Ad-Blocker verwenden, deaktivieren Sie diesen bitte für diese Seite um sicherzustellen, dass Piwik problemlos läuft." + "AdblockIsMaybeUsed": "Für den Fall, dass Sie einen Ad-Blocker verwenden, deaktivieren Sie diesen bitte für diese Seite um sicherzustellen, dass Piwik problemlos läuft.", + "ChangeCurrentWebsite": "Wählen Sie eine Webseite, aktuell gewählte Webseite: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/el.json b/plugins/CoreHome/lang/el.json index 242a7af9e6..ed3ae34bb2 100644 --- a/plugins/CoreHome/lang/el.json +++ b/plugins/CoreHome/lang/el.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Αναζήτηση για το %s. Πλήκτρο συντόμευσης: Πατήστε 'f' για αναζήτηση.", "MenuEntries": "Καταχωρήσεις μενού", "Segments": "Τμήματα", - "AdblockIsMaybeUsed": "Σε περίπτωση που χρησιμοποιείτε κάποια πρόγραμμα που μπλοκάρει τις διαφημίσεις, παρακαλούμε απενεργοποιήστε το για αυτόν τον ιστοτόπο για να είστε σίγουροι ότι το Piwik δουλεύει σωστά." + "AdblockIsMaybeUsed": "Σε περίπτωση που χρησιμοποιείτε κάποια πρόγραμμα που μπλοκάρει τις διαφημίσεις, παρακαλούμε απενεργοποιήστε το για αυτόν τον ιστοτόπο για να είστε σίγουροι ότι το Piwik δουλεύει σωστά.", + "ChangeCurrentWebsite": "Επιλέξτε ένα ιστοτόπο, επιλεγμένος ιστοτόπος αυτή τη στιγμή: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json index 6e38e1d0f2..75566a08f2 100644 --- a/plugins/CoreHome/lang/en.json +++ b/plugins/CoreHome/lang/en.json @@ -53,6 +53,7 @@ "QuickAccessTitle": "Search for %s. Shortcut: Press 'f' to search.", "MenuEntries": "Menu entries", "Segments": "Segments", - "AdblockIsMaybeUsed": "In case you are using an ad blocker, please disable it for this site to make sure Piwik works without any issues." + "AdblockIsMaybeUsed": "In case you are using an ad blocker, please disable it for this site to make sure Piwik works without any issues.", + "ChangeCurrentWebsite": "Choose a website, currently selected website: %s" } } diff --git a/plugins/CoreHome/lang/fr.json b/plugins/CoreHome/lang/fr.json index dc5980c335..c63eb35c23 100644 --- a/plugins/CoreHome/lang/fr.json +++ b/plugins/CoreHome/lang/fr.json @@ -48,6 +48,7 @@ "YouAreUsingTheLatestVersion": "Vous utilisez la dernière version de Piwik!", "ClickRowToExpandOrContract": "Cliquez sur cette rangée pour afficher ou masquer le sous-tableau", "UndoPivotBySubtable": "Ce rapport a été pivoté %s Annuler le pivot", - "PivotBySubtable": "Ce rapport n'a pas été pivoté %s Pivoter par %s" + "PivotBySubtable": "Ce rapport n'a pas été pivoté %s Pivoter par %s", + "Segments": "Segments" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/it.json b/plugins/CoreHome/lang/it.json index 1d1513c8de..37c9c5fc1a 100644 --- a/plugins/CoreHome/lang/it.json +++ b/plugins/CoreHome/lang/it.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Cerca %s. Shortcut: Premi 'f' per la ricerca.", "MenuEntries": "Ingressi da menù", "Segments": "Segmenti", - "AdblockIsMaybeUsed": "Se stai utilizzando un plugin di blocco della pubblicità, disabilitalo per questo sito, per essere sicuro che Piwik lavori senza problemi." + "AdblockIsMaybeUsed": "Se stai utilizzando un plugin di blocco della pubblicità, disabilitalo per questo sito, per essere sicuro che Piwik lavori senza problemi.", + "ChangeCurrentWebsite": "Scegli un sito, attualmente è selezionato: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/ja.json b/plugins/CoreHome/lang/ja.json index 6efb5237e9..d328182c53 100644 --- a/plugins/CoreHome/lang/ja.json +++ b/plugins/CoreHome/lang/ja.json @@ -48,6 +48,11 @@ "YouAreUsingTheLatestVersion": "Piwik の最新バージョンを使用しています !", "ClickRowToExpandOrContract": "サブテーブルを拡大または縮小するには、この列をクリックしてください。", "UndoPivotBySubtable": "このレポートは、%s 元に戻すピボットを旋回しました。", - "PivotBySubtable": "このレポートは、%s が %s ピボット を旋回していません。" + "PivotBySubtable": "このレポートは、%s が %s ピボット を旋回していません。", + "QuickAccessTitle": "%s を検索。ショートカット: ' f' キーを押して検索します。", + "MenuEntries": "メニュー エントリー", + "Segments": "セグメント", + "AdblockIsMaybeUsed": "広告ブロッカーを使用している場合には、Piwik が問題なく動作することを確認するために、このサイトでそれを無効にしてください。", + "ChangeCurrentWebsite": "ウェブサイトを選ぶ 現在選択されているWebサイト:%s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/ko.json b/plugins/CoreHome/lang/ko.json index f43ee7f5ae..aee10161d2 100644 --- a/plugins/CoreHome/lang/ko.json +++ b/plugins/CoreHome/lang/ko.json @@ -3,6 +3,7 @@ "CategoryNoData": "이 카테고리 안에 데이터가 없습니다. \"모든 집단 포함\"을 시도해보세요.", "CheckForUpdates": "업데이트 확인", "CheckPiwikOut": "Piwik 출시 확인!", + "ClickToEditX": "%s 수정하기", "CloseWidgetDirections": "위젯의 상단에있는 'X'아이콘을 클릭하여 이 위젯을 닫을 수 있습니다.", "DataForThisReportHasBeenPurged": "이 보고서의 데이터는 %s 개월 이상 되었기 때문에 제거되었습니다.", "DataTableExcludeAggregateRows": "요약 행 표시 %s 숨기기", @@ -13,6 +14,7 @@ "DonateCall3": "Piwik이 당신의 업무나 시도하는 일에 특별한 가치를 더해준다고 느끼신다면, %1$s기부해 주세요!%2$s", "DonateFormInstructions": "슬라이더를 클릭하여 금액을 선택하고, 기부에 기명을 클릭합니다.", "ExcludeRowsWithLowPopulation": "모든 행을 표시 %s 적은 량의 항목은 제외", + "ExternalHelp": "도움말 (새로운 탭에 열기)", "FlattenDataTable": "이 보고서는 계층형입니다. %s 펼치기", "HowMuchIsPiwikWorth": "당신에게 Piwik의 가치는 얼마인가요?", "IncludeRowsWithLowPopulation": "수가 적은 줄 숨기기 %s 모든 행을 표시", @@ -24,7 +26,9 @@ "JavascriptDisabled": "Piwik의 표준보기를 이용하려면 JavaScript가가 활성화되어 있어야 합니다.<br \/>하지만, 당신의 브라우저는 활성화되어있지 않거나 지원되지 않는 것 같습니다.<br \/>표준보기를 이용하려면 브라우저 옵션을 변경하여 JavaScript를 활성화하고 <br \/> %1$s다시 시도하세요%2$s.<br \/>", "MakeADifference": "개발 지원: Piwik 2.0에 %1$s기부해 주세요%2$s!", "MakeOneTimeDonation": "또는, 한 번만 기부합니다.", + "Menu": "메뉴", "NoPrivilegesAskPiwikAdmin": "'%s'로 로그인되었지만 권한이 설정되어 있지 않습니다. %s Piwik 관리자(클릭 이메일) %s에 '보기'권한을 부여 받으세요.", + "OnlyForSuperUserAccess": "해당 위젯은 유저가 수퍼 유저일 경우에만 나타납니다.", "PageOf": "%1$s \/ %2$s", "PeriodRange": "기간", "ReportGeneratedOn": "%s에 생성된 보고서", @@ -33,12 +37,20 @@ "SharePiwikShort": "Piwik은 오픈소스기반 무료 웹 분석 도구입니다. 이제 소중한 정보를 소유하세요!", "ShareThis": "공유하기", "ShowJSCode": "삽입할 자바스크립트 코드 보기", + "SkipToContent": "내용 건너뛰기", "SubscribeAndBecomePiwikSupporter": "Piwik의 후원자가 되기 위해서 보안 신용카드 결제 페이지 (페이팔)로 이동합니다!", "SupportPiwik": "Piwik 지원!", + "TableNoData": "이 테이블에 데이터가 없습니다.", "ThereIsNoDataForThisReport": "이 보고서에는 데이터가 없습니다.", "UnFlattenDataTable": "보고서가 펼쳐져 있습니다 %s 계층으로 표시하기", "ViewAllPiwikVideoTutorials": "모든 비디오 튜토리얼 보기", "WebAnalyticsReports": "웹 분석 보고서", - "YouAreUsingTheLatestVersion": "Piwik 최신 버전을 사용하고 있습니다!" + "YouAreUsingTheLatestVersion": "Piwik 최신 버전을 사용하고 있습니다!", + "ClickRowToExpandOrContract": "하위 테이블의 행을 확장하거나 축소하기 위해 클릭하세요.", + "QuickAccessTitle": "%s 검색하기. 단축키: 검색을 하려면 'f'를 누르세요.", + "MenuEntries": "메뉴", + "Segments": "세그멘트", + "AdblockIsMaybeUsed": "Piwik가 문제 없이 동작하기 위해서 광고 차단기능이 있는 플러그인을 꺼주시길 바랍니다.", + "ChangeCurrentWebsite": "웹사이트를 선택하세요, 현재 선택된 웹사이트: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/lt.json b/plugins/CoreHome/lang/lt.json index f0b88807bf..907f1bf178 100644 --- a/plugins/CoreHome/lang/lt.json +++ b/plugins/CoreHome/lang/lt.json @@ -3,6 +3,7 @@ "CategoryNoData": "Šioje kategorijoje duomenų nėra. Pabandykite \"Įtraukti visą populiaciją\"", "JavascriptDisabled": "JavaScript turi būti įjungta, kad būtų galima naudoti standartinę Piwik išvaizdą.<br \/>Tačiau panašu, kad JavaScript yra arba išjungta, arba nepalaikoma Jūsų naršyklės.<br \/>Norėdami matyti standartinę išvaizdą, įjunkite JavaScript keisdami savo naršyklės nustatymus, tada %1$sbandykite dar kartą%2$s.<br \/>", "PageOf": "%1$s iš %2$s", + "SharePiwikShort": "Piwik! Nemokama\/laisva saityno analitika. Būkite savo duomenų savininkais.", "ShowJSCode": "Parodyti JavaScript kodą įvedimui", "ThereIsNoDataForThisReport": "Nėra duomenų šiai ataskaitai.", "WebAnalyticsReports": "Žiniatinklio analizės sprendimo ataskaitos" diff --git a/plugins/CoreHome/lang/nb.json b/plugins/CoreHome/lang/nb.json index 91308236ff..33cc4649d0 100644 --- a/plugins/CoreHome/lang/nb.json +++ b/plugins/CoreHome/lang/nb.json @@ -1,12 +1,12 @@ { "CoreHome": { - "CategoryNoData": "Ingen data i denne kategorien. Prøv å velge \"Inkluder hele populasjon\".", + "CategoryNoData": "Ingen data i denne kategorien. Prøv å velge «Inkluder hele populasjonen».", "CheckForUpdates": "Se etter oppdateringer", "CheckPiwikOut": "Sjekk ut Piwik!", "ClickToEditX": "Klikk for å redigere %s", - "CloseWidgetDirections": "Du kan lukke dette elementet ved å klikke på X-ikonet på toppen av elementet.", + "CloseWidgetDirections": "Du kan lukke dette elementet ved å klikke på X-ikonet over widgeten.", "DataForThisReportHasBeenPurged": "Dataene for denne rapporten er mer enn %s måneder gamle og har blitt fjernet.", - "DataTableExcludeAggregateRows": "Aggregerte rader er vist som %s Skjul dem", + "DataTableExcludeAggregateRows": "Aggregerte rader vises %s Skjul dem", "DataTableIncludeAggregateRows": "Aggregerte rader er skjult %s Vi dem", "Default": "standard", "DonateCall1": "Det vil aldri koste noe å bruke Piwik, men det betyr ikke at det ikke koster oss noe å lage.", @@ -20,7 +20,7 @@ "IncludeRowsWithLowPopulation": "Rader med lav populasjon er skjult %s Vis alle rader", "InjectedHostEmailBody": "Hei, jeg prøvde å få tilgang til Piwik i dag og ble møtt av «ukjent vertsnavn»-advarselen.", "InjectedHostEmailSubject": "Piwik ble åpnet med et ukjent vertsnavn: %s", - "InjectedHostNonSuperUserWarning": "%1$sKlikk her for å få sikker tilgang til Piwik%2$s og for å fjerne denne advarselen. Du ømsker kanskje også å kontakte din Piwik-administrator for å varsle dem om dette problemet (%3$sklikk her for å sende epost%4$s).", + "InjectedHostNonSuperUserWarning": "%1$sKlikk her for å få sikker tilgang til Piwik%2$s og for å fjerne denne advarselen. Du vil kanskje også kontakte din Piwik-administrator for å varsle dem om dette problemet (%3$sklikk her for å sende e-post%4$s).", "InjectedHostSuperUserWarning": "Piwik kan være feilkonfigurert (for eksempel hvis Piwik nylig var flyttet til en ny tjener eller ny URL). Du kan enten %1$sklikke her og legge til %2$s som et gyldig Piwik-vertsnavn (hvis du stoler på det)%3$s eller %4$sklikk her og gå til %5$s for sikker tilgang til Piwik%6$s.", "InjectedHostWarningIntro": "Du har tilgang til Piwik fra %1$s, men Piwik er konfigurert til å kjøre på adressen: %2$s", "JavascriptDisabled": "JavaScript må være aktivert for at du skal kunne bruke Piwik i standardvisning.<br \/>Det ser ut til at JavaScript enten er deaktivert eller ikke støttet av din nettleser.<br \/>For å bruke standardvisning, må du aktivere JavaScript i nettleserinnstillingene og deretter %1$sprøve igjen%2$s.<br \/>", @@ -28,13 +28,13 @@ "MakeOneTimeDonation": "Bidra med en engangsdonasjon i stedet.", "Menu": "Meny", "NoPrivilegesAskPiwikAdmin": "Du er logget inn som «%s», men det ser ut til at du ikke har noen rettigheter satt i Piwik. %s Be din Piwik-administrator (klikk for å sende e-post)%s å gi deg «vis»-tilgang til et nettsted.", - "OnlyForSuperUserAccess": "Dette elementet er vises kun til brukere som har superbruker-tilgang.", + "OnlyForSuperUserAccess": "Denne widgeten vises kun til brukere som har superbruker-tilgang.", "PageOf": "%1$s av %2$s", - "PeriodRange": "Område", + "PeriodRange": "Periode", "ReportGeneratedOn": "Rapport generert %s", "ReportGeneratedXAgo": "Rapport generert %s siden", "SharePiwikLong": "Hei! Jeg har nettopp funnet et bra stykke fri programvare: Piwik!\n\nPiwik lar deg spore besøkende på ditt nettsted gratis. Du bør definitivt prøve det ut.", - "SharePiwikShort": "Piwik! Gratis og åpen kildekode web analyse. Du eier dataene.", + "SharePiwikShort": "Piwik! Gratis og åpen kildekode for nettstatistikk. Du eier dataene.", "ShareThis": "Del dette", "ShowJSCode": "Vis JavaScript-koden til å sette inn på din nettside.", "SkipToContent": "Hopp til innhold", @@ -48,6 +48,11 @@ "YouAreUsingTheLatestVersion": "Du bruker den nyeste versjonen av Piwik!", "ClickRowToExpandOrContract": "Klikk denne raden for utvide eller slå sammen undertabellen.", "UndoPivotBySubtable": "Denne rapporten er pivotert %s Angre pivot", - "PivotBySubtable": "Denne rapporten er ikke pivotert %s Pivot etter %s" + "PivotBySubtable": "Denne rapporten er ikke pivotert %s Pivot etter %s", + "QuickAccessTitle": "Søk etter %s. Snarvei: Trykk «f» for å søke.", + "MenuEntries": "Menyvalg", + "Segments": "Segmenter", + "AdblockIsMaybeUsed": "Hvis du bruker en ad-blocker, vennligst deaktiver den for dette nettstedet for å forsikre deg om at Piwik fungerer som det skal.", + "ChangeCurrentWebsite": "Velg et nettsted. Nettstedet som nå er valgt er: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/pt-br.json b/plugins/CoreHome/lang/pt-br.json index 69f02cb1ad..b7522980e0 100644 --- a/plugins/CoreHome/lang/pt-br.json +++ b/plugins/CoreHome/lang/pt-br.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Buscar por %s. Atalho: Tecle 'f' para buscar.", "MenuEntries": "Itens do menu", "Segments": "Segmentos", - "AdblockIsMaybeUsed": "Caso você esteja usando um bloqueador de anúncios, por favor desative-o para este site, pois assim poderá certificar-se de que o Piwik funciona sem qualquer problema." + "AdblockIsMaybeUsed": "Caso você esteja usando um bloqueador de anúncios, por favor desative-o para este site, pois assim poderá certificar-se de que o Piwik funciona sem qualquer problema.", + "ChangeCurrentWebsite": "Escolha um website; website atualmente selecionado: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/tr.json b/plugins/CoreHome/lang/tr.json index 572a8f8459..eddff9951e 100644 --- a/plugins/CoreHome/lang/tr.json +++ b/plugins/CoreHome/lang/tr.json @@ -31,7 +31,7 @@ "ShowJSCode": "Eklenecek JavaScript kodu göster", "SubscribeAndBecomePiwikSupporter": "Piwik Destekçisi olmak için güvenli bir kredi kartı ödeme sayfasına (Paypal) geçin!", "SupportPiwik": "Piwike destek ol", - "TableNoData": "Bu tablo için bir data yok.", + "TableNoData": "Bu tablo için bir veri yok.", "ThereIsNoDataForThisReport": "Bu rapor için veri yok.", "ViewAllPiwikVideoTutorials": "Tüm Piwik Yardım Videolarına Bak", "WebAnalyticsReports": "Web Analiz Raporları", diff --git a/plugins/CoreHome/stylesheets/coreHome.less b/plugins/CoreHome/stylesheets/coreHome.less index 9be7d14879..ab4b749b02 100644 --- a/plugins/CoreHome/stylesheets/coreHome.less +++ b/plugins/CoreHome/stylesheets/coreHome.less @@ -177,28 +177,24 @@ div.pk-emptyGraph { } .Piwik_Popover_Error_Title { - color: @theme-color-link; - font-weight: bold; font-size: 16px; } .Piwik_Popover_Error_Title span { - color: #222; font-weight: normal; font-size: 16px; } .Piwik_Popover_Error_Message { - color: #7e7363; padding: 20px 0 0 0; - font-size: 14px; + font-size: 13px; } a.Piwik_Popover_Error_Back { display: block; margin: 20px 0 0 0; - color: #1D3256; - font-size: 14px; + font-size: 13px; + text-decoration: underline; } #alert.ui-confirm input { diff --git a/plugins/CoreHome/stylesheets/layout.less b/plugins/CoreHome/stylesheets/layout.less index 5cffdf3be8..a398657c7c 100644 --- a/plugins/CoreHome/stylesheets/layout.less +++ b/plugins/CoreHome/stylesheets/layout.less @@ -30,7 +30,7 @@ height: 100%; transition: background-color 150ms linear; - &:hover { + &:hover, &:focus { background-color: @theme-color-menu-contrast-background; } @@ -59,7 +59,7 @@ .navbar { a { text-decoration: none; - &:hover, &:focus, &:active { + &:hover, &:focus { text-decoration: none; } } @@ -244,7 +244,7 @@ > .item { cursor: default; font-weight: bold; - &:hover { + &:hover, &:focus { text-decoration: none; } } @@ -257,7 +257,7 @@ padding: 11px 22px 11px 45px; decoration: none; transition: background-color 200ms linear; - &:hover { + &:hover, &:focus { text-decoration: none; color: @theme-color-menu-contrast-textActive; } @@ -300,7 +300,7 @@ font-size: 13px; font-weight: normal; - &:hover { + &:hover, &:focus { color: @theme-color-menu-contrast-textActive; } @@ -325,7 +325,7 @@ > .item { cursor: pointer; - &:hover { + &:hover, &:focus { color: @theme-color-menu-contrast-textActive; } } @@ -388,7 +388,7 @@ border: 1px solid @theme-color-background-tinyContrast; transition: box-shadow 150ms linear; &.expanded, - &:hover { + &:hover, &:focus { box-shadow: 0 1px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); } } diff --git a/plugins/CoreHome/templates/_dataTable.twig b/plugins/CoreHome/templates/_dataTable.twig index 6bc35bca9e..697af2c9b6 100644 --- a/plugins/CoreHome/templates/_dataTable.twig +++ b/plugins/CoreHome/templates/_dataTable.twig @@ -12,7 +12,7 @@ data-params="{% if clientSideParameters is empty %}{}{% else %}{{ clientSideParameters|json_encode }}{% endif %}"> <div class="reportDocumentation"> {% if properties.documentation|default is not empty %}<p>{{ properties.documentation|raw }}</p>{% endif %} - {% if reportLastUpdatedMessage is defined %}<span class='helpDate'>{{ reportLastUpdatedMessage }}</span>{% endif %} + {% if reportLastUpdatedMessage is defined %}<span class='helpDate'>{{ reportLastUpdatedMessage|raw }}</span>{% endif %} </div> <div class="dataTableWrapper"> {% if error is defined %} diff --git a/plugins/CoreHome/templates/_menu.twig b/plugins/CoreHome/templates/_menu.twig index 51b87bc301..94321a033f 100644 --- a/plugins/CoreHome/templates/_menu.twig +++ b/plugins/CoreHome/templates/_menu.twig @@ -1,10 +1,9 @@ - {% macro menu(menu, anchorlink, cssClass, currentModule, currentAction) %} <div id="secondNavBar" class="{{ cssClass }}"> <div id="search" ng-cloak> <div piwik-quick-access class="borderedControl"></div> </div> - <ul class="navbar"> + <ul class="navbar" role="menu"> {% for level1,level2 in menu %} {% set hasSubmenuItem = false %} @@ -15,7 +14,7 @@ {% endfor %} {% if hasSubmenuItem %} - <li class="menuTab"> + <li class="menuTab" role="menuitem"> <a class="item"> <span class="menu-icon {{ level2._icon|default('icon-arrow-right') }}"></span>{{ level1|translate }} @@ -23,12 +22,14 @@ {{ 'CoreHome_Menu'|translate }} </span> </a> - <ul> + <ul role="menu"> {% for name,urlParameters in level2 %} {% if name|slice(0,1) != '_' %} <li {% if urlParameters._url.module is defined and urlParameters._url.module == currentModule and urlParameters._url.action is defined and urlParameters._url.action == currentAction %}class="active"{% endif %} - > + role="menuitem" + > <a class="item" + title="{{ urlParameters._tooltip|default(name)|translate|e('html_attr') }}" href="index.php?{{ urlParameters._url|urlRewriteWithParameters|slice(1) }}"> {{ name|translate }} </a> diff --git a/plugins/CoreHome/templates/_siteSelectHeader.twig b/plugins/CoreHome/templates/_siteSelectHeader.twig index 7353e2e07a..beacabd849 100644 --- a/plugins/CoreHome/templates/_siteSelectHeader.twig +++ b/plugins/CoreHome/templates/_siteSelectHeader.twig @@ -1,3 +1,3 @@ <div class="top_bar_sites_selector piwikTopControl"> - <div piwik-siteselector class="sites_autocomplete"></div> + <div piwik-siteselector show-selected-site="true" class="sites_autocomplete"></div> </div>
\ No newline at end of file diff --git a/plugins/CoreHome/templates/_topBar.twig b/plugins/CoreHome/templates/_topBar.twig index e69e9c1978..c1158f46bb 100644 --- a/plugins/CoreHome/templates/_topBar.twig +++ b/plugins/CoreHome/templates/_topBar.twig @@ -1,5 +1,5 @@ {{ postEvent("Template.beforeTopBar", userAlias, userLogin, topMenu, userMenu) }} -<ul class="navbar-right"> +<ul role="menubar" class="navbar-right"> {% macro menuItemLabel(label, icon) %} {% if icon is defined and icon and icon starts with 'icon-' %} @@ -27,7 +27,7 @@ {% spaceless %} {% for label,menu in topMenu %} - <li>{{ _self.topMenuItem(label, menu, topMenuModule, topMenuAction) }}</li> + <li role="menuitem">{{ _self.topMenuItem(label, menu, topMenuModule, topMenuAction) }}</li> {% endfor %} {% endspaceless %} diff --git a/plugins/CoreHome/tests/Integration/Column/UserIdTest.php b/plugins/CoreHome/tests/Integration/Column/UserIdTest.php index 33c268afae..70715a4dc2 100644 --- a/plugins/CoreHome/tests/Integration/Column/UserIdTest.php +++ b/plugins/CoreHome/tests/Integration/Column/UserIdTest.php @@ -12,6 +12,7 @@ use Piwik\Access; use Piwik\Cache; use Piwik\DataAccess\ArchiveTableCreator; use Piwik\Db; +use Piwik\Metrics; use Piwik\Plugin\Manager; use Piwik\Plugins\CoreHome\Columns\UserId; use Piwik\Tests\Framework\Fixture; @@ -164,6 +165,17 @@ class UserIdTest extends IntegrationTestCase $this->assertDataTableHasUsers($this->getDataTableWithUsers()); } + public function test_hasDataTableUsers_shouldBeAbleToDetectIfNbUsersMetricIdIsused() + { + $table = $this->getDataTableWithZeroUsers(); + $table->renameColumn('nb_users', Metrics::INDEX_NB_USERS); + $this->assertNotDataTableHasUsers($table); + + $table = $this->getDataTableWithUsers(); + $table->renameColumn('nb_users', Metrics::INDEX_NB_USERS); + $this->assertDataTableHasUsers($this->getDataTableWithUsers()); + } + private function getDataTableWithoutUsersColumn() { $tableWithoutUsers = new DataTable(); diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index ea9a386ced..c9b4a48a7c 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -247,7 +247,7 @@ class Controller extends Plugin\ControllerAdmin $view->deactivateNonce = Nonce::getNonce(static::DEACTIVATE_NONCE); $view->pluginsInfo = $this->getPluginsInfo($themesOnly); - $users = \Piwik\Plugins\UsersManager\API::getInstance()->getUsers(); + $users = Request::processRequest('UsersManager.getUsers'); $view->otherUsersCount = count($users) - 1; $view->themeEnabled = \Piwik\Plugin\Manager::getInstance()->getThemeEnabled()->getPluginName(); diff --git a/plugins/CorePluginsAdmin/Marketplace.php b/plugins/CorePluginsAdmin/Marketplace.php index c4835856b0..b46d1932e4 100644 --- a/plugins/CorePluginsAdmin/Marketplace.php +++ b/plugins/CorePluginsAdmin/Marketplace.php @@ -114,14 +114,14 @@ class Marketplace $pluginsHavingUpdate = array(); } - foreach ($pluginsHavingUpdate as &$updatePlugin) { + foreach ($pluginsHavingUpdate as $key => $updatePlugin) { foreach ($loadedPlugins as $loadedPlugin) { if (!empty($updatePlugin['name']) && $loadedPlugin->getPluginName() == $updatePlugin['name'] ) { $updatePlugin['currentVersion'] = $loadedPlugin->getVersion(); $updatePlugin['isActivated'] = $pluginManager->isPluginActivated($updatePlugin['name']); - $updatePlugin = $this->addMissingRequirements($updatePlugin); + $pluginsHavingUpdate[$key] = $this->addMissingRequirements($updatePlugin); break; } } @@ -129,9 +129,7 @@ class Marketplace // remove plugins that have updates but for some reason are not loaded foreach ($pluginsHavingUpdate as $key => $updatePlugin) { - if (empty($updatePlugin['currentVersion']) - || empty($updatePlugin['isActivated']) - ) { + if (empty($updatePlugin['currentVersion'])) { unset($pluginsHavingUpdate[$key]); } } diff --git a/plugins/CorePluginsAdmin/lang/cs.json b/plugins/CorePluginsAdmin/lang/cs.json index fa4527c353..8ae77b996f 100644 --- a/plugins/CorePluginsAdmin/lang/cs.json +++ b/plugins/CorePluginsAdmin/lang/cs.json @@ -23,7 +23,7 @@ "Developer": "Vývojář", "DevelopersLearnHowToDevelopPlugins": "Vývojáři: naučte se, jak rozšířit Piwik vývojem %szásuvných modulů nebo témat vzhledu%s.", "DoMoreContactPiwikAdmins": "Pokud chcete nainstalovat nový zásuvný modul nebo šablonu,, kontaktujte vaše administrátory Piwiku.", - "EmailToEnquireUpdatedVersion": "Napište e-mail %1$s a požádejte o novější verzi %2$s.", + "EmailToEnquireUpdatedVersion": "Napište email %1$s a požádejte o novější verzi %2$s.", "FeaturedPlugin": "Doporučovaný zásuvný modul", "ChangeLookByManageThemes": "Vzhled Piwiku můžete změnit %sve správě motivů%s.", "GetEarlyAccessForPaidPlugins": "Poznámka: Momentálně jsou všechny zásuvné moduly v obchodě zdarma. V budoucnu máme v plánu povolit placené zásuvné moduly. %sKontaktujte nás%s pro včasný přístup.", diff --git a/plugins/CorePluginsAdmin/lang/id.json b/plugins/CorePluginsAdmin/lang/id.json index 100b8e195b..1e4ef37aaa 100644 --- a/plugins/CorePluginsAdmin/lang/id.json +++ b/plugins/CorePluginsAdmin/lang/id.json @@ -12,6 +12,7 @@ "PluginHomepage": "Alamat Pengaya", "PluginKeywords": "Kata Kunci", "PluginsManagement": "Pengatur Pengaya", + "PluginVersionInfo": "%1$s dari %2$s", "Status": "Status", "Version": "Versi", "Websites": "Situs" diff --git a/plugins/CorePluginsAdmin/lang/ja.json b/plugins/CorePluginsAdmin/lang/ja.json index 2da547a233..708b4a6560 100644 --- a/plugins/CorePluginsAdmin/lang/ja.json +++ b/plugins/CorePluginsAdmin/lang/ja.json @@ -38,12 +38,18 @@ "LastCommitTime": "(最終更新 %s)", "LastUpdated": "最終更新日", "LicenseHomepage": "ライセンスのホームページ", + "LikeThisPlugin": "このプラグインを気に入りましたか?", + "ConsiderDonating": "寄付を検討", + "CommunityContributedPlugin": "このプラグインは、コミュニティの貢献により無料で公開されています。", + "ConsiderDonatingCreatorOf": "%s の作成者への寄付を検討", "PluginsExtendPiwik": "プラグインは Piwik の機能性を拡張します。", "OncePluginIsInstalledYouMayActivateHere": "プラグインをインストールすると、ここで有効化と無効化を行うことができます。", "Marketplace": "マーケットプレイス", "MarketplaceSellPluginSubject": "マーケットプレイス - プラグインの販売", "MenuPlatform": "プラットフォーム", "MissingRequirementsNotice": "%1$s %3$s が必要です。 %1$s %2$s を最新バージョンにアップデートしてください。", + "MissingRequirementsPleaseInstallNotice": "%3$s で必要とされる、 %1$s %2$s をインストールしてください。", + "NewVersion": "新しいバージョン", "NoPluginsFound": "プラグインは見つかりませんでした", "NotAllowedToBrowseMarketplacePlugins": "Piwik プラットフォームのカスタマイズまたは拡張のために、インストール可能なプラグインのリストを閲覧することができます。もしこれらのいづれかのインストールが必要な場合は、管理者にお問い合わせください。", "NotAllowedToBrowseMarketplaceThemes": "Piwik プラットフォームの外観をカスタマイズするのにインストール可能な Piwik テーマのリストをご確認ください。これらのいづれかをインストールしたい場合は、管理者にお問い合わせください。", diff --git a/plugins/CorePluginsAdmin/lang/ko.json b/plugins/CorePluginsAdmin/lang/ko.json index 6949605d48..406ec44c78 100644 --- a/plugins/CorePluginsAdmin/lang/ko.json +++ b/plugins/CorePluginsAdmin/lang/ko.json @@ -1,19 +1,59 @@ { "CorePluginsAdmin": { + "ActionActivatePlugin": "플러그인 활성화", + "ActionActivateTheme": "테마 활성화", + "ActionInstall": "설치", + "ActionUninstall": "제거", "Activate": "활성화", "Activated": "활성화됨", "Active": "활성", + "Activity": "활동", + "AllowedUploadFormats": "zip 포멧을 통해 플러그인이나 테마를 업로드 할 수 있습니다.", "AuthorHomepage": "작성자 홈페이지", + "Authors": "작성자", + "BackToExtendPiwik": "마켓플레이스로 돌아가", + "BeCarefulUsingPlugins": "Piwik 팀으로부터 검증되지 않은 플러그인은 사용 시 주의해주세요. 해당 플러그인을 리뷰하지 않았습니다.", + "BeCarefulUsingThemes": "Piwik 팀으로부터 검증되지 않은 플러그인은 사용 시 주의해주세요. 해당 플러그인을 리뷰하지 않았습니다.", + "Changelog": "변", "Deactivate": "비활성화", + "Developer": "개발자", + "DoMoreContactPiwikAdmins": "새로운 플러그인이나 테마를 설치하기 위해서, Piwik 관리자에게 연락해주세요.", + "GetEarlyAccessForPaidPlugins": "참고사항: 모든 플러그인은 현재 무료로 사용할 수 있습니다. 향후 유료 마켓플레이스를 제공할 예정입니다 (자세한 사항은 %s연락 주시길%s 바랍니다.).", "Inactive": "비활성", + "InstallNewPlugins": "새로운 플러그인 설치", + "InstallNewThemes": "새로운 테마 설치", + "LastUpdated": "최근 업데이트", "LicenseHomepage": "라이센스 홈페이지", + "LikeThisPlugin": "플러그인 좋아요", + "ConsiderDonating": "기부 고려하기", "PluginsExtendPiwik": "플러그인 확장과 Piwik의 기능성 확장페이지입니다.", "OncePluginIsInstalledYouMayActivateHere": "플러그인이 한번 설치되면 이곳에서 활성화하거나 비활성화할 수 있습니다.", + "Marketplace": "마켓플레이스", + "MenuPlatform": "플랫폼", + "NewVersion": "새로운 버전", + "NoPluginsFound": "플러그인을 찾을 수 없습니다.", + "NoThemesFound": "테마를 찾을 수 없습니다.", + "NoZipFileSelected": "ZIP 파일을 선택해주세요.", + "OriginThirdParty": "서드파티", "PluginHomepage": "플러그인 홈페이지", "PluginKeywords": "검색어", "PluginsManagement": "플러그인 관리", + "PluginWebsite": "플러그인 웹사이트", + "Screenshots": "스크린샷", + "SortByAlpha": "알파버전", + "SortByNewest": "최신", + "SortByPopular": "인기순", "Status": "상태", + "StepDownloadingPluginFromMarketplace": "마켓플레이스에서 플러그인 다운로드 중", + "StepDownloadingThemeFromMarketplace": "마켓플레이스에서 테마 다운로드 중", + "StepUnzippingPlugin": "플러그인 압축 푸는 중", + "StepUnzippingTheme": "테마 압축 푸는 중", + "Theme": "테마", + "Themes": "테마", + "ThemesManagement": "테마 관리", + "UploadZipFile": "ZIP 파일 업로드", "Version": "버전", + "ViewRepositoryChangelog": "변경사항 보기", "Websites": "웹사이트" } }
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/lang/pt-br.json b/plugins/CorePluginsAdmin/lang/pt-br.json index a068f02cca..479cc31bb8 100644 --- a/plugins/CorePluginsAdmin/lang/pt-br.json +++ b/plugins/CorePluginsAdmin/lang/pt-br.json @@ -8,7 +8,7 @@ "Activated": "Ativado", "Active": "Ativo", "Activity": "Atividade", - "AllowedUploadFormats": "Você pode fazer upload de um plugin ou tema em formato zip através desta página.", + "AllowedUploadFormats": "Você pode carregar um plugin ou tema em formato zip através desta página.", "AuthorHomepage": "Página do autor", "Authors": "Autores", "BackToExtendPiwik": "Voltar ao Marketplace", @@ -89,8 +89,8 @@ "TeaserExtendPiwik": "Estenda Piwik com plugins e temas", "TeaserExtendPiwikByPlugin": "Estenda Piwik instalando um novo plugin", "TeaserExtendPiwikByTheme": "Aproveite um outro look & feel instalando um novo tema", - "TeaserExtendPiwikByUpload": "Estenda Piwik fazendo o upload de um arquivo ZIP", - "InstallingNewPluginViaMarketplaceOrUpload": "Você pode instalar automaticamente plugins do mercado ou %supload um plugin%s em formato .zip.", + "TeaserExtendPiwikByUpload": "Estenda o Piwik carregando um arquivo ZIP", + "InstallingNewPluginViaMarketplaceOrUpload": "Você pode instalar automaticamente plugins do mercado ou %scarregar um plugin%s em formato .zip.", "Theme": "Tema", "Themes": "Temas", "ThemesDescription": "Os temas podem alterar a aparência da interface do usuário do Piwik, e fornecer uma experiência completamente nova visual para desfrutar dos seus relatórios de análise.", @@ -98,7 +98,7 @@ "UninstallConfirm": "Você está prestes a desinstalar um plugin %s. O plugin será completamente removido da sua plataforma e isso não poderá ser desfeito. Tem certeza que deseja fazer isso?", "Updated": "Atualizado(a)", "UpdatingPlugin": "Atualizando %s", - "UploadZipFile": "Enviar arquivo ZIP", + "UploadZipFile": "Carregar arquivo ZIP", "Version": "Versão", "ViewRepositoryChangelog": "Ver as mudanças", "Websites": "Websites" diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig index d7e7c459b0..f0b66123e5 100644 --- a/plugins/CorePluginsAdmin/templates/macros.twig +++ b/plugins/CorePluginsAdmin/templates/macros.twig @@ -184,7 +184,7 @@ 'http://piwik.org', 'http://www.piwik.org', 'http://piwik.org/', 'http://www.piwik.org/' ] %} <span class="plugin-homepage"> - <a href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': ' '})|raw }})</a> + <a target="_blank" href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': ' '})|raw }})</a> </span> {% endif %} diff --git a/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php b/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php index dff84a7fee..bbfd7af081 100644 --- a/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php +++ b/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\CorePluginsAdmin\tests\Integration; use Piwik\Config; use Piwik\Option; use Piwik\Plugins\CorePluginsAdmin\UpdateCommunication; +use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; /** @@ -121,7 +122,8 @@ class UpdateCommunicationTest extends IntegrationTestCase public function test_sendNotificationIfUpdatesAvailable_ShouldSendCorrectText() { $subject = 'CoreUpdater_NotificationSubjectAvailablePluginUpdate'; - $message = 'ScheduledReports_EmailHello + $rootUrl = Fixture::getTestRootUrl(); + $message = "ScheduledReports_EmailHello CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate @@ -130,9 +132,9 @@ CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate * MyTest3 31.0.0 CoreUpdater_NotificationClickToUpdatePlugins -http://localhost/tests/PHPUnit/proxy/index.php?module=CorePluginsAdmin&action=plugins +{$rootUrl}index.php?module=CorePluginsAdmin&action=plugins -Installation_HappyAnalysing'; +Installation_HappyAnalysing"; $mock = $this->getCommunicationMockHavingManyUpdates(); diff --git a/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php index 156138d770..d43e11ebf4 100644 --- a/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php +++ b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\CoreUpdater\Test; use Piwik\Config; use Piwik\Option; use Piwik\Plugins\CoreUpdater\UpdateCommunication; +use Piwik\Tests\Framework\Fixture; use Piwik\UpdateCheck; use Piwik\Version; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; @@ -67,33 +68,35 @@ class UpdateCommunicationTest extends IntegrationTestCase public function test_sendNotifications_shouldSentCorrectEmail() { - $message = 'ScheduledReports_EmailHello + $rootUrl = Fixture::getTestRootUrl(); + $message = "ScheduledReports_EmailHello CoreUpdater_ThereIsNewVersionAvailableForUpdate CoreUpdater_YouCanUpgradeAutomaticallyOrDownloadPackage -http://localhost/tests/PHPUnit/proxy/index.php?module=CoreUpdater&action=newVersionAvailable +{$rootUrl}index.php?module=CoreUpdater&action=newVersionAvailable CoreUpdater_ViewVersionChangelog http://piwik.org/changelog/piwik-33-0-0/ CoreUpdater_FeedbackRequest -http://piwik.org/contact/'; +http://piwik.org/contact/"; $this->assertEmailForVersion('33.0.0', $message); } public function test_sendNotifications_shouldNotIncludeChangelogIfNotMajorVersionUpdate() { - $message = 'ScheduledReports_EmailHello + $rootUrl = Fixture::getTestRootUrl(); + $message = "ScheduledReports_EmailHello CoreUpdater_ThereIsNewVersionAvailableForUpdate CoreUpdater_YouCanUpgradeAutomaticallyOrDownloadPackage -http://localhost/tests/PHPUnit/proxy/index.php?module=CoreUpdater&action=newVersionAvailable +{$rootUrl}index.php?module=CoreUpdater&action=newVersionAvailable CoreUpdater_FeedbackRequest -http://piwik.org/contact/'; +http://piwik.org/contact/"; $this->assertEmailForVersion('33.0.0-b1', $message); } diff --git a/plugins/CoreUpdater/lang/cs.json b/plugins/CoreUpdater/lang/cs.json index 1aba35db9f..d77907655a 100644 --- a/plugins/CoreUpdater/lang/cs.json +++ b/plugins/CoreUpdater/lang/cs.json @@ -19,7 +19,7 @@ "ExceptionArchiveIncompatible": "Nekompatibilní archiv: %s", "ExceptionArchiveIncomplete": "Archiv je nekompletní: některé soubory chybí (např.: %s).", "FeedbackRequest": "Zde můžete s týmem Piwiku sdílet nápady a návrhy:", - "HelpMessageContent": "Zkontrolujte %1$s Piwik FAQ %2$s , kde jsou vysvětleny nejčastější chybi při aktualizaci. %3$s Požádejte vašeho systémového administrátora - může vám pomoct s chybou, která je nejčastěji způsobena nastavením serveru MySQL.", + "HelpMessageContent": "Zkontrolujte %1$s Piwik FAQ %2$s , kde jsou vysvětleny nejčastější chybi při aktualizaci. %3$s Požádejte svéo systémového administrátora - může vám pomoci s chybou, která je nejčastěji způsobena nastavením serveru MySQL.", "HelpMessageIntroductionWhenError": "Níže je hlavní chybová hláška. Pomůže vám zjistit příčinu, ale pokud budete potřebovat další pomoc:", "HelpMessageIntroductionWhenWarning": "Aktualizace proběhla v pořádku, ale byly zaznamenány problémy. Prosím přečtěte si jejich popis výše. Pro další pomoc:", "HighTrafficPiwikServerEnableMaintenance": "Pokud provozujete Piwik server s velkým provozem, doporučujeme %sna chvíli zakázat sledování návštěvníků a přepnout uživatelské rozhraní do režimu údržby%s.", diff --git a/plugins/CoreUpdater/lang/fr.json b/plugins/CoreUpdater/lang/fr.json index 5b170cfe55..4a4e84f983 100644 --- a/plugins/CoreUpdater/lang/fr.json +++ b/plugins/CoreUpdater/lang/fr.json @@ -22,7 +22,7 @@ "HelpMessageContent": "Allez voir la %1$s FAQ Piwik %2$s qui explique les erreurs courantes lors de la mise à jour. %3$s Demandez à votre administrateur système - il pourra être à même avec cette erreur qui est très probablement liée à la configuration de votre serveur MySQL.", "HelpMessageIntroductionWhenError": "Ci-dessus est le message d'erreur du noyau. Cela devrait expliquer la cause, mais si vous nécessitez davantage d'aide merci de :", "HelpMessageIntroductionWhenWarning": "La mise à jour a été complétée avec succès. Cependant, des problèmes sont survenus. Lisez la description ci-dessus pour plus de détails. Pour davantage d'aide :", - "HighTrafficPiwikServerEnableMaintenance": "Si vous gérez un serveur Piwik à haut traffic, nous recommandons de %sdésactiver momentanément le suivit des visiteurs et de mettre l'interface utilisateur de Piwik en mode de maintenance%s.", + "HighTrafficPiwikServerEnableMaintenance": "Si vous gérez un serveur Piwik à haut traffic, nous recommandons de %sdésactiver momentanément le suivi des visiteurs et de mettre l'interface utilisateur de Piwik en mode de maintenance%s.", "IncompatbilePluginsWillBeDisabledInfo": "Note : certains plugins ne sont pas compatbles avec Piwik %s. Ils seront désactivés lorsque vous mettrez à jour :", "InstallingTheLatestVersion": "Installation de la dernière version", "LatestBetaRelease": "Dernière version beta", diff --git a/plugins/CoreUpdater/lang/ja.json b/plugins/CoreUpdater/lang/ja.json index c7f307ea0c..25e1fb5caf 100644 --- a/plugins/CoreUpdater/lang/ja.json +++ b/plugins/CoreUpdater/lang/ja.json @@ -25,6 +25,11 @@ "HighTrafficPiwikServerEnableMaintenance": "高トラフィックの Piwik サーバーを管理する場合、 %smomentarily disable visitor Tracking and put the Piwik User Interface in maintenance mode%s をお勧めします。", "IncompatbilePluginsWillBeDisabledInfo": "注 : 一部のプラグインは、Piwik %s に互換性がありません。アップグレード時に、これらのプラグインは無効になります。", "InstallingTheLatestVersion": "最新バージョンをインストール中", + "LatestBetaRelease": "最新のベータ版 リリース", + "LatestStableRelease": "最新の安定版リリース", + "Latest2XStableRelease": "最新の安定版 2.X", + "Latest2XBetaRelease": "最新のベータ版 2.X", + "LtsSupportVersion": "長期サポート版", "MajorUpdateWarning1": "これは最新版へのアップデートです!通常よりも時間がかかります", "MajorUpdateWarning2": "以下のアドバイスは、特に大規模なインストールの際、重要です。", "NoteForLargePiwikInstances": "Piwik のデータベースが大きい場合の重要注意", @@ -48,11 +53,15 @@ "UpdateHasBeenCancelledExplanation": "Piwik ワンクリックアップデートがキャンセルされました。 あなたが上記のエラーメッセージを修正できない場合は、Piwik を手作業でアップデートすることをお勧めします。%1$s アップデートを始めるには、%2$sアップデートのドキュメント%3$sを参照してください!", "UpdateTitle": "アップデート", "UpdateSuccessTitle": "Piwik は正常にアップグレードされました!", + "UpdateErrorTitle": "アップデートエラー", + "ThankYouUpdatePiwik": "Piwik を利用し、最新の状態を維持して頂きありがとうございます!", + "PostUpdateMessage": "Piwik は、常に無料でダウンロードして自由に使用できますが、成長し、改善するためには皆様のご支援が必要です。", "PostUpdateSupport": "Piwik の使い方で助けが必要な場合は、そのクリエイターからサポートを得ることができます:", "EnterpriseSolutions": "エンタープライズソリューション", "CloudHosting": "クラウドホスティング", "Updating": "アップデート", "UpdateUsingHttpsFailed": "以下のエラーにより、セキュリティで保護された HTTPS 接続で Piwik の最新バージョンをダウンロードできませんでした。", + "UpdateUsingHttpsFailedHelp": "Piwik の最新バージョンのダウンロードが ( セキュリティで保護された HTTPS 接続で ) 失敗する様々な理由として、たとえばネットワーク エラーや低速なネットワーク速度または間違ったシステム構成などがあります。 それはまた、あなたのサーバが MITM 攻撃の標的であり、誰かが Piwik の悪質なバージョンに置き換えて更新しようとしている可能性があることに注意してください。", "UpdateUsingHttpsFailedHelpWhatToDo": "MITM 攻撃を防ぐために、セキュリティで保護された HTTPS 接続を使用してダウンロードを再試行することをお勧めします。", "UsingHttps": "(推奨) セキュリティで保護された HTTPS 接続を使用してください", "UsingHttp": "セキュリティ保護されていない HTTP 接続の使用", diff --git a/plugins/CoreUpdater/lang/ko.json b/plugins/CoreUpdater/lang/ko.json index b54a774bb7..1131e70c6d 100644 --- a/plugins/CoreUpdater/lang/ko.json +++ b/plugins/CoreUpdater/lang/ko.json @@ -21,6 +21,11 @@ "HelpMessageIntroductionWhenError": "위와 같은 경우는 코어 에러 메세지입니다. 원인을 설명할 수 있으나 도움이 필요하다면:", "HelpMessageIntroductionWhenWarning": "업데이트는 성공적으로 완료되었습니다. 하지만 진행중에 문제가 있었습니다. 자세한 사항은 위의 설명을 읽어주세요. 더 많은 도움은:", "InstallingTheLatestVersion": "최근 버전 설치중", + "LatestBetaRelease": "최근 릴리즈 된 베타 버전", + "LatestStableRelease": "최근 릴리즈 된 안정 버전", + "Latest2XStableRelease": "최근 안정 2.X 버전", + "Latest2XBetaRelease": "최근 베타 2.X 버전", + "LtsSupportVersion": "Long Term Support 버전", "MajorUpdateWarning1": "중요한 업데이트입니다! 평소보다 더 오래 걸릴지도 모릅니다.", "MajorUpdateWarning2": "다음은 무거운 설치시 알아야하는 중요한 내용입니다.", "NoteForLargePiwikInstances": "용량이 큰 Piwik 설치시 주의해야 할 점", diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php index 1a41a07df3..5acf9b50be 100644 --- a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php +++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\DataTable; +use Piwik\Metrics; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\View; @@ -40,11 +41,13 @@ class AllColumns extends HtmlTable $this->dataTable->filter(function (DataTable $dataTable) use ($properties) { $columnsToDisplay = array('label', 'nb_visits'); - if (in_array('nb_uniq_visitors', $dataTable->getColumns())) { + $columns = $dataTable->getColumns(); + + if (in_array('nb_uniq_visitors', $columns)) { $columnsToDisplay[] = 'nb_uniq_visitors'; } - if (in_array('nb_users', $dataTable->getColumns())) { + if (in_array('nb_users', $columns)) { $columnsToDisplay[] = 'nb_users'; } diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts -Subproject 6c05ca43d7c83dd795eb713b9feb624d8d6d2eb +Subproject 013821049089ae8d16f67e0a1b6c6525cb88a5a diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions new file mode 160000 +Subproject 5027b250120c8ecccb25d82e50fe99351412298 diff --git a/plugins/CustomVariables/API.php b/plugins/CustomVariables/API.php index fa3a99dbca..69e6ee720c 100644 --- a/plugins/CustomVariables/API.php +++ b/plugins/CustomVariables/API.php @@ -8,10 +8,13 @@ */ namespace Piwik\Plugins\CustomVariables; +use Piwik\API\Request; use Piwik\Archive; +use Piwik\Container\StaticContainer; use Piwik\DataTable; use Piwik\Date; use Piwik\Metrics; +use Piwik\Piwik; use Piwik\Plugins\Actions\Actions\ActionSiteSearch; /** @@ -71,6 +74,7 @@ class API extends \Piwik\Plugin\API } } + if ($flat) { $dataTable->filterSubtables('Piwik\Plugins\CustomVariables\DataTable\Filter\CustomVariablesValuesFromNameId'); } else { @@ -109,9 +113,64 @@ class API extends \Piwik\Plugin\API // Hack Ecommerce product price tracking to display correctly $dataTable->renameColumn('price_viewed', 'price'); } + $dataTable->filter('Piwik\Plugins\CustomVariables\DataTable\Filter\CustomVariablesValuesFromNameId'); return $dataTable; } + + /** + * Get a list of all available custom variable slots (scope + index) and which names have been used so far in + * each slot since the beginning of the website. + * + * @param int $idSite + * @return array + */ + public function getUsagesOfSlots($idSite) + { + Piwik::checkUserHasAdminAccess($idSite); + + $numVars = CustomVariables::getNumUsableCustomVariables(); + + $usedCustomVariables = array( + 'visit' => array_fill(1, $numVars, array()), + 'page' => array_fill(1, $numVars, array()), + ); + + /** @var DataTable $customVarUsages */ + $today = StaticContainer::get('CustomVariables.today'); + $date = '2008-12-12,' . $today; + $customVarUsages = Request::processRequest('CustomVariables.getCustomVariables', + array('idSite' => $idSite, 'period' => 'range', 'date' => $date, + 'format' => 'original') + ); + + foreach ($customVarUsages->getRows() as $row) { + $slots = $row->getMetadata('slots'); + + if (!empty($slots)) { + foreach ($slots as $slot) { + $usedCustomVariables[$slot['scope']][$slot['index']][] = array( + 'name' => $row->getColumn('label'), + 'nb_visits' => $row->getColumn('nb_visits'), + 'nb_actions' => $row->getColumn('nb_actions'), + ); + } + } + } + + $grouped = array(); + foreach ($usedCustomVariables as $scope => $scopes) { + foreach ($scopes as $index => $cvars) { + $grouped[] = array( + 'scope' => $scope, + 'index' => $index, + 'usages' => $cvars + ); + } + } + + return $grouped; + } } diff --git a/plugins/CustomVariables/Archiver.php b/plugins/CustomVariables/Archiver.php index bc43cbc8dd..39aa9e6bb7 100644 --- a/plugins/CustomVariables/Archiver.php +++ b/plugins/CustomVariables/Archiver.php @@ -8,10 +8,10 @@ */ namespace Piwik\Plugins\CustomVariables; -use Piwik\Common; use Piwik\Config; use Piwik\DataAccess\LogAggregator; use Piwik\DataArray; +use Piwik\DataTable; use Piwik\Metrics; use Piwik\Tracker\GoalManager; use Piwik\Tracker; @@ -35,6 +35,9 @@ class Archiver extends \Piwik\Plugin\Archiver protected $maximumRowsInSubDataTable; protected $newEmptyRow; + private $metadata = array(); + private $metadataFlat = array(); + function __construct($processor) { parent::__construct($processor); @@ -50,7 +53,7 @@ class Archiver extends \Piwik\Plugin\Archiver public function aggregateMultipleReports() { - $columnsAggregationOperation = null; + $columnsAggregationOperation = array('slots' => 'uniquearraymerge'); $this->getProcessor()->aggregateDataTableRecords( self::CUSTOM_VARIABLE_RECORD_NAME, @@ -74,6 +77,16 @@ class Archiver extends \Piwik\Plugin\Archiver $this->removeVisitsMetricsFromActionsAggregate(); $this->dataArray->enrichMetricsWithConversions(); $table = $this->dataArray->asDataTable(); + + foreach ($table->getRows() as $row) { + $label = $row->getColumn('label'); + if (!empty($this->metadata[$label])) { + foreach ($this->metadata[$label] as $name => $value) { + $row->addMetadata($name, $value); + } + } + } + $blob = $table->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $columnToSort = Metrics::INDEX_NB_VISITS @@ -118,6 +131,8 @@ class Archiver extends \Piwik\Plugin\Archiver $key = $row[$keyField]; $value = $this->cleanCustomVarValue($row[$valueField]); + $this->addMetadata($keyField, $key, Model::SCOPE_VISIT); + $this->dataArray->sumMetricsVisits($key, $row); $this->dataArray->sumMetricsVisitsPivot($key, $value, $row); } @@ -137,6 +152,8 @@ class Archiver extends \Piwik\Plugin\Archiver $key = $row[$keyField]; $value = $this->cleanCustomVarValue($row[$valueField]); + $this->addMetadata($keyField, $key, Model::SCOPE_PAGE); + $alreadyAggregated = $this->aggregateEcommerceCategories($key, $value, $row); if (!$alreadyAggregated) { $this->aggregateActionByKeyAndValue($key, $value, $row); @@ -145,6 +162,22 @@ class Archiver extends \Piwik\Plugin\Archiver } } + private function addMetadata($keyField, $label, $scope) + { + $index = (int) str_replace('custom_var_k', '', $keyField); + + if (!array_key_exists($label, $this->metadata)) { + $this->metadata[$label] = array('slots' => array()); + } + + $uniqueId = $label . 'scope' . $scope . 'index' . $index; + + if (!isset($this->metadataFlat[$uniqueId])) { + $this->metadata[$label]['slots'][] = array('scope' => $scope, 'index' => $index); + $this->metadataFlat[$uniqueId] = true; + } + } + /** * @param string $key * @param string $value @@ -205,6 +238,7 @@ class Archiver extends \Piwik\Plugin\Archiver } while ($row = $query->fetch()) { $key = $row[$keyField]; + $value = $this->cleanCustomVarValue($row[$valueField]); $this->dataArray->sumMetricsGoals($key, $row); $this->dataArray->sumMetricsGoalsPivot($key, $value, $row); diff --git a/plugins/CustomVariables/Columns/Base.php b/plugins/CustomVariables/Columns/Base.php index 1818ca3145..4c6a4ea542 100644 --- a/plugins/CustomVariables/Columns/Base.php +++ b/plugins/CustomVariables/Columns/Base.php @@ -16,7 +16,7 @@ use Piwik\Plugins\CustomVariables\CustomVariables; class Base extends VisitDimension { - protected function configureSegmentsFor($fieldPrefix, $segmentNameSuffix) + protected function configureSegmentsFor($segmentNameSuffix) { $numCustomVariables = CustomVariables::getNumUsableCustomVariables(); @@ -25,10 +25,7 @@ class Base extends VisitDimension $segment->setSegment('customVariable' . $segmentNameSuffix); $segment->setName($this->getName() . ' (' . Piwik::translate('CustomVariables_ScopeVisit') . ')'); $segment->setCategory('CustomVariables_CustomVariables'); - $segment->setSqlSegment($this->getSegmentColumns('log_visit.' . $fieldPrefix, $numCustomVariables)); - $segment->setSuggestedValuesCallback(function ($idSite, $ignore, DataTable $table) use ($segmentNameSuffix) { - return $table->getColumnsStartingWith('customVariable' . $segmentNameSuffix); - }); + $segment->setUnionOfSegments($this->getSegmentColumns('customVariable' . $segmentNameSuffix, $numCustomVariables)); $this->addSegment($segment); $segment = new Segment(); @@ -36,10 +33,7 @@ class Base extends VisitDimension $segment->setSegment('customVariablePage' . $segmentNameSuffix); $segment->setName($this->getName() . ' (' . Piwik::translate('CustomVariables_ScopePage') . ')'); $segment->setCategory('CustomVariables_CustomVariables'); - $segment->setSqlSegment($this->getSegmentColumns('log_link_visit_action.' . $fieldPrefix, $numCustomVariables)); - $segment->setSuggestedValuesCallback(function ($idSite, $ignore, DataTable $table) use ($segmentNameSuffix) { - return $table->getColumnsStartingWith('customVariablePage' . $segmentNameSuffix); - }); + $segment->setUnionOfSegments($this->getSegmentColumns('customVariablePage' . $segmentNameSuffix, $numCustomVariables)); $this->addSegment($segment); } diff --git a/plugins/CustomVariables/Columns/CustomVariableName.php b/plugins/CustomVariables/Columns/CustomVariableName.php index 85aa46436f..b42f2e8ae7 100644 --- a/plugins/CustomVariables/Columns/CustomVariableName.php +++ b/plugins/CustomVariables/Columns/CustomVariableName.php @@ -14,7 +14,7 @@ class CustomVariableName extends Base { protected function configureSegments() { - $this->configureSegmentsFor('custom_var_k', 'Name'); + $this->configureSegmentsFor('Name'); } public function getName() diff --git a/plugins/CustomVariables/Columns/CustomVariableValue.php b/plugins/CustomVariables/Columns/CustomVariableValue.php index 03046758cf..a565a8136c 100644 --- a/plugins/CustomVariables/Columns/CustomVariableValue.php +++ b/plugins/CustomVariables/Columns/CustomVariableValue.php @@ -14,7 +14,7 @@ class CustomVariableValue extends Base { protected function configureSegments() { - $this->configureSegmentsFor('custom_var_v', 'Value'); + $this->configureSegmentsFor('Value'); } public function getName() diff --git a/plugins/CustomVariables/Controller.php b/plugins/CustomVariables/Controller.php index 59a4a949de..e73e58b257 100644 --- a/plugins/CustomVariables/Controller.php +++ b/plugins/CustomVariables/Controller.php @@ -8,7 +8,20 @@ */ namespace Piwik\Plugins\CustomVariables; +use Piwik\Common; +use Piwik\DataTable; +use Piwik\Piwik; + class Controller extends \Piwik\Plugin\Controller { + public function manage() + { + $idSite = Common::getRequestVar('idSite'); + + Piwik::checkUserHasAdminAccess($idSite); + + return $this->renderTemplate('manage', array()); + } + } diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php index b8a57ad6a6..9059482c33 100644 --- a/plugins/CustomVariables/CustomVariables.php +++ b/plugins/CustomVariables/CustomVariables.php @@ -22,7 +22,10 @@ class CustomVariables extends \Piwik\Plugin { return array( 'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata', - 'Live.getAllVisitorDetails' => 'extendVisitorDetails' + 'Live.getAllVisitorDetails' => 'extendVisitorDetails', + 'AssetManager.getJavaScriptFiles' => 'getJsFiles', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', + 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', ); } @@ -144,4 +147,35 @@ class CustomVariables extends \Piwik\Plugin } } + public function getClientSideTranslationKeys(&$translationKeys) + { + $translationKeys[] = 'CustomVariables_CustomVariables'; + $translationKeys[] = 'CustomVariables_ManageDescription'; + $translationKeys[] = 'CustomVariables_ScopeX'; + $translationKeys[] = 'CustomVariables_Index'; + $translationKeys[] = 'CustomVariables_Usages'; + $translationKeys[] = 'CustomVariables_Unused'; + $translationKeys[] = 'CustomVariables_CreateNewSlot'; + $translationKeys[] = 'CustomVariables_UsageDetails'; + $translationKeys[] = 'CustomVariables_CurrentAvailableCustomVariables'; + $translationKeys[] = 'CustomVariables_ToCreateCustomVarExecute'; + $translationKeys[] = 'CustomVariables_CreatingCustomVariableTakesTime'; + $translationKeys[] = 'CustomVariables_SlotsReportIsGeneratedOverTime'; + $translationKeys[] = 'General_Loading'; + $translationKeys[] = 'General_TrackingScopeVisit'; + $translationKeys[] = 'General_TrackingScopePage'; + } + + public function getStylesheetFiles(&$stylesheets) + { + $stylesheets[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less"; + } + + public function getJsFiles(&$jsFiles) + { + $jsFiles[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js"; + $jsFiles[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js"; + $jsFiles[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js"; + } + } diff --git a/plugins/CustomVariables/Menu.php b/plugins/CustomVariables/Menu.php new file mode 100644 index 0000000000..b97aa1bb01 --- /dev/null +++ b/plugins/CustomVariables/Menu.php @@ -0,0 +1,33 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomVariables; + +use Piwik\Common; +use Piwik\Menu\MenuUser; +use Piwik\Piwik; +use Piwik\Plugins\UsersManager\UserPreferences; + +/** + * This class allows you to add, remove or rename menu items. + * To configure a menu (such as Admin Menu, Reporting Menu, User Menu...) simply call the corresponding methods as + * described in the API-Reference http://developer.piwik.org/api-reference/Piwik/Menu/MenuAbstract + */ +class Menu extends \Piwik\Plugin\Menu +{ + public function configureUserMenu(MenuUser $menu) + { + $userPreferences = new UserPreferences(); + $default = $userPreferences->getDefaultWebsiteId(); + $idSite = Common::getRequestVar('idSite', $default, 'int'); + + if (Piwik::isUserHasAdminAccess($idSite)) { + $menu->addManageItem('Custom Variables', $this->urlForAction('manage'), $orderId = 15); + } + } +} diff --git a/plugins/CustomVariables/Model.php b/plugins/CustomVariables/Model.php index 9d98a352fa..235809e1da 100644 --- a/plugins/CustomVariables/Model.php +++ b/plugins/CustomVariables/Model.php @@ -15,12 +15,14 @@ use Piwik\Log; class Model { - const SCOPE_PAGE = 'log_link_visit_action'; - const SCOPE_VISIT = 'log_visit'; - const SCOPE_CONVERSION = 'log_conversion'; const DEFAULT_CUSTOM_VAR_COUNT = 5; + const SCOPE_PAGE = 'page'; + const SCOPE_VISIT = 'visit'; + const SCOPE_CONVERSION = 'conversion'; + private $scope = null; + private $table = null; public function __construct($scope) { @@ -29,21 +31,27 @@ class Model } $this->scope = $scope; + $this->table = Common::prefixTable($this->getTableNameFromScope($scope)); } - public function getScopeName() + private function getTableNameFromScope($scope) { // actually we should have a class for each scope but don't want to overengineer it for now - switch ($this->scope) { + switch ($scope) { case self::SCOPE_PAGE: - return 'Page'; + return 'log_link_visit_action'; case self::SCOPE_VISIT: - return 'Visit'; + return 'log_visit'; case self::SCOPE_CONVERSION: - return 'Conversion'; + return 'log_conversion'; } } + public function getScopeName() + { + return ucfirst($this->scope); + } + /** * @see getHighestCustomVarIndex() * @return int @@ -96,8 +104,7 @@ class Model private function getCustomVarColumnNames() { - $dbTable = $this->getDbTableName(); - $columns = Db::getColumnNamesFromTable($dbTable); + $columns = Db::getColumnNamesFromTable($this->table); $customVarColumns = array_filter($columns, function ($column) { return false !== strpos($column, 'custom_var_'); @@ -108,14 +115,13 @@ class Model public function removeCustomVariable() { - $dbTable = $this->getDbTableName(); - $index = $this->getHighestCustomVarIndex(); + $index = $this->getHighestCustomVarIndex(); if ($index < 1) { return null; } - Db::exec(sprintf('ALTER TABLE %s ', $dbTable) + Db::exec(sprintf('ALTER TABLE %s ', $this->table) . sprintf('DROP COLUMN custom_var_k%d,', $index) . sprintf('DROP COLUMN custom_var_v%d;', $index)); @@ -124,22 +130,16 @@ class Model public function addCustomVariable() { - $dbTable = $this->getDbTableName(); - $index = $this->getHighestCustomVarIndex() + 1; - $maxLen = CustomVariables::getMaxLengthCustomVariables(); + $index = $this->getHighestCustomVarIndex() + 1; + $maxLen = CustomVariables::getMaxLengthCustomVariables(); - Db::exec(sprintf('ALTER TABLE %s ', $dbTable) + Db::exec(sprintf('ALTER TABLE %s ', $this->table) . sprintf('ADD COLUMN custom_var_k%d VARCHAR(%d) DEFAULT NULL,', $index, $maxLen) . sprintf('ADD COLUMN custom_var_v%d VARCHAR(%d) DEFAULT NULL;', $index, $maxLen)); return $index; } - private function getDbTableName() - { - return Common::prefixTable($this->scope); - } - public static function getCustomVariableIndexFromFieldName($fieldName) { $onlyNumber = str_replace(array('custom_var_k', 'custom_var_v'), '', $fieldName); diff --git a/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php b/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php index 44e6d59350..2b068ba03e 100644 --- a/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php +++ b/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php @@ -9,6 +9,8 @@ namespace Piwik\Plugins\CustomVariables\Tracker; use Piwik\Common; +use Piwik\Plugins\CustomVariables\Model; +use Piwik\Tracker\Action; use Piwik\Tracker\Request; use Piwik\Tracker\RequestProcessor; use Piwik\Tracker\Visit\VisitProperties; @@ -36,7 +38,7 @@ class CustomVariablesRequestProcessor extends RequestProcessor public function processRequestParams(VisitProperties $visitProperties, Request $request) { // TODO: re-add optimization where if custom variables exist in request, don't bother selecting them in Visitor - $visitorCustomVariables = $request->getCustomVariables($scope = 'visit'); + $visitorCustomVariables = $request->getCustomVariables($scope = Model::SCOPE_VISIT); if (!empty($visitorCustomVariables)) { Common::printDebug("Visit level Custom Variables: "); Common::printDebug($visitorCustomVariables); @@ -62,4 +64,25 @@ class CustomVariablesRequestProcessor extends RequestProcessor $valuesToUpdate = array_merge($valuesToUpdate, $visitCustomVariables); } } + + public function afterRequestProcessed(VisitProperties $visitProperties, Request $request) + { + $action = $request->getMetadata('Actions', 'action'); + + if (empty($action) || !($action instanceof Action)) { + return; + } + + $customVariables = $action->getCustomVariables(); + + if (!empty($customVariables)) { + Common::printDebug("Page level Custom Variables: "); + Common::printDebug($customVariables); + + foreach ($customVariables as $field => $value) { + $action->setCustomField($field, $value); + } + } + + } } diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js new file mode 100644 index 0000000000..f8098eb114 --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js @@ -0,0 +1,22 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').controller('ManageCustomVarsController', ManageCustomVarsController); + + ManageCustomVarsController.$inject = ['manageCustomVarsModel', 'piwik', '$filter']; + + function ManageCustomVarsController(manageCustomVarsModel, piwik, $filter) { + manageCustomVarsModel.fetchUsages(); + + this.model = manageCustomVarsModel; + this.siteName = piwik.siteName; + this.scopes = [ + {value: 'visit', name: _pk_translate('General_TrackingScopeVisit')}, + {value: 'page', name: _pk_translate('General_TrackingScopePage')} + ]; + } +})();
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html new file mode 100644 index 0000000000..6ab9ea6ba6 --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html @@ -0,0 +1,54 @@ +<div class="manageCustomVars"> + <h2 piwik-enriched-headline help-url="http://piwik.org/docs/custom-variables/">{{ 'CustomVariables_CustomVariables'|translate }}</h2> + + <p> + <span ng-bind-html="'CustomVariables_ManageDescription'|translate:manageCustomVars.siteName"></span> + </p> + + <div class="alert alert-info" ng-show="!manageCustomVars.model.isLoading && manageCustomVars.model.hasCustomVariablesInGeneral && !manageCustomVars.model.hasAtLeastOneUsage"> + {{ 'CustomVariables_SlotsReportIsGeneratedOverTime'|translate }} + </div> + + <div ng-repeat="scope in manageCustomVars.scopes"> + <h2 class="secondary">{{ 'CustomVariables_ScopeX'|translate:(scope.name) }}</h2> + <table class="dataTable entityTable"> + <thead> + <tr> + <th>{{'CustomVariables_Index'|translate }}</th> + <th>{{'CustomVariables_Usages'|translate }}</th> + </tr> + </thead> + <tbody> + <tr> + <td colspan="3" ng-show="manageCustomVars.model.isLoading">{{ 'General_Loading'|translate }}</td> + </tr> + <tr ng-repeat="customVariables in manageCustomVars.model.customVariables|filter:{scope: scope.value}"> + <td class="index">{{ customVariables.index }}</td> + <td> + <span ng-show="(customVariables.usages|length) === 0" + class="unused">{{'CustomVariables_Unused'|translate }}</span> + <span ng-show="customVariables.usages|length" ng-repeat="cvar in customVariables.usages|orderBy:'-nb_actions'"> + <span title="{{ 'CustomVariables_UsageDetails'|translate:(cvar.nb_visits ? cvar.nb_visits : 0):(cvar.nb_actions ? cvar.nb_actions : 0) }}">{{ cvar.name }}</span><span ng-show="!$last">, </span> + </span> + </td> + </tr> + </tbody> + </table> + </div> + + <h2 class="secondary" ng-show="!manageCustomVars.model.isLoading">{{ 'CustomVariables_CreateNewSlot'|translate }}</h2> + + <p ng-show="!manageCustomVars.model.isLoading"> + {{ 'CustomVariables_CreatingCustomVariableTakesTime'|translate }} + <br /><br /> + <span ng-bind-html="'CustomVariables_CurrentAvailableCustomVariables'|translate:('<strong>'+manageCustomVars.model.numSlotsAvailable+'</strong>')"></span> + <br /> + <br /> + {{ 'CustomVariables_ToCreateCustomVarExecute'|translate }} + <br /> + <br /> + <code>./console customvariables:set-max-custom-variables {{ manageCustomVars.model.numSlotsAvailable + 1 }}</code> + </p> + + +</div>
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js new file mode 100644 index 0000000000..1623d50b27 --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js @@ -0,0 +1,26 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Usage: + * <div piwik-manage-custom-vars> + */ +(function () { + angular.module('piwikApp').directive('piwikManageCustomVars', piwikManageCustomVars); + + piwikManageCustomVars.$inject = ['piwik']; + + function piwikManageCustomVars(piwik){ + return { + restrict: 'A', + scope: {}, + templateUrl: 'plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html?cb=' + piwik.cacheBuster, + controller: 'ManageCustomVarsController', + controllerAs: 'manageCustomVars' + }; + } +})();
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less new file mode 100644 index 0000000000..b666abebca --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less @@ -0,0 +1,18 @@ +.manageCustomVars { + .unused { + color: @color-silver; + } + + table, p { + width: 900px; + } + + .alert-info { + margin-top: 15px; + } + + .scope, .index { + width: 90px; + max-width: 90px; + } +}
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js new file mode 100644 index 0000000000..ceb6442a8f --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js @@ -0,0 +1,59 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').factory('manageCustomVarsModel', manageCustomVarsModel); + + manageCustomVarsModel.$inject = ['piwikApi']; + + function manageCustomVarsModel(piwikApi) { + + var model = { + customVariables : [], + extractions : [], + isLoading: false, + fetchUsages: fetchUsages, + hasCustomVariablesInGeneral: false, + hasAtLeastOneUsage: false, + numSlotsAvailable: 5, + }; + + return model; + + function fetchCustomVariables() { + return piwikApi.fetch({method: 'CustomVariables.getCustomVariables', period: 'year', date: 'today', filter_limit: 1}) + .then(function (customVariables) { + model.hasCustomVariablesInGeneral = (customVariables && customVariables.length > 0); + }); + } + + function fetchUsages() { + + model.isLoading = true; + + fetchCustomVariables().then(function () { + return piwikApi.fetch({method: 'CustomVariables.getUsagesOfSlots'}); + + }).then(function (customVariables) { + model.customVariables = customVariables; + + angular.forEach(customVariables, function (customVar) { + if (customVar.index > model.numSlotsAvailable) { + model.numSlotsAvailable = customVar.index; + } + + if (customVar.usages && customVar.usages.length > 0) { + model.hasAtLeastOneUsage = true; + } + }); + + })['finally'](function () { // .finally() is not IE8 compatible see https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c + model.isLoading = false; + }); + } + + } +})();
\ No newline at end of file diff --git a/plugins/CustomVariables/config/config.php b/plugins/CustomVariables/config/config.php index 82a0058e02..cba8dd0f59 100644 --- a/plugins/CustomVariables/config/config.php +++ b/plugins/CustomVariables/config/config.php @@ -1,9 +1,6 @@ <?php return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\CustomVariables\Tracker\CustomVariablesRequestProcessor'), - )), - + // in tests we do not use 'today' to make tests results deterministic + 'CustomVariables.today' => 'today', ); diff --git a/plugins/CustomVariables/config/test.php b/plugins/CustomVariables/config/test.php new file mode 100644 index 0000000000..d1ef28244a --- /dev/null +++ b/plugins/CustomVariables/config/test.php @@ -0,0 +1,4 @@ +<?php +return array( + 'CustomVariables.today' => '2015-01-01' +);
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/cs.json b/plugins/CustomVariables/lang/cs.json index 5a75dd2cd1..98a5db6ea6 100644 --- a/plugins/CustomVariables/lang/cs.json +++ b/plugins/CustomVariables/lang/cs.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Toto hlášení obsahuje informace o vašich vlastních proměnných. Klikněte na proměnnou pro zobrazení distribuce hodnot. %s Pro více informací si přečtěte %sdokumentaci o vlastních proměnných na piwik.org%s", "PluginDescription": "Vlastní proměnné jsou páry (jméno, hodnota), které můžete nastavit návštěvníkovi, nebo libovolné akci, pomocí javascriptového API. Piwik potom ohlásí, kolik návštěv, stránek, konverzí bylo pro každé z těchto jmen a hodnot. Prohlédněte si detailně tyto proměnné pro každého uživatele a akci v záznamu návštěvníků.<br \/>Vyžadováno, pokud chcete použít <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">analýzu E-obchodu<\/a>!", "ScopePage": "rozsah stránky", - "ScopeVisit": "rozsah návštěvy" + "ScopeVisit": "rozsah návštěvy", + "ManageDescription": "Tento přehled zobrazuje všechny sloty vlastních proměnných a jejich využití pro stránku '%s'. Jména v každém ze slotů jsou seřazena dle jejich celkového využití.", + "ScopeX": "Rozsah %s", + "Index": "Index", + "Usages": "Využití", + "Unused": "Nevyužito", + "CreateNewSlot": "Navýšit počet dostupných slotů vlastních proměnných", + "UsageDetails": "%s návštěv a %s akcí od vytvoření této webové stránky.", + "CreatingCustomVariableTakesTime": "Vytvoření nové vlastní proměnné hodnoty může trvat déle v závislosti na velikosti vaší databáze. Proto je toto možné provést pouze pomocí příkazu spuštěnému v příkazové řádce.", + "CurrentAvailableCustomVariables": "Aktuálně můžete použít až %s vlastních proměnných na stránku.", + "ToCreateCustomVarExecute": "Pro vytvoření nového slotu pro vlastní proměnnou spusťte následující příkaz ve své instalaci Piwik:", + "SlotsReportIsGeneratedOverTime": "Data pro tento report budou dostupná později. Než budou data vidět, může to den nebo dva trvat a poté několik týdnů než bude report vypovídající." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/de.json b/plugins/CustomVariables/lang/de.json index 0dd793331a..c60483ec21 100644 --- a/plugins/CustomVariables/lang/de.json +++ b/plugins/CustomVariables/lang/de.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Dieser Bericht enthält Informationen über Ihre benutzerdefinierten Variablen. Klicken Sie auf einen Variablennamen, um die Verteilung der Werte zu sehen. %s Für mehr Informationen über benutzerdefinierte Variablen, lesen Sie die %sDokumentation auf piwik.org%s", "PluginDescription": "Benutzerdefinierte Variablen sind (Namen-, Wert-) Paare, welche Sie mit Hilfe der Javascript API auf Besucher oder deren Aktionen festlegen können. Piwik wird Sie dann über die Menge an Besuchen, Seiten, Konversionen für jede der benutzerdefinierten Variablen und Werte informieren. Die detaillierten benutzerdefinierten Variablen für jeden Benutzer und Aktionen im Besucher-Log einsehbar.<br \/>Benötigt um das Feature <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Ecommerce Analyse<\/a>nutzen zu können.", "ScopePage": "Anwendungsbereich Seite", - "ScopeVisit": "Anwendungsbereich Besuch" + "ScopeVisit": "Anwendungsbereich Besuch", + "ManageDescription": "Diese Übersicht zeigt alle benutzerdefinierten Variablen und deren Gebrauch für Webseite %s. Die Namen auf jeder Position sind danach sortiert, wie oft sie insgesamt benützt wurden.", + "ScopeX": "Anwendungsbereich %s", + "Index": "Index", + "Usages": "Verwendungen", + "Unused": "Unbenutzt", + "CreateNewSlot": "Die Anzahl verfügbarer benutzerdefinierter Variablen erhöhen", + "UsageDetails": "%s Besuche und %s Aktionen seit der Erstellung dieser Webseite.", + "CreatingCustomVariableTakesTime": "Neue benutzerdefinierte Variablen zu erstellen kann viel Zeit beanspruchen, abhängig von der Grösse Ihrer Datenbank. Deshalb ist dieser Vorgang nur über Kommandozeilen verfügbar.", + "CurrentAvailableCustomVariables": "Aktuell können Sie bis zu %s benutzerdefinierte Variablen pro Seite einsetzen.", + "ToCreateCustomVarExecute": "Um eine neue benutzerdefinierte Variable zu erstellen, führen Sie in Ihrer Piwik Installation folgende Befehle aus:", + "SlotsReportIsGeneratedOverTime": "Daten für diesen Bericht werden periodisch veröffentlicht. Es mag ein oder zwei Tage dauern, bis Daten sichtbar sind und einige Wochen bis der Bericht vollständig aussagekräftig ist." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/el.json b/plugins/CustomVariables/lang/el.json index baf9456547..25b790a4f9 100644 --- a/plugins/CustomVariables/lang/el.json +++ b/plugins/CustomVariables/lang/el.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Αυτή η αναφορά περιέχει πληροφορίες για τις Προσαρμοσμένες Μεταβλητές. Πατήστε σε ένα όνομα μεταβλητής για να δείτε την κατανομή των τιμών. %s Για περισσότερες πληροφορίες για τις Προσαρμοσμένες Μεταβλητές γενικά, διαβάστε την %sτεκμηρίωση Προσαρμοσμένων Μεταβλητών στο piwik.org%s.", "PluginDescription": "Οι Προσαρμοσμένες Μεταβλητές (όνομα, τιμή) είναι ζεύγη που δίνετε με χρήση του Javascript API σε επισκέπτες ή οποιαδήποτε ενέργειά τους. Το Piwik τότε θα αναφέρει τις επισκέψεις, σελίδες, μετατροπές για κάθε μία από τις προσαρμοσμένες αυτές μεταβλητές. Δείτε τις λεπτομερείς Προσαρμοσμένες Μεταβλητές για κάθε χρήστη και ενέργεια στο Ημερολόγιο Επισκεπτών.<br \/>Απαιτείται για τη χρήση του χαρακτηριστικού <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Αναλυτικά Ηλ. Εμπορίου<\/a>!", "ScopePage": "σελίδα σκοπού", - "ScopeVisit": "επίσκεψη σκοπού" + "ScopeVisit": "επίσκεψη σκοπού", + "ManageDescription": "Η σύνοψη αυτή δείχνει όλες τις σχισμές προσαρμοσμένων μεταβλητών και τις χρήσεις τους για τον ιστοτόπο '%s'. Τα ονόματα σε κάθε σχισμή ταξινομούνται με βάση το πόσο συχνά χρησιμοποιούνται στο σύνολο.", + "ScopeX": "Εμβέλεια %s", + "Index": "Ευρετήριο", + "Usages": "Χρήσεις", + "Unused": "Δεν χρησιμοποιούνται", + "CreateNewSlot": "Αυξήστε τον αριθμό των σχισμών Προσαρμοσμένων Μεταβλητών", + "UsageDetails": "%s επισκέψεις και %s ενέργειες από την δημιουργία του ιστοτόπου.", + "CreatingCustomVariableTakesTime": "Η δημιουργία σχισμής προσαρμοσμένης μεταβλητής μπορεί να διαρκέσει αρκετή ώρα ανάλογα με το μέγεθος της βάσης. Οπότε, αυτό είναι πρέπει να γίνεται από εντολή που χρειάζεται να εκτελεστεί από την γραμμή εντολών.", + "CurrentAvailableCustomVariables": "Αυτή τη στιγμή μπορείτε να χρησιμοποιήσετε %s Προσαρμοσμένες Μεταβλητές ανά ιστοτόπο.", + "ToCreateCustomVarExecute": "Για να δημιουργήσετε μια σχισμή προσαρμοσμένης μεταβλητής πρέπει να εκτελέσετε την παρακάτω εντολή για την εγκατάσταση του PIwik σας:", + "SlotsReportIsGeneratedOverTime": "Τα δεδομένα για αυτή την αναφορά θα συμπληρώνονται με το πέρασμα του χρόνου. Μπορεί να χρειαστούν μία ή δύο μέρες για να δείτε δεδομένα και μερικές εβδομάδες μέχρι η αναφορά να είναι ακριβής." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/en.json b/plugins/CustomVariables/lang/en.json index 7d99f97bf2..45052df4d9 100644 --- a/plugins/CustomVariables/lang/en.json +++ b/plugins/CustomVariables/lang/en.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "This report contains information about your Custom Variables. Click on a variable name to see the distribution of the values. %s For more information about Custom Variables in general, read the %sCustom Variables documentation on piwik.org%s", "PluginDescription": "Custom Variables are (name, value) pairs that you can assign using the Javascript API to visitors or any of their action. Piwik will then report how many visits, pages, conversions for each of these custom names and values. View the detailed Custom Variables for each user and action in the Visitor Log.<br />Required to use <a href=\"http://piwik.org/docs/ecommerce-analytics/\">Ecommerce Analytics</a> feature!", "ScopePage": "scope page", - "ScopeVisit": "scope visit" + "ScopeVisit": "scope visit", + "ManageDescription": "This overview shows all custom variable slots and their usages for website '%s'. The names within each slot are ordered by how often they were used in total.", + "ScopeX": "Scope %s", + "Index": "Index", + "Usages": "Usages", + "Unused": "Unused", + "CreateNewSlot": "Increase the number of available Custom Variables slots", + "UsageDetails": "%s visits and %s actions since creation of this website.", + "CreatingCustomVariableTakesTime": "Creating a new custom variable slot can take a long time depending on the size of your database. Therefore it is only possible to do this via a command which needs to be executed on the command line.", + "CurrentAvailableCustomVariables": "Currently you can use up to %s Custom Variables per site.", + "ToCreateCustomVarExecute": "To create a new custom variable slot execute the following command within your Piwik installation: ", + "SlotsReportIsGeneratedOverTime": "Data for this report will be populated over time. It may take a day or two to see any data and a few weeks until the report is fully accurate." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/it.json b/plugins/CustomVariables/lang/it.json index 3162fc9027..ec7b07a778 100644 --- a/plugins/CustomVariables/lang/it.json +++ b/plugins/CustomVariables/lang/it.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Questo report contiene le informazioni sulle variabili personalizzate. Clicca su un nome di variabile per visualizzarne la distribuzione dei valori. %s Per ulteriori informazioni sulle variabili personalizzate in generale, leggere la %sDocumentazione Variabili Personalizzatesu piwik.org %s", "PluginDescription": "Le variabili personalizzate sono coppie (nome, valore) che puoi assegnare ai visitatori o a una qualunque loro azione tramite API Javascript. Poi Piwik riporterà le visite, le pagine e le conversioni per questi nomi e valori. Guarda nel dettaglio le Variabili Personalizzate per ciascun utente nel Log Visitatori.<br>Necessario per utilizzare la funzione <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Statistiche Ecommerce<\/a>", "ScopePage": "ambito pagina", - "ScopeVisit": "ambito visita" + "ScopeVisit": "ambito visita", + "ManageDescription": "Questa panoramica mostra tutti gli slot di variabili personali e il loro utilizzo nel sito '%s'. I nomi in ciascuno slot vengono ordinati a seconda di quanto sono stati utilizzati in totale.", + "ScopeX": "Ambito %s", + "Index": "Indice", + "Usages": "Usi", + "Unused": "Inutilizzate", + "CreateNewSlot": "Aumenta il numero degli slot di variabili personali disponibili", + "UsageDetails": "%s visite e %s azioni dalla creazione di questo sito web.", + "CreatingCustomVariableTakesTime": "La creazione di un nuovo slot di variabili personali può richiedere un tempo lungo che dipende dalla dimensione del tuo database. Pertanto è possibile fare ciò solo tramite un comando che deve essere eseguito da riga di comando.", + "CurrentAvailableCustomVariables": "Attualmente puoi utilizzare fino a %s variabili personali per sito.", + "ToCreateCustomVarExecute": "Per creare un nuovo slot di variabili personali esegui il comando seguente nella tua installazione di Piwik.", + "SlotsReportIsGeneratedOverTime": "I dati per questo report saranno popolati nel tempo. Ciò può richiedere un giorno o due per vedere dei dati, e qualche settimana perché il report sia totalmente accurato." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/pt-br.json b/plugins/CustomVariables/lang/pt-br.json index 172cf7eabb..9f46739d4a 100644 --- a/plugins/CustomVariables/lang/pt-br.json +++ b/plugins/CustomVariables/lang/pt-br.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Este relatório contém informações sobre as suas variáveis personalizadas. Clique em um nome de variável para ver a distribuição dos valores. %s para mais informações sobre variáveis personalizadas em geral, leia a documentação %sCustom Variáveis em piwik.org%s", "PluginDescription": "Variáveis Personalizadas são pares (nome, valor) que você pode atribuir utilizando a API Javascript para visitantes ou qualquer ações deles. Então, Piwik reporta o número de visitas, páginas e conversões para cada um desses nomes e valores personalizados. Veja as Variáveis Personalizadas detalhadas para cada usuário e ação no Registro de Visitantes.<br \/> É necessário utilizar a função <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Ecommerce Analytics<\/a>!", "ScopePage": "página escopo", - "ScopeVisit": "visita escopo" + "ScopeVisit": "visita escopo", + "ManageDescription": "Esta visão geral mostra todos os compartimentos personalizados de variáveis e seus usos para o website '%s'. Os nomes dentro de cada compartimento são ordenados por quantas vezes foram utilizados no total.", + "ScopeX": "Escopo %s", + "Index": "Índice", + "Usages": "Utilizações", + "Unused": "Não utilizado", + "CreateNewSlot": "Aumentar o número de compartimentos de Variáveis Personalizadas disponíveis", + "UsageDetails": "%s visitas e %s ações desde a criação deste website.", + "CreatingCustomVariableTakesTime": "Criar um novo compartimento de variável personalizada pode levar um longo tempo, dependendo do tamanho do seu banco de dados. Por isso, só é possível fazer isso através de um comando que deve ser executado na linha de comando.", + "CurrentAvailableCustomVariables": "Atualmente você pode usar até %s Variáveis Personalizadas por site.", + "ToCreateCustomVarExecute": "Para criar um novo compartimento de variável personalizada execute o seguinte comando dentro da sua instalação Piwik:", + "SlotsReportIsGeneratedOverTime": "Os dados para este relatório serão preenchidos ao longo do tempo. Pode demorar um dia ou dois para ver todos os dados e algumas semanas até que o relatório seja totalmente preciso." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/sk.json b/plugins/CustomVariables/lang/sk.json index f936be8fdd..84445a2338 100644 --- a/plugins/CustomVariables/lang/sk.json +++ b/plugins/CustomVariables/lang/sk.json @@ -1,7 +1,10 @@ { "CustomVariables": { - "ColumnCustomVariableName": "Názov vlastnej premmennej", + "ColumnCustomVariableName": "Názov vlastnej premennej", "ColumnCustomVariableValue": "Hodnota vlastnej premennej", - "CustomVariables": "Vlastné premenné" + "CustomVariables": "Vlastné premenné", + "ScopeX": "Rozsah %s", + "Index": "Index", + "Unused": "Nepoužité" } }
\ No newline at end of file diff --git a/plugins/CustomVariables/templates/manage.twig b/plugins/CustomVariables/templates/manage.twig new file mode 100644 index 0000000000..dc170ff2c9 --- /dev/null +++ b/plugins/CustomVariables/templates/manage.twig @@ -0,0 +1,11 @@ +{% extends 'user.twig' %} + +{% block topcontrols %} + <div class="top_bar_sites_selector piwikTopControl"> + <div piwik-siteselector show-selected-site="true" class="sites_autocomplete"></div> + </div> +{% endblock %} + +{% block content %} + <div piwik-manage-custom-vars> +{% endblock %}
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/Integration/ModelTest.php b/plugins/CustomVariables/tests/Integration/ModelTest.php index 128e5b09ef..905a5f6a1d 100644 --- a/plugins/CustomVariables/tests/Integration/ModelTest.php +++ b/plugins/CustomVariables/tests/Integration/ModelTest.php @@ -19,7 +19,7 @@ use Piwik\Tests\Framework\TestCase\IntegrationTestCase; */ class ModelTest extends IntegrationTestCase { - private static $cvarScopes = array('log_link_visit_action', 'log_visit', 'log_conversion'); + private static $cvarScopes = array('page', 'visit', 'conversion'); public function setUp() { diff --git a/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml b/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml index e504b8e8c7..766be4e97d 100644 --- a/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml +++ b/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml @@ -17,6 +17,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_1</segment> <subtable> <row> @@ -56,6 +62,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_2</segment> <subtable> <row> @@ -95,6 +107,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_3</segment> <subtable> <row> @@ -134,6 +152,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_4</segment> <subtable> <row> @@ -173,6 +197,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_5</segment> <subtable> <row> @@ -212,6 +242,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>6</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_6</segment> <subtable> <row> @@ -251,6 +287,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>7</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_7</segment> <subtable> <row> @@ -290,6 +332,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>8</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_8</segment> <subtable> <row> @@ -312,100 +360,148 @@ </row> </subtable> </row> - <row> - <label>Name_PAGE_1</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_1</segment> - <subtable> - <row> - <label>Val_PAGE1</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_2</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_2</segment> - <subtable> - <row> - <label>Val_PAGE2</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_3</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_3</segment> - <subtable> - <row> - <label>Val_PAGE3</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_4</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_4</segment> - <subtable> - <row> - <label>Val_PAGE4</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_5</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_5</segment> - <subtable> - <row> - <label>Val_PAGE5</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_6</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_6</segment> - <subtable> - <row> - <label>Val_PAGE6</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_7</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_7</segment> - <subtable> - <row> - <label>Val_PAGE7</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_8</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_8</segment> - <subtable> - <row> - <label>Val_PAGE8</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> -</result> + <row> + <label>Name_PAGE_1</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_1</segment> + <subtable> + <row> + <label>Val_PAGE1</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_2</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_2</segment> + <subtable> + <row> + <label>Val_PAGE2</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_3</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>3</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_3</segment> + <subtable> + <row> + <label>Val_PAGE3</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_4</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_4</segment> + <subtable> + <row> + <label>Val_PAGE4</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_5</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_5</segment> + <subtable> + <row> + <label>Val_PAGE5</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_6</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>6</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_6</segment> + <subtable> + <row> + <label>Val_PAGE6</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_7</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>7</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_7</segment> + <subtable> + <row> + <label>Val_PAGE7</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_8</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>8</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_8</segment> + <subtable> + <row> + <label>Val_PAGE8</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> +</result>
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/UI/.gitignore b/plugins/CustomVariables/tests/UI/.gitignore new file mode 100644 index 0000000000..f39be478e7 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/.gitignore @@ -0,0 +1,2 @@ +/processed-ui-screenshots +/screenshot-diffs
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/UI/CustomVariables_spec.js b/plugins/CustomVariables/tests/UI/CustomVariables_spec.js new file mode 100644 index 0000000000..1e247fca56 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/CustomVariables_spec.js @@ -0,0 +1,25 @@ +/*! + * Piwik - free/libre analytics platform + * + * Screenshot integration tests. + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +describe("CustomVariables", function () { + this.timeout(0); + + this.fixture = "Piwik\\Plugins\\CustomVariables\\tests\\Fixtures\\VisitWithManyCustomVariables"; + + it('should show an overview of all used custom variables', function (done) { + expect.screenshot('manage').to.be.captureSelector('.pageWrap', function (page) { + page.load("?idSite=1&period=day&date=2010-01-03&module=CustomVariables&action=manage"); + }, done); + }); + + it('should be visible in the menu', function (done) { + expect.screenshot('link_in_menu').to.be.captureSelector('li:contains(Manage)', function (page) { + }, done); + }); +});
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png Binary files differnew file mode 100644 index 0000000000..39480bbf29 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png diff --git a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png Binary files differnew file mode 100644 index 0000000000..c03b7e3795 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png diff --git a/plugins/DBStats/lang/cs.json b/plugins/DBStats/lang/cs.json index bd3a1b434d..57074b7cdd 100644 --- a/plugins/DBStats/lang/cs.json +++ b/plugins/DBStats/lang/cs.json @@ -6,7 +6,7 @@ "EstimatedSize": "Odhadovaná velikost", "IndexSize": "Velikost indexu", "LearnMore": "Abyste lépe zjistili, jak Piwik zpracovává data a jak jej nastavit pro weby se středním a velkým provozem, podívejte se do dokumentace %s.", - "MainDescription": "Piwik ukládá všechny vaše data webové analýzy v MySQL databázi. Nyní tabulky Piwiku využívají %s.", + "MainDescription": "Piwik ukládá všechna vaše data webové analýzy v MySQL databázi. Nyní tabulky Piwiku využívají %s.", "MetricDataByYear": "Tabulky měření za rok", "MetricTables": "Tabulky měření", "OtherTables": "Ostatní tabulky", diff --git a/plugins/DBStats/lang/ko.json b/plugins/DBStats/lang/ko.json index f56a86418f..8490083722 100644 --- a/plugins/DBStats/lang/ko.json +++ b/plugins/DBStats/lang/ko.json @@ -10,6 +10,7 @@ "MetricDataByYear": "연별 통계 표", "MetricTables": "통계표", "OtherTables": "기타 표", + "PluginDescription": "자세한 MySQL 데이터베이스 사용 보고서를 제공합니다. 진단 상황에서 슈퍼 유저만이 사용할 수 있습니다.", "ReportDataByYear": "연별 보고표", "ReportTables": "보고표", "RowCount": "행 수", diff --git a/plugins/Dashboard/lang/cs.json b/plugins/Dashboard/lang/cs.json index 8db9ceb7cf..1a952809f1 100644 --- a/plugins/Dashboard/lang/cs.json +++ b/plugins/Dashboard/lang/cs.json @@ -13,7 +13,7 @@ "DefaultDashboard": "Výchozí nástěnka - používáte výchozí rozvržení nástěnky", "DeleteWidgetConfirm": "Opravdu chcete odstranit tento widget z nástěnky?", "EmptyDashboard": "Prázdná nástěnka - vyberte si své oblíbené widgety", - "LoadingWidget": "Načítám widget, prosím čekejte...", + "LoadingWidget": "Načítání widgetu, chvíli strpení...", "ManageDashboard": "Správa nástěnky", "Maximise": "Maximalizovat", "Minimise": "Minimalizovat", @@ -21,6 +21,7 @@ "PluginDescription": "Vaše nástěnka analýzy webu. Přizpůsobte si vaši nástěnku přidáním nových widgetů, jejich přesunutím nebo změnou rozvržení sloupců. Každý uživatel může přizpůsobit svou vlastní nástěnku.", "RemoveDashboard": "Odstranit nástěnku", "RemoveDashboardConfirm": "Opravdu chcete odstranit nástěnku: %s?", + "RemoveDefaultDashboardNotPossible": "Výchozí nástěnka nemůže být odstraněna", "RenameDashboard": "Přejmenovat nástěnku", "ResetDashboard": "Obnovit nástěnku", "ResetDashboardConfirm": "Opravdu chcete obnovit nastavení nástěnky do továrního stavu?", diff --git a/plugins/Dashboard/lang/de.json b/plugins/Dashboard/lang/de.json index 6b6a1a6db9..35cc204fec 100644 --- a/plugins/Dashboard/lang/de.json +++ b/plugins/Dashboard/lang/de.json @@ -21,6 +21,7 @@ "PluginDescription": "Ihr Web Analyse Dashboard. Personalisieren Sie Ihr Dashboard, in dem Sie neue Widgets hinzufügen, per Drag und Drop herumschieben, und das Dashboard Spaltenlayout ändern. Jeder Benutzer kann sein eigenes personalisiertes Dashboard verwalten.", "RemoveDashboard": "Dashboard entfernen", "RemoveDashboardConfirm": "Sind Sie sicher, dass Sie das Dashboard \"%s\" löschen möchten?", + "RemoveDefaultDashboardNotPossible": "Das Standard-Dashboard kann nicht entfernt werden.", "RenameDashboard": "Dashboard umbenennen", "ResetDashboard": "Dashboard zurücksetzen", "ResetDashboardConfirm": "Wollen Sie wirklich das Dashboardlayout auf die Standardeinstellungen zurücksetzen?", diff --git a/plugins/Dashboard/lang/el.json b/plugins/Dashboard/lang/el.json index 0f03e52550..965d5329d1 100644 --- a/plugins/Dashboard/lang/el.json +++ b/plugins/Dashboard/lang/el.json @@ -21,6 +21,7 @@ "PluginDescription": "Ο κεντρικός πίνακας Αναλυτικών Ιστού. Προσαρμόστε τον πίνακα με την προσθήκη νέων γραφικών συστατικών, σύρτε και αφήστε τα όπου θέλετε και αλλάξτε την διάταξη στήλης του πίνακα. Κάθε χρήστης μπορεί να διαχειρίζεται τον δικό του πίνακα.", "RemoveDashboard": "Απομάκρυνση κεντρικού πίνακα", "RemoveDashboardConfirm": "Θέλετε, σίγουρα, να απομακρύνετε τον Κεντρικό Πίνακα «%s»;", + "RemoveDefaultDashboardNotPossible": "Ο προκαθορισμένος πίνακας εργαλείων δεν είναι δυνατό να αφαιρεθεί", "RenameDashboard": "Μετονομασία κεντρικού πίνακα", "ResetDashboard": "Επαναφορά κεντρικού πίνακα", "ResetDashboardConfirm": "Θέλετε σίγουρα να επαναφέρετε τον κεντρικό σας πίνακα στις προεπιλεγμένες Μικροεφαρμογές;", diff --git a/plugins/Dashboard/lang/fr.json b/plugins/Dashboard/lang/fr.json index 933c82bf36..1dc3fdb4a1 100644 --- a/plugins/Dashboard/lang/fr.json +++ b/plugins/Dashboard/lang/fr.json @@ -21,6 +21,7 @@ "PluginDescription": "Votre tableau de bord, personnalisez le en ajoutant des widgets, en les déplaçant et en modifiant la disposition des colonnes. Chaque utilisateur possède son propre tableau de bord.", "RemoveDashboard": "Supprimer le tableau de bord", "RemoveDashboardConfirm": "Êtes vous sûr(e) de vouloir supprimer le tableau de bord \"%s\" ?", + "RemoveDefaultDashboardNotPossible": "Le tableau de bord par défaut ne peut pas être supprimé", "RenameDashboard": "Renommer le tableau de bord", "ResetDashboard": "Restaurer le tableau de bord", "ResetDashboardConfirm": "Voulez-vous vraiment restaurer votre tableau de bord avec les widgets par défaut ?", diff --git a/plugins/Dashboard/lang/it.json b/plugins/Dashboard/lang/it.json index b97e35430d..8105f3f90f 100644 --- a/plugins/Dashboard/lang/it.json +++ b/plugins/Dashboard/lang/it.json @@ -21,6 +21,7 @@ "PluginDescription": "La tua Dashboard delle Statistiche Web. Personalizzala aggiungendo nuovi widgets, trascinandoli e spostandoli e cambiando l'aspetto delle colonne. Ciascun utente può gestire una propria dashboard.", "RemoveDashboard": "Rimuovi la dashboard", "RemoveDashboardConfirm": "Sei sicuro di voler rimuovere la dashboard \"%s\"?", + "RemoveDefaultDashboardNotPossible": "La dashboard predefinita non può essere eliminata", "RenameDashboard": "Rinomina dashboard", "ResetDashboard": "Reimposta la dashboard", "ResetDashboardConfirm": "Sei sicuro di voler ripristinare il layout della dashboard alla selezione di widget predefinita?", diff --git a/plugins/Dashboard/lang/ja.json b/plugins/Dashboard/lang/ja.json index 10c0b763d9..f673fd143f 100644 --- a/plugins/Dashboard/lang/ja.json +++ b/plugins/Dashboard/lang/ja.json @@ -21,6 +21,7 @@ "PluginDescription": "ユーザーの Web Analytics のダッシュ ボード。新しいウィジェットを追加することによって、ダッシュ ボードをカスタマイズ、ドラッグ アンドドロップしでダッシュ ボードのカラムのレイアウトを変更します。各ユーザーは、独自のカスタム ダッシュ ボードを管理できます。", "RemoveDashboard": "ダッシュボードの削除", "RemoveDashboardConfirm": "本当にダッシュボード \"%s\" を削除しますか?", + "RemoveDefaultDashboardNotPossible": "既定のダッシュ ボードを削除できません。", "RenameDashboard": "ダッシュボードの名称変更", "ResetDashboard": "ダッシュボードのリセット", "ResetDashboardConfirm": "ダッシュボードのレイアウトをリセットしてデフォルトのウィジェットに戻しますか?", diff --git a/plugins/Dashboard/lang/ko.json b/plugins/Dashboard/lang/ko.json index 535fe10319..6c4dd23b1c 100644 --- a/plugins/Dashboard/lang/ko.json +++ b/plugins/Dashboard/lang/ko.json @@ -21,6 +21,7 @@ "PluginDescription": "당신의 사이트를 분석한 대시보드입니다. 드래그 앤 드롭으로 새로운 위젯을 추가하거나 대시보드 레이아웃을 바꾸는 기능을 통해 당신만의 대시보드를 꾸며보세요. 모든 사용자들은 자신만의 대시보드를 가질 수 있습니다.", "RemoveDashboard": "대시보드 삭제", "RemoveDashboardConfirm": "정말 대시보드 \"%s\"를 삭제 하시겠습니까?", + "RemoveDefaultDashboardNotPossible": "기본 대시보드는 삭제할 수 없습니다.", "RenameDashboard": "대시보드 이름 변경", "ResetDashboard": "대시보드 재설정", "ResetDashboardConfirm": "대시보드의 레이아웃을 재설정하여 기본 위젯으로 복원하시겠습니까?", diff --git a/plugins/Dashboard/lang/pt-br.json b/plugins/Dashboard/lang/pt-br.json index 1ec93d487c..e913ffd254 100644 --- a/plugins/Dashboard/lang/pt-br.json +++ b/plugins/Dashboard/lang/pt-br.json @@ -21,6 +21,7 @@ "PluginDescription": "Seu Painel de Análise da Rede. Personalize seu painel adicionando novos widgets, arrastando para suas posições, e mudando o leiaute das colunas do painel. Cada usuário pode gerenciar seus próprios painéis personalizados.", "RemoveDashboard": "Remover painel", "RemoveDashboardConfirm": "Tem certeza de que deseja remover o painel \"%s\"?", + "RemoveDefaultDashboardNotPossible": "O painel padrão não pode ser removido", "RenameDashboard": "Renomear painel", "ResetDashboard": "Resetar o painel", "ResetDashboardConfirm": "Tem certeza de que deseja redefinir o layout do painel para a seleção Widgets padrão?", diff --git a/plugins/Dashboard/lang/sk.json b/plugins/Dashboard/lang/sk.json index ddd7c84c40..9382122bef 100644 --- a/plugins/Dashboard/lang/sk.json +++ b/plugins/Dashboard/lang/sk.json @@ -21,6 +21,7 @@ "PluginDescription": "Vaša nástenka webovej analýzy. Prispôsobte si svoj panel pridaním nových miniaplikácií, popresúvajte ich, a zmeňte rozvrhnutie stĺpcov panela. Každý používateľ môže spravovať svoju vlastnú nástenku.", "RemoveDashboard": "Odstrániť nástenku", "RemoveDashboardConfirm": "Naozaj chcete odstrániť panel \"%s\"?", + "RemoveDefaultDashboardNotPossible": "Štandardnú nástenku nie je možné odstrániť.", "RenameDashboard": "Premenovať nástenku", "ResetDashboard": "Resetovať nástenku", "ResetDashboardConfirm": "Naozaj chcete resetovať rozvrhnutie vašej nástenky na výber prednastavených miniaplikácií?", diff --git a/plugins/Dashboard/lang/tr.json b/plugins/Dashboard/lang/tr.json index f8ef152082..645b2c2f13 100644 --- a/plugins/Dashboard/lang/tr.json +++ b/plugins/Dashboard/lang/tr.json @@ -19,6 +19,7 @@ "NotUndo": "Bu eylemi geri alamazsınız.", "RemoveDashboard": "Paneli kaldır", "RemoveDashboardConfirm": "\"%s\" panelini kaldırmak istediğinize emin misiniz?", + "RemoveDefaultDashboardNotPossible": "Varsayılan pano silinemez", "RenameDashboard": "Panel yeniden adlandır", "ResetDashboard": "Paneli sıfırla", "ResetDashboardConfirm": "Kontrol paneli şablonunuzu varsayılan widget seçimlerine sıfırlamak istediğinizden emin misiniz?", diff --git a/plugins/DevicePlugins/lang/cs.json b/plugins/DevicePlugins/lang/cs.json index 759e2e487c..3854a6916a 100644 --- a/plugins/DevicePlugins/lang/cs.json +++ b/plugins/DevicePlugins/lang/cs.json @@ -3,7 +3,7 @@ "BrowserWithNoPluginsEnabled": "%1$s bez povolených zásuvných modulů", "BrowserWithPluginsEnabled": "%1$s s povolenými zásuvnými moduly %2$s", "PluginDescription": "Hlásí podporované zásuvné moduly v prohlížečích návštěvníků.", - "PluginDetectionDoesNotWorkInIE": "Poznámka: Detekce zásuvných modulů nepracuje v prohlížeči Interet Explorer. Toto hlášení je založeno na ostatních prohlížečích", + "PluginDetectionDoesNotWorkInIE": "Poznámka: Detekce zásuvných modulů nepracuje v prohlížeči Interet Explorer. Toto hlášení je založeno pouze na jiných prohlížečích", "WidgetPlugins": "Seznam zásuvných modulů", "WidgetPluginsDocumentation": "Toto hlášení zobrazuje zásuvné moduly, které měli vaši návštěvníci povoleny. Tato informace může být důležitá při rozhodování o tom, jakým způsobem prezentovat obsah." } diff --git a/plugins/DevicesDetection/lang/ja.json b/plugins/DevicesDetection/lang/ja.json index 071d79b713..b53d32e622 100644 --- a/plugins/DevicesDetection/lang/ja.json +++ b/plugins/DevicesDetection/lang/ja.json @@ -32,8 +32,10 @@ "PluginDescription": "ブランド ( メーカー ) 、モデル ( デバイス バージョン ) 、デバイスの種類 ( テレビ、コンソール、スマート フォン、デスクトップ ) など、ユーザー デバイスに関する拡張情報を提供します。", "SmartDisplay": "スマートディスプレイ", "Smartphone": "スマートフォン", + "PortableMediaPlayer": "ポータブルメディアプレーヤー", "Devices": "デバイス", "Tablet": "タブレット", + "Phablet": "ファブレット", "TV": "TV", "UserAgent": "ユーザーエージェント", "WidgetBrowsers": "ブラウザ", diff --git a/plugins/DevicesDetection/lang/ko.json b/plugins/DevicesDetection/lang/ko.json index 865556c18b..55711fbbe6 100644 --- a/plugins/DevicesDetection/lang/ko.json +++ b/plugins/DevicesDetection/lang/ko.json @@ -1,15 +1,43 @@ { "DevicesDetection": { - "BrowserEngine": "브라우저", + "BrowserEngine": "브라우저 엔진", "BrowserEngineDocumentation": "이 차트는 방문자의 브라우저를 제품군별로 표시합니다. %s 웹개발자에게 가장 중요한 정보는 사용자의 렌더링 엔진 종류입니다. 엔진의 이름과 괄호 안에 일반적인 해당 브라우저 이름을 표시하고 있습니다.", + "BrowserEngines": "브라우저 엔진들", "BrowserFamily": "브라우저 타입", - "Browsers": "브라우저", + "Browsers": "브라우저들", "BrowserVersion": "브라우저 버전", + "BrowserVersions": "브라우저 버전들", + "Camera": "카메라", + "CarBrowser": "자동차 브라우저", + "Software": "소프트웨어", "ColumnBrowser": "브라우저", - "ColumnOperatingSystem": "운영체제", + "ColumnOperatingSystem": "운영 체제", + "ColumnOperatingSystemVersion": "운영 체제 버전", + "Console": "콘솔", + "dataTableLabelBrands": "브랜드", + "dataTableLabelModels": "모델", + "dataTableLabelSystemVersion": "운영 체제 버전", "dataTableLabelTypes": "유형", + "Device": "기기장치", + "DeviceBrand": "기기장치 브랜드", + "DeviceDetection": "기기장치 탐지", + "DeviceModel": "기기장치 모델", + "DevicesDetection": "방문자 기기장치들", + "DeviceType": "기기장치 타입", + "FeaturePhone": "피처 폰", + "OperatingSystemFamilies": "운영 체제 제품군들", "OperatingSystemFamily": "운영 체제 제품군", "OperatingSystems": "운영체제", + "OperatingSystemVersions": "운영 체제 버전들", + "PluginDescription": "유저의 기기장치에 관하여 좀 더 자세한 정보를 제공합니다. 이는 브랜드(생산자), 모델(기기장치 버전), 타입(텔레비전, 콘솔, 스마트폰, 데스크탑 컴퓨터 등) 외 여러 정보를 포함합니다.", + "SmartDisplay": "스마트 디스플레이", + "Smartphone": "스마트폰", + "PortableMediaPlayer": "휴대용 미디어 플레이어", + "Devices": "기기장치들", + "Tablet": "태블릿", + "Phablet": "패블릿", + "TV": "텔레비전", + "UserAgent": "사용자 에이전트", "WidgetBrowsers": "방문자 브라우저", "WidgetBrowsersDocumentation": "사용자가 어떤 브라우저를 사용하는지에 대한 보고서입니다. 각 브라우저의 버전은 별도로 기재되어 있습니다." } diff --git a/plugins/DevicesDetection/lang/tr.json b/plugins/DevicesDetection/lang/tr.json index 15b3d61a9f..affb773e90 100644 --- a/plugins/DevicesDetection/lang/tr.json +++ b/plugins/DevicesDetection/lang/tr.json @@ -7,8 +7,10 @@ "BrowserVersions": "Tarayıcı versiyonları", "Camera": "Kamera", "CarBrowser": "Araba tarayıcısı", + "Software": "Yazılım", "ColumnBrowser": "Tarayıcı", "ColumnOperatingSystem": "İşletim sistemi", + "ColumnOperatingSystemVersion": "İşletim Sistemi Versiyonu", "Console": "Konsol", "dataTableLabelBrands": "Marka", "dataTableLabelModels": "Model", @@ -21,6 +23,7 @@ "DevicesDetection": "Ziyaretçi Cihazları", "DeviceType": "Cihaz tipi", "OperatingSystemFamilies": "İşletim Sistemi aileleri", + "OperatingSystemFamily": "İşletim Sistemi Ailesi", "OperatingSystems": "İşletim sistemleri", "OperatingSystemVersions": "İşletim Sistemi versiyonları", "SmartDisplay": "Akıllı görüntüleme", diff --git a/plugins/Ecommerce/config/config.php b/plugins/Ecommerce/config/config.php deleted file mode 100644 index 3431748e41..0000000000 --- a/plugins/Ecommerce/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Ecommerce\Tracker\EcommerceRequestProcessor'), - )), - -); diff --git a/plugins/Ecommerce/lang/ja.json b/plugins/Ecommerce/lang/ja.json index 62ccf9fe7d..822234b795 100644 --- a/plugins/Ecommerce/lang/ja.json +++ b/plugins/Ecommerce/lang/ja.json @@ -1,5 +1,8 @@ { "Ecommerce": { - "Sales": "売り上げ" + "PluginDescription": "E コマースでは、いつユーザーがカートに製品を追加し、いつ E コマース販売に結びついたか、を追跡できます。製品、製品のカテゴリビュー、および放棄されたカートも追跡できます。", + "Sales": "売り上げ", + "SalesBy": "%s 別売り上げ高", + "SalesAdjective": "売り上げ %s" } }
\ No newline at end of file diff --git a/plugins/Events/Actions/ActionEvent.php b/plugins/Events/Actions/ActionEvent.php index 9921c8d39b..5106ec6092 100644 --- a/plugins/Events/Actions/ActionEvent.php +++ b/plugins/Events/Actions/ActionEvent.php @@ -60,9 +60,17 @@ class ActionEvent extends Action protected function getActionsToLookup() { - return array( - 'idaction_url' => $this->getUrlAndType() - ); + $actionUrl = false; + + $url = $this->getActionUrl(); + + if (!empty($url)) { + // normalize urls by stripping protocol and www + $url = Tracker\PageUrl::normalizeUrl($url); + $actionUrl = array($url['url'], $this->getActionType(), $url['prefixId']); + } + + return array('idaction_url' => $actionUrl); } // Do not track this Event URL as Entry/Exit Page URL (leave the existing entry/exit) diff --git a/plugins/Events/lang/ja.json b/plugins/Events/lang/ja.json index 4304953ddf..10051096aa 100644 --- a/plugins/Events/lang/ja.json +++ b/plugins/Events/lang/ja.json @@ -1,5 +1,6 @@ { "Events": { + "PluginDescription": "イベントを追跡し、ビジターアクティビティのレポートを取得します。", "AvgEventValue": "イベントの平均値: %s", "AvgValue": "平均値", "AvgValueDocumentation": "このイベントのすべての値の平均値", @@ -14,6 +15,7 @@ "EventsWithValue": "値を持つイベント", "EventsWithValueDocumentation": "イベントの値が設定されたイベント数", "EventValue": "イベントの値", + "EventValueTooltip": "総イベント値は %s 件のイベント値の合計です。 %s 最小値は %s 、最大値は %s です 。", "MaxValue": "最大値", "MaxValueDocumentation": "このイベントの最大値", "MinValue": "最小値", diff --git a/plugins/Events/lang/sk.json b/plugins/Events/lang/sk.json index 396cf2c319..a45b722eb4 100644 --- a/plugins/Events/lang/sk.json +++ b/plugins/Events/lang/sk.json @@ -1,5 +1,14 @@ { "Events": { - "AvgValue": "Priemerná hodnota" + "PluginDescription": "Sledovanie Udalostí a získanie reportu ohľadom aktivity vašich návštevníkov.", + "AvgValue": "Priemerná hodnota", + "Events": "Udalosti", + "EventsWithValue": "Udalosti s hodnotou", + "EventsWithValueDocumentation": "Počet udalostí, kde bola nastavená hodnota Udalosti", + "EventValueTooltip": "Hodnota Udalostí celom je suma %s hodnôt udalostí %s medzi minimom %s a maximom %s.", + "TopEvents": "Top Udalosti", + "TotalEvents": "Udalosti celkom", + "TotalEventsDocumentation": "Celkový počet udalostí", + "ViewEvents": "Zobraziť udalosti" } }
\ No newline at end of file diff --git a/plugins/ExamplePlugin/tests/travis/addons.apt.packages.yml b/plugins/ExamplePlugin/tests/travis/addons.apt.packages.yml new file mode 100644 index 0000000000..db3733500a --- /dev/null +++ b/plugins/ExamplePlugin/tests/travis/addons.apt.packages.yml @@ -0,0 +1,3 @@ +- custom apt package +- another custom apt package + diff --git a/plugins/ExamplePlugin/tests/travis/addons.apt.sources.yml b/plugins/ExamplePlugin/tests/travis/addons.apt.sources.yml new file mode 100644 index 0000000000..3504520e3c --- /dev/null +++ b/plugins/ExamplePlugin/tests/travis/addons.apt.sources.yml @@ -0,0 +1 @@ +- custom apt source
\ No newline at end of file diff --git a/plugins/Feedback/lang/cs.json b/plugins/Feedback/lang/cs.json index 42d124d0a4..935e9913b4 100644 --- a/plugins/Feedback/lang/cs.json +++ b/plugins/Feedback/lang/cs.json @@ -5,11 +5,11 @@ "IWantTo": "Chci:", "LearnWaysToParticipate": "Naučit způsoby jak %s spolupracovat %s", "ManuallySendEmailTo": "Prosím pošlete ručně zprávu", - "PluginDescription": "Pošlete tým Piwiku vaši odezvu. Podělte se o své návrhy a nápady a pomozte nám, aby se Piwi stal nejlepší analytickou platformou vůbec.", + "PluginDescription": "Pošlete tým Piwiku vaši odezvu. Podělte se o své návrhy a nápady a pomozte nám, aby se Piwik stal nejlepší analytickou platformou vůbec.", "PrivacyClaim": "Piwik respektuje vaše %1$ssoukromí%2$s a dává vám plnou kontrolu nad vašimi daty.", "RateFeatureLeaveMessageDislike": "Je nám líto, že se vám to nelíbí. Řekněte nám, jak se můžeme zlepšit.", "RateFeatureLeaveMessageLike": "Jsme rádi, že se vám to líbí. Řekněte nám, co se vám líbí nejvíc, nebo jestli máte nějaký návrh na novou funkci.", - "RateFeatureSendFeedbackInformation": "Piwik platforma nám (týmu Piwiku) pošle e-mail s vaší e-mailovou adresou, abychom vás v případě otázek mohli kontaktovat.", + "RateFeatureSendFeedbackInformation": "Piwik platforma nám (týmu Piwiku) pošle zprávu s vaší emailovou adresou, abychom vás v případě otázek mohli kontaktovat.", "RateFeatureThankYouTitle": "Děkujeme za ohodnocení %s.", "RateFeatureTitle": "Líbí se vám vlastnost %s? Ohodnoťte ji a zanechte komentář", "SendFeedback": "Odeslat odezvu", @@ -22,8 +22,8 @@ "PiwikProIntro": "Piwik Pro poskytuje konzultace s experty pro ty, kteří hostují Piwik na vlastní infrastruktuře.", "PiwikProOfferIntro": "Naše nabídka obsahuje", "PiwikProReviewPiwikSetup": "Kontrolu vašeho nastavení Piwik", - "PiwikProOptimizationMaintenance": "Služby optimalizace & údržby Piwiku", - "PiwikProPhoneEmailSupport": "Telefonická a e-mailová podpora", + "PiwikProOptimizationMaintenance": "Služby optimalizace a údržby Piwiku", + "PiwikProPhoneEmailSupport": "Telefonická a emailová podpora", "PiwikProTraining": "Uživatelská, technická a vývojářská školení", "PiwikProPremiumFeatures": "Prémiové funkce", "PiwikProCustomDevelopment": "Služby vlastního vývoje", diff --git a/plugins/Feedback/lang/fr.json b/plugins/Feedback/lang/fr.json index 9bfff5b40f..b992c5cfbc 100644 --- a/plugins/Feedback/lang/fr.json +++ b/plugins/Feedback/lang/fr.json @@ -1,7 +1,7 @@ { "Feedback": { "DoYouHaveBugReportOrFeatureRequest": "Avez-vous un bug à rapporter ou une fonctionnalité à demander ?", - "HowToCreateTicket": "Veuillez consulter les recommandations sur la rédaction d'un bon %1$srapport de bug%2$s ou %3$sdemande de fonctionnalité%4$s. Puis s'enregistrer ou se connecter sur %5$snotre système de suivit des incidents%6$s et créer un %7$snouvel incident%8$s.", + "HowToCreateTicket": "Veuillez consulter les recommandations sur la rédaction d'un bon %1$srapport de bug%2$s ou %3$sdemande de fonctionnalité%4$s. Puis s'enregistrer ou se connecter sur %5$snotre système de suivi des incidents%6$s et créer un %7$snouvel incident%8$s.", "IWantTo": "Je veux:", "LearnWaysToParticipate": "Renseignez vous sur les manières dont vous pouvez %s participer%s", "ManuallySendEmailTo": "Merci d'envoyer manuellement votre message à", diff --git a/plugins/Feedback/lang/ja.json b/plugins/Feedback/lang/ja.json index c66634eb43..1819573a93 100644 --- a/plugins/Feedback/lang/ja.json +++ b/plugins/Feedback/lang/ja.json @@ -17,12 +17,14 @@ "TopLinkTooltip": "あなたの意見をお知らせください。または、プロフェッショナルサポートをリクエストしてください。", "ViewAnswersToFAQ": "%s Frequently Asked Questions %s への回答をご覧ください。", "ViewUserGuides": "Piwik の設定方法と、効果的なデータ分析方法は、%1$s user guides %2$s をご確認ください。", + "CommunityHelp": "コミュニティヘルプ", "ProfessionalHelp": "プロフェッショナルヘルプ", "PiwikProIntro": "Piwik PRO は、専門家のサポートと Piwik 独自のインフラストラクチャ上でホストするクライアントへのコンサルティングを提供します。", "PiwikProOfferIntro": "当社オファーには以下が含まれます", "PiwikProReviewPiwikSetup": "Piwik セットアップのレビュー", "PiwikProOptimizationMaintenance": "Piwikiの最適化と保守のサービス", "PiwikProPhoneEmailSupport": "電話とEメールのサポート", + "PiwikProTraining": "ユーザー及び技術開発者のトレーニング", "PiwikProPremiumFeatures": "プレミア機能", "PiwikProCustomDevelopment": "カスタム開発サービス", "PiwikProAnalystConsulting": "アナリストのコンサルティングサービス", diff --git a/plugins/Feedback/lang/ko.json b/plugins/Feedback/lang/ko.json index ed80f5b83b..64a1086e78 100644 --- a/plugins/Feedback/lang/ko.json +++ b/plugins/Feedback/lang/ko.json @@ -1,12 +1,34 @@ { "Feedback": { "DoYouHaveBugReportOrFeatureRequest": "발견한 버그나 기능에 대한 의견을 보고하시겠습니까?", + "HowToCreateTicket": "좋은 %1$s버그 리포트%2$s 혹은 %3$s기능 개선%4$s을 작성하는 추천법에 대해 읽어주세요. 그 후 %5$s우리의 이슈 트래커%6$s에 가입 및 로그인 하셔서 %7$s새로운 이슈%8$s를 만들어주세요.", "IWantTo": "내가 원하는 것은:", "LearnWaysToParticipate": "당신이 %s참여%s할 수있는 모든 방법", "ManuallySendEmailTo": "당신의 메시지를 다음 주소로 직접 보내주세요:", + "PluginDescription": "Piwik 팀에게 피드백 보내기. 당신의 아이디어와 제안은 더 나은 세상에서 가장 우수한 분석 플랫폼이 될 Piwik를 만드는데 큰 도움이 됩니다.", + "PrivacyClaim": "Piwik는 당신의 %1$s프라이버시%2$s를 존중하며 동시에 당신의 데이터가 당신의 통제 하에 있도록 노력합니다.", + "RateFeatureLeaveMessageDislike": "안 좋아하신다고요! 죄송합니다! 우리가 이를 개선할 수 있도록 도움을 주세요!", + "RateFeatureLeaveMessageLike": "좋아요에 감사 드립니다! 어떤 점을 좋게 생각하시는지 혹은 어떤 기능적 개선 요구가 있는지 알고 싶습니다.", + "RateFeatureSendFeedbackInformation": "당신의 Piwik 플랫폼은 우리들(Piwik 팀)에게 (당신의 이메일 주소를 포함한) 메일을 보낼 것입니다. 따라서 당신의 질문에 따라 우리가 연락을 취할 수 있습니다.", + "RateFeatureThankYouTitle": "'%s' 등급에 감사 드립니다.", + "RateFeatureTitle": "'%s'의 기능이 어떤가요? 평가 및 코멘트 해주세요.", "SendFeedback": "의견 보내기", "ThankYou": "우리가 Piwik을 향상시키는 데 도움을 주셔서 감사합니다!", - "TopLinkTooltip": "당신이 생각하는 기술 지원 요청을 알려주세요.", + "TopLinkTooltip": "당신이 필요로 하는 기술 지원 요청을 알려주세요.", + "ViewAnswersToFAQ": "%s자주 하는 질문%s에서 답변 보기", + "ViewUserGuides": "%1$s사용자 가이드%2$s에서 효율적으로 당신의 데이터를 분석하는 방법과 Piwik를 구성하는 법에 대해서 알 수 있습니다.", + "CommunityHelp": "커뮤니티에서의 도움", + "ProfessionalHelp": "전문가의 도움", + "PiwikProIntro": "Piwik PRO는 전문가들이 구축한 인프라에서 그들의 지원과 컨설팅을 받을 수 있는 서비스입니다.", + "PiwikProOfferIntro": "제공되는 것들", + "PiwikProReviewPiwikSetup": "Piwik 셋업", + "PiwikProOptimizationMaintenance": "Piwik 최적화 및 관리 보수", + "PiwikProPhoneEmailSupport": "전화 및 이메일", + "PiwikProTraining": "사용자, 기술자 및 개발자", + "PiwikProPremiumFeatures": "프리미엄", + "PiwikProCustomDevelopment": "맞춤 개발", + "PiwikProAnalystConsulting": "전문가 컨설팅", + "ContactUs": "연락주세요", "VisitTheForums": "%s포럼%s으로 이동" } }
\ No newline at end of file diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php index 351e700782..89af85f9c0 100644 --- a/plugins/Goals/API.php +++ b/plugins/Goals/API.php @@ -366,7 +366,6 @@ class API extends \Piwik\Plugin\API 'date' => $date, 'idGoal' => $idGoal, 'columns' => $columns, - 'serialize' => '0', 'format_metrics' => 'bc' )); diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php index 9ab7a96d66..571a5f75cd 100644 --- a/plugins/Goals/Pages.php +++ b/plugins/Goals/Pages.php @@ -292,8 +292,14 @@ class Pages $report['viewDataTable'] = 'tableGoals'; } + if (!empty($report['parameters'])) { + $params = array_merge($customParams, $report['parameters']); + } else { + $params = $customParams; + } + $widget = $this->createWidgetForReport($report['module'], $report['action']); - $widget->setParameters($customParams); + $widget->setParameters($params); $widget->setCategoryId($categoryText); $widget->setSubcategoryId($categoryText); $widget->setOrder($order); diff --git a/plugins/Goals/Reports/Base.php b/plugins/Goals/Reports/Base.php index 3f9cb9c98a..f9e36d6787 100644 --- a/plugins/Goals/Reports/Base.php +++ b/plugins/Goals/Reports/Base.php @@ -33,7 +33,7 @@ abstract class Base extends \Piwik\Plugin\Report $this->parameters = array('idGoal' => $goal['idgoal']); $this->order = $this->orderGoal + $goal['idgoal'] * 3; - $availableReports[] = $this->buildReportMetadata($availableReports, $infos); + $availableReports[] = $this->buildReportMetadata(); } $this->init(); diff --git a/plugins/Goals/config/config.php b/plugins/Goals/config/config.php deleted file mode 100644 index 1199467965..0000000000 --- a/plugins/Goals/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Goals\Tracker\GoalsRequestProcessor'), - )), - -); diff --git a/plugins/Goals/lang/cs.json b/plugins/Goals/lang/cs.json index 7bc7cbc8bb..e229f04e14 100644 --- a/plugins/Goals/lang/cs.json +++ b/plugins/Goals/lang/cs.json @@ -9,15 +9,15 @@ "BestCountries": "Země s nejvyšším počtem konverzí jsou:", "BestKeywords": "Klíčová slova s nejvyšším počtem konverzí jsou:", "BestReferrers": "Odkazující stránky s nejvyšším počtem konverzí jsou:", - "CaseSensitive": "shoda s velikostí písmen", + "CaseSensitive": "rozlišovat velikost písmen", "CancelAndReturnToGoals": "Zrušit a %svrátit se na seznam cílů%s", "CategoryTextGeneral_Visitors": "Umístění uživatele", "CategoryTextReferrers_Referrers": "Referrery", "CategoryTextVisitsSummary_VisitsSummary": "Uživatelský atribut", "CategoryTextGeneral_Visit": "Zapojení", "ChooseGoal": "Vyberte cíl", - "ClickOutlink": "Kliknout na odkaz na externí web", - "SendEvent": "Poslat událost", + "ClickOutlink": "Kliknou na odkaz na externí web", + "SendEvent": "Odešlou událost", "ColumnAverageOrderRevenueDocumentation": "Průměrná hodnota objednávky (AOV) je celkový příjem ze všech objednávek dělený jejich počtem.", "ColumnAveragePriceDocumentation": "Průměrný příjem z tohoto %s.", "ColumnAverageQuantityDocumentation": "Průměrný počet tohoto %s prodaný v elektronických objednávkách.", @@ -45,7 +45,7 @@ "DefaultRevenueHelp": "Na příklad kontaktní formulář odeslaný návštěvníkem má průměrnou cenu $10. Piwik vám pomůže dobře pochopit chování skupin uživatelů", "DeleteGoalConfirm": "Jste si jisti, že chcete vymazat tento cíl %s?", "DocumentationRevenueGeneratedByProductSales": "Tržby produktu. Nepočítají se daně, poplatky za doručení a slevy.", - "Download": "Stáhnout soubor", + "Download": "Stáhnou soubor", "Ecommerce": "Obchody", "EcommerceAndGoalsMenu": "Obchody a Cíle", "EcommerceLog": "Logy", @@ -58,17 +58,17 @@ "GoalConversion": "Cíl konverze", "GoalConversions": "Konverze cíle", "GoalConversionsBy": "Konverze cíle %s podle typu návštěvy", - "GoalIsTriggered": "Cíle je zaznamenáván", - "GoalIsTriggeredWhen": "Cíl je zaznamenáván, když", + "GoalIsTriggered": "Cíl je zaznamenán", + "GoalIsTriggeredWhen": "Cíl je zaznamenán, pokud", "GoalName": "Jméno cíle", "Goals": "Cíle", "ManageGoals": "Spravovat cíle", "GoalsOverview": "Přehled cílů", - "GoalsOverviewDocumentation": "Toto je přehled vašich konverzí cílů. VNe vchozím stavu graf zobrazuje součet všech konverzí. %s Pod grafem jsou zobrazena hlášení pro každý cíl. Můžete je zvětšit kliknutím, pokud chcete.", + "GoalsOverviewDocumentation": "Toto je přehled vašich konverzí cílů. Ve výchozím stavu graf zobrazuje součet všech konverzí. %s Pod grafem jsou zobrazena hlášení pro každý cíl. Můžete je zvětšit kliknutím, pokud chcete.", "GoalX": "Cíl: %s", "HelpOneConversionPerVisit": "Pokud byla stránka odpovídající cíli při návštěvě obnovena nebo zobrazena vícekrát, bude cíl započítán pouze jednou a to při prvním zobrazení.", "IsExactly": "je přesně %s", - "LearnMoreAboutGoalTrackingDocumentation": "Více o sledování cílu se dozvíte v %suživatelské dokumentaci%s.", + "LearnMoreAboutGoalTrackingDocumentation": "Více o sledování cílů se dozvíte v %suživatelské dokumentaci%s.", "LeftInCart": "%s Zbylo v košíku", "ManageGoalsOrCreateANewGoal": "%sSpravovat cíle%s nebo ho vytvořte.", "Manually": "ručně", @@ -86,7 +86,7 @@ "PluginDescription": "Vytvořte cíle a sledujte detailní hlášení o jejich konverzích: vývoj v čase, příjem za návštěvu, konverze prro referrer, pro klíčové slovo a více.", "ProductCategory": "Kategorie produktu", "ProductName": "Název produktu", - "Products": "Produktů", + "Products": "Produkty", "ProductSKU": "SKU produktu", "ReturningVisitorsConversionRateIs": "Poměr konverze navracejících se uživatelů je %s", "SingleGoalOverviewDocumentation": "Toto je přehled konverzí jednoho cíle. %s Linky lze zvětšit kliknutím na ně.", @@ -96,12 +96,12 @@ "ViewAndEditGoals": "Zobrazit a editovat cíle", "GoalsBy": "Cíle podle %s", "GoalsAdjective": "Cíle %s", - "VisitPageTitle": "Navštívit stránku s daným titulkem", + "VisitPageTitle": "Navštíví stránku s daným titulkem", "VisitsUntilConv": "Návštěv ke konverzi", - "VisitUrl": "Navštívít zadanou URL (stránku, nebo skupiny stránek)", - "WhenVisitors": "když návštěvníci", + "VisitUrl": "Navštíví zadanou URL (stránku nebo skupiny stránek)", + "WhenVisitors": "pokud návštěvníci", "WhereThe": "když", - "WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore": "kde návštívená stránka obsahuhe volání metody JavaScriptu piwikTracker.trackGoal() (%svíce%s)", - "YouCanEnableEcommerceReports": "Pro tyto stránky můžete %s povolit na stránce %s." + "WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore": "kde navštívená stránka obsahuhe volání metody JavaScriptu 'trackGoal' (%svíce%s)", + "YouCanEnableEcommerceReports": "Pro tyto stránky můžete povolit %s na stránce %s." } }
\ No newline at end of file diff --git a/plugins/Goals/lang/fr.json b/plugins/Goals/lang/fr.json index c8945cf794..8518515c2e 100644 --- a/plugins/Goals/lang/fr.json +++ b/plugins/Goals/lang/fr.json @@ -76,7 +76,7 @@ "MatchesExpression": "correspond à l'expression %s", "NewGoalIntro": "Le suivi de la conversion des objectifs et une des manières les plus efficaces de mesurer et améliorer vos objectifs d'affaires.", "NewVisitorsConversionRateIs": "Le taux de conversion des nouveaux visiteurs est %s", - "NoGoalsNeedAccess": "Uniquement un Administrateur ou un utilisateur avec un accès Super Utilisateur peut gérer les Objectifs d'un site web donné. Veuillez demander à votre administrateur Piwik de mettre en place un Objectif pour votre site web. <br \/>Le suivit d'Objectifs est une bonne manière de mieux comprendre et de maximiser les performances de votre site web!", + "NoGoalsNeedAccess": "Uniquement un Administrateur ou un utilisateur avec un accès Super Utilisateur peut gérer les Objectifs d'un site web donné. Veuillez demander à votre administrateur Piwik de mettre en place un Objectif pour votre site web. <br \/>Le suivi d'objectifs est une bonne manière de mieux comprendre et de maximiser les performances de votre site web!", "NeedAccess": "Uniquement un administrateur ou un utilisateur avec le rôle super utilisateur peut gérer les objectifs pour un site web.", "Optional": "(optionnel)", "OverallConversionRate": "%s taux de conversion global (visites avec un objectif rempli)", diff --git a/plugins/Goals/lang/ja.json b/plugins/Goals/lang/ja.json index 9699895d82..bbb26f8859 100644 --- a/plugins/Goals/lang/ja.json +++ b/plugins/Goals/lang/ja.json @@ -11,6 +11,10 @@ "BestReferrers": "ベストコンバージョンの参照元ウェブサイト", "CaseSensitive": "大文字小文字を区別する一致", "CancelAndReturnToGoals": "キャンセルして目標 %s のリスト %s に戻る", + "CategoryTextGeneral_Visitors": "ユーザーの位置情報", + "CategoryTextReferrers_Referrers": "リファラー", + "CategoryTextVisitsSummary_VisitsSummary": "ユーザー属性", + "CategoryTextGeneral_Visit": "エンゲージメント", "ChooseGoal": "Choose Goal", "ClickOutlink": "外部ウェブサイトへのリンクをクリック", "SendEvent": "イベントを送信", @@ -35,6 +39,7 @@ "ConversionsOverview": "コンバージョンの概観", "ConversionsOverviewBy": "ビジットの種類によるコンバージョン概観", "DaysToConv": "日数別のコンバージョン", + "Details": "目標の詳細", "DefaultGoalConvertedOncePerVisit": "(デフォルト)目標達成は訪問の都度1回だけカウント", "DefaultRevenue": "目標のデフォルト収益", "DefaultRevenueHelp": "例えば、ビジターによって送信されたコンタクトフォームが、平均して $10 の価値を持つかもしれません。 Piwik は、ビジターセグメントがどれくらいうまく実行しているかを理解するのを助けます。", @@ -71,6 +76,8 @@ "MatchesExpression": "正規表現 %s に一致する", "NewGoalIntro": "目標コンバージョントラッキングは、あなたのビジネス目標の測定や改善のために、最も効率的な方法の一つです。", "NewVisitorsConversionRateIs": "新規ビジターのコンバージョン率は %s です", + "NoGoalsNeedAccess": "管理者またはスーパー ユーザーのアクセス権を持つユーザーだけが、ウェブサイトの目標を管理できます。ウェブサイトの目標を設定する場合は Piwik の管理者に依頼してください。<br> 目標を追跡することは、ウェブサイトの理解に役立ち、パフォーマンスを最大限に高めるための素晴らしい方法です!", + "NeedAccess": "管理者またはスーパー ユーザーのアクセス権を持つユーザーだけが、ウェブサイトの目標を管理できます。", "Optional": "(オプション)", "OverallConversionRate": "%s 総コンバージョン率(目標達成ビジット)", "OverallRevenue": "%s 総収益", @@ -87,6 +94,8 @@ "UpdateGoal": "目標を更新", "URL": "URL", "ViewAndEditGoals": "目標の表示と編集", + "GoalsBy": "%s の目標", + "GoalsAdjective": "目標 %s", "VisitPageTitle": "入力されたタイトルのページを訪問", "VisitsUntilConv": "ビジット数別のコンバージョン", "VisitUrl": "特定の URL を訪問(ページまたはページグループ)", diff --git a/plugins/Goals/lang/ko.json b/plugins/Goals/lang/ko.json index 705ff073c9..8dc370de94 100644 --- a/plugins/Goals/lang/ko.json +++ b/plugins/Goals/lang/ko.json @@ -10,7 +10,14 @@ "BestKeywords": "상위 전환 검색어:", "BestReferrers": "최고 전환 참조 웹사이트:", "CaseSensitive": "대소문자 구분함", + "CancelAndReturnToGoals": "취소하고 %s목표 리스트로 돌아가기%s", + "CategoryTextGeneral_Visitors": "사용자 위치", + "CategoryTextReferrers_Referrers": "리퍼러", + "CategoryTextVisitsSummary_VisitsSummary": "사용자 속성", + "CategoryTextGeneral_Visit": "약속", + "ChooseGoal": "목표 선택", "ClickOutlink": "외부 웹사이트에 대한 링크 클릭", + "SendEvent": "이벤트 보내기", "ColumnAverageOrderRevenueDocumentation": "평균 주문액 (AOV)은 모든 전자상 거래 주문의 총수익을 주문수로 나눈 것입니다.", "ColumnAveragePriceDocumentation": "이 %s의 평균 수익.", "ColumnAverageQuantityDocumentation": "전자상거래 주문에서 판매된 %s의 평균 수량입니다.", @@ -32,6 +39,7 @@ "ConversionsOverview": "전환 개요", "ConversionsOverviewBy": "방문 유형별 전환 개요", "DaysToConv": "일별 전환", + "Details": "목표 상세", "DefaultGoalConvertedOncePerVisit": "(기본) 목표 달성은 방문할 때 마다 한번만 계산", "DefaultRevenue": "목표의 기본 수익은", "DefaultRevenueHelp": "예를 들어 방문자가 전송된 연락처 양식이 평균적으로 10원의 가치가 있을지도 모릅니다. Piwik은 방문자 세그먼트가 얼마나 잘 수행하고 있는지를 이해하는 것을 돕고 있습니다.", @@ -54,30 +62,40 @@ "GoalIsTriggeredWhen": "목표 트리거 시점", "GoalName": "목표 이름", "Goals": "목표", + "ManageGoals": "목표 관리", "GoalsOverview": "목표 개요", "GoalsOverviewDocumentation": "이것은 목표 전환 개요입니다. 먼저 그래프는 모든 총 전환을 보여줍니다. %s 그래프 아래에 각각의 목표에 대한 전환 보고서를 볼 수 있습니다. 스파크 라인은 클릭하여 확대 할 수 있습니다.", "GoalX": "목표 %s", "HelpOneConversionPerVisit": "만약 목표에 일치하는 페이지가 업데이트되거나 한번 이상 방문해도, 첫 번째 방문시에만 목표가 달성됩니다.", "IsExactly": "%s와 일치", + "LearnMoreAboutGoalTrackingDocumentation": "사용자 문서 내 %sPiwik 목표 추적%s 문서를 통해서 좀 더 알 수 있습니다.", "LeftInCart": "카트에 남겨진 %s", + "ManageGoalsOrCreateANewGoal": "%s목표 관리%s 혹은 새로운 목표를 만드세요!", "Manually": "수동으로", "ManuallyTriggeredUsingJavascriptFunction": "JavaScript API의 trackGoal()을 사용하여 수동 트리거", "MatchesExpression": "정규식 %s에 일치", + "NewGoalIntro": "목표 전환 추적은 당신의 비지니스 목적을 측정하고 향상시키는 매우 효과적인 방법입니다.", "NewVisitorsConversionRateIs": "신규 방문자의 전환율은 %s", + "NoGoalsNeedAccess": "주어진 웹사이트는 오직 관리자나 슈퍼 유저 권한을 가진 사용자만이 목표를 관리할 수 있습니다. Piwik의 관리자에게 당신의 웹사이트 목표 설정을 요청하세요. <br>목표 추적은 웹사이트 성능을 이해하고 향상시키는데 큰 도움이 됩니다.", + "NeedAccess": "주어진 웹사이트는 오직 관리자나 슈퍼 유저 권한을 가진 사용자만이 목표를 관리할 수 있습니다.", "Optional": "(선택항목)", "OverallConversionRate": "%s 총 전환율 (목표 달성 방문)", "OverallRevenue": "%s 총 수익", "PageTitle": "페이지 제목", "Pattern": "패턴", + "PluginDescription": "목표를 만들고 목표 전환들 - 시간에 따른 변화, 방문 당 수익, 리퍼러와 키워드 당 전환들 - 에 대한 상세 보고서 보기", "ProductCategory": "제품 카테고리", "ProductName": "제품 이름", "Products": "제품", "ProductSKU": "제품 번호", "ReturningVisitorsConversionRateIs": "재방분 전환율은 %s", "SingleGoalOverviewDocumentation": "이것은 하나의 목표 전환 개요입니다. %s 그래프 아래의 스파크라인은 클릭하여 확대할 수 있습니다.", + "ThereIsNoGoalToManage": "웹사이트 %s에는 관리할 목표가 없습니다.", "UpdateGoal": "목표 갱신", "URL": "URL", "ViewAndEditGoals": "목표 보기 및 편집", + "GoalsBy": "%s가 만든 목표", + "GoalsAdjective": "%s 목표들", "VisitPageTitle": "입력된 제목의 페이지 방문", "VisitsUntilConv": "방문수별 전환", "VisitUrl": "특정 URL 방문 (페이지 또는 페이지 그룹)", diff --git a/plugins/Heartbeat/config/config.php b/plugins/Heartbeat/config/config.php deleted file mode 100644 index 6f7a1417ab..0000000000 --- a/plugins/Heartbeat/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Heartbeat\Tracker\PingRequestProcessor'), - )), - -); diff --git a/plugins/ImageGraph/API.php b/plugins/ImageGraph/API.php index 4e7b466b21..ffdd223d46 100644 --- a/plugins/ImageGraph/API.php +++ b/plugins/ImageGraph/API.php @@ -122,7 +122,8 @@ class API extends \Piwik\Plugin\API $gridColor = API::DEFAULT_GRID_COLOR, $idSubtable = false, $legendAppendMetric = true, - $segment = false + $segment = false, + $idDimension = false ) { Piwik::checkUserHasViewAccess($idSite); @@ -151,6 +152,9 @@ class API extends \Piwik\Plugin\API if (!empty($idGoal)) { $apiParameters = array('idGoal' => $idGoal); } + if (!empty($idDimension)) { + $apiParameters = array('idDimension' => $idDimension); + } // Fetch the metadata for given api-action $parameters = array( 'idSite' => $idSite, @@ -305,6 +309,7 @@ class API extends \Piwik\Plugin\API 'column' => $plottedMetric, 'language' => $languageLoaded, 'idGoal' => $idGoal, + 'idDimension' => $idDimension, 'legendAppendMetric' => $legendAppendMetric, 'labelUseAbsoluteUrl' => false ); @@ -361,6 +366,7 @@ class API extends \Piwik\Plugin\API 'segment' => $segment, 'apiParameters' => false, 'idGoal' => $idGoal, + 'idDimension' => $idDimension, 'language' => $languageLoaded, 'showTimer' => true, 'hideMetricsDoc' => false, @@ -506,7 +512,10 @@ class API extends \Piwik\Plugin\API if ($idGoal != '') { $idGoal = '_' . $idGoal; } - $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png'; + if ($idDimension != '') { + $idDimension = '__' . $idDimension; + } + $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . $idDimension . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png'; $fileName = str_replace(array(' ', '/'), '_', $fileName); if (!Filesystem::isValidFilename($fileName)) { diff --git a/plugins/Insights/Visualizations/Insight.php b/plugins/Insights/Visualizations/Insight.php index 7fe304e5be..91d2c90b63 100644 --- a/plugins/Insights/Visualizations/Insight.php +++ b/plugins/Insights/Visualizations/Insight.php @@ -40,6 +40,10 @@ class Insight extends Visualization $report = $this->requestConfig->apiMethodToRequestDataTable; $report = str_replace('.', '_', $report); + if (!empty($this->requestConfig->request_parameters_to_modify['reportUniqueId'])) { + $report = $this->requestConfig->request_parameters_to_modify['reportUniqueId']; + } + $this->requestConfig->apiMethodToRequestDataTable = 'Insights.getInsights'; $this->requestConfig->request_parameters_to_modify = array( diff --git a/plugins/Insights/lang/cs.json b/plugins/Insights/lang/cs.json index f389736692..7abc7c7112 100644 --- a/plugins/Insights/lang/cs.json +++ b/plugins/Insights/lang/cs.json @@ -8,7 +8,7 @@ "DayComparedToPreviousWeek": "Stejný den v minulém týdnu", "DayComparedToPreviousYear": "Stejný den v předchozím roce", "Filter": "Filtr", - "FilterIncreaserAndDecreaser": "Zvyšovač & snižovač", + "FilterIncreaserAndDecreaser": "Zvyšovač a snižovač", "FilterOnlyDecreaser": "Pouze snižovač", "FilterOnlyDisappeared": "Pouze ztracení", "FilterOnlyIncreaser": "Pouze zvyšovač", diff --git a/plugins/Insights/lang/ja.json b/plugins/Insights/lang/ja.json index 9695c7fa54..db9cce115b 100644 --- a/plugins/Insights/lang/ja.json +++ b/plugins/Insights/lang/ja.json @@ -1,5 +1,6 @@ { "Insights": { + "PluginDescription": "トラフィックについてのインサイトを提供しています。インサイトは、ダッシュボードウィジェットだけでなく、データの中で最も重要なトレンドを見るためのレポートで新しいアイコンとして使用できます。", "ControlComparedToDescription": "比較する実績値は", "ControlFilterByDescription": "すべて、変動のみ、新規のみ、削除のみを表示", "DatePeriodCombinationNotSupported": "この日付と期間の組み合わせに対するインサイトは作成できません。", diff --git a/plugins/Installation/lang/cs.json b/plugins/Installation/lang/cs.json index b6ffa6dba8..8e32ed6f4a 100644 --- a/plugins/Installation/lang/cs.json +++ b/plugins/Installation/lang/cs.json @@ -4,7 +4,7 @@ "ConfigurationHelp": "Váš konfigurační soubor Piwiku je špatně nastavený. Můžete buď odstranit soubor config\/config.ini a znovu začít instalaci, nebo opravit nastavení databáze.", "ConfirmDeleteExistingTables": "Jste si jistí, že chcete vymazat tabulky: %s z vaší databáze? UPOZORNĚNÍ: DATA Z TĚCHTO TABULEK NEPŮJDOU OBNOVIT!", "Congratulations": "Gratulujeme", - "CongratulationsHelp": "<p>Gratulujeme! Vaše instalace Piwiku je komplentní.<\/p><p>Ujistěte se, že máte JavaScriptový kód vložen do vašich stránek a čekejte na první návštěvníky!<\/p>", + "CongratulationsHelp": "<p>Gratulujeme! Vaše instalace Piwiku je kompletní.<\/p><p>Ujistěte se, že máte JavaScriptový kód vložen do svých stránek a čekejte na první návštěvníky!<\/p>", "DatabaseAbilities": "Schopnosti databáze", "DatabaseCreation": "Vytvoření databáze", "DatabaseErrorConnect": "Nastala chyba při připojování k databázovému serveru", @@ -15,7 +15,7 @@ "DatabaseSetupLogin": "uživatelské jméno", "DatabaseSetupServer": "server s databází", "DatabaseSetupTablePrefix": "prefix tabulek", - "Email": "E-mail", + "Email": "Email", "Extension": "přípona", "Filesystem": "Souborový systém", "GetInvolved": "Pokud se vám líbí, co vidíte, %1$szapojte se%2$s.", @@ -28,12 +28,12 @@ "InvalidStateError": "Chyba: Piwik je již nainstalován. %1$s Jít zpět %2$s do piwiku%3$s.", "JsTagArchivingHelp1": "Pro střední a velké stránky jsou určité optimalizace, které umožňují Piwiku běžet rychleji, jako třeba %1$snastavení automatického archivování%2$s.", "JSTracking_EndNote": "Poznámka: Po dokončení procesu instalace můžete vytvořit vlastní sledovací kód na stránce %1$ssledovací kód%2$s.", - "JSTracking_Intro": "Pokud chcete sledovat Pokud chcete Piwikem sledovat váš provoz na webu, musíte zajistit, že do každé stránky bude přidán extra kód.", + "JSTracking_Intro": "Pokud chcete Piwikem sledovat provoz na vašem webu, musíte zajistit, že do každé stránky bude přidán extra kód.", "LargePiwikInstances": "Nápověda pro velké instalace Piwiku", "Legend": "Legenda", "LoadDataInfileRecommended": "Pokud váš Piwik server zaznamenává provoz na stránkách s vysokým provozem t. j. 100000 stránek za měsíc, doporučujeme se pokusit o řešení tohoto problému.", "LoadDataInfileUnavailableHelp": "Použití %1$s velmi zrychlí archivační proces Piwiku. Pro zpřístupnění zkuste aktualizovat PHP a Mysql a ujistěte se, že má databázový uživatel privilegium %2$s.", - "NfsFilesystemWarning": "Váš server používá soborový systém NFS.", + "NfsFilesystemWarning": "Váš server používá souborový systém NFS.", "NfsFilesystemWarningSuffixAdmin": "To znamená, že Piwik bude při použití sessions založených na souborech velmi pomalý.", "NfsFilesystemWarningSuffixInstall": "Sessions založené na souborech jsou na NFS velmi pomalé. Proto Piwik použije databázové sessions. Pokud bude k nástěnkám přistupovat současně hodně uživatelů, možná budete muset zvýšit maximální počet klientských databázových spojení v konfiguraci databázového serveru.", "NoConfigFound": "Konfigurační soubor Piwiku nebyl nalezen a snažíte se vstoupit na stránku Piwiku.<br \/><b> » Můžete <a href='index.php'>teď nainstalovat<\/a><\/b><br \/><small>Pokud jste Piwik již instalovali a máte v DB nějaké tabulky, nemějte obavy. Můžete je použít a zachovat jejich data.!<\/small>", @@ -48,7 +48,7 @@ "Requirements": "Požadavky Piwiku", "RestartWebServer": "Po uložení změn restartujte Vás web server.", "ReusingTables": "Použití existujících tabulek", - "PiwikOrgNewsletter": "odesílat e-maily s důležitými událostmi v komunitě Piwiku", + "PiwikOrgNewsletter": "odesílat emaily s důležitými událostmi v komunitě Piwiku", "PiwikProNewsletter": "Zasílejte mi informace o službách a nabídkách %sPiwik pro%s", "SeeBelowForMoreInfo": "Pro více informací se podívejte níže.", "SetupWebsite": "Nastavit Web", @@ -57,7 +57,7 @@ "SetupWebsiteSetupSuccess": "Web %s byl úspěšně vytvořen", "SetupWebSiteURL": "URL webových stránek", "SiteSetup": "Prosím vytvořte první stránky, které budou sledovány a analyzovány Piwikem:", - "SiteSetupFootnote": "Poznámka: Jakmile bude instalace Piwiku dokončena, budete moct přidat další stránky ke sledování.", + "SiteSetupFootnote": "Poznámka: Jakmile bude instalace Piwiku dokončena, budete moci přidat další stránky ke sledování.", "SuperUser": "Super uživatel", "SuperUserLogin": "Přihlašovací jméno super uživatele", "SuperUserSetupError": "Při přidávání super uživatele došlo k chybě", @@ -85,7 +85,7 @@ "SystemCheckMbstring": "mbstring", "SystemCheckMbstringHelp": "Rozšíření mbstring je nutné ke zpracování vícebajtových znaků v uživatelském rozhraní a odpovědích API. Také zajistěte, aby byla v souboru php.ini hodnota mbstring.func_overload nastavena na 0.", "SystemCheckMemoryLimit": "Limit paměti", - "SystemCheckMemoryLimitHelp": "Na webech s vysokým provozem, může archivace vyžadovat více paměti něž je nyní povoleno.<br \/>Pokud je potřeba podívejte se na direktivu memory_limit ve vašem souboru php.ini.", + "SystemCheckMemoryLimitHelp": "Na webech s vysokým provozem, může archivace vyžadovat více paměti něž je nyní povoleno.<br \/>Pokud je potřeba, podívejte se na direktivu memory_limit ve vašem souboru php.ini.", "SystemCheckOpenURL": "Otevřít URL", "SystemCheckOpenURLHelp": "Předplatné novinek, informace o aktualizaci a aktualizace jedním kliknutím vyžadují rozšíření \"curl\", allow_url_fopen=On, nebo povolené fsockopen().", "SystemCheckOtherExtensions": "Ostatní rozšíření", @@ -93,20 +93,20 @@ "SystemCheckPageSpeedDisabled": "PageSpeed zakázán", "SystemCheckPageSpeedWarn": "Doporučujeme modul PageSpeed zakázat v konfiguraci webového serveru: %s Bylo hlášeno, že způsobuje problémy s Piwikem.", "SystemCheckPackHelp": "Funkce pack() je nutná ke sledování návštěvnosti v Piwiku.", - "SystemCheckParseIniFileHelp": "Tato vestavěná funkce byla na vašem hostiteli zakázaná. Piwik se pokusí tuto funkci emulovat, ale můžete zaznamenat další bezpečnostní omezení. Výkonnost trackeru bude také omezena.", + "SystemCheckParseIniFileHelp": "Tato vestavěná funkce byla na vašem hostiteli zakázána. Piwik se pokusí tuto funkci emulovat, ale můžete zaznamenat další bezpečnostní omezení. Výkonnost trackeru bude také omezena.", "SystemCheckPdoAndMysqliHelp": "Na GNU\/Linux systému můžete zkompilovat PHP s následujícími volbami: %1$s Přidejte následující řádky do php.ini: %2$s", "SystemCheckPhp": "Verze PHP", "SystemCheckPhpPdoAndMysqli": "Více informací na: %1$sPHP PDO%2$s and %3$sMYSQLI%4$s.", "SystemCheckSplHelp": "Musíte překompilovat PHP s povolenou standardní PHP knihovou (ve výchozím stavu povolena).", "SystemCheckSettings": "Požadovaná konfigurace PHP (php.ini)", - "SystemCheckSummaryNoProblems": "Hurááá! Nejsou zde žádné problémy s nastavením Piwiku. Gratulujeme", + "SystemCheckSummaryNoProblems": "Hurááá! Nejsou zde žádné problémy s nastavením Piwiku. Gratulujeme!", "SystemCheckSummaryThereWereErrors": "Ajaj! Piwik zjistil %1$skritické problémy%2$s s nastavením instalace Piwiku. %3$sTyto problémy musí být okamžitě vyřešeny.%4$s", - "SystemCheckSummaryThereWereWarnings": "JVyskytly se problémy s vaším systémem. Piwik poběží, ale možná se nevyhnete menším problémům.", - "SystemCheckTimeLimitHelp": "na webech s vysokým přenosem, může archivace vyžadovat více času, než je nyní povoleno.<br \/>Pokud je potřeba podívejte se na direktivu max_execution_time ve vašem souboru php.ini", + "SystemCheckSummaryThereWereWarnings": "Vyskytly se problémy s vaším systémem. Piwik poběží, ale možná se nevyhnete menším problémům.", + "SystemCheckTimeLimitHelp": "na webech s vysokým přenosem, může archivace vyžadovat více času, než je nyní povoleno. Pokud je potřeba, podívejte se na direktivu max_execution_time ve svém souboru php.ini", "SystemCheckTracker": "Stav sledování", - "SystemCheckTrackerHelp": "Get požadavek na piwik.php selhal. Zkuste vyloučit tuto URL z HTTP autentizace a zakažte mod_security (možná budete muset požádat vašeho poskytovatele hostingu). Více informací naleznete v záznamu chyb web serveru.", + "SystemCheckTrackerHelp": "Get požadavek na piwik.php selhal. Zkuste vyloučit tuto URL z HTTP autentizace a zakažte mod_security (možná budete muset požádat svého poskytovatele hostingu). Více informací naleznete v záznamu chyb web serveru.", "SystemCheckWarnDomHelp": "Měli byste povolit \"dom\" rozšíření t. j. nainstalovat balíček \"php-dom\" nebo \"php-xml\".", - "SystemCheckWarning": "Piwik bude pracovat normálně, ale některé funkce budou chybět", + "SystemCheckWarning": "Piwik bude pracovat normálně, ale některé funkce mohou chybět", "SystemCheckWarnJsonHelp": "Pro lepší výkon byste měli povolit rozšíření \"json\" t. j. nainstalovat balíček \"php-json\".", "SystemCheckWarnLibXmlHelp": "Protože je vyžadováno dalšími php rozšířeními, měli byste povolit rozšíření \"libxml\" t. j. nainstalovat balíček \"php-libxml\".", "SystemCheckWarnSimpleXMLHelp": "Měli byste povolit rozšíření \"SimpleXML\" t. j. nainstalovat balíček \"php-simplexml\" nebo \"php-xml\".", @@ -116,9 +116,9 @@ "SystemCheckZlibHelp": "Musíte nakonfigurovat a překompilovat PHP s podporou pro \"zlib\", --with-zlib", "SystemCheckCronArchiveProcess": "Archivační cron", "SystemCheckCronArchiveProcessCLI": "Správa procesů pomocí CLI", - "SystemCheckPhpSetting": "Abyste zabránili vážným problémům, ve vašem souboru php.ini musíte nastavit %s", + "SystemCheckPhpSetting": "Abyste zabránili vážným problémům, ve svém souboru php.ini musíte nastavit %s", "SystemCheckUpdateHttps": "Aktualizace přes HTTPS", - "SystemCheckUpdateHttpsNotSupported": "Piwik nemůže použít HTTPS pro aktualizaci, bude použit nezabezpečený protokol HTTP. Ověřte že je podporováno CURL nebo allow_url_fopen a že je nainstalované rozšíření openssl PHP: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/.", + "SystemCheckUpdateHttpsNotSupported": "Piwik nemůže použít HTTPS pro aktualizaci, bude použit nezabezpečený protokol HTTP. Ověřte, že je podporováno CURL nebo allow_url_fopen a že je nainstalované rozšíření openssl PHP: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/.", "NotSupported": "nepodporováno", "Tables": "Vytváření tabulek", "TablesCreatedSuccess": "Tabulky vytvořeny úspěšně!", @@ -132,9 +132,9 @@ "Timezone": "Časové pásmo webových stránek", "WeHopeYouWillEnjoyPiwik": "Doufáme, že si užijete používání Piwiku tak, jako si my užíváme jeho vývoj.", "Welcome": "Vítejte!", - "WelcomeHelp": "<p>Piwik je open source program pro analýzu webu, pomocí kterého můžete jednoduše získat informace, které chcete od vaších návštěvníků.<\/p><p>Tento proces je rozdělen to %s jednoduchých kroků a zabere přibližne 5 minut.<\/p>", + "WelcomeHelp": "<p>Piwik je volně dostupný open source program pro analýzu webu, pomocí kterého můžete jednoduše získávat informace o návštěvnících svého webu.<\/p><p>Tento proces je rozdělen do %s jednoduchých kroků a zabere přibližne 5 minut.<\/p>", "WelcomeToCommunity": "Vítejte v komunitě Piwiku!", "CannotConnectToDb": "Nelze se připojit k databázi", - "CannotConnectToDbResolvingExplanation": "To může být dočasný problém, zkuste %1$sobnovit stránku%2$s. Pokud problém přetrvá, kontaktujte vašeho administrátora Piwiku." + "CannotConnectToDbResolvingExplanation": "To může být dočasný problém, zkuste %1$sobnovit stránku%2$s. Pokud problém přetrvá, kontaktujte svého administrátora Piwiku." } }
\ No newline at end of file diff --git a/plugins/Installation/lang/fr.json b/plugins/Installation/lang/fr.json index 1fea6290ca..d20170b82a 100644 --- a/plugins/Installation/lang/fr.json +++ b/plugins/Installation/lang/fr.json @@ -115,7 +115,7 @@ "SystemCheckZlibHelp": "Vous devez reconfigurer et recompiler PHP avec le support zlib, --with-zlib.", "SystemCheckCronArchiveProcess": "Tâche Cron d'archivage", "SystemCheckCronArchiveProcessCLI": "Gestion des processus via la ligne de commande", - "SystemCheckPhpSetting": "Pour éviter certaines erreurs critique, vous devez définier les entrées suivantes dans votre fichier php.ini : %s", + "SystemCheckPhpSetting": "Pour éviter certaines erreurs critique, vous devez définir les entrées suivantes dans votre fichier php.ini : %s", "SystemCheckUpdateHttps": "Mettre à jour via HTTPS", "SystemCheckUpdateHttpsNotSupported": "Piwik ne peut pas utiliser du HTTPS pour se mettre à jour, il va rétrograder vers une mise à jour utilisant du HTTP non sécurisé. Vérifiez que CURL ou allow_url_fopen est supporté et que l'extesion PHP openssl est installée: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/", "NotSupported": "non supporté", diff --git a/plugins/Installation/lang/ja.json b/plugins/Installation/lang/ja.json index d6d57a8600..a2bfe0e5b4 100644 --- a/plugins/Installation/lang/ja.json +++ b/plugins/Installation/lang/ja.json @@ -15,6 +15,7 @@ "DatabaseSetupLogin": "ログイン名", "DatabaseSetupServer": "データベースサーバー", "DatabaseSetupTablePrefix": "テーブルプレフィックス", + "Email": "メール", "Extension": "エクステンション", "Filesystem": "ファイルシステム", "GetInvolved": "もし Piwik を気にって頂けたら、是非 %1$sget involved%2$s 。", @@ -24,6 +25,7 @@ "InstallationStatus": "インストール状況", "InsufficientPrivilegesHelp": "phpMyAdminのようなツールを使用するか、正しいSQLクエリを実行することにより、これらの権限を追加できます。やり方がわからない場合は、システム管理者に依頼して、権限を付与してもらってください。", "InsufficientPrivilegesMain": "データベースが存在しない ( および作成できない ) 、または具体的なユーザーが十分な権限を持っていない可能性があります。データベースユーザーは、次の権限を有する必要があります。: %s", + "InvalidStateError": "エラー: Piwik が既にインストールされています。Piwik %3$s へ %1$s 戻る %2$s 。", "JsTagArchivingHelp1": "中~高トラフィックのウェブサイトのために、Piwik をより早く(例えば %1$ssetting up auto-archiving%2$s のように) 実行するための最適化の手段があります。", "JSTracking_EndNote": "注:インストールの処理後、%1$sTracking Code%2$s の管理セクションで、トラッキングコードのカスタマイズを行うことができます。", "JSTracking_Intro": "Piwik でウェブトラフィックの追跡をするには、一部の特別なコードが各 web ページに追加されているかどうかを確認する必要があります。", @@ -36,20 +38,28 @@ "NfsFilesystemWarningSuffixInstall": "NFS 上でファイルベースのセッションを利用すると極端に遅いので、Piwik はデータベースセッションを使用します。同時に多くのダッシュボードユーザーがいる場合、データベースサーバーへのクライアント接続の最大数を増やす必要があります。", "NoConfigFound": "Piwik の設定ファイルを見つけることができませんでしたが、あなたは Piwik ページにアクセスしようとしています。<br \/><b> »<a href='index.php'>ただちに Piwik をインストール<\/a>することができます。<\/b><br \/><small>以前に Piwik をインストールしたことがあり、DB に多少のデータがある場合でも心配しないでください。 既存のデータを保持したまま、同じテーブルを再利用することができます!<\/small>", "Optional": "オプション", + "Password": "パスワード", "PasswordDoNotMatch": "パスワードが一致しませんでした", + "PasswordRepeat": "パスワード (再入力)", "PercentDone": "%s %% 完了", "PleaseFixTheFollowingErrors": "次のエラーを修正してください", + "DefaultSettings": "Piwik のデフォルトの設定", + "DefaultSettingsHelp": "Piwik の設定はデフォルトです。後で管理画面からカスタマイズすることができます。", "Requirements": "Piwik の動作環境", "RestartWebServer": "この変更を行った後、ウェブサーバーを再起動してください。", "ReusingTables": "表の再利用", "PiwikOrgNewsletter": "私に Piwik コミュニティの最新情報をメールで送る", + "PiwikProNewsletter": "%s Piwik PRO %s のサービスや最新情報を送ってください", "SeeBelowForMoreInfo": "詳細は、以下をご確認ください。", "SetupWebsite": "ウェブサイトのセットアップ", "SetupWebsiteError": "ウェブサイトを追加する際にエラーが発生しました", + "SetupWebSiteName": "ウェブサイトの名前", "SetupWebsiteSetupSuccess": "ウェブサイト %s は正常に作成されました!", + "SetupWebSiteURL": "ウェブサイトの URL", "SiteSetup": "Piwik で追跡・分析したい初めのウェブサイトを設定してください。", "SiteSetupFootnote": "注: Piwik のインストールが完了すると、さらに追跡対象のウェブサイトを追加することができるようになります!", "SuperUser": "スーパーユーザー", + "SuperUserLogin": "スーパーユーザーのログイン", "SuperUserSetupError": "スーパーユーザーの追加時に、エラーが発生しました。", "SuperUserSetupSuccess": "スーパーユーザーの作成に成功しました!", "SystemCheck": "システムの確認", @@ -63,6 +73,7 @@ "SystemCheckExtensions": "その他の必須エクステンション", "SystemCheckFileIntegrity": "ファイルの整合性", "SystemCheckFunctions": "必須関数", + "SystemCheckFunctionHelp": "あなたは、このビルトイン関数を有効にする必要があります。", "SystemCheckGDFreeType": "GD > 2.x + Freetype (graphics)", "SystemCheckGDHelp": "スパークライン(小さなグラフ)は動作しません。", "SystemCheckGlobHelp": "この組み込み関数はホストで無効化されています。 Piwik はこの関数のエミュレートを試みますが、さらなるセキュリティ制限に遭遇する場合があります。 また、機能性にも影響を与える場合があります。", @@ -93,6 +104,7 @@ "SystemCheckSummaryThereWereWarnings": "システムにいくつかの問題があります。Piwik は実行できますが、いくつか小さな問題がみられる可能性があります。", "SystemCheckTimeLimitHelp": "アクセス数の多いウェブサイトでは、アーカイブ処理の実行に、現在許可されている以上に多くの時間を必要とする場合があります。<br \/>必要であれば、php.ini ファイルのディレクティブ max_execution_time を参照してください。", "SystemCheckTracker": "トラッカー状況", + "SystemCheckTrackerHelp": "失敗した piwik.php へのリクエストを取得します。HTTP 認証からのこの URL のホワイト リスト化を試みて、mod_security を無効にします(ウェブホストに依頼する必要があります)。エラーの詳細については、web サーバーのエラー ログ ファイルを確認します。", "SystemCheckWarnDomHelp": "\"dom\" エクステンションを有効にする必要があります(例えば \"php-dom\" と \"php-xml\" パッケージのどちらか、あるいは両方をインストールする)。", "SystemCheckWarning": "Piwik は正常に動作しますが、いくつかの機能は動作しないかもしれません。", "SystemCheckWarnJsonHelp": "より良いパフォーマンスのために、\"json\" エクステンションを有効にする必要があります(例えば \"php-json\" パッケージをインストールする)。", @@ -117,6 +129,7 @@ "TablesUpdatedSuccess": "データベースの %1$s から %2$s へのアップデートが成功しました。", "TablesWarningHelp": "既存のデータベーステーブルを再利用するか、データベース内の全データを消去してクリーンインストールするかを選択してください。", "TablesWithSameNamesFound": "%1$s既存のデータベース内のいくつかのテーブル%2$sが、Piwik が作成しようとしているテーブルと同じ名前を持っています", + "Timezone": "ウェブサイトのタイムゾーン", "WeHopeYouWillEnjoyPiwik": "私達が Piwik の開発を楽しんでいるように、Piwik のご利用をお楽しみ頂ける事を願っています。", "Welcome": "ようこそ!", "WelcomeHelp": "<p>Piwik は、ビジターからあなたが必要とする情報を収集することを容易にする、オープンソースのウェブ解析ソフトウェアです。<\/p><p>この処理は %s の簡単なステップに分かれていて、5分程度で完了します。<\/p>", diff --git a/plugins/Installation/lang/ko.json b/plugins/Installation/lang/ko.json index ff083470d6..3fa7374b09 100644 --- a/plugins/Installation/lang/ko.json +++ b/plugins/Installation/lang/ko.json @@ -1,5 +1,6 @@ { "Installation": { + "CollaborativeProject": "Piwik는 전세계 사람들과 함께 만들어 나가는 협동 프로젝트입니다.", "ConfigurationHelp": "Piwik 설정 파일이 잘못 설정되어있는 것 같습니다. config\/config.ini.php를 제거하고 설치를 다시 시작하거나 데이터베이스의 연결 설정을 수정하세요.", "ConfirmDeleteExistingTables": "테이블 %s 을(를) 데이터베이스로부터 삭제하기를 원합니까? 경고: 이 테이블의 데이터는 복구되지 않습니다!", "Congratulations": "축하합니다", @@ -14,12 +15,20 @@ "DatabaseSetupLogin": "로그인", "DatabaseSetupServer": "데이터베이스 서버", "DatabaseSetupTablePrefix": "테이블 접두사", + "Email": "이메일", "Extension": "확장", "Filesystem": "파일시스템", + "GetInvolved": "만약 이를 좋아한다면, 언제라도 %1$s참여할 수 있습니다%2$s.", "GoBackAndDefinePrefix": "돌아가서 Piwik 테이블을 위해 Prefix를 정의합니다", + "HappyAnalysing": "즐거운 분석!", "Installation": "설치", "InstallationStatus": "설치 상태", "InsufficientPrivilegesHelp": "phpMyAdmin 같은 도구를 사용하거나 올바른 SQL 쿼리를 실행하여 이러한 권한을 추가 할 수 있습니다. 방법을 잘 모를 경우 시스템 관리자에게 문의하여 권한을 부여 받으세요.", + "InsufficientPrivilegesMain": "데이터베이스가 존재하지 않거나 (혹은 생성되지 않았거나), 지정된 사용자가 충분한 권한을 가지고 있지 않습니다. 데이터베이스 사용자는 반드시 아래의 권한을 가지고 있어야 합니다: %s", + "InvalidStateError": "에러: Piwik가 이미 설치되어 있습니다. %1$s 뒤돌아 가기 %2$s 설치된 Piwik로...%3$s.", + "JsTagArchivingHelp1": "중간의 혹은 높은 트래픽을 가진 웹사이트를 위해 Piwik를 빠르게 동작시키는 여러 최적화 기법(예: %1$s자동-아카이브 작업 설정%2$s)이 제공되고 있습니다.", + "JSTracking_EndNote": "참고: 인스톨 과정이 끝난 후 %1$s트래킹 코드%2$s 관리자 화면에서 맞춤 트래킹 코드를 생성할 수 있습니다.", + "JSTracking_Intro": "웹사이트 내 트래픽을 추적하기 위해서 당신의 각 웹페이지에 코드를 추가해야 합니다.", "LargePiwikInstances": "높은 트래픽 환경에서 Piwik를 사용하기 위한 팁", "Legend": "범례", "LoadDataInfileRecommended": "트래픽이 높은 웹사이트를 Piwik 서버에서 추적하려는 경우 (예: 한달 100,000 페이지뷰 이상), 이 문제를 해결하고 사용할 것을 권장합니다.", @@ -29,22 +38,34 @@ "NfsFilesystemWarningSuffixInstall": "NFS의 파일 기반 세션을 사용하면 매우 느립니다, 그래서 Piwik 데이터베이스 세션을 사용합니다. 대시보드 사용자가 많은 경우는, 데이터베이스 서버에 클라이언트 연결의 최대 수를 증가해야 할지도 모릅니다.", "NoConfigFound": "Piwik 설정 파일을 찾을 수 없으며 당신은 Piwik 페이지로 접근을 계속 시도하고 있습니다.<br \/><b> » <a href='index.php'>지금 Piwik을 설치하세요.<\/a><\/b><br \/><small>만약 Piwik을 이전에 설치한 적이 있고 DB에 테이블이 좀 있다면 걱정하지마세요, 같은 테이블을 재사용할 수 있고 현재 데이터를 보존할 수 있습니다!<\/small>", "Optional": "옵션", + "Password": "비밀번호", "PasswordDoNotMatch": "비밀번호가 맞지 않습니다", + "PasswordRepeat": "비밀번호 (반복 입력)", "PercentDone": "%s %% 완료", "PleaseFixTheFollowingErrors": "다음 오류를 수정하세요", + "DefaultSettings": "기본 Piwik 설정", + "DefaultSettingsHelp": "Piwik 기본 설정에 따릅니다. 이를 지금 혹은 후에 관리자 화면에서 변경할 수 있습니다.", "Requirements": "Piwik 요구 사항", "RestartWebServer": "변경한 후 웹서버를 다시 시작하세요.", + "ReusingTables": "테이블", + "PiwikOrgNewsletter": "주요 Piwik 커뮤니티의 업데이트 이메일 구독", + "PiwikProNewsletter": "%sPiwik PRO%s 서비스 및 신청과 관련한 정보 받기", "SeeBelowForMoreInfo": "자세한 내용은 아래를 참조하세요.", "SetupWebsite": "웹사이트 설정", "SetupWebsiteError": "웹사이트 추가중에 에러가 있었습니다", + "SetupWebSiteName": "웹사이트", "SetupWebsiteSetupSuccess": "웹사이트 %s가 성공적으로 작성되었습니다!", - "SiteSetup": "Piwik으로 추적 및 분석하려는 첫 번째 웹사이트를 설정하하세요:", + "SetupWebSiteURL": "웹사이트 URL", + "SiteSetup": "Piwik으로 추적 및 분석하려는 첫 번째 웹사이트를 설정하세요:", "SiteSetupFootnote": "참고: Piwik 설치가 완료되면, 더 많은 웹사이트를 추적할 수 있습니다!", - "SuperUser": "수퍼 유저", - "SuperUserSetupSuccess": "수퍼 유저가 만들어졌습니다!", + "SuperUser": "슈퍼 유저", + "SuperUserLogin": "슈퍼 유저 로그인", + "SuperUserSetupError": "슈퍼 유저를 추가함에 있어 문제가 발생했습니다.", + "SuperUserSetupSuccess": "슈퍼 유저가 만들어졌습니다!", "SystemCheck": "시스템 체크", "SystemCheckAutoUpdateHelp": "주의: Piwik의 원클릭 업데이트는 Piwik 폴더와 파일에 쓰기 권한이 필요합니다.", "SystemCheckCreateFunctionHelp": "Piwik은 콜백에 익명 함수를 사용합니다.", + "SystemCheckDatabaseExtensions": "MySQL 확장", "SystemCheckDatabaseHelp": "Piwik은 mysqli 확장하거나 PDO와 pdo_mysql 확장의 둘 중 하나가 필요합니다.", "SystemCheckDebugBacktraceHelp": "View::factory는 모듈을 호출하여 뷰를 만들 수 없습니다.", "SystemCheckError": "에러 발생 - 진행 전에 수정이 필요함", @@ -52,30 +73,38 @@ "SystemCheckExtensions": "필수 확장", "SystemCheckFileIntegrity": "파일 무결성", "SystemCheckFunctions": "필수 기능", + "SystemCheckFunctionHelp": "빌트인 함수들을 활성화해야 합니다.", + "SystemCheckGDFreeType": "GD > 2.x + Freetype (그래픽)", "SystemCheckGDHelp": "스파크라인(소규모 그래프)은 작동하지 않습니다.", "SystemCheckGlobHelp": "이 내장 함수는 호스트에서 비활성화되어 있습니다. Piwik이 함수의 에뮬레이션을 시도하지만, 새로운 보안 제한으로 발생하는 경우가 있습니다. 또한 기능에 영향을 미칠수 있습니다.", "SystemCheckGzcompressHelp": "zlib 확장과 gzcompress() 함수를 활성화해야합니다.", "SystemCheckGzuncompressHelp": "zlib 확장과 gzuncompress() 함수를 활성화해야합니다.", "SystemCheckIconvHelp": "\"iconv\"의 지원을 활성화하고, PHP 재구축 설정을 해야합니다 --with-iconv.", + "SystemCheckJsonHelp": "Piwik가 JSON 데이터를 읽고 쓰기 위해서 php5-json 확장이 필요합니다.", "SystemCheckMailHelp": "mail() 없이 피드백과 잊어버린 비밀번호 메세지는 보내지지 않습니다.", "SystemCheckMbstring": "mbstring", + "SystemCheckMbstringHelp": "mbstring 확장은 사용자 인터페이스와 API 응답 내 멀티바이트 문자열을 다루는데 필요합니다. 또한, php.ini 내 mbstring.func_overload이 0으로 되어 있는지 확인하세요.", "SystemCheckMemoryLimit": "메모리 제한", "SystemCheckMemoryLimitHelp": "높은 트래픽의 웹사이트에서는 압축 진행중에 현재 허용된 것보다 더 높은 메모리를 필요로 할 수 있습니다. 만약 필요하다면 php.ini 파일에서 memory_limit을 변경하세요.", "SystemCheckOpenURL": "URL 열기", "SystemCheckOpenURLHelp": "뉴스 레터 구독, 업데이트 알림, 원 클릭 업데이트는 \"curl\"확장, allow_url_fopen = On 또는 fsockopen()이 유효해야합니다.", "SystemCheckOtherExtensions": "기타 확장", "SystemCheckOtherFunctions": "기타 함수", + "SystemCheckPageSpeedDisabled": "PageSpeed 비활성화", + "SystemCheckPageSpeedWarn": "우리는 웹서버 %s 내 PageSpeed 모듈을 비활성화 하는 것을 추천합니다. 그간 Piwik에 여러 문제들이 PageSpeed와 연관있었습니다.", "SystemCheckPackHelp": "pack() 함수는 Piwik에서 방문자를 추적하는 데 필요합니다.", "SystemCheckParseIniFileHelp": "이 내장 함수는 호스트에서 비활성화되어 있습니다. Piwik이 함수의 에뮬레이션을 시도하지만, 새로운 보안 제한으로 발생하는 경우가 있습니다. 또한 트래커 성능에도 영향을 미칩니다.", "SystemCheckPdoAndMysqliHelp": "Linux 서버는 다음 옵션으로 PHP를 컴파일합니다: %1$sphp.ini에 다음 줄을 추가합니다: %2$s", "SystemCheckPhp": "PHP 버전", "SystemCheckPhpPdoAndMysqli": "자세한 문서: %1$sPHP PDO%2$s and %3$sMYSQLI%4$s.", "SystemCheckSplHelp": "스탠다드 PHP 라이브러리가 활성화된 상태로 PHP를 설정하고 리빌딩해야 합니다. (기본).", + "SystemCheckSettings": "요구되는 PHP 설정 (php.ini)", "SystemCheckSummaryNoProblems": "만세! Piwik 설정에 아무런 문제가 없습니다. 스스로에게 칭찬하세요.", "SystemCheckSummaryThereWereErrors": "헐! Piwik 설정에서 일부 %1$s심각한 오류%2$s를 감지했습니다. %3$s이 문제는 즉시 해결해야합니다.%4$s", "SystemCheckSummaryThereWereWarnings": "시스템에 몇몇 문제가 있습니다. Piwik가 설치되었지만 몇가지 사소한 문제가 발생할 수 있습니다.", "SystemCheckTimeLimitHelp": "높은 트래픽의 웹사이트에서는 압축 진행중에 현재 허용된 것보다 더 많은 시간을 필요로 할 수 있습니다. 만약 필요하다면 php.ini 파일에서 max_execution_time을 변경하세요..", "SystemCheckTracker": "추적기 상태", + "SystemCheckTrackerHelp": "piwik.php에서 GET 리퀘스트가 실패하였습니다. 현재 URL을 HTTP Authentication의 화이트 리스트에 추가해주시고 mod_security를 꺼주시길 바랍니다(이는 당신의 웹페이지 관리자에게 연락해야 할 것입니다). 이번 에러와 관련하여 웹 서버의 에러 로그 파일을 확인하실 것을 권장합니다.", "SystemCheckWarnDomHelp": "\"dom\"확장을 활성화해야합니다 (예: \"php-dom\"과 \"php-xml\"패키지 중 하나 또는 모두 설치).", "SystemCheckWarning": "Piwik 은 정상적으로 작동하지만 몇몇의 기능이 작동하지 않을 수 있습니다.", "SystemCheckWarnJsonHelp": "더 나은 성능을 위해 \"json\"확장을 활성화해야합니다 (예: \"php-json\"패키지를 설치).", @@ -85,15 +114,27 @@ "SystemCheckWriteDirs": "쓰기 접근 가능 디렉토리", "SystemCheckWriteDirsHelp": "리눅스 시스템에서 이 에러를 고치기 위해서, 다음 명령어를 입력해보세요.", "SystemCheckZlibHelp": "zlib 지원 활성화된 상태로 PHP를 설정하고 리빌빙해야 합니다.", + "SystemCheckCronArchiveProcess": "아카이브 cron", + "SystemCheckCronArchiveProcessCLI": "CLI를 통해서 프로세스 관리", + "SystemCheckPhpSetting": "치명적인 문제를 막기 위해, 다음의 것을 php.ini 파일에 설정하셔야 합니다: %s", + "SystemCheckUpdateHttps": "HTTPS를 통한 업데이트", + "SystemCheckUpdateHttpsNotSupported": "Piwik는 업데이트 시 HTTPS를 사용할 수 없으며, 안전하지 않은 HTTP 업데이트로 전환될 것입니다. CURL 혹은 allow_url_fopen을 확인하시고, 또한 openssl PHP 확장이 설치되어 있는지 확인하세요.: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/", + "NotSupported": "지원되지 않음", "Tables": "테이블 생성", "TablesCreatedSuccess": "테이블 생성을 성공적으로 마쳤습니다!", "TablesDelete": "감지된 테이블 삭제", "TablesDeletedSuccess": "존재하는 Piwik 테이블을 성공적으로 삭제했습니다", "TablesFound": "데이터베이스에서 다음의 테이블을 찾았습니다", "TablesReuse": "존재하는 테이블을 재사용", + "TablesUpdatedSuccess": "데이터베이스가 %1$s에서 %2$s로 성공적으로 업데이트 되었습니다.", "TablesWarningHelp": "존재하는 데이터베이스 테이블을 재사용하거나 모든 존재하는 데이터를 삭제하고 클린 설치를 할 지 선택하세요.", "TablesWithSameNamesFound": "몇몇의 테이블: %1$s \/ 데이터베이스: %2$s 가 Piwik이 생성하려고 하는 테이블 이름과 같습니다.", + "Timezone": "웹사이트 시간대", + "WeHopeYouWillEnjoyPiwik": "당신이 Piwik를 사용함에 있어 즐기는 만큼 우리는 이를 만듦에 즐거움을 느낀다.", "Welcome": "환영합니다!", - "WelcomeHelp": "<p>Piwik 은 오픈 소스 웹 분석 소프트웨어로써 쉽고 원하는 정보를 당신의 방문자들로부터 얻을 수 있게 해줍니다.<\/p><p>이 과정은 %s 의 쉬운 단계로 나뉘어 있고 5분 정도만 소요됩니다.<\/p>" + "WelcomeHelp": "<p>Piwik 은 오픈 소스 웹 분석 소프트웨어로써 쉽고 원하는 정보를 당신의 방문자들로부터 얻을 수 있게 해줍니다.<\/p><p>이 과정은 %s 의 쉬운 단계로 나뉘어 있고 5분 정도만 소요됩니다.<\/p>", + "WelcomeToCommunity": "Piwik 커뮤니티에 오신 걸 환영합니다!", + "CannotConnectToDb": "데이터베이스에 연결할 수 없습니다.", + "CannotConnectToDbResolvingExplanation": "이것은 일시적인 문제일 것입니다. %1$s페이지 새로고침%2$s을 해보세요. 만약 문제가 지속적으로 발생한다면 Piwik 관리자에게 연락하세요." } }
\ No newline at end of file diff --git a/plugins/Installation/lang/nb.json b/plugins/Installation/lang/nb.json index 136a352d20..fcc75d8190 100644 --- a/plugins/Installation/lang/nb.json +++ b/plugins/Installation/lang/nb.json @@ -1,5 +1,7 @@ { "Installation": { + "CollaborativeProject": "Piwik er et samarbeidsprosjekt, bygget med kjærlighet av folk fra hele verden.", + "ConfigurationHelp": "Det ser ut til at det er en feil i din konfigurasjonsfil for Piwik. Du kan enten slette config\/config.ini.php og fortsette installasjonen, eller endre tilkoblingsinnstillingene til databasen.", "ConfirmDeleteExistingTables": "Er du sikker på at du vil slette tabellene %s fra databasen din? ADVARSEL: DATA FRA DISSE TABELLENE KAN IKKE GJENOPPRETTES!", "Congratulations": "Gratulerer", "CongratulationsHelp": "<p>Gratulerer! Din Piwik installasjon er fullført.<\/p><p>Sørg for at JavaScript-koden er lagt inn på dine sider, og vent på dine første besøkende!<\/p>", @@ -8,36 +10,52 @@ "DatabaseErrorConnect": "Klarte ikke koble til databasetjeneren", "DatabaseServerVersion": "Databasetjener-versjon", "DatabaseSetup": "Databaseinnstillinger", - "DatabaseSetupAdapter": "adapter", - "DatabaseSetupDatabaseName": "databasenavn", - "DatabaseSetupLogin": "brukernavn", - "DatabaseSetupServer": "databasetjener", - "DatabaseSetupTablePrefix": "tabellprefiks", + "DatabaseSetupAdapter": "Adapter", + "DatabaseSetupDatabaseName": "Databasenavn", + "DatabaseSetupLogin": "Brukernavn", + "DatabaseSetupServer": "Databasetjener", + "DatabaseSetupTablePrefix": "Tabellprefiks", "Email": "E-post", "Extension": "utvidelse", "Filesystem": "Filsystem", + "GetInvolved": "Hvis du liker det du ser, kan du %1$sinvolvere deg%2$s.", "GoBackAndDefinePrefix": "Gå tilbake og definer et prefiks for Piwik-tabellene", + "HappyAnalysing": "God analyse!", "Installation": "Installasjon", "InstallationStatus": "Installasjonsstatus", + "InsufficientPrivilegesHelp": "Du kan legge til disse privilegiene ved å bruke et verktøy som phpMyAdmin, eller å kjøre de rette SQL-spørringene. Hvis du ikke vet hvordan du gjør disse tingene, vennligst spør din sysadmin om å hjelpe deg.", + "InsufficientPrivilegesMain": "Enten finnes ikke databasen (og kunne ikke opprettes), eller så har brukeren ikke tilstrekkelige rettigheter. Databasebrukeren må ha de følgende rettighetene: %s", "InvalidStateError": "Feil: Piwik er allerede installert. %1$s Gå tilbake %2$s til Piwik%3$s.", + "JsTagArchivingHelp1": "For nettsteder med medium til høy trafikk er det noen optimaliseringer som bør gjøres for å hjelpe Piwik å kjøre raskere (slik som å %1$ssette opp auto-arkivering%2$s).", + "JSTracking_EndNote": "Merk: etter installasjonsprosessen kan du generere tilpassede sporingskoder i adminseksjonen %1$sSporingskode%2$s.", + "JSTracking_Intro": "For å spore nettrafikk med Piwik må du legge til litt ekstra kode på alle sider på ditt nettsted.", "LargePiwikInstances": "Hjelp for store Piwik-instanser", "Legend": "Forklaring", - "NfsFilesystemWarning": "Din tjeneren bruker et NFS filsystem.", + "LoadDataInfileRecommended": "Hvis din Piwik-server sporer nettsider med mye trafikk (for eksempel >100 000 sider per måned), anbefaler vi deg å prøve å fikse dette problemet.", + "LoadDataInfileUnavailableHelp": "Å bruke %1$s vil gjøre arkiveringsprosessen til Piwik mye raskere. For å gjøre det tilgjengelig, prøv å oppdatere PHP og MySQL og forsikre deg om at databasebrukeren har rettigheten %2$s.", + "NfsFilesystemWarning": "Din tjeneren bruker et NFS-filsystem.", "NfsFilesystemWarningSuffixAdmin": "Dette betyr Piwik vil være svært treg når du bruker filbaserte økter.", - "NoConfigFound": "Klarte ikke finne konfigurasjonsfilen for Piwik, og du prøver å få tilgang til en Piwik-side. <br \/><b> »Du kan <a href='index.php'>installere Piwik nå<\/a><\/b><br \/><small>Hvis du prøvde å installere Piwik tidligere og har noen tabeller i databasen, kan du ta det med ro. Du kan bruke de gamle tabellene og beholde dine eksisterende data!<\/small>", + "NfsFilesystemWarningSuffixInstall": "Å bruke filbaserte sesjoner på NFS er ekstremt tregt, så Piwik vil bruke databasesesjoner. Hvis det er mange som ser på kontrollpanelene samtidig, kan det tenkes at du må øke maks antall klienttilkoblinger som er tillatt på databaseserveren.", + "NoConfigFound": "Klarte ikke finne konfigurasjonsfilen for Piwik, og du prøver å få tilgang til en Piwik-side. <br \/><b> » Du kan <a href='index.php'>installere Piwik nå<\/a><\/b><br \/><small>Hvis du prøvde å installere Piwik tidligere og har noen tabeller i databasen, kan du ta det med ro. Du kan bruke de gamle tabellene og beholde dine eksisterende data!<\/small>", "Optional": "Valgfritt", "Password": "Passord", "PasswordDoNotMatch": "passordene stemmer ikke overens", "PasswordRepeat": "Passord (gjenta)", "PercentDone": "%s %% ferdig", "PleaseFixTheFollowingErrors": "Fiks følgende feil", + "DefaultSettings": "Standard Piwik-innstillinger", + "DefaultSettingsHelp": "Piwik kommer med standardinnstillinger. Du kan tilpasse dem nå eller gjøre det senere i administrasjonsskjermen.", "Requirements": "Krav for å kjøre Piwik", + "RestartWebServer": "Etter å ha gjort denne endringen må du restarte webserveren.", "ReusingTables": "Gjenbruker tabellene", + "PiwikOrgNewsletter": "Send meg e-poster med viktige oppdateringer om Piwik", "PiwikProNewsletter": "send meg informasjon om %sPiwik PRO%s tjenester og tilbud", "SeeBelowForMoreInfo": "Se nedenfor for mer informasjon.", "SetupWebsite": "Legg til et nettsted", "SetupWebsiteError": "Det skjedde en feil da nettstedet ble lagt til.", + "SetupWebSiteName": "Nettstedsnavn", "SetupWebsiteSetupSuccess": "Nettstedet %s ble opprettet.", + "SetupWebSiteURL": "Nettsteds-URL", "SuperUser": "Superbruker", "SuperUserSetupSuccess": "Superbruker ble opprettet!", "SystemCheck": "Systemsjekk", diff --git a/plugins/Installation/lang/tr.json b/plugins/Installation/lang/tr.json index 33f074fa7d..2b51d7634d 100644 --- a/plugins/Installation/lang/tr.json +++ b/plugins/Installation/lang/tr.json @@ -2,27 +2,38 @@ "Installation": { "Congratulations": "Tebrikler", "CongratulationsHelp": "<p>Tebrikler! Piwik Başarıyla kuruldu.<\/p><p>JavaScript kodunu sayfalarınıza yerleştirdiğinizden emin olun ve ilk ziyaretçilerinizi beklemeye başlayın!<\/p>", - "DatabaseCreation": "Veritabaı oluşturma", + "DatabaseCreation": "Veritabanı oluşturma", + "DatabaseErrorConnect": "Veritabanına bağlanırken hata oluştu", "DatabaseServerVersion": "Veritabanı sunucu versiyonu", "DatabaseSetup": "Veritabanı Yüklemesi", "DatabaseSetupDatabaseName": "Veritabanı adı", "DatabaseSetupLogin": "Giriş", "DatabaseSetupServer": "Veritabanı Sunucusu", "DatabaseSetupTablePrefix": "Tablo öneki", + "Email": "E-posta", "Extension": "eklenti", "Filesystem": "Dosya sistemi", "Installation": "Yükleme", "InstallationStatus": "Yükleme durumu", + "InvalidStateError": "Hata: Piwik zaten kurulmuş. %1$s Geri dön %2$s Piwik%3$s.", "LargePiwikInstances": "Büyük Piwik örnekleri için yardım", "Legend": "Gösterge", "NfsFilesystemWarning": "Sunucunuz NFS dosya sistemini kullanıyor.", "Optional": "Opsiyonel", + "Password": "Şifre", "PasswordDoNotMatch": "Girilen şifreler aynı değil.", + "PasswordRepeat": "Şifre (Tekrardan)", "PercentDone": "%s %% tamamlandı", + "PleaseFixTheFollowingErrors": "Oluşan hataları düzeltin lütfen.", + "DefaultSettings": "Varsayılan Piwik ayarları", + "DefaultSettingsHelp": "Piwik varsayılan ayarlarla gelecektir. Şimdi özelleştirebilirsiniz yada daha sonra yönetici ekranından da yapabilirsiniz.", "Requirements": "Piwik gereklilikleri", + "RestartWebServer": "Bu değişiklikten sonra, web sunucunuzu yeniden başlatın.", "SetupWebsite": "Web sitesi ekle", "SetupWebsiteError": "Web site eklenirken bir sorun oluştu.", + "SetupWebSiteName": "Web site adı", "SetupWebsiteSetupSuccess": "Web site %s başarıyla eklendi.", + "SetupWebSiteURL": "Web site adresi", "SystemCheck": "Sistem kontrolü", "SystemCheckDatabaseHelp": "Piwik mysqli eklentisi ya da PDO ve pdo_mysql eklentilerine ihtiyaç duyar.", "SystemCheckError": "Bir hata oluştu. Devam edebilmek için hatayı gidermelisiniz.", @@ -32,11 +43,20 @@ "SystemCheckMbstring": "mbstring", "SystemCheckMemoryLimit": "Hafıza limiti", "SystemCheckOtherExtensions": "Diğer eklentiler", + "SystemCheckOtherFunctions": "Diğer fonksiyonlar", + "SystemCheckPageSpeedDisabled": "Sayfa Hızı devre dışı", "SystemCheckPhp": "PHP versiyonu", + "SystemCheckPhpPdoAndMysqli": "Daha fazla bilgi için: %1$sPHP PDO%2$s ve %3$sMYSQLI%4$s.", + "SystemCheckSettings": "Gerekli PHP yapılandırmaları (php.ini)", + "SystemCheckSummaryNoProblems": "Vayy! Piwik kurulumunda hiçbir sorun bulunamadı. Sırtınızı sıvazlayabiliriz.", "SystemCheckWarning": "Piwik normal olarak çalışacaktır ancak bazı özellikler eksik olacak.", + "NotSupported": "desteklenmiyor", "Tables": "Tablolar oluşturuluyor", - "TablesCreatedSuccess": "Tablolar başarılı bir şekilde yaratıldı!", + "TablesCreatedSuccess": "Tablolar başarılı bir şekilde oluşturuldu!", + "Timezone": "Web site zaman dilimi", "Welcome": "Hoşgeldiniz!", - "CannotConnectToDb": "Veritabanına bağlanamadı" + "WelcomeToCommunity": "Piwik topluluğuna hoşgeldiniz!", + "CannotConnectToDb": "Veritabanına bağlanamadı", + "CannotConnectToDbResolvingExplanation": "Bu geçici bir sorun olabilir, %1$ssayfayı yenilemeyi deneyin%2$s. Eğer sorun devam ederse Piwik yöneticinize başvurun." } }
\ No newline at end of file diff --git a/plugins/Intl/Commands/GenerateIntl.php b/plugins/Intl/Commands/GenerateIntl.php index 45637452a8..d9710f5977 100644 --- a/plugins/Intl/Commands/GenerateIntl.php +++ b/plugins/Intl/Commands/GenerateIntl.php @@ -262,7 +262,9 @@ class GenerateIntl extends ConsoleCommand $translations['Intl']['Time_AM'] = $calendarData['dayPeriods']['format']['wide']['am']; $translations['Intl']['Time_PM'] = $calendarData['dayPeriods']['format']['wide']['pm']; - $translations['Intl']['Format_Time'] = $calendarData['timeFormats']['medium']; + $translations['Intl']['Format_Time'] = '{time}'; + $translations['Intl']['Format_Time_12'] = $calendarData['dateTimeFormats']['availableFormats']['hms']; + $translations['Intl']['Format_Time_24'] = $calendarData['dateTimeFormats']['availableFormats']['Hms']; $translations['Intl']['Format_Date_Long'] = $calendarData['dateFormats']['full']; $translations['Intl']['Format_Date_Day_Month'] = $calendarData['dateTimeFormats']['availableFormats']['MMMEd']; $translations['Intl']['Format_Date_Short'] = $calendarData['dateFormats']['medium']; @@ -273,8 +275,8 @@ class GenerateIntl extends ConsoleCommand } $translations['Intl']['Format_Year'] = $calendarData['dateTimeFormats']['availableFormats']['y']; - $translations['Intl']['Format_DateTime_Long'] = $calendarData['dateFormats']['full'] . ' ' . $calendarData['timeFormats']['medium']; - $translations['Intl']['Format_DateTime_Short'] = $calendarData['dateFormats']['medium'] . ' ' . $calendarData['timeFormats']['medium']; + $translations['Intl']['Format_DateTime_Long'] = $calendarData['dateFormats']['full'] . ' {time}'; + $translations['Intl']['Format_DateTime_Short'] = $calendarData['dateFormats']['medium'] . ' {time}'; $translations['Intl']['Format_Interval_Long_D'] = $this->transformDateFormat($calendarData['dateTimeFormats']['intervalFormats']['yMMMd']['d'], array('MMMM' => 'MMM', 'LLLL' => 'LLL', 'MMM' => 'MMMM', 'LLL' => 'LLLL')); $translations['Intl']['Format_Interval_Long_M'] = $this->transformDateFormat($calendarData['dateTimeFormats']['intervalFormats']['yMMMd']['M'], array('MMMM' => 'MMM', 'LLLL' => 'LLL', 'MMM' => 'MMMM', 'LLL' => 'LLLL')); diff --git a/plugins/Intl/DateTimeFormatProvider.php b/plugins/Intl/DateTimeFormatProvider.php new file mode 100644 index 0000000000..19fcd7acca --- /dev/null +++ b/plugins/Intl/DateTimeFormatProvider.php @@ -0,0 +1,133 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Intl; + +use Piwik\Plugins\LanguagesManager\LanguagesManager; +use Piwik\Translation\Translator; + +/** + * Provides date and time formats. + */ +class DateTimeFormatProvider extends \Piwik\Intl\Data\Provider\DateTimeFormatProvider +{ + protected $use12HourClock; + + /** + * @var Translator + */ + protected $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + /** + * Returns the format pattern for the given format type + * + * @param int $format one of the format constants + * + * @return string + */ + public function getFormatPattern($format) + { + + switch ($format) { + case self::DATETIME_FORMAT_LONG: + $pattern = $this->translator->translate('Intl_Format_DateTime_Long'); + break; + + case self::DATETIME_FORMAT_SHORT: + $pattern = $this->translator->translate('Intl_Format_DateTime_Short'); + break; + + case self::DATE_FORMAT_LONG: + $pattern = $this->translator->translate('Intl_Format_Date_Long'); + break; + + case self::DATE_FORMAT_DAY_MONTH: + $pattern = $this->translator->translate('Intl_Format_Date_Day_Month'); + break; + + case self::DATE_FORMAT_SHORT: + $pattern = $this->translator->translate('Intl_Format_Date_Short'); + break; + + case self::DATE_FORMAT_MONTH_SHORT: + $pattern = $this->translator->translate('Intl_Format_Month_Short'); + break; + + case self::DATE_FORMAT_MONTH_LONG: + $pattern = $this->translator->translate('Intl_Format_Month_Long'); + break; + + case self::DATE_FORMAT_YEAR: + $pattern = $this->translator->translate('Intl_Format_Year'); + break; + + case self::TIME_FORMAT: + $pattern = $this->translator->translate('Intl_Format_Time'); + break; + + default: + $pattern = $format; + } + + if (strpos($pattern, '{time}') !== false) { + $pattern = str_replace('{time}', $this->getTimeFormat(), $pattern); + } + + return $pattern; + } + + /** + * Returns interval format pattern for the given format type + * + * @param bool $short whether to return short or long format pattern + * @param string $maxDifference maximal difference in interval dates (Y, M or D) + * + * @return string + */ + public function getRangeFormatPattern($short=false, $maxDifference='Y') + { + return $this->translator->translate( + sprintf( + 'Intl_Format_Interval_%s_%s', + $short ? 'Short' : 'Long', + $maxDifference + )); + } + + protected function getTimeFormat() + { + if (is_null($this->use12HourClock)) { + $this->use12HourClock = LanguagesManager::uses12HourClockForCurrentUser(); + } + + $timeFormat = 'Intl_Format_Time_24'; + + if ($this->use12HourClock) { + $timeFormat = 'Intl_Format_Time_12'; + } + + $template = $this->translator->translate($timeFormat); + + return $template; + } + + /** + * For testing purpose only: Overwrites time format + * + * @param bool $use12HourClock + */ + public function forceTimeFormat($use12HourClock = false) + { + $this->use12HourClock = $use12HourClock; + } +}
\ No newline at end of file diff --git a/plugins/Intl/config/config.php b/plugins/Intl/config/config.php new file mode 100644 index 0000000000..11e540614e --- /dev/null +++ b/plugins/Intl/config/config.php @@ -0,0 +1,5 @@ +<?php + +return array( + 'Piwik\Intl\Data\Provider\DateTimeFormatProvider' => DI\object('Piwik\Plugins\Intl\DateTimeFormatProvider') +);
\ No newline at end of file diff --git a/plugins/Intl/lang/am.json b/plugins/Intl/lang/am.json index 4999259850..e6fc689e52 100644 --- a/plugins/Intl/lang/am.json +++ b/plugins/Intl/lang/am.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "ቅዳሜ", "Day_Short_StandAlone_7": "እሑድ", "EnglishLanguageName": "Amharic", - "Format_DateTime_Long": "EEEE ፣d MMMM y h:mm:ss a", - "Format_DateTime_Short": "d MMM y h:mm:ss a", + "Format_DateTime_Long": "EEEE ፣d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E፣ MMM d", "Format_Date_Long": "EEEE ፣d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "MMM d፣ y – MMM d፣ y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ሰዓቶች", "Language_aa": "አፋርኛ", diff --git a/plugins/Intl/lang/ar.json b/plugins/Intl/lang/ar.json index 481c4e62ce..be7becbd50 100644 --- a/plugins/Intl/lang/ar.json +++ b/plugins/Intl/lang/ar.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "السبت", "Day_Short_StandAlone_7": "الأحد", "EnglishLanguageName": "Arabic", - "Format_DateTime_Long": "EEEE، d MMMM، y h:mm:ss a", - "Format_DateTime_Short": "dd\/MM\/y h:mm:ss a", + "Format_DateTime_Long": "EEEE، d MMMM، y {time}", + "Format_DateTime_Short": "dd\/MM\/y {time}", "Format_Date_Day_Month": "E، d MMM", "Format_Date_Long": "EEEE، d MMMM، y", "Format_Date_Short": "dd\/MM\/y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM، y – d MMM، y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ساعات", "Language_aa": "الأفارية", diff --git a/plugins/Intl/lang/be.json b/plugins/Intl/lang/be.json index 88bb7c14cb..c5b8f8c826 100644 --- a/plugins/Intl/lang/be.json +++ b/plugins/Intl/lang/be.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Нд", "EnglishLanguageName": "Belarusian", - "Format_DateTime_Long": "EEEE, d MMMM y HH.mm.ss", - "Format_DateTime_Short": "d.M.y HH.mm.ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d.M.y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d.M.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLL y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "hh.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "гадзіны", "Language_ab": "Абхазская", diff --git a/plugins/Intl/lang/bg.json b/plugins/Intl/lang/bg.json index 02880b589d..511e99fffd 100644 --- a/plugins/Intl/lang/bg.json +++ b/plugins/Intl/lang/bg.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Нд", "EnglishLanguageName": "Bulgarian", - "Format_DateTime_Long": "EEEE, d MMMM y 'г'. H:mm:ss", - "Format_DateTime_Short": "d.MM.y 'г'. H:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y 'г'. {time}", + "Format_DateTime_Short": "d.MM.y 'г'. {time}", "Format_Date_Day_Month": "E, d.MM", "Format_Date_Long": "EEEE, d MMMM y 'г'.", "Format_Date_Short": "d.MM.y 'г'.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d.MM.y 'г'. – d.MM.y 'г'.", "Format_Month_Long": "MMMM y 'г'.", "Format_Month_Short": "MM.y 'г'.", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y 'г'.", "Hours": "часове", "Language_aa": "Афар", diff --git a/plugins/Intl/lang/bn.json b/plugins/Intl/lang/bn.json index 2bac13f1be..92676e3ab4 100644 --- a/plugins/Intl/lang/bn.json +++ b/plugins/Intl/lang/bn.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "শনি", "Day_Short_StandAlone_7": "রবি", "EnglishLanguageName": "Bengali", - "Format_DateTime_Long": "EEEE, d MMMM, y h:mm:ss a", - "Format_DateTime_Short": "d MMM, y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM, y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE, d MMMM, y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ঘন্টা", "Language_aa": "আফার", diff --git a/plugins/Intl/lang/bs.json b/plugins/Intl/lang/bs.json index 89e7950363..abd090f32c 100644 --- a/plugins/Intl/lang/bs.json +++ b/plugins/Intl/lang/bs.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sub", "Day_Short_StandAlone_7": "Ned", "EnglishLanguageName": "Bosnian", - "Format_DateTime_Long": "EEEE, dd. MMMM y. HH:mm:ss", - "Format_DateTime_Short": "dd. MMM. y. HH:mm:ss", + "Format_DateTime_Long": "EEEE, dd. MMMM y. {time}", + "Format_DateTime_Short": "dd. MMM. y. {time}", "Format_Date_Day_Month": "E, dd. MMM", "Format_Date_Long": "EEEE, dd. MMMM y.", "Format_Date_Short": "dd. MMM. y.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd. MMM y. – dd. MMM y.", "Format_Month_Long": "LLLL y.", "Format_Month_Short": "MMM y.", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "hh:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y.", "Hours": "sati", "Language_aa": "Afarski", diff --git a/plugins/Intl/lang/ca.json b/plugins/Intl/lang/ca.json index d2b8a9a26e..7a0b18b324 100644 --- a/plugins/Intl/lang/ca.json +++ b/plugins/Intl/lang/ca.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Ds.", "Day_Short_StandAlone_7": "Dg.", "EnglishLanguageName": "Catalan", - "Format_DateTime_Long": "EEEE, d MMMM 'de' y H:mm:ss", - "Format_DateTime_Short": "d MMM y H:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM 'de' y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM 'de' y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "LLLL 'de' y", "Format_Month_Short": "LLL 'de' y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "hores", "Language_aa": "Àfar", diff --git a/plugins/Intl/lang/cs.json b/plugins/Intl/lang/cs.json index 813e0506ee..5d13f324a1 100644 --- a/plugins/Intl/lang/cs.json +++ b/plugins/Intl/lang/cs.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "So", "Day_Short_StandAlone_7": "Ne", "EnglishLanguageName": "Czech", - "Format_DateTime_Long": "EEEE d. MMMM y H:mm:ss", - "Format_DateTime_Short": "d. M. y H:mm:ss", + "Format_DateTime_Long": "EEEE d. MMMM y {time}", + "Format_DateTime_Short": "d. M. y {time}", "Format_Date_Day_Month": "E d. M.", "Format_Date_Long": "EEEE d. MMMM y", "Format_Date_Short": "d. M. y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. M. y – d. M. y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLLL y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "hodiny", "Language_aa": "Afarština", diff --git a/plugins/Intl/lang/cy.json b/plugins/Intl/lang/cy.json index 69a71a6022..eea1cb48d6 100644 --- a/plugins/Intl/lang/cy.json +++ b/plugins/Intl/lang/cy.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sad", "Day_Short_StandAlone_7": "Sul", "EnglishLanguageName": "Welsh", - "Format_DateTime_Long": "EEEE, d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "oriau", "Language_aa": "Affareg", diff --git a/plugins/Intl/lang/da.json b/plugins/Intl/lang/da.json index 3710ea0c0b..960a7be385 100644 --- a/plugins/Intl/lang/da.json +++ b/plugins/Intl/lang/da.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lør", "Day_Short_StandAlone_7": "Søn", "EnglishLanguageName": "Danish", - "Format_DateTime_Long": "EEEE 'den' d. MMMM y HH.mm.ss", - "Format_DateTime_Short": "d. MMM y HH.mm.ss", + "Format_DateTime_Long": "EEEE 'den' d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE 'den' d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "timer", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/de.json b/plugins/Intl/lang/de.json index 813e67dde7..d6a6e7e70b 100644 --- a/plugins/Intl/lang/de.json +++ b/plugins/Intl/lang/de.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sa", "Day_Short_StandAlone_7": "So", "EnglishLanguageName": "German", - "Format_DateTime_Long": "EEEE, d. MMMM y HH:mm:ss", - "Format_DateTime_Short": "dd.MM.y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "dd.MM.y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "dd.MM.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "Stunden", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/el.json b/plugins/Intl/lang/el.json index 2af13ce2f3..49c3bf9897 100644 --- a/plugins/Intl/lang/el.json +++ b/plugins/Intl/lang/el.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Σάβ", "Day_Short_StandAlone_7": "Κυρ", "EnglishLanguageName": "Greek", - "Format_DateTime_Long": "EEEE, d MMMM y h:mm:ss a", - "Format_DateTime_Short": "d MMM y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd MMM y – dd MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ώρες", "Language_aa": "Αφάρ", diff --git a/plugins/Intl/lang/en.json b/plugins/Intl/lang/en.json index a52b7b2cf6..db73bc9a08 100644 --- a/plugins/Intl/lang/en.json +++ b/plugins/Intl/lang/en.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sat", "Day_Short_StandAlone_7": "Sun", "EnglishLanguageName": "English", - "Format_DateTime_Long": "EEEE, MMMM d, y h:mm:ss a", - "Format_DateTime_Short": "MMM d, y h:mm:ss a", + "Format_DateTime_Long": "EEEE, MMMM d, y {time}", + "Format_DateTime_Short": "MMM d, y {time}", "Format_Date_Day_Month": "E, MMM d", "Format_Date_Long": "EEEE, MMMM d, y", "Format_Date_Short": "MMM d, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "MMM d, y – MMM d, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "hours", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/es.json b/plugins/Intl/lang/es.json index 10590cb926..4a08bbae01 100644 --- a/plugins/Intl/lang/es.json +++ b/plugins/Intl/lang/es.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sáb.", "Day_Short_StandAlone_7": "Dom.", "EnglishLanguageName": "Spanish", - "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y H:mm:ss", - "Format_DateTime_Short": "d MMM y H:mm:ss", + "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d 'de' MMMM 'de' y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM 'de' y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/et.json b/plugins/Intl/lang/et.json index db9277a739..bb9cdd7b16 100644 --- a/plugins/Intl/lang/et.json +++ b/plugins/Intl/lang/et.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "L", "Day_Short_StandAlone_7": "P", "EnglishLanguageName": "Estonian", - "Format_DateTime_Long": "EEEE, d. MMMM y H:mm.ss", - "Format_DateTime_Short": "d. MMM y H:mm.ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm.ss a", + "Format_Time_24": "H:mm.ss", "Format_Year": "y", "Hours": "tunnid", "Language_aa": "Afari", diff --git a/plugins/Intl/lang/eu.json b/plugins/Intl/lang/eu.json index a102605b6d..ec30c090f1 100644 --- a/plugins/Intl/lang/eu.json +++ b/plugins/Intl/lang/eu.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lr.", "Day_Short_StandAlone_7": "Ig.", "EnglishLanguageName": "Basque", - "Format_DateTime_Long": "y('e')'ko' MMMM d, EEEE HH:mm:ss", - "Format_DateTime_Short": "y MMM d HH:mm:ss", + "Format_DateTime_Long": "y('e')'ko' MMMM d, EEEE {time}", + "Format_DateTime_Short": "y MMM d {time}", "Format_Date_Day_Month": "MMM d, E", "Format_Date_Long": "y('e')'ko' MMMM d, EEEE", "Format_Date_Short": "y MMM d", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y('e')'ko' MMM d – y('e')'ko' MMM d", "Format_Month_Long": "y('e')'ko' MMMM", "Format_Month_Short": "y MMM", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ordu", "Language_ab": "Abkhazera", diff --git a/plugins/Intl/lang/fa.json b/plugins/Intl/lang/fa.json index 26110dfa0d..992ec00575 100644 --- a/plugins/Intl/lang/fa.json +++ b/plugins/Intl/lang/fa.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "شنبه", "Day_Short_StandAlone_7": "یکشنبه", "EnglishLanguageName": "Persian", - "Format_DateTime_Long": "EEEE d MMMM y H:mm:ss", - "Format_DateTime_Short": "d MMM y H:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d LLL", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y تا d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "ساعت", "Language_aa": "آفاری", diff --git a/plugins/Intl/lang/fi.json b/plugins/Intl/lang/fi.json index eb8f503320..ce88fa7bd5 100644 --- a/plugins/Intl/lang/fi.json +++ b/plugins/Intl/lang/fi.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "La", "Day_Short_StandAlone_7": "Su", "EnglishLanguageName": "Finnish", - "Format_DateTime_Long": "cccc d. MMMM y H.mm.ss", - "Format_DateTime_Short": "d.M.y H.mm.ss", + "Format_DateTime_Long": "cccc d. MMMM y {time}", + "Format_DateTime_Short": "d.M.y {time}", "Format_Date_Day_Month": "ccc d. MMM", "Format_Date_Long": "cccc d. MMMM y", "Format_Date_Short": "d.M.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMMM y – d. MMMM y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLL y", - "Format_Time": "H.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "H.mm.ss", "Format_Year": "y", "Hours": "tunnit", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/fr.json b/plugins/Intl/lang/fr.json index 57438a91f0..3b66a6e02e 100644 --- a/plugins/Intl/lang/fr.json +++ b/plugins/Intl/lang/fr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sam.", "Day_Short_StandAlone_7": "Dim.", "EnglishLanguageName": "French", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "heures", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/gl.json b/plugins/Intl/lang/gl.json index 15c88b8530..91d274e898 100644 --- a/plugins/Intl/lang/gl.json +++ b/plugins/Intl/lang/gl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sáb", "Day_Short_StandAlone_7": "Dom", "EnglishLanguageName": "Galician", - "Format_DateTime_Long": "EEEE dd MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM, y HH:mm:ss", + "Format_DateTime_Long": "EEEE dd MMMM y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE dd MMMM y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_ab": "Abkhazo", diff --git a/plugins/Intl/lang/he.json b/plugins/Intl/lang/he.json index 50c63fc1d4..78f07a3bc1 100644 --- a/plugins/Intl/lang/he.json +++ b/plugins/Intl/lang/he.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "שבת", "Day_Short_StandAlone_7": "יום א׳", "EnglishLanguageName": "Hebrew", - "Format_DateTime_Long": "EEEE, d בMMMM y H:mm:ss", - "Format_DateTime_Short": "d בMMM y H:mm:ss", + "Format_DateTime_Long": "EEEE, d בMMMM y {time}", + "Format_DateTime_Short": "d בMMM y {time}", "Format_Date_Day_Month": "E, d בMMM", "Format_Date_Long": "EEEE, d בMMMM y", "Format_Date_Short": "d בMMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "שעות", "Language_aa": "אפארית", diff --git a/plugins/Intl/lang/hi.json b/plugins/Intl/lang/hi.json index 73c68868e8..d9284fd761 100644 --- a/plugins/Intl/lang/hi.json +++ b/plugins/Intl/lang/hi.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "शनि", "Day_Short_StandAlone_7": "रवि", "EnglishLanguageName": "Hindi", - "Format_DateTime_Long": "EEEE, d MMMM y h:mm:ss a", - "Format_DateTime_Short": "dd\/MM\/y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "dd\/MM\/y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "dd\/MM\/y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "घंटे", "Language_aa": "अफ़ार", diff --git a/plugins/Intl/lang/hr.json b/plugins/Intl/lang/hr.json index 48bcdd8a09..39f7d403d9 100644 --- a/plugins/Intl/lang/hr.json +++ b/plugins/Intl/lang/hr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sub", "Day_Short_StandAlone_7": "Ned", "EnglishLanguageName": "Croatian", - "Format_DateTime_Long": "EEEE, d. MMMM y. HH:mm:ss", - "Format_DateTime_Short": "d. MMM y. HH:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y. {time}", + "Format_DateTime_Short": "d. MMM y. {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y.", "Format_Date_Short": "d. MMM y.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd. MMM y. – dd. MMM y.", "Format_Month_Long": "LLLL y.", "Format_Month_Short": "LLL y.", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "hh:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y.", "Hours": "sati", "Language_aa": "Afarski", diff --git a/plugins/Intl/lang/hu.json b/plugins/Intl/lang/hu.json index 4df06ba706..e730da6862 100644 --- a/plugins/Intl/lang/hu.json +++ b/plugins/Intl/lang/hu.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Szo", "Day_Short_StandAlone_7": "V", "EnglishLanguageName": "Hungarian", - "Format_DateTime_Long": "y. MMMM d., EEEE H:mm:ss", - "Format_DateTime_Short": "y. MMM d. H:mm:ss", + "Format_DateTime_Long": "y. MMMM d., EEEE {time}", + "Format_DateTime_Short": "y. MMM d. {time}", "Format_Date_Day_Month": "MMM d., E", "Format_Date_Long": "y. MMMM d., EEEE", "Format_Date_Short": "y. MMM d.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y. MMM d. – y. MMM d.", "Format_Month_Long": "y. MMMM", "Format_Month_Short": "y. MMM", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "H:mm:ss", "Format_Year": "y.", "Hours": "óra", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/id.json b/plugins/Intl/lang/id.json index ef6ae7fafa..2ed5145d34 100644 --- a/plugins/Intl/lang/id.json +++ b/plugins/Intl/lang/id.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sab", "Day_Short_StandAlone_7": "Min", "EnglishLanguageName": "Indonesian", - "Format_DateTime_Long": "EEEE, dd MMMM y HH.mm.ss", - "Format_DateTime_Short": "d MMM y HH.mm.ss", + "Format_DateTime_Long": "EEEE, dd MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, dd MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "jam", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/is.json b/plugins/Intl/lang/is.json index 1160564dd8..b27b8783aa 100644 --- a/plugins/Intl/lang/is.json +++ b/plugins/Intl/lang/is.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lau.", "Day_Short_StandAlone_7": "Sun.", "EnglishLanguageName": "Icelandic", - "Format_DateTime_Long": "EEEE, d. MMMM y HH:mm:ss", - "Format_DateTime_Short": "d. MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "klukkustundir", "Language_ab": "Abkasíska", diff --git a/plugins/Intl/lang/it.json b/plugins/Intl/lang/it.json index 5f88825369..a8fdaac903 100644 --- a/plugins/Intl/lang/it.json +++ b/plugins/Intl/lang/it.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sab", "Day_Short_StandAlone_7": "Dom", "EnglishLanguageName": "Italian", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "dd MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "dd MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "dd MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd MMM y – dd MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ore", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ja.json b/plugins/Intl/lang/ja.json index b1434f36d6..e63c4094d6 100644 --- a/plugins/Intl/lang/ja.json +++ b/plugins/Intl/lang/ja.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "土", "Day_Short_StandAlone_7": "日", "EnglishLanguageName": "Japanese", - "Format_DateTime_Long": "y年M月d日EEEE H:mm:ss", - "Format_DateTime_Short": "y\/MM\/dd H:mm:ss", + "Format_DateTime_Long": "y年M月d日EEEE {time}", + "Format_DateTime_Short": "y\/MM\/dd {time}", "Format_Date_Day_Month": "M月d日(E)", "Format_Date_Long": "y年M月d日EEEE", "Format_Date_Short": "y\/MM\/dd", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y年M月d日~y年M月d日", "Format_Month_Long": "y年M月", "Format_Month_Short": "y年M月", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "aK:mm:ss", + "Format_Time_24": "H:mm:ss", "Format_Year": "y年", "Hours": "時間", "Language_aa": "アファル語", diff --git a/plugins/Intl/lang/ka.json b/plugins/Intl/lang/ka.json index 819f7ba2a0..91af287421 100644 --- a/plugins/Intl/lang/ka.json +++ b/plugins/Intl/lang/ka.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "შაბ", "Day_Short_StandAlone_7": "კვი", "EnglishLanguageName": "Georgian", - "Format_DateTime_Long": "EEEE, dd MMMM, y HH:mm:ss", - "Format_DateTime_Short": "d MMM. y HH:mm:ss", + "Format_DateTime_Long": "EEEE, dd MMMM, y {time}", + "Format_DateTime_Short": "d MMM. y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, dd MMMM, y", "Format_Date_Short": "d MMM. y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd MMM. y – d MMM. y", "Format_Month_Long": "MMMM, y", "Format_Month_Short": "MMM. y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "საათი", "Language_aa": "აფარი", diff --git a/plugins/Intl/lang/ko.json b/plugins/Intl/lang/ko.json index 70b13cb503..229b1e6144 100644 --- a/plugins/Intl/lang/ko.json +++ b/plugins/Intl/lang/ko.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "토", "Day_Short_StandAlone_7": "일", "EnglishLanguageName": "Korean", - "Format_DateTime_Long": "y년 M월 d일 EEEE a h:mm:ss", - "Format_DateTime_Short": "y. M. d. a h:mm:ss", + "Format_DateTime_Long": "y년 M월 d일 EEEE {time}", + "Format_DateTime_Short": "y. M. d. {time}", "Format_Date_Day_Month": "MMM d일 (E)", "Format_Date_Long": "y년 M월 d일 EEEE", "Format_Date_Short": "y. M. d.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y년 M월 d일 ~ y년 M월 d일", "Format_Month_Long": "y년 MMMM", "Format_Month_Short": "y년 MMM", - "Format_Time": "a h:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "H시 m분 s초", "Format_Year": "y년", "Hours": "시간", "Language_aa": "아파르어", diff --git a/plugins/Intl/lang/lt.json b/plugins/Intl/lang/lt.json index 7dc87c76a1..2fefcf67dc 100644 --- a/plugins/Intl/lang/lt.json +++ b/plugins/Intl/lang/lt.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Št", "Day_Short_StandAlone_7": "Sk", "EnglishLanguageName": "Lithuanian", - "Format_DateTime_Long": "y 'm'. MMMM d 'd'., EEEE HH:mm:ss", - "Format_DateTime_Short": "y-MM-dd HH:mm:ss", + "Format_DateTime_Long": "y 'm'. MMMM d 'd'., EEEE {time}", + "Format_DateTime_Short": "y-MM-dd {time}", "Format_Date_Day_Month": "MM-dd, E", "Format_Date_Long": "y 'm'. MMMM d 'd'., EEEE", "Format_Date_Short": "y-MM-dd", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y MMM d – y MMM d", "Format_Month_Long": "y 'm'. LLLL", "Format_Month_Short": "y-MM", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "hh:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "valandos", "Language_aa": "Afarų", diff --git a/plugins/Intl/lang/lv.json b/plugins/Intl/lang/lv.json index 2a91a4b6fb..583d3e51a0 100644 --- a/plugins/Intl/lang/lv.json +++ b/plugins/Intl/lang/lv.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Se", "Day_Short_StandAlone_7": "Sv", "EnglishLanguageName": "Latvian", - "Format_DateTime_Long": "EEEE, y. 'gada' d. MMMM HH:mm:ss", - "Format_DateTime_Short": "y. 'gada' d. MMM HH:mm:ss", + "Format_DateTime_Long": "EEEE, y. 'gada' d. MMMM {time}", + "Format_DateTime_Short": "y. 'gada' d. MMM {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, y. 'gada' d. MMMM", "Format_Date_Short": "y. 'gada' d. MMM", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y. 'gada' d. MMM – y. 'gada' d. MMM", "Format_Month_Long": "y. 'g'. MMMM", "Format_Month_Short": "y. 'g'. MMM", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y. 'g'.", "Hours": "stundas", "Language_aa": "Afāru", diff --git a/plugins/Intl/lang/nb.json b/plugins/Intl/lang/nb.json index 84d427bd0b..812c461829 100644 --- a/plugins/Intl/lang/nb.json +++ b/plugins/Intl/lang/nb.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lør.", "Day_Short_StandAlone_7": "Søn.", "EnglishLanguageName": "Norwegian Bokmål", - "Format_DateTime_Long": "EEEE d. MMMM y HH.mm.ss", - "Format_DateTime_Short": "d. MMM y HH.mm.ss", + "Format_DateTime_Long": "EEEE d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y–d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "timer", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/nl.json b/plugins/Intl/lang/nl.json index 8c9cfa5953..66398aa589 100644 --- a/plugins/Intl/lang/nl.json +++ b/plugins/Intl/lang/nl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Za", "Day_Short_StandAlone_7": "Zo", "EnglishLanguageName": "Dutch", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "uur", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/nn.json b/plugins/Intl/lang/nn.json index 6f913f08ea..cbbc0bfdda 100644 --- a/plugins/Intl/lang/nn.json +++ b/plugins/Intl/lang/nn.json @@ -296,8 +296,8 @@ "Day_Short_StandAlone_6": "La.", "Day_Short_StandAlone_7": "Sø.", "EnglishLanguageName": "Norwegian Nynorsk", - "Format_DateTime_Long": "EEEE d. MMMM y HH:mm:ss", - "Format_DateTime_Short": "d. MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -309,7 +309,9 @@ "Format_Interval_Short_Y": "d. MMM y–d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "hr", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/pl.json b/plugins/Intl/lang/pl.json index 7183cd5cbc..f190ed6d4d 100644 --- a/plugins/Intl/lang/pl.json +++ b/plugins/Intl/lang/pl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sob.", "Day_Short_StandAlone_7": "Niedz.", "EnglishLanguageName": "Polish", - "Format_DateTime_Long": "EEEE, d MMMM y HH:mm:ss", - "Format_DateTime_Short": "dd.MM.y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "dd.MM.y {time}", "Format_Date_Day_Month": "E, d.MM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "dd.MM.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd.MM.y–dd.MM.y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "MM.y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "godziny", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/pt-br.json b/plugins/Intl/lang/pt-br.json index 4e934e942d..9f2a028bb4 100644 --- a/plugins/Intl/lang/pt-br.json +++ b/plugins/Intl/lang/pt-br.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sáb", "Day_Short_StandAlone_7": "Dom", "EnglishLanguageName": "Brazilian Portuguese", - "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y HH:mm:ss", - "Format_DateTime_Short": "d 'de' MMM 'de' y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y {time}", + "Format_DateTime_Short": "d 'de' MMM 'de' y {time}", "Format_Date_Day_Month": "E, d 'de' MMM", "Format_Date_Long": "EEEE, d 'de' MMMM 'de' y", "Format_Date_Short": "d 'de' MMM 'de' y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d 'de' MMM 'de' y – d 'de' MMM 'de' y", "Format_Month_Long": "MMMM 'de' y", "Format_Month_Short": "MMM 'de' y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/pt.json b/plugins/Intl/lang/pt.json index bacafa576d..60270770df 100644 --- a/plugins/Intl/lang/pt.json +++ b/plugins/Intl/lang/pt.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sábado", "Day_Short_StandAlone_7": "Domingo", "EnglishLanguageName": "Portuguese", - "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y HH:mm:ss", - "Format_DateTime_Short": "dd\/MM\/y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y {time}", + "Format_DateTime_Short": "dd\/MM\/y {time}", "Format_Date_Day_Month": "E, d\/MM", "Format_Date_Long": "EEEE, d 'de' MMMM 'de' y", "Format_Date_Short": "dd\/MM\/y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d 'de' MMM 'de' y – d 'de' MMM 'de' y", "Format_Month_Long": "MMMM 'de' y", "Format_Month_Short": "MM\/y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ro.json b/plugins/Intl/lang/ro.json index 2e8973de30..8f5b576ffb 100644 --- a/plugins/Intl/lang/ro.json +++ b/plugins/Intl/lang/ro.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sâm.", "Day_Short_StandAlone_7": "Dum.", "EnglishLanguageName": "Romanian", - "Format_DateTime_Long": "EEEE, d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ore", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ru.json b/plugins/Intl/lang/ru.json index b26d882971..593daf525d 100644 --- a/plugins/Intl/lang/ru.json +++ b/plugins/Intl/lang/ru.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Вс", "EnglishLanguageName": "Russian", - "Format_DateTime_Long": "EEEE, d MMMM y 'г'. H:mm:ss", - "Format_DateTime_Short": "d MMM y 'г'. H:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y 'г'. {time}", + "Format_DateTime_Short": "d MMM y 'г'. {time}", "Format_Date_Day_Month": "ccc, d MMM", "Format_Date_Long": "EEEE, d MMMM y 'г'.", "Format_Date_Short": "d MMM y 'г'.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y 'г'.", "Format_Month_Long": "LLLL y 'г'.", "Format_Month_Short": "LLL y 'г'.", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "часы", "Language_aa": "Афар", diff --git a/plugins/Intl/lang/sk.json b/plugins/Intl/lang/sk.json index d926c8f1bd..db39d8d374 100644 --- a/plugins/Intl/lang/sk.json +++ b/plugins/Intl/lang/sk.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "So", "Day_Short_StandAlone_7": "Ne", "EnglishLanguageName": "Slovak", - "Format_DateTime_Long": "EEEE, d. MMMM y H:mm:ss", - "Format_DateTime_Short": "d. M. y H:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "d. M. y {time}", "Format_Date_Day_Month": "E d. M.", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "d. M. y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. M. y – d. M. y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "M\/y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "hodiny", "Language_aa": "Afarčina", diff --git a/plugins/Intl/lang/sl.json b/plugins/Intl/lang/sl.json index 054f53ca8b..2610cb1fa1 100644 --- a/plugins/Intl/lang/sl.json +++ b/plugins/Intl/lang/sl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sob", "Day_Short_StandAlone_7": "Ned", "EnglishLanguageName": "Slovenian", - "Format_DateTime_Long": "EEEE, dd. MMMM y HH:mm:ss", - "Format_DateTime_Short": "d. MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, dd. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, dd. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y–d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ur", "Language_aa": "Afarščina", diff --git a/plugins/Intl/lang/sq.json b/plugins/Intl/lang/sq.json index ee9aa0eae3..1c9acf73d3 100644 --- a/plugins/Intl/lang/sq.json +++ b/plugins/Intl/lang/sq.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sht", "Day_Short_StandAlone_7": "Die", "EnglishLanguageName": "Albanian", - "Format_DateTime_Long": "EEEE, d MMMM y h:mm:ss a", - "Format_DateTime_Short": "d MMM y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "orë", "Language_ab": "Abkazisht", diff --git a/plugins/Intl/lang/sr.json b/plugins/Intl/lang/sr.json index 003e91483c..3e59d08f33 100644 --- a/plugins/Intl/lang/sr.json +++ b/plugins/Intl/lang/sr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Суб", "Day_Short_StandAlone_7": "Нед", "EnglishLanguageName": "Serbian", - "Format_DateTime_Long": "EEEE, dd. MMMM y. HH.mm.ss", - "Format_DateTime_Short": "dd.MM.y. HH.mm.ss", + "Format_DateTime_Long": "EEEE, dd. MMMM y. {time}", + "Format_DateTime_Short": "dd.MM.y. {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE, dd. MMMM y.", "Format_Date_Short": "dd.MM.y.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd. MMM y. – dd. MMM y.", "Format_Month_Long": "MMMM y.", "Format_Month_Short": "MMM y.", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "hh.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y.", "Hours": "сати", "Language_aa": "Афарски", diff --git a/plugins/Intl/lang/sv.json b/plugins/Intl/lang/sv.json index c829d88418..6652fd399b 100644 --- a/plugins/Intl/lang/sv.json +++ b/plugins/Intl/lang/sv.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lör", "Day_Short_StandAlone_7": "Sön", "EnglishLanguageName": "Swedish", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y–d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "timmar", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ta.json b/plugins/Intl/lang/ta.json index 43708c2ba9..a24fad18e1 100644 --- a/plugins/Intl/lang/ta.json +++ b/plugins/Intl/lang/ta.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "சனி", "Day_Short_StandAlone_7": "ஞாயி.", "EnglishLanguageName": "Tamil", - "Format_DateTime_Long": "EEEE, d MMMM, y a h:mm:ss", - "Format_DateTime_Short": "d MMM, y a h:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM, y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "MMM d, E", "Format_Date_Long": "EEEE, d MMMM, y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "a h:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "மணிநேரங்கள்", "Language_aa": "அஃபார்", diff --git a/plugins/Intl/lang/te.json b/plugins/Intl/lang/te.json index 7c9679b3a1..9122d011da 100644 --- a/plugins/Intl/lang/te.json +++ b/plugins/Intl/lang/te.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "శని", "Day_Short_StandAlone_7": "ఆది", "EnglishLanguageName": "Telugu", - "Format_DateTime_Long": "d, MMMM y, EEEE h:mm:ss a", - "Format_DateTime_Short": "d MMM, y h:mm:ss a", + "Format_DateTime_Long": "d, MMMM y, EEEE {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "d, MMMM y, EEEE", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "గంటలు", "Language_aa": "అఫార్", diff --git a/plugins/Intl/lang/th.json b/plugins/Intl/lang/th.json index afe4e21c49..52034808c2 100644 --- a/plugins/Intl/lang/th.json +++ b/plugins/Intl/lang/th.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "ส.", "Day_Short_StandAlone_7": "อา.", "EnglishLanguageName": "Thai", - "Format_DateTime_Long": "EEEEที่ d MMMM G y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEEที่ d MMMM G y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEEที่ d MMMM G y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM G y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ชั่วโมง", "Language_aa": "อะฟาร์", diff --git a/plugins/Intl/lang/tl.json b/plugins/Intl/lang/tl.json index caebf74edf..793664da58 100644 --- a/plugins/Intl/lang/tl.json +++ b/plugins/Intl/lang/tl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sab", "Day_Short_StandAlone_7": "Lin", "EnglishLanguageName": "Tagalog", - "Format_DateTime_Long": "EEEE, MMMM d, y h:mm:ss a", - "Format_DateTime_Short": "MMM d, y h:mm:ss a", + "Format_DateTime_Long": "EEEE, MMMM d, y {time}", + "Format_DateTime_Short": "MMM d, y {time}", "Format_Date_Day_Month": "E, MMM d", "Format_Date_Long": "EEEE, MMMM d, y", "Format_Date_Short": "MMM d, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "MMM d, y – MMM d, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "mga oras", "Language_ab": "Abkhazian", diff --git a/plugins/Intl/lang/tr.json b/plugins/Intl/lang/tr.json index 0d2b4082e6..3ceb15c281 100644 --- a/plugins/Intl/lang/tr.json +++ b/plugins/Intl/lang/tr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Cmt", "Day_Short_StandAlone_7": "Paz", "EnglishLanguageName": "Turkish", - "Format_DateTime_Long": "d MMMM y EEEE HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "d MMMM y EEEE {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "d MMMM E", "Format_Date_Long": "d MMMM y EEEE", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "saat", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/uk.json b/plugins/Intl/lang/uk.json index 69ec802cbb..44d6c12e92 100644 --- a/plugins/Intl/lang/uk.json +++ b/plugins/Intl/lang/uk.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Нд", "EnglishLanguageName": "Ukrainian", - "Format_DateTime_Long": "EEEE, d MMMM y 'р'. HH:mm:ss", - "Format_DateTime_Short": "d MMM y 'р'. HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y 'р'. {time}", + "Format_DateTime_Short": "d MMM y 'р'. {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y 'р'.", "Format_Date_Short": "d MMM y 'р'.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLL y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "години", "Language_aa": "Афарська", diff --git a/plugins/Intl/lang/vi.json b/plugins/Intl/lang/vi.json index e89f2f527b..9da64b533a 100644 --- a/plugins/Intl/lang/vi.json +++ b/plugins/Intl/lang/vi.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Th 7", "Day_Short_StandAlone_7": "CN", "EnglishLanguageName": "Vietnamese", - "Format_DateTime_Long": "EEEE, 'ngày' dd MMMM 'năm' y HH:mm:ss", - "Format_DateTime_Short": "d MMM, y HH:mm:ss", + "Format_DateTime_Long": "EEEE, 'ngày' dd MMMM 'năm' y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, 'ngày' dd MMMM 'năm' y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "'Ngày' dd 'tháng' M 'năm' y - 'Ngày' dd 'tháng' M 'năm' y", "Format_Month_Long": "MMMM 'năm' y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "giờ", "Language_aa": "Tiếng Afar", diff --git a/plugins/Intl/lang/zh-cn.json b/plugins/Intl/lang/zh-cn.json index 5b48c6c6d8..5cd4c2c680 100644 --- a/plugins/Intl/lang/zh-cn.json +++ b/plugins/Intl/lang/zh-cn.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "周六", "Day_Short_StandAlone_7": "周日", "EnglishLanguageName": "Simplified Chinese", - "Format_DateTime_Long": "y年M月d日EEEE ah:mm:ss", - "Format_DateTime_Short": "y年M月d日 ah:mm:ss", + "Format_DateTime_Long": "y年M月d日EEEE {time}", + "Format_DateTime_Short": "y年M月d日 {time}", "Format_Date_Day_Month": "M月d日E", "Format_Date_Long": "y年M月d日EEEE", "Format_Date_Short": "y年M月d日", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y年M月d日至y年M月d日", "Format_Month_Long": "y年M月", "Format_Month_Short": "y年M月", - "Format_Time": "ah:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "ah:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y年", "Hours": "小时", "Language_aa": "阿法文", diff --git a/plugins/Intl/lang/zh-tw.json b/plugins/Intl/lang/zh-tw.json index 5fac85b0ec..1bc630d29a 100644 --- a/plugins/Intl/lang/zh-tw.json +++ b/plugins/Intl/lang/zh-tw.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "週六", "Day_Short_StandAlone_7": "週日", "EnglishLanguageName": "Traditional Chinese", - "Format_DateTime_Long": "y年M月d日 EEEE ah:mm:ss", - "Format_DateTime_Short": "y年M月d日 ah:mm:ss", + "Format_DateTime_Long": "y年M月d日 EEEE {time}", + "Format_DateTime_Short": "y年M月d日 {time}", "Format_Date_Day_Month": "M月d日 E", "Format_Date_Long": "y年M月d日 EEEE", "Format_Date_Short": "y年M月d日", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y年M月d日至y年M月d日", "Format_Month_Long": "y年M月", "Format_Month_Short": "y年M月", - "Format_Time": "ah:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "ah:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y年", "Hours": "小時", "Language_aa": "阿法文", diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php index dc252abbda..9d75f62c2f 100644 --- a/plugins/LanguagesManager/API.php +++ b/plugins/LanguagesManager/API.php @@ -275,6 +275,45 @@ class API extends \Piwik\Plugin\API return true; } + /** + * Returns whether the user uses 12 hour clock + * + * @param string $login + * @return string + */ + public function uses12HourClockForUser($login) + { + if ($login == 'anonymous') { + return false; + } + + Piwik::checkUserHasSuperUserAccessOrIsTheUser($login); + + $lang = $this->getModel()->uses12HourClock($login); + + return $lang; + } + + /** + * Returns whether the user uses 12 hour clock + * + * @param string $login + * @param bool $use12HourClock + * @return string + */ + public function set12HourClockForUser($login, $use12HourClock) + { + if ($login == 'anonymous') { + return false; + } + + Piwik::checkUserHasSuperUserAccessOrIsTheUser($login); + + $lang = $this->getModel()->set12HourClock($login, $use12HourClock); + + return $lang; + } + private function loadAvailableLanguages() { if (!is_null($this->availableLanguageNames)) { diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php index 012de9cd50..2a468d550a 100644 --- a/plugins/LanguagesManager/LanguagesManager.php +++ b/plugins/LanguagesManager/LanguagesManager.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\LanguagesManager; use Exception; +use Piwik\API\Request; use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; @@ -138,6 +139,19 @@ class LanguagesManager extends \Piwik\Plugin } /** + * @return boolean + */ + public static function uses12HourClockForCurrentUser() + { + try { + $currentUser = Piwik::getCurrentUserLogin(); + return Request::processRequest('LanguagesManager.uses12HourClockForUser', array('login' => $currentUser)); + } catch (Exception $e) { + return false; + } + } + + /** * @return string Two letters language code, eg. "fr" */ public static function getLanguageCodeForCurrentUser() diff --git a/plugins/LanguagesManager/Model.php b/plugins/LanguagesManager/Model.php index e40452c134..fad84238cd 100644 --- a/plugins/LanguagesManager/Model.php +++ b/plugins/LanguagesManager/Model.php @@ -57,10 +57,41 @@ class Model return true; } + /** + * Returns whether the given user has choosen to use 12 hour clock + * + * @param $userLogin + * @return bool + * @throws \Exception + */ + public function uses12HourClock($userLogin) + { + return (bool) Db::fetchOne('SELECT use_12_hour_clock FROM ' . $this->table . + ' WHERE login = ? ', array($userLogin)); + } + + /** + * Sets whether the given user wants to use 12 hout clock + * + * @param string $login + * @param string $use12HourClock + * @return bool + */ + public function set12HourClock($login, $use12HourClock) + { + $query = 'INSERT INTO ' . $this->table . + ' (login, use_12_hour_clock) VALUES (?,?) ON DUPLICATE KEY UPDATE use_12_hour_clock=?'; + $bind = array($login, $use12HourClock, $use12HourClock); + Db::query($query, $bind); + + return true; + } + public static function install() { $userLanguage = "login VARCHAR( 100 ) NOT NULL , language VARCHAR( 10 ) NOT NULL , + use_12_hour_clock TINYINT(1) NOT NULL DEFAULT 0 , PRIMARY KEY ( login )"; DbHelper::createTable(self::$rawPrefix, $userLanguage); } diff --git a/plugins/LanguagesManager/Test/Integration/ModelTest.php b/plugins/LanguagesManager/Test/Integration/ModelTest.php new file mode 100644 index 0000000000..023b43f293 --- /dev/null +++ b/plugins/LanguagesManager/Test/Integration/ModelTest.php @@ -0,0 +1,136 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\LanguagesManager\tests\Integration; + +use Piwik\Common; +use Piwik\Db; +use Piwik\Plugins\LanguagesManager\Model; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group LanguagesManager + * @group ModelTest + * @group Plugins + */ +class ModelTest extends IntegrationTestCase +{ + + /** + * @var Model + */ + protected $model; + + public function setUp() + { + $this->model = new Model(); + parent::setUp(); + } + + public function test_install_ShouldNotFailAndActuallyCreateTheDatabases() + { + $this->assertContainTables(array('user_language')); + + $columns = Db::fetchAll('show columns from ' . Common::prefixTable('user_language')); + $this->assertCount(3, $columns); + } + + public function test_uninstall_ShouldNotFailAndRemovesAllAlertTables() + { + Model::uninstall(); + + $this->assertNotContainTables(array('user_language')); + + Model::install(); + } + + public function test_handlesUserLanguageEntriesCorrectly() + { + $this->model->setLanguageForUser('admin', 'de'); + + $this->assertTableEntryCount(1); + + $this->assertEquals('de', $this->model->getLanguageForUser('admin')); + + $this->model->deleteUserLanguage('admin'); + + $this->assertTableEntryCount(0); + } + + public function test_handlesUserTimeFormatEntriesCorrectly() + { + $this->model->set12HourClock('admin', false); + + $this->assertTableEntryCount(1); + + $this->assertEquals(false, $this->model->uses12HourClock('admin')); + + $this->model->deleteUserLanguage('admin'); + + $this->assertTableEntryCount(0); + } + + public function test_handlesUserLanguageAndTimeFormatEntriesCorrectly() + { + $this->model->setLanguageForUser('admin', 'de'); + + $this->assertTableEntryCount(1); + + $this->model->set12HourClock('admin', false); + $this->model->set12HourClock('user', true); + + $this->assertTableEntryCount(2); + + $this->assertEquals('de', $this->model->getLanguageForUser('admin')); + $this->assertEquals('', $this->model->getLanguageForUser('user')); + $this->assertEquals(false, $this->model->uses12HourClock('admin')); + $this->assertEquals(true, $this->model->uses12HourClock('user')); + + $this->model->deleteUserLanguage('admin'); + + $this->assertTableEntryCount(1); + } + + private function assertTableEntryCount($count) + { + $entryCount = Db::fetchOne('SELECT COUNT(*) FROM ' . Common::prefixTable('user_language')); + + $this->assertEquals($count, $entryCount); + + } + + private function assertContainTables($expectedTables) + { + $tableNames = $this->getCurrentAvailableTableNames(); + + foreach ($expectedTables as $expectedTable) { + $this->assertContains(Common::prefixTable($expectedTable), $tableNames); + } + } + + private function assertNotContainTables($expectedTables) + { + $tableNames = $this->getCurrentAvailableTableNames(); + + foreach ($expectedTables as $expectedTable) { + $this->assertNotContains(Common::prefixTable($expectedTable), $tableNames); + } + } + + private function getCurrentAvailableTableNames() + { + $tables = Db::fetchAll('show tables'); + + $tableNames = array(); + foreach ($tables as $table) { + $tableNames[] = array_shift($table); + } + + return $tableNames; + } +} diff --git a/plugins/LanguagesManager/Updates/2.15.1-b1.php b/plugins/LanguagesManager/Updates/2.15.1-b1.php new file mode 100644 index 0000000000..3465ad7b57 --- /dev/null +++ b/plugins/LanguagesManager/Updates/2.15.1-b1.php @@ -0,0 +1,32 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Plugins\LanguagesManager; + +use Piwik\Common; +use Piwik\Updater; +use Piwik\Updates; + + +class Updates_2_15_1_b1 extends Updates +{ + public function getMigrationQueries(Updater $updater) + { + $updateSql = array( + 'ALTER TABLE `' . Common::prefixTable('user_language') + . '` ADD COLUMN `use_12_hour_clock` TINYINT(1) NOT NULL DEFAULT 0 AFTER `language`' => array(1060) + ); + return $updateSql; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrationQueries(__FILE__, $this->getMigrationQueries($updater)); + } +} diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php index 93e8d67881..1a71aa7b83 100644 --- a/plugins/Live/Live.php +++ b/plugins/Live/Live.php @@ -54,5 +54,7 @@ class Live extends \Piwik\Plugin $translationKeys[] = "Live_RowActionTooltipDefault"; $translationKeys[] = "Live_RowActionTooltipWithDimension"; $translationKeys[] = "Live_SegmentedVisitorLogTitle"; + $translationKeys[] = "General_Segment"; + $translationKeys[] = "General_And"; } }
\ No newline at end of file diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php index 6d679a8a05..fa7be4dd63 100644 --- a/plugins/Live/Visitor.php +++ b/plugins/Live/Visitor.php @@ -286,7 +286,7 @@ class Visitor implements VisitorInterface unset($actionDetails[$actionIdx]); continue; - } elseif ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { + } elseif ($actionDetail['type'] == Action::TYPE_EVENT) { // Handle Event if (strlen($actionDetail['pageTitle']) > 0) { $actionDetail['eventName'] = $actionDetail['pageTitle']; @@ -301,7 +301,7 @@ class Visitor implements VisitorInterface } // Event value / Generation time - if ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { + if ($actionDetail['type'] == Action::TYPE_EVENT) { if (strlen($actionDetail['custom_float']) > 0) { $actionDetail['eventValue'] = round($actionDetail['custom_float'], self::EVENT_VALUE_PRECISION); } @@ -310,7 +310,7 @@ class Visitor implements VisitorInterface } unset($actionDetail['custom_float']); - if ($actionDetail['type'] != Action::TYPE_EVENT_CATEGORY) { + if ($actionDetail['type'] != Action::TYPE_EVENT) { unset($actionDetail['eventCategory']); unset($actionDetail['eventAction']); } @@ -423,7 +423,7 @@ class Visitor implements VisitorInterface $details['type'] = 'search'; $details['icon'] = 'plugins/Morpheus/images/search_ico.png'; break; - case Action::TYPE_EVENT_CATEGORY: + case Action::TYPE_EVENT: $details['type'] = 'event'; $details['icon'] = 'plugins/Morpheus/images/event.png'; break; diff --git a/plugins/Live/javascripts/rowaction.js b/plugins/Live/javascripts/rowaction.js index 8baba18a2d..5c54e961b5 100644 --- a/plugins/Live/javascripts/rowaction.js +++ b/plugins/Live/javascripts/rowaction.js @@ -150,6 +150,12 @@ var segmentName = getDimensionFromApiMethod(apiMethod); var segmentValue = findTitleOfRowHavingRawSegmentValue(apiMethod, segment); + if (!segmentName || (segment && segment.indexOf(';') > 0)) { + segmentName = _pk_translate('General_Segment'); + var segmentParts = segment.split(';'); + segmentValue = segmentParts.join(' ' + _pk_translate('General_And') + ' '); + } + segmentName = piwikHelper.escape(segmentName); segmentName = piwikHelper.htmlEntities(segmentName); segmentValue = piwikHelper.escape(segmentValue); diff --git a/plugins/Live/lang/cs.json b/plugins/Live/lang/cs.json index ec3ea624fd..caf56e0adc 100644 --- a/plugins/Live/lang/cs.json +++ b/plugins/Live/lang/cs.json @@ -4,7 +4,7 @@ "AveragePageGenerationTime": "Pro tohoto návštěvníka se každá stránka načetla v průměru za %1$s.", "CalculatedOverNPageViews": "Vypočítáno na základě %1$s posledních zobrazení stránek tohoto návštěvníka.", "ClickToViewMoreAboutVisit": "Klikněte pro zobrazení více informací o této návštěvě", - "ConvertedNGoals": "Zkonvertováno %s cílů", + "ConvertedNGoals": "Proměněno %s cílů", "EcommerceSummaryConversions": "%1$s%2$s objednávek celkem za %3$s%4$s, zakoupeno %5$s položek.", "FirstVisit": "První návštěva", "GoalType": "Typ", diff --git a/plugins/Live/lang/fr.json b/plugins/Live/lang/fr.json index 0b099319fd..ad9dd10a44 100644 --- a/plugins/Live/lang/fr.json +++ b/plugins/Live/lang/fr.json @@ -9,7 +9,7 @@ "FirstVisit": "Première visite", "GoalType": "Type", "HideMap": "Cacher la carte", - "KeywordRankedOnSearchResultForThisVisitor": "Le mot-clé %1$s a été a été noté %2$s dans la page de résultats de recherche %3$s pour ce visiteur", + "KeywordRankedOnSearchResultForThisVisitor": "Le mot-clé %1$s a été noté %2$s dans la page de résultats de recherche %3$s pour ce visiteur", "LastHours": "Dernières %s heures", "LastMinutes": "Dernières %s minutes", "LastVisit": "Dernière visite", diff --git a/plugins/Live/lang/ja.json b/plugins/Live/lang/ja.json index eee0e823c0..1bcc483702 100644 --- a/plugins/Live/lang/ja.json +++ b/plugins/Live/lang/ja.json @@ -35,6 +35,12 @@ "VisitorsInRealTime": "リアルタイムのビジター", "VisitorsLastVisit": "このビジターの最新のビジットは %s 日前です。", "VisitsFrom": "から %1$s%2$s が %3$s を訪問", - "VisitSummary": "ウェブサイト %3$s で、合計 %1$s%2$s を消費し、%4$s は %6$s 訪問で、%5$s ページ閲覧しました。%7$s" + "VisitSummary": "ウェブサイト %3$s で、合計 %1$s%2$s を消費し、%4$s は %6$s 訪問で、%5$s ページ閲覧しました。%7$s", + "RowActionTooltipDefault": "この行で分割されたビジターログを表示", + "RowActionTooltipWithDimension": "この %s で分割されたビジターログを表示", + "RowActionTooltipTitle": "セグメントビジターログを表示", + "SegmentedVisitorLogTitle": "%s が \" %s \" のビジットを示すビジターログ", + "OnClickPause": "%s が開始されました。クリックして一時停止します。", + "OnClickStart": "%s は停止しています。クリックして開始します。" } }
\ No newline at end of file diff --git a/plugins/Live/lang/ko.json b/plugins/Live/lang/ko.json index e6c78b24d0..e1de77a61f 100644 --- a/plugins/Live/lang/ko.json +++ b/plugins/Live/lang/ko.json @@ -1,16 +1,34 @@ { "Live": { + "CalculatedOverNPageViews": "해당 방문자의 %1$s 번 페이지 방문을 통해 계산되었습니다.", + "ClickToViewMoreAboutVisit": "이 방문에 대해 더 자세한 정보를 보기 위해서 클릭하세요.", + "FirstVisit": "첫 방문", "GoalType": "유형", + "HideMap": "지도 숨기기", "KeywordRankedOnSearchResultForThisVisitor": "키워드 %1$s 이 방문자의 %3$s 검색 결과 페이지에서 %2$s에 랭크되었습니다.", "LastHours": "최근 %s시간", "LastMinutes": "최근 %s분", + "LastVisit": "최근 방문", "LinkVisitorLog": "방문자 기록 자세히 보기", + "LoadMoreVisits": "더 많은 방문들 불러오기", "MorePagesNotDisplayed": "이 방문객의 표시는 더이상 없습니다", + "NbVisitor": "1명 방문자", + "NbVisitors": "%s 방문자들", + "NextVisitor": "다음 방문자", + "NoMoreVisits": "이 방문자가 방문한 페이지는 더 이상 없습니다.", "PageRefreshed": "이 페이지를 볼 수 있음 \/ 업데이트된 횟수", + "PreviousVisitor": "이전 방문자", + "RealTimeVisitorCount": "실시간 방문자 합계", "Referrer_URL": "참조 URL", + "ShowMap": "지도 보이기", + "ViewVisitorProfile": "방문자 프로필 보기", + "VisitedPages": "방문한 페이지들", "VisitorLog": "방문자 기록", "VisitorLogDocumentation": "이 표는 선택한 날짜 기간 내에 최신 방문에 표시하고 있습니다. 방문 날짜 위로 마우스를 이동하고, 그 방문자의 마지막 방문이 며칠 전인지 볼 수 있습니다. %s 기간내에 오늘이 포함되어 있으면, 방문객을 실시간으로 볼 수 있습니다! %s 여기에 표시되는 것은 보관을위한 cron 작업 설정 빈도에 상관없는 라이브 데이터입니다.", + "VisitorProfile": "방문자 프로필", "VisitorsInRealTime": "실시간 방문자", - "VisitorsLastVisit": "이 방문객의 최신 방문은 %s일 입니다." + "VisitorsLastVisit": "이 방문객의 최신 방문은 %s일 입니다.", + "OnClickPause": "%s이\/가 시작되었습니다. 멈추기 위해서 클릭해주세요.", + "OnClickStart": "%s이\/가 멈추었습니다. 시작하기 위해서 클릭해주세요." } }
\ No newline at end of file diff --git a/plugins/LogViewer b/plugins/LogViewer -Subproject 25df816eed9b0d44ebcdb89f1dfc9dde8cb3de2 +Subproject 0ee846df5038e82a87abb98e35b4d7cd1718d22 diff --git a/plugins/Login/lang/cs.json b/plugins/Login/lang/cs.json index 311341374e..8dc2f4f2f0 100644 --- a/plugins/Login/lang/cs.json +++ b/plugins/Login/lang/cs.json @@ -1,15 +1,15 @@ { "Login": { - "ConfirmationLinkSent": "Do vaší e-mailové schránky byl odeslán potvrzovací e-mail. Pro potvrzení požadavku na změnu hesla navštivte odkaz v e-mailu uvedený.", - "ContactAdmin": "Možná příčina: Váš hosting zakázal funkci mail().. <br \/>Prosím kontaktujte vašeho administrátora Piwiku.", + "ConfirmationLinkSent": "Do vaší schránky byl odeslán potvrzovací email. Pro potvrzení požadavku na změnu hesla navštivte odkaz uvedený v emailu.", + "ContactAdmin": "Možná příčina: Váš hosting zakázal funkci mail().. <br \/>Prosím kontaktujte svého administrátora Piwiku.", "ExceptionInvalidSuperUserAccessAuthenticationMethod": "Uživatel se Super uživatelským přístupem nemůže být autentizován mechanismem %s.", "ExceptionPasswordMD5HashExpected": "Parametr hesla je očekáván jako MD5 hash hesla", "InvalidNonceOrHeadersOrReferrer": "Zabezpečení formuláře selhalo. Prosím obnovte formulář a zkontrolujte, že máte povolené cookies. Pokud používáte proxy server, %smusíte nakonfigurovat Piwik aby přijímal proxy hlavičku%s, která přeposílá hlavičku hosta. Zkontrolujte také, že je správně posílána hlavička referrer.", "InvalidOrExpiredToken": "Klíč je neplatný, nebo vypršel", - "InvalidUsernameEmail": "Neplatné uživatelské jméno a\/nebo e-mailová adresa", + "InvalidUsernameEmail": "Neplatné uživatelské jméno a\/nebo emailová adresa", "LogIn": "Přihlásit", - "LoginOrEmail": "Uživatelské jméno nebo E-mail", - "LoginPasswordNotCorrect": "Uživatelské jméno & heslo nejsou správné", + "LoginOrEmail": "Uživatelské jméno nebo Email", + "LoginPasswordNotCorrect": "Chybná kombinace uživatelského jména a hesla.", "LostYourPassword": "Zapomněli jste vaše heslo?", "MailPasswordChangeBody": "Ahoj %1$s,\n\nZ %2$s byl zaslán požadavek o změnu hesla. Pro potvrzení nových přihlašovacích údajů navštivte následující odkaz:\n\n%3$s\n\nPoznámka: Tento token je platný 24 hodin.\n\nA díky za používání Piwiku!", "MailTopicPasswordChange": "Potvrďte změnu hesla", diff --git a/plugins/Login/lang/ko.json b/plugins/Login/lang/ko.json index bdc9d72937..e924445eda 100644 --- a/plugins/Login/lang/ko.json +++ b/plugins/Login/lang/ko.json @@ -2,6 +2,7 @@ "Login": { "ConfirmationLinkSent": "확인 링크가 당신의 받은 편지함으로 전송되었습니다. 이메일을 확인하고 비밀번호 변경요청을 승인하려면 이 링크를 방문하세요.", "ContactAdmin": "가능한 이유: 호스트가 메일 기능을 비활성화했을 수 있습니다. <br \/>당신의 Piwik 관리자에게 연락해보세요.", + "ExceptionInvalidSuperUserAccessAuthenticationMethod": "슈퍼 유저 접근의 '%s' 방법으로는 인증되지 않습니다.", "ExceptionPasswordMD5HashExpected": "비밀번호 매개변수는 MD5해시 값이 사용되고 있습니다.", "InvalidNonceOrHeadersOrReferrer": "양식 보안 실패, 양식을 새로고침하여 쿠키가 활성화되어 있는지 확인하세요. 프록시 서버를 사용하는 경우라면, 호스트 헤더에 %sPiwik configure에서 프록시 헤더를 수락%s하고 전달해야합니다. 또한 리퍼러 헤더가 올바르게 전송되는지 확인합니다.", "InvalidOrExpiredToken": "토큰이 잘못되었거나 만료되었습니다.", @@ -15,7 +16,8 @@ "PasswordChanged": "비밀번호가 변경되었습니다.", "PasswordRepeat": "비빌번호 (재입력)", "PasswordsDoNotMatch": "비밀번호가 일치하지 않습니다.", - "RememberMe": "저를 기억해 주세요", + "PluginDescription": "아이디와 비밀번호를 통해 사용자 인증뿐만 아니라 비밀번호 재설정 기능까지 제공합니다. 마켓에서 구할 수 있는 LoginLdap와 같은 또 다른 로그인 플러그인을 사용하여 인증 방법을 변경할 수 있습니다.", + "RememberMe": "계정 기억하기", "ResetPasswordInstructions": "계정에 사용할 새로운 비밀번호를 입력하세요." } }
\ No newline at end of file diff --git a/plugins/Login/lang/sl.json b/plugins/Login/lang/sl.json index e14d69523b..7c83e0c2b4 100644 --- a/plugins/Login/lang/sl.json +++ b/plugins/Login/lang/sl.json @@ -1,11 +1,17 @@ { "Login": { + "ConfirmationLinkSent": "Potrditveno sporočilo je bilo poslano na vaš elektronski naslov. Poglejte elektronsko pošto in sledite povezavi v njem, da potrdite zahtevo za spremembo vašega gesla.", + "ContactAdmin": "Možen razlog: na strežniku je onemogočena mail() funkcija. <br \/>Prosimo, obrnite se na vašega Piwik administratorja.", + "ExceptionInvalidSuperUserAccessAuthenticationMethod": "Uporabnik z vlogo \"Super User\" se ne more prijaviti z uporabo mehanizma '%s'.", "InvalidOrExpiredToken": "Žeton je neveljaven ali pa je potekel.", "InvalidUsernameEmail": "Napačno uporabniško ime ali e-mail naslov.", "LogIn": "Vpis", "LoginOrEmail": "Uporabniško ime ali E-mail", "LoginPasswordNotCorrect": "Napačna kombinacija uporabniškega imena in gesla.", "LostYourPassword": "Ste pozabili geslo?", + "MailPasswordChangeBody": "Pozdravljen\/-a %1$s,\n\ns strani %2$s je bil prejet zahtevek za zamenjavo gesla. Za potrditev verodostojnosti te zahteve, prosimo sledite povezavi:\n\n%3$s\n\nOpozorilo: ta žeton bo potekel po 24-ih urah.\n\nHvala, ker uporabljate Piwik!", + "MailTopicPasswordChange": "Potrdite spremembo gesla", + "PasswordChanged": "Vaše geslo je bilo spremenjeno.", "PasswordRepeat": "Geslo (ponovno)", "PasswordsDoNotMatch": "Gesli se ne ujemata.", "RememberMe": "Zapomni si me" diff --git a/plugins/Login/lang/tr.json b/plugins/Login/lang/tr.json index d9c1c638ec..981d278bf0 100644 --- a/plugins/Login/lang/tr.json +++ b/plugins/Login/lang/tr.json @@ -1,13 +1,18 @@ { "Login": { + "ConfirmationLinkSent": "Onaylama bağlantısı gelen kutunuza gönderildi. Şifre değişikliğinı onaylamak için e-posta hesabınızı kontrol edin ve bağlantıyı ziyaret edin.", + "ContactAdmin": "Olası nedeni: Sunucunuzda mail() fonksiyonunu devre dışı bırakmış olabilir. <br \/>Lütfen Piwik yöneticinizle iletişime geçiniz.", "InvalidOrExpiredToken": "Güvenlik kodu yanlış yada süresi dolmuş.", "InvalidUsernameEmail": "Geçersiz kullanıcı adı ve\/veya e-posta adresi", "LogIn": "Oturum aç", "LoginOrEmail": "Giriş veya E-posta", "LoginPasswordNotCorrect": "Kullanıcı adı & Şifre yanlış", "LostYourPassword": "Şifrenizi mi unuttunuz?", + "MailTopicPasswordChange": "Parola Değişikliğini Onayla", + "PasswordChanged": "Parolanız değiştirildi.", "PasswordRepeat": "Şifre (Tekrar)", "PasswordsDoNotMatch": "Şifre eşleşmedi", - "RememberMe": "Beni Hatırla" + "RememberMe": "Beni Hatırla", + "ResetPasswordInstructions": "Hesabınız için yeni parolanızı giriniz." } }
\ No newline at end of file diff --git a/plugins/Login/templates/login.twig b/plugins/Login/templates/login.twig index 4bcef0d799..0140418af4 100644 --- a/plugins/Login/templates/login.twig +++ b/plugins/Login/templates/login.twig @@ -1,12 +1,12 @@ {% extends '@Morpheus/layout.twig' %} +{% block meta %} + <meta name="robots" content="index,follow"> +{% endblock %} + {% block head %} {{ parent() }} - {% block meta %} - <meta name="robots" content="index,follow"> - {% endblock %} - <script type="text/javascript" src="libs/bower_components/jquery-placeholder/jquery.placeholder.js"></script> <!--[if lt IE 9]> <script src="libs/bower_components/html5shiv/dist/html5shiv.min.js"></script> diff --git a/plugins/MobileAppMeasurable/lang/ja.json b/plugins/MobileAppMeasurable/lang/ja.json new file mode 100644 index 0000000000..deee803801 --- /dev/null +++ b/plugins/MobileAppMeasurable/lang/ja.json @@ -0,0 +1,7 @@ +{ + "MobileAppMeasurable": { + "MobileApp": "モバイルアプリ", + "MobileApps": "モバイルアプリ", + "MobileAppDescription": "IOS、Android や他のモバイル オペレーティング システムのネイティブ モバイル アプリケーション。" + } +}
\ No newline at end of file diff --git a/plugins/MobileAppMeasurable/lang/nb.json b/plugins/MobileAppMeasurable/lang/nb.json new file mode 100644 index 0000000000..49eee5557f --- /dev/null +++ b/plugins/MobileAppMeasurable/lang/nb.json @@ -0,0 +1,7 @@ +{ + "MobileAppMeasurable": { + "MobileApp": "Mobil-app", + "MobileApps": "Mobil-apper", + "MobileAppDescription": "En spesialbygget mobil-app for iOS, Android eller hvilken som helst annen mobil plattform." + } +}
\ No newline at end of file diff --git a/plugins/MobileMessaging/API.php b/plugins/MobileMessaging/API.php index aaf30e6a64..345c633b15 100644 --- a/plugins/MobileMessaging/API.php +++ b/plugins/MobileMessaging/API.php @@ -8,7 +8,6 @@ */ namespace Piwik\Plugins\MobileMessaging; -use Piwik\Common; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugins\MobileMessaging\SMSProvider; @@ -27,15 +26,6 @@ class API extends \Piwik\Plugin\API const SMS_FROM = 'Piwik'; /** - * @param string $provider - * @return SMSProvider - */ - private static function getSMSProviderInstance($provider) - { - return SMSProvider::factory($provider); - } - - /** * determine if SMS API credential are available for the current user * * @return bool true if SMS API credential are available for the current user @@ -83,7 +73,7 @@ class API extends \Piwik\Plugin\API { $this->checkCredentialManagementRights(); - $smsProviderInstance = self::getSMSProviderInstance($provider); + $smsProviderInstance = SMSProvider::factory($provider); $smsProviderInstance->verifyCredential($apiKey); $settings = $this->getCredentialManagerSettings(); @@ -160,7 +150,7 @@ class API extends \Piwik\Plugin\API Piwik::checkUserIsNotAnonymous(); $credential = $this->getSMSAPICredential(); - $SMSProvider = self::getSMSProviderInstance($credential[MobileMessaging::PROVIDER_OPTION]); + $SMSProvider = SMSProvider::factory($credential[MobileMessaging::PROVIDER_OPTION]); $SMSProvider->sendSMS( $credential[MobileMessaging::API_KEY_OPTION], $content, @@ -183,7 +173,7 @@ class API extends \Piwik\Plugin\API $this->checkCredentialManagementRights(); $credential = $this->getSMSAPICredential(); - $SMSProvider = self::getSMSProviderInstance($credential[MobileMessaging::PROVIDER_OPTION]); + $SMSProvider = SMSProvider::factory($credential[MobileMessaging::PROVIDER_OPTION]); return $SMSProvider->getCreditLeft( $credential[MobileMessaging::API_KEY_OPTION] ); diff --git a/plugins/MobileMessaging/Controller.php b/plugins/MobileMessaging/Controller.php index 2bb6f8d5f5..7ba976c67e 100644 --- a/plugins/MobileMessaging/Controller.php +++ b/plugins/MobileMessaging/Controller.php @@ -94,7 +94,12 @@ class Controller extends ControllerAdmin $view->creditLeft = $mobileMessagingAPI->getCreditLeft(); } - $view->smsProviders = SMSProvider::getAvailableSMSProviders(); + $providers = array(); + foreach (SMSProvider::findAvailableSmsProviders() as $provider) { + $providers[$provider->getId()] = $provider->getDescription(); + } + + $view->smsProviders = $providers; // construct the list of countries from the lang files $countries = array(); diff --git a/plugins/MobileMessaging/SMSProvider.php b/plugins/MobileMessaging/SMSProvider.php index 1711bfafed..14aec1bb24 100644 --- a/plugins/MobileMessaging/SMSProvider.php +++ b/plugins/MobileMessaging/SMSProvider.php @@ -8,63 +8,120 @@ */ namespace Piwik\Plugins\MobileMessaging; -use Exception; -use Piwik\Development; +use Piwik\Container\StaticContainer; +use Piwik\Plugin; use Piwik\Piwik; -use Piwik\BaseFactory; /** - * The SMSProvider abstract class is used as a base class for SMS provider implementations. + * The SMSProvider abstract class is used as a base class for SMS provider implementations. To create your own custom + * SMSProvider extend this class and implement the methods to send text messages. The class needs to be placed in a + * `SMSProvider` directory of your plugin. * + * @api */ -abstract class SMSProvider extends BaseFactory +abstract class SMSProvider { const MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS = 160; const MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS = 153; const MAX_UCS2_CHARS_IN_ONE_UNIQUE_SMS = 70; const MAX_UCS2_CHARS_IN_ONE_CONCATENATED_SMS = 67; - protected static $availableSMSProviders = array( - 'Clockwork' => 'You can use <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/"><img src="plugins/MobileMessaging/images/Clockwork.png"/></a> to send SMS Reports from Piwik.<br/> - <ul> - <li> First, <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/">get an API Key from Clockwork</a> (Signup is free!) - </li><li> Enter your Clockwork API Key on this page. </li> - </ul> - <br/><em>About Clockwork: </em><ul> - <li>Clockwork gives you fast, reliable high quality worldwide SMS delivery, over 450 networks in every corner of the globe. - </li><li>Cost per SMS message is around ~0.08USD (0.06EUR). - </li><li>Most countries and networks are supported but we suggest you check the latest position on their coverage map <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/sms-coverage/">here</a>. - </li> - </ul> - ', - ); - - protected static function getClassNameFromClassId($id) + /** + * Get the ID of the SMS Provider. Eg 'Clockwork' or 'FreeMobile' + * @return string + */ + abstract public function getId(); + + /** + * Get a description about the SMS Provider. For example who the SMS Provider is, instructions how the API Key + * needs to be set, and more. You may return HTML here for better formatting. + * + * @return string + */ + abstract public function getDescription(); + + /** + * Verify the SMS API credential. + * + * @param string $apiKey API Key + * @return bool true if SMS API Key is valid, false otherwise + */ + abstract public function verifyCredential($apiKey); + + /** + * Get the amount of remaining credits. + * + * @param string $apiKey API Key + * @return string remaining credits + */ + abstract public function getCreditLeft($apiKey); + + /** + * Actually send the given text message. This method should only send the text message, it should not trigger + * any notifications etc. + * + * @param string $apiKey + * @param string $smsText + * @param string $phoneNumber + * @param string $from + * @return bool true + */ + abstract public function sendSMS($apiKey, $smsText, $phoneNumber, $from); + + /** + * Defines whether the SMS Provider is available. If a certain provider should be used only be a limited + * range of users you can restrict the provider here. For example there is a Development SMS Provider that is only + * available when the development is actually enabled. You could also create a SMS Provider that is only available + * to Super Users etc. Usually this method does not have to be implemented by a SMS Provider. + * + * @return bool + */ + public function isAvailable() { - return __NAMESPACE__ . '\\SMSProvider\\' . $id; + return true; } - protected static function getInvalidClassIdExceptionMessage($id) + /** + * @param string $provider The name of the string + * @return SMSProvider + * @throws \Exception + * @ignore + */ + public static function factory($provider) { - return Piwik::translate('MobileMessaging_Exception_UnknownProvider', - array($id, implode(', ', array_keys(self::getAvailableSMSProviders()))) - ); + $providers = self::findAvailableSmsProviders(); + + if (!array_key_exists($provider, $providers)) { + throw new \Exception(Piwik::translate('MobileMessaging_Exception_UnknownProvider', + array($provider, implode(', ', array_keys($providers))) + )); + } + + return $providers[$provider]; } /** * Returns all available SMS Providers - * - * @return array + * + * @return SMSProvider[] + * @ignore */ - public static function getAvailableSMSProviders() + public static function findAvailableSmsProviders() { - $smsProviders = self::$availableSMSProviders; + /** @var SMSProvider[] $smsProviders */ + $smsProviders = Plugin\Manager::getInstance()->findMultipleComponents('SMSProvider', 'Piwik\Plugins\MobileMessaging\SMSProvider'); + + $providers = array(); - if (Development::isEnabled()) { - $smsProviders['Development'] = 'Development SMS Provider<br />All sent SMS will be displayed as Notification'; + foreach ($smsProviders as $provider) { + /** @var SMSProvider $provider */ + $provider = StaticContainer::get($provider); + if ($provider->isAvailable()) { + $providers[$provider->getId()] = $provider; + } } - return $smsProviders; + return $providers; } /** @@ -72,6 +129,7 @@ abstract class SMSProvider extends BaseFactory * * @param string $string * @return bool true if $string contains UCS2 characters + * @ignore */ public static function containsUCS2Characters($string) { @@ -94,6 +152,7 @@ abstract class SMSProvider extends BaseFactory * @param int $maximumNumberOfConcatenatedSMS * @param string $appendedString * @return string original $string or truncated $string appended with $appendedString + * @ignore */ public static function truncate($string, $maximumNumberOfConcatenatedSMS, $appendedString = 'MobileMessaging_SMS_Content_Too_Long') { @@ -150,30 +209,4 @@ abstract class SMSProvider extends BaseFactory $maxCharsInOneConcatenatedSMS * $maximumNumberOfConcatenatedSMS; } - /** - * verify the SMS API credential - * - * @param string $apiKey API Key - * @return bool true if SMS API credential are valid, false otherwise - */ - abstract public function verifyCredential($apiKey); - - /** - * get remaining credits - * - * @param string $apiKey API Key - * @return string remaining credits - */ - abstract public function getCreditLeft($apiKey); - - /** - * send SMS - * - * @param string $apiKey - * @param string $smsText - * @param string $phoneNumber - * @param string $from - * @return bool true - */ - abstract public function sendSMS($apiKey, $smsText, $phoneNumber, $from); } diff --git a/plugins/MobileMessaging/SMSProvider/Clockwork.php b/plugins/MobileMessaging/SMSProvider/Clockwork.php index 2ae8e95da4..50a7f0e50b 100644 --- a/plugins/MobileMessaging/SMSProvider/Clockwork.php +++ b/plugins/MobileMessaging/SMSProvider/Clockwork.php @@ -15,8 +15,9 @@ use Piwik\Plugins\MobileMessaging\APIException; use Piwik\Plugins\MobileMessaging\SMSProvider; require_once PIWIK_INCLUDE_PATH . "/plugins/MobileMessaging/APIException.php"; + /** - * + * @ignore */ class Clockwork extends SMSProvider { @@ -31,6 +32,27 @@ class Clockwork extends SMSProvider const MAXIMUM_FROM_LENGTH = 11; const MAXIMUM_CONCATENATED_SMS = 3; + public function getId() + { + return 'Clockwork'; + } + + public function getDescription() + { + return 'You can use <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/"><img src="plugins/MobileMessaging/images/Clockwork.png"/></a> to send SMS Reports from Piwik.<br/> + <ul> + <li> First, <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/">get an API Key from Clockwork</a> (Signup is free!) + </li><li> Enter your Clockwork API Key on this page. </li> + </ul> + <br/><em>About Clockwork: </em><ul> + <li>Clockwork gives you fast, reliable high quality worldwide SMS delivery, over 450 networks in every corner of the globe. + </li><li>Cost per SMS message is around ~0.08USD (0.06EUR). + </li><li>Most countries and networks are supported but we suggest you check the latest position on their coverage map <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/sms-coverage/">here</a>. + </li> + </ul> + '; + } + public function verifyCredential($apiKey) { $this->getCreditLeft($apiKey); diff --git a/plugins/MobileMessaging/SMSProvider/Development.php b/plugins/MobileMessaging/SMSProvider/Development.php index ef6cbe951d..18cc57809a 100644 --- a/plugins/MobileMessaging/SMSProvider/Development.php +++ b/plugins/MobileMessaging/SMSProvider/Development.php @@ -10,14 +10,31 @@ namespace Piwik\Plugins\MobileMessaging\SMSProvider; use Piwik\Notification; use Piwik\Plugins\MobileMessaging\SMSProvider; +use Piwik\Development as PiwikDevelopment; /** * Used for development only * + * @ignore */ class Development extends SMSProvider { + public function getId() + { + return 'Development'; + } + + public function getDescription() + { + return 'Development SMS Provider<br />All sent SMS will be displayed as Notification'; + } + + public function isAvailable() + { + return PiwikDevelopment::isEnabled(); + } + public function verifyCredential($apiKey) { return true; diff --git a/plugins/MobileMessaging/SMSProvider/StubbedProvider.php b/plugins/MobileMessaging/SMSProvider/StubbedProvider.php index b9527a3b8d..8bda726a3e 100644 --- a/plugins/MobileMessaging/SMSProvider/StubbedProvider.php +++ b/plugins/MobileMessaging/SMSProvider/StubbedProvider.php @@ -13,10 +13,26 @@ use Piwik\Plugins\MobileMessaging\SMSProvider; /** * Used for testing * + * @ignore */ class StubbedProvider extends SMSProvider { + public function getId() + { + return 'StubbedProvider'; + } + + public function getDescription() + { + return 'Only during testing available'; + } + + public function isAvailable() + { + return defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE; + } + public function verifyCredential($apiKey) { return true; diff --git a/plugins/MobileMessaging/lang/cs.json b/plugins/MobileMessaging/lang/cs.json index efe1d113af..6b4d0ad68b 100644 --- a/plugins/MobileMessaging/lang/cs.json +++ b/plugins/MobileMessaging/lang/cs.json @@ -6,24 +6,24 @@ "MobileReport_NoPhoneNumbers": "Prosím aktivujte aspoň jedno telefonní číslo přejitím na", "MultiSites_Must_Be_Activated": "Pro vytváření SMS zpráv se statistikami povolte v Piwiku plugin MultiSites.", "PhoneNumbers": "Telefonní čísla", - "PluginDescription": "Vytvářejte a stahujte vlastní SMS hlášení a nechte si je zasílat do vašeho telefonu denně, týdnnebo ě měsíčně.", + "PluginDescription": "Vytvářejte a stahujte vlastní SMS hlášení a nechte si je zasílat do svého telefonu denně, týdně nebo měsíčně.", "Settings_APIKey": "Klíč k API", "Settings_CountryCode": "Kód země", - "Settings_CredentialNotProvided": "Než budete moct vytvářet a spravovat telefonní čísla, musíte připojit Piwik k vašemu SMS účtu výše.", - "Settings_CredentialNotProvidedByAdmin": "Než budete moct vytvářet a spravovat telefonní čísla, požádejte vašeho administrátora, aby připojil Piwik k SMS účtu.", + "Settings_CredentialNotProvided": "Než budete moci vytvářet a spravovat telefonní čísla, musíte připojit Piwik ke svému SMS účtu výše.", + "Settings_CredentialNotProvidedByAdmin": "Než budete moci vytvářet a spravovat telefonní čísla, požádejte svého administrátora, aby připojil Piwik k SMS účtu.", "Settings_CredentialProvided": ";Váš %s SMS účet je nastaven správně.", "Settings_DeleteAccountConfirm": "Opravdu chcete odstranit tento SMS účet?", "Settings_InvalidActivationCode": "Zadaný kód nebyl platný, prosím zkuste to znovu.", "Settings_LetUsersManageAPICredential": "Povolit uživatelům správu vlastních pověření API SMS účtů", "Settings_LetUsersManageAPICredential_No_Help": "Všichni uživatelé mohou přijímat hlášení prostřednictvím SMS a budou používat kredity vašeho účtu.", - "Settings_LetUsersManageAPICredential_Yes_Help": "Každý uživatel bude moct nastavit svůj SMS API účet an nebude používat váš kredit.", + "Settings_LetUsersManageAPICredential_Yes_Help": "Každý uživatel bude moci nastavit svůj SMS API účet a nebude používat váš kredit.", "Settings_ManagePhoneNumbers": "Spravovat telefonní čísla", "Settings_PhoneActivated": "Telefonní číslo ověřeno! Nyní můžete přijímat SMS se statistikami.", "Settings_PhoneNumber": "Telefonní číslo", "Settings_PhoneNumbers_Add": "Přidat nové telefonní číslo", "Settings_PhoneNumbers_CountryCode_Help": "Pokud nevíte telefonní kód vaší země, podívejte se zde", - "Settings_PhoneNumbers_Help": "Než budete moct přijímat SMS se statistikami, telefonní číslo musí být zadáno níže.", - "Settings_PhoneNumbers_HelpAdd": "Po stisknutí \"přidat\" bude na telefon odeslána SMS s kódem. Uživatel, který obdrží SMS by se měl přihlásit do Piwiku, kliknout na nastavení, pak na Mobilní zprávy a zadat kód. Poté bude moct dostávat statistiky do svého telefonu.", + "Settings_PhoneNumbers_Help": "Než budete moci přijímat SMS se statistikami, telefonní číslo musí být zadáno níže.", + "Settings_PhoneNumbers_HelpAdd": "Po stisknutí \"přidat\" bude na telefon odeslána SMS s kódem. Uživatel, který obdrží SMS by se měl přihlásit do Piwiku, kliknout na nastavení, pak na Mobilní zprávy a zadat kód. Poté bude moci dostávat statistiky do svého telefonu.", "Settings_PleaseSignUp": "Pokud chcetevytvářet SMS hlášení a dostávat krátké zprávy o statistikách do svého telefonu, zaregistrujte se na SMS API a zadejte svoje informace níže.", "Settings_SMSAPIAccount": "Správa SMS API účtu", "Settings_SMSProvider": "SMS Provider", @@ -34,8 +34,8 @@ "Settings_VerificationCodeJustSent": "Právě jsme na zadané telefonní číslo odeslali SMS s kódem. Zadejte ho výše a stiskněte \"ověřit\".", "SettingsMenu": "Mobilní zprávy", "SMS_Content_Too_Long": "[příliš dlouhé]", - "TopLinkTooltip": "Nechte si webové analýzi zasílat na e-mail nebo na váš mobilní telefon.", - "TopMenu": "Email & SMS Reporty", + "TopLinkTooltip": "Nechte si webové analýzy zasílat na email nebo na svůj mobilní telefon.", + "TopMenu": "Email a SMS reporty", "VerificationText": "Kód je %s. Pro ověření vašeh otelefonního čísla tento kód zkopírujte do formuláře přístupného na Piwik > %s > %s." } }
\ No newline at end of file diff --git a/plugins/MobileMessaging/lang/tr.json b/plugins/MobileMessaging/lang/tr.json new file mode 100644 index 0000000000..3a034272ba --- /dev/null +++ b/plugins/MobileMessaging/lang/tr.json @@ -0,0 +1,22 @@ +{ + "MobileMessaging": { + "PhoneNumbers": "Telefon Numaraları", + "Settings_APIKey": "API Anahtarı", + "Settings_CountryCode": "Ülke Kodu", + "Settings_DeleteAccountConfirm": "Bu SMS hesabını silmek istediğinizden emin misiniz?", + "Settings_InvalidActivationCode": "Girilen kod geçerli değil, lütfen tekrar deneyin.", + "Settings_ManagePhoneNumbers": "Telefon Numaralarını Yönet", + "Settings_PhoneActivated": "Telefon numarası doğrulandı! Artık istatistikleri SMS ile alabilirsiniz.", + "Settings_PhoneNumber": "Telefon Numarası", + "Settings_PhoneNumbers_Add": "Yeni telefon numarası ekle", + "Settings_PhoneNumbers_CountryCode_Help": "Telefon ülke kodunu bilmiyorsanız, buradan ülkelere bakabilirsiniz.", + "Settings_SMSAPIAccount": "SMS API Hesabını Yönet", + "Settings_SMSProvider": "SMS Sağlayıcı", + "Settings_ValidatePhoneNumber": "Onayla", + "Settings_VerificationCodeJustSent": "Biz az önce SMS ile numaranıza kod gönderdik: lütfen yukarıya kodu girin ve \"Onayla\" 'ya tıklayın.", + "SettingsMenu": "Mobil Mesajlaşma", + "SMS_Content_Too_Long": "[çok uzun]", + "TopLinkTooltip": "Web Analytics Raporlarını e-posta kutunuza veya cep telefonunuza teslim alın.", + "TopMenu": "E-posta ve SMS Raporları" + } +}
\ No newline at end of file diff --git a/plugins/Monolog/tests/System/TrackerLoggingTest.php b/plugins/Monolog/tests/System/TrackerLoggingTest.php index 70c933d13f..8716229116 100644 --- a/plugins/Monolog/tests/System/TrackerLoggingTest.php +++ b/plugins/Monolog/tests/System/TrackerLoggingTest.php @@ -83,10 +83,8 @@ DEBUG: 'apiv' => '1',", $response); private function setTrackerConfig($trackerConfig) { $testingEnvironment = self::$fixture->getTestEnvironment(); - $configOverride = $testingEnvironment->configOverride; - $configOverride['Tracker'] = $trackerConfig; - $configOverride['log']['log_writers'] = array('screen'); - $testingEnvironment->configOverride = $configOverride; + $testingEnvironment->overrideConfig('Tracker', $trackerConfig); + $testingEnvironment->overrideConfig('log', 'log_writers', array('screen')); $testingEnvironment->save(); } diff --git a/plugins/MultiSites/API.php b/plugins/MultiSites/API.php index 60b9fab90a..23ae821738 100755 --- a/plugins/MultiSites/API.php +++ b/plugins/MultiSites/API.php @@ -147,7 +147,6 @@ class API extends \Piwik\Plugin\API 'limit' => SettingsPiwik::getWebsitesCountToDisplay(), 'showColumns' => '', 'hideColumns' => '', - 'serialize' => 0, 'format' => 'original')); if (!empty($sites)) { diff --git a/plugins/MultiSites/lang/ko.json b/plugins/MultiSites/lang/ko.json index c7bdb7ee7b..69e154b027 100644 --- a/plugins/MultiSites/lang/ko.json +++ b/plugins/MultiSites/lang/ko.json @@ -1,6 +1,7 @@ { "MultiSites": { "Evolution": "변화 추이", - "TopLinkTooltip": "웹사이트의 모든 웹분석 통계를 비교합니다." + "TopLinkTooltip": "웹사이트의 모든 웹분석 통계를 비교합니다.", + "Pagination": "%s - %s 의 %s" } }
\ No newline at end of file diff --git a/plugins/Overlay/Controller.php b/plugins/Overlay/Controller.php index 795d7b991d..6327d34ddf 100644 --- a/plugins/Overlay/Controller.php +++ b/plugins/Overlay/Controller.php @@ -16,14 +16,26 @@ use Piwik\Metrics; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugins\Actions\ArchivingHelper; +use Piwik\Plugins\SegmentEditor\SegmentFormatter; use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\ProxyHttp; +use Piwik\Segment; use Piwik\Tracker\Action; use Piwik\Tracker\PageUrl; use Piwik\View; class Controller extends \Piwik\Plugin\Controller { + /** + * @var SegmentFormatter + */ + private $segmentFormatter; + + public function __construct(SegmentFormatter $segmentFormatter) + { + $this->segmentFormatter = $segmentFormatter; + parent::__construct(); + } /** The index of the plugin */ public function index() @@ -38,6 +50,7 @@ class Controller extends \Piwik\Plugin\Controller $view = new View($template); $this->setGeneralVariablesView($view); + $view->segment = Request::getRawSegmentFromRequest(); $view->ssl = ProxyHttp::isHttps(); @@ -52,8 +65,9 @@ class Controller extends \Piwik\Plugin\Controller $period = Common::getRequestVar('period'); $date = Common::getRequestVar('date'); $currentUrl = Common::getRequestVar('currentUrl'); + $segment = Request::getRawSegmentFromRequest(); $currentUrl = Common::unsanitizeInputValue($currentUrl); - $segment = ''; + $segmentSidebar = ''; $normalizedCurrentUrl = PageUrl::excludeQueryParametersFromUrl($currentUrl, $idSite); $normalizedCurrentUrl = Common::unsanitizeInputValue($normalizedCurrentUrl); @@ -63,16 +77,21 @@ class Controller extends \Piwik\Plugin\Controller $path = ArchivingHelper::getActionExplodedNames($normalizedCurrentUrl, Action::TYPE_PAGE_URL); $path = array_map('urlencode', $path); $label = implode('>', $path); - $request = new Request( - 'method=Actions.getPageUrls' - . '&idSite=' . urlencode($idSite) - . '&date=' . urlencode($date) - . '&period=' . urlencode($period) - . '&label=' . urlencode($label) - . '&format=original' - . '&format_metrics=0' + + $params = array( + 'idSite' => $idSite, + 'date' => $date, + 'period' => $period, + 'label' => $label, + 'format' => 'original', + 'format_metrics' => 0, ); - $dataTable = $request->process(); + + if (!empty($segment)) { + $params['segment'] = $segment; + } + + $dataTable = Request::processRequest('Actions.getPageUrls', $params); $formatter = new Metrics\Formatter\Html(); @@ -84,7 +103,10 @@ class Controller extends \Piwik\Plugin\Controller $showMetrics = array('nb_hits', 'nb_visits', 'nb_users', 'nb_uniq_visitors', 'bounce_rate', 'exit_rate', 'avg_time_on_page'); - $segment = $row->getMetadata('segment'); + $segmentSidebar = $row->getMetadata('segment'); + if (!empty($segmentSidebar) && !empty($segment)) { + $segmentSidebar = $segment . ';' . $segmentSidebar; + } foreach ($showMetrics as $metric) { $value = $row->getColumn($metric); @@ -127,7 +149,8 @@ class Controller extends \Piwik\Plugin\Controller $view->idSite = $idSite; $view->period = $period; $view->date = $date; - $view->segment = $segment; + $view->segment = $segmentSidebar; + $view->segmentDescription = $this->segmentFormatter->getHumanReadable($segment, $idSite); $this->outputCORSHeaders(); return $view->render(); @@ -142,64 +165,20 @@ class Controller extends \Piwik\Plugin\Controller $idSite = Common::getRequestVar('idSite', 0, 'int'); Piwik::checkUserHasViewAccess($idSite); + $view = new View('@Overlay/startOverlaySession'); + $sitesManager = APISitesManager::getInstance(); $site = $sitesManager->getSiteFromId($idSite); $urls = $sitesManager->getSiteUrlsFromId($idSite); + $view->isHttps = ProxyHttp::isHttps(); + $view->knownUrls = json_encode($urls); + $view->mainUrl = $site['main_url']; + $this->outputCORSHeaders(); Common::sendHeader('Content-Type: text/html; charset=UTF-8'); - return ' - <html><head><title></title></head><body> - <script type="text/javascript"> - function handleProtocol(url) { - if (' . (ProxyHttp::isHttps() ? 'true' : 'false') . ') { - return url.replace(/http:\/\//i, "https://"); - } else { - return url.replace(/https:\/\//i, "http://"); - } - } - - function removeUrlPrefix(url) { - return url.replace(/http(s)?:\/\/(www\.)?/i, ""); - } - - if (window.location.hash) { - var match = false; - - var urlToRedirect = window.location.hash.substr(1); - var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect); - - var knownUrls = ' . json_encode($urls) . '; - for (var i = 0; i < knownUrls.length; i++) { - var testUrl = removeUrlPrefix(knownUrls[i]); - if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) { - match = true; - if (navigator.appName == "Microsoft Internet Explorer") { - // internet explorer loses the referrer if we use window.location.href=X - var referLink = document.createElement("a"); - referLink.href = handleProtocol(urlToRedirect); - document.body.appendChild(referLink); - referLink.click(); - } else { - window.location.href = handleProtocol(urlToRedirect); - } - break; - } - } - - if (!match) { - var idSite = window.location.href.match(/idSite=([0-9]+)/i)[1]; - window.location.href = "index.php?module=Overlay&action=showErrorWrongDomain" - + "&idSite=" + idSite - + "&url=" + encodeURIComponent(urlToRedirect); - } - } - else { - window.location.href = handleProtocol("' . $site['main_url'] . '"); - }; - </script> - </body></html> - '; + + return $view->render(); } /** diff --git a/plugins/Overlay/client/client.js b/plugins/Overlay/client/client.js index 75bd19f3b3..5fd99c466b 100644 --- a/plugins/Overlay/client/client.js +++ b/plugins/Overlay/client/client.js @@ -10,7 +10,7 @@ var Piwik_Overlay_Client = (function () { var idSite; /** The current period and date */ - var period, date; + var period, date, segment; /** Reference to the status bar DOM element */ var statusBar; @@ -84,11 +84,12 @@ var Piwik_Overlay_Client = (function () { return { /** Initialize in-site analytics */ - initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate) { + initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate, pSegment) { piwikRoot = pPiwikRoot; idSite = pIdSite; period = pPeriod; date = pDate; + segment = pSegment; var load = this.loadScript; var loading = this.loadingNotification; @@ -146,6 +147,10 @@ var Piwik_Overlay_Client = (function () { var url = piwikRoot + 'index.php?module=API&method=Overlay.' + method + '&idSite=' + idSite + '&period=' + period + '&date=' + date + '&format=JSON&filter_limit=-1'; + if (segment) { + url += '&segment=' + segment; + } + if (additionalParams) { url += '&' + additionalParams; } diff --git a/plugins/Overlay/config/ui-test.php b/plugins/Overlay/config/ui-test.php new file mode 100644 index 0000000000..1bb5ae8b75 --- /dev/null +++ b/plugins/Overlay/config/ui-test.php @@ -0,0 +1,14 @@ +<?php + +return array( + + // Overlay needs the full URLs in order to find the links in the embedded page (otherwise the % + // tooltips don't show up) + 'tests.ui.url_normalizer_blacklist.api' => DI\add(array( + 'Overlay.getFollowingPages', + )), + 'tests.ui.url_normalizer_blacklist.controller' => DI\add(array( + 'Overlay.renderSidebar', + )), + +);
\ No newline at end of file diff --git a/plugins/Overlay/javascripts/Overlay_Helper.js b/plugins/Overlay/javascripts/Overlay_Helper.js index e0681e7b45..42ff388d81 100644 --- a/plugins/Overlay/javascripts/Overlay_Helper.js +++ b/plugins/Overlay/javascripts/Overlay_Helper.js @@ -20,11 +20,17 @@ var Overlay_Helper = { }, /** Get the url to launch overlay */ - getOverlayLink: function (idSite, period, date, link) { + getOverlayLink: function (idSite, period, date, segment, link) { var url = 'index.php?module=Overlay&period=' + encodeURIComponent(period) + '&date=' + encodeURIComponent(date) + '&idSite=' + encodeURIComponent(idSite); + + if (segment) { + url += '&segment=' + encodeURIComponent(segment); + } + if (link) { url += '#?l=' + Overlay_Helper.encodeFrameUrl(link); } + return url; } diff --git a/plugins/Overlay/javascripts/Piwik_Overlay.js b/plugins/Overlay/javascripts/Piwik_Overlay.js index 4c78080806..4bde0ee343 100644 --- a/plugins/Overlay/javascripts/Piwik_Overlay.js +++ b/plugins/Overlay/javascripts/Piwik_Overlay.js @@ -10,7 +10,7 @@ var Piwik_Overlay = (function () { var $body, $iframe, $sidebar, $main, $location, $loading, $errorNotLoading; var $rowEvolutionLink, $transitionsLink, $fullScreenLink, $visitorLogLink; - var idSite, period, date; + var idSite, period, date, segment; var iframeSrcBase; var iframeDomain = ''; @@ -28,13 +28,19 @@ var Piwik_Overlay = (function () { iframeCurrentPage = currentUrl; iframeDomain = currentUrl.match(/http(s)?:\/\/(www\.)?([^\/]*)/i)[3]; - globalAjaxQueue.abort(); - var ajaxRequest = new ajaxHelper(); - ajaxRequest.addParams({ + var params = { module: 'Overlay', action: 'renderSidebar', currentUrl: currentUrl - }, 'get'); + }; + + if (segment) { + params.segment = segment; + } + + globalAjaxQueue.abort(); + var ajaxRequest = new ajaxHelper(); + ajaxRequest.addParams(params, 'get'); ajaxRequest.setCallback( function (response) { hideLoading(); @@ -111,6 +117,16 @@ var Piwik_Overlay = (function () { $fullScreenLink.show(); } + function getOverlaySegment(url) { + var location = broadcast.getParamValue('segment', url); + + // angular will encode the value again since it is added as the fragment path, not the fragment query parameter, + // so we have to decode it again after getParamValue + location = decodeURIComponent(location); + + return location; + } + function getOverlayLocationFromHash(urlHash) { var location = broadcast.getParamValue('l', urlHash); @@ -143,11 +159,12 @@ var Piwik_Overlay = (function () { return { /** This method is called when Overlay loads */ - init: function (iframeSrc, pIdSite, pPeriod, pDate) { + init: function (iframeSrc, pIdSite, pPeriod, pDate, pSegment) { iframeSrcBase = iframeSrc; idSite = pIdSite; period = pPeriod; date = pDate; + segment = pSegment; $body = $('body'); $iframe = $('#overlayIframe'); @@ -201,7 +218,7 @@ var Piwik_Overlay = (function () { if (parts.length == 2) { period = parts[0]; date = parts[1]; - window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, iframeCurrentPage); + window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, segment, iframeCurrentPage); } }); @@ -219,7 +236,11 @@ var Piwik_Overlay = (function () { // handle transitions link $transitionsLink.click(function () { - DataTable_RowActions_Transitions.launchForUrl(iframeCurrentPageNormalized); + var unescapedSegment = null; + if (segment) { + unescapedSegment = unescape(segment); + } + DataTable_RowActions_Transitions.launchForUrl(iframeCurrentPageNormalized, unescapedSegment); return false; }); diff --git a/plugins/Overlay/javascripts/rowaction.js b/plugins/Overlay/javascripts/rowaction.js index c84e60a33b..de063b990e 100644 --- a/plugins/Overlay/javascripts/rowaction.js +++ b/plugins/Overlay/javascripts/rowaction.js @@ -15,17 +15,47 @@ function DataTable_RowActions_Overlay(dataTable) { DataTable_RowActions_Overlay.prototype = new DataTable_RowAction; +DataTable_RowActions_Overlay.registeredReports = []; +DataTable_RowActions_Overlay.registerReport = function (handler) { + DataTable_RowActions_Overlay.registeredReports.push(handler); +} + + DataTable_RowActions_Overlay.prototype.onClick = function (actionA, tr, e) { if (!actionA.data('overlay-manipulated')) { actionA.data('overlay-manipulated', 1); - var link = tr.find('> td:first > a').attr('href'); - link = $('<textarea>').html(link).val(); // remove html entities + var segment, link; + + var i = 0; + for (i; i < DataTable_RowActions_Overlay.registeredReports.length; i++) { + var report = DataTable_RowActions_Overlay.registeredReports[i]; + if (report + && report.onClick + && report.isAvailableOnReport + && report.isAvailableOnReport(this.dataTable.param)) { + var result = report.onClick.apply(this, arguments); + + if (!result || !result.link) { + return; + } + + link = result.link; + if (result.segment) { + segment = result.segment; + } + break; + } + } + + if (link) { + var href = Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', segment, link); - actionA.attr({ - target: '_blank', - href: Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', link) - }); + actionA.attr({ + target: '_blank', + href: href + }); + } } return true; @@ -54,7 +84,18 @@ DataTable_RowActions_Registry.register({ if (!window.DataTable_RowActions_Transitions) { return false; } - return DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action); + + var i = 0; + for (i; i < DataTable_RowActions_Overlay.registeredReports.length; i++) { + var report = DataTable_RowActions_Overlay.registeredReports[i]; + if (report + && report.isAvailableOnReport + && report.isAvailableOnReport(dataTableParams)) { + return true; + } + } + + return false; }, isAvailableOnRow: function (dataTableParams, tr) { diff --git a/plugins/Overlay/lang/ja.json b/plugins/Overlay/lang/ja.json index 952ea205ac..3c99e23c55 100644 --- a/plugins/Overlay/lang/ja.json +++ b/plugins/Overlay/lang/ja.json @@ -15,6 +15,7 @@ "Overlay": "ページオーバーレイ", "PluginDescription": "実際の web サイト上のオーバーレイ分析データを参照してください。ユーザーが各リンクをクリックした回数が表示されます。注: 有効なトランジションプラグインが必要です。", "RedirectUrlError": "URL \"%s\" に対するページオーバーレイを開こうとしています。%s Piwik 設定のドメインが、すべてリンクに一致しません。", + "RedirectUrlErrorAdmin": "%s 設定で %s 追加の URL としてドメインを追加できます。", "RedirectUrlErrorUser": "管理者に、任意のドメインを追加 URL として追加するよう管理者に依頼してください。" } }
\ No newline at end of file diff --git a/plugins/Overlay/lang/ko.json b/plugins/Overlay/lang/ko.json index 4b761eaf5e..1c2ebbc404 100644 --- a/plugins/Overlay/lang/ko.json +++ b/plugins/Overlay/lang/ko.json @@ -13,6 +13,7 @@ "OneClick": "1회 클릭", "OpenFullScreen": "전체 화면 (사이드바 아님)으로 이동", "Overlay": "페이지 오버레이", + "PluginDescription": "당신의 웹사이트 분석 데이터를 오버레이 형식으로 볼 수 있습니다. 사용자가 얼마나 각 링크를 클릭했는지 확인해보세요. 참고: Transitions 플러그인이 활성화 되어 있어야 합니다.", "RedirectUrlError": "URL \"%s\"에 대한 페이지 오버레이를 열려고 시도합니다. %s은 Piwik 설정한 도메인과 어떤 링크도 일치하지 않습니다.", "RedirectUrlErrorAdmin": "당신은 %s설정%s에서 추가적인 URL로 도메인을 추가할 수 있습니다.", "RedirectUrlErrorUser": "도메인에 추가적인 URL을 추가하려면 관리자에게 문의하세요." diff --git a/plugins/Overlay/stylesheets/overlay.css b/plugins/Overlay/stylesheets/overlay.css index 4e30660775..a13dd67a46 100644 --- a/plugins/Overlay/stylesheets/overlay.css +++ b/plugins/Overlay/stylesheets/overlay.css @@ -35,12 +35,20 @@ a#overlayTitle .icon-help { margin: 20px 10px; } -#overlayLocation { +#overlayLocation, .overlaySegment { width: 200px; - margin: 0 0 30px 10px; font-size: 12px; } +#overlayLocation { + margin: 0 0 10px 10px; +} + +.overlaySegment { + margin: 0 0 30px 0; + word-break: break-all; +} + #overlayLoading { background: url(../../Morpheus/images/loading-blue.gif) no-repeat center 10px; width: 190px; diff --git a/plugins/Overlay/templates/index.twig b/plugins/Overlay/templates/index.twig index 10a0b40d76..effe5fbade 100644 --- a/plugins/Overlay/templates/index.twig +++ b/plugins/Overlay/templates/index.twig @@ -61,8 +61,8 @@ <script type="text/javascript"> broadcast._isInit = true; $(function () { - var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ rawDate }}'; - Piwik_Overlay.init(iframeSrc, '{{ idSite }}', '{{ period }}', '{{ rawDate }}'); + var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ rawDate }}&segment={{ segment }}'; + Piwik_Overlay.init(iframeSrc, '{{ idSite }}', '{{ period }}', '{{ rawDate }}', '{{ segment }}'); window.Piwik_Overlay_Translations = { domain: "{{ 'Overlay_Domain'|translate }}" diff --git a/plugins/Overlay/templates/index_noframe.twig b/plugins/Overlay/templates/index_noframe.twig index 0478715edb..f3c9e61579 100644 --- a/plugins/Overlay/templates/index_noframe.twig +++ b/plugins/Overlay/templates/index_noframe.twig @@ -6,7 +6,7 @@ <div id="overlayNoFrame"> <script type="text/javascript"> - var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ date }}'; + var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ date }}&segment={{ segment }}'; var locationParts = window.location.href.split('#'); if (locationParts.length > 1) { diff --git a/plugins/Overlay/templates/renderSidebar.twig b/plugins/Overlay/templates/renderSidebar.twig index 09d8cf0d40..36ff9c8ba1 100644 --- a/plugins/Overlay/templates/renderSidebar.twig +++ b/plugins/Overlay/templates/renderSidebar.twig @@ -8,6 +8,11 @@ </span> </div> + <div class="overlaySegment"> + <strong>{{ 'General_Segment'|translate }}:</strong> + <span>{{ segmentDescription }}</span> + </div> + {% if data|length > 0 %} <h2 class="overlayMainMetrics">{{ 'General_MainMetrics'|translate }}</h2> <ul class="overlayMetrics"> diff --git a/plugins/Overlay/templates/startOverlaySession.twig b/plugins/Overlay/templates/startOverlaySession.twig new file mode 100644 index 0000000000..b1db8ce5b2 --- /dev/null +++ b/plugins/Overlay/templates/startOverlaySession.twig @@ -0,0 +1,50 @@ +<html><head><title></title></head><body> +<script type="text/javascript"> + function handleProtocol(url) { + if ({% if isHttps %}true{% else %}false{% endif %}) { + return url.replace(/http:\/\//i, "https://"); + } else { + return url.replace(/https:\/\//i, "http://"); + } + } + + function removeUrlPrefix(url) { + return url.replace(/http(s)?:\/\/(www\.)?/i, ""); + } + + if (window.location.hash) { + var match = false; + + var urlToRedirect = window.location.hash.substr(1); + var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect); + + var knownUrls = {{ knownUrls|raw }}; + for (var i = 0; i < knownUrls.length; i++) { + var testUrl = removeUrlPrefix(knownUrls[i]); + if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) { + match = true; + if (navigator.appName == "Microsoft Internet Explorer") { + // internet explorer loses the referrer if we use window.location.href=X + var referLink = document.createElement("a"); + referLink.href = handleProtocol(urlToRedirect); + document.body.appendChild(referLink); + referLink.click(); + } else { + window.location.href = handleProtocol(urlToRedirect); + } + break; + } + } + + if (!match) { + var idSite = window.location.href.match(/idSite=([0-9]+)/i)[1]; + window.location.href = "index.php?module=Overlay&action=showErrorWrongDomain" + + "&idSite=" + idSite + + "&url=" + encodeURIComponent(urlToRedirect); + } + } + else { + window.location.href = handleProtocol("{{ mainUrl|e('js') }}"); + }; +</script> +</body></html>
\ No newline at end of file diff --git a/plugins/PrivacyManager/lang/cs.json b/plugins/PrivacyManager/lang/cs.json index 45881dd487..1677ed1252 100644 --- a/plugins/PrivacyManager/lang/cs.json +++ b/plugins/PrivacyManager/lang/cs.json @@ -2,7 +2,7 @@ "PrivacyManager": { "AnonymizeIpDescription": "Zvolte \"ano\", pokud nemá Piwik sledovat plně kvalifikované IP adresy.", "AnonymizeIpInlineHelp": "Skryje poslední byte IP adresy návštěvníka, aby souhlasila se zákony vaší země.", - "AnonymizeIpExtendedHelp": "Když uživatelé navštíví vaše stránky, Piwik neuloží jejich plnou IP adresu(jako %s), ale nejprve bude anonymizována (na %s). Anonymizace IP adres je jeden z požadavků práva na ochranu soukromí v některých zemí, jako je třeba Německo.", + "AnonymizeIpExtendedHelp": "Když uživatelé navštíví vaše stránky, Piwik neuloží jejich plnou IP adresu (jako %s), ale nejprve bude anonymizována (na %s). Anonymizace IP adres je jedním z požadavků práva na ochranu soukromí v některých zemí, jako je třeba Německo.", "AnonymizeIpMaskLengtDescription": "Zvolte, kolik bitů z návštěvníkovy IP adresy má být maskováno.", "AnonymizeIpMaskLength": "%s bitů - např. %s", "CannotLockSoDeleteLogActions": "Tabulka log_action nebude vyprázdněna: dejte Mysql uživateli %s privilegium LOCK TABLES.", diff --git a/plugins/PrivacyManager/lang/ja.json b/plugins/PrivacyManager/lang/ja.json index ed8f1a4bf7..aee8d4f898 100644 --- a/plugins/PrivacyManager/lang/ja.json +++ b/plugins/PrivacyManager/lang/ja.json @@ -13,6 +13,8 @@ "DeleteDataDescription": "データベースのサイズを小さく保つために、古い訪問者のログ、かつ\/または生成されたレポートを定期的に削除するようPiwikを設定できます。", "DeleteDataDescription2": "必要であれば、事前処理されたレポートは削除せず、ビジットとページビューとコンバージョンのログデータのみの削除もできます。あるいは、事前処理されたレポートを削除し、ログデータを保存することもできます。", "DeleteDataInterval": "古いデータを削除:毎", + "DeleteOldVisitorLogs": "古いビジターログを削除", + "DeleteOldArchivedReports": "古いアーカイブレポートを削除", "DeleteLogDescription2": "自動ログ削除を有効にする時は、データが失われないように、以前のすべての日次レポートが処理されていることを確認する必要があります。", "DeleteLogInfo": "以下のテーブルのログが削除されます: %s", "DeleteLogsConfirm": "あなたは、ログデータの削除を有効にしようとしています。古いログデータが削除され、レポートが既に作成されていない場合には、歴史的な過去の分析データを見ることはできません。あなたは本当にこれをしたいですか?", @@ -25,6 +27,7 @@ "DeleteReportsInfo2": "\"%s\"が有効でない場合は、要求されれば、古いレポートが自動的に再作成されます。", "DeleteReportsInfo3": "\"%s\" を有効にすると、データは永久に失われます。", "DeleteReportsOlderThan": "この月数より古いリポートを削除", + "DeleteSchedulingSettings": "古いデータの削除をスケジュールします。", "DeleteDataSettings": "古いビジターのログとリポートを削除", "DoNotTrack_Description": "「トラック(追跡)しない」機能は、ユーザーが訪問しないウェブサイト(分析サービス、広告ネットワーク、ソーシャルプラットフォームを含む)によるトラッキングのオプトアウトを可能にするためのテクノロジーとポリシーの提言です。", "DoNotTrack_Disable": "「トラック(追跡)しない」機能のサポートを無効にする", @@ -39,15 +42,18 @@ "GeolocationAnonymizeIpNote": "注) 位置情報探索機能は、匿名化された 1byte の場合とほぼ同じ結果になります。2byte 以上を使用すると、位置情報探索機能が不正確になります。", "GetPurgeEstimate": "パージ推定値を取得", "KeepBasicMetrics": "基本のメトリックスを保持(ビジット、ページビュー、直帰率、目標コンバージョン、eコマースコンバージョン等)", + "KeepDataFor": "すべてのデータを保持", "KeepReportSegments": "上記の保持されるデータについては、レポートのセグメントも保持する", "LastDelete": "最後に削除したのは", "LeastDaysInput": "日数は %s より大きい数を指定してください。", "LeastMonthsInput": "月数は %s より大きい数を指定してください。", "MenuPrivacySettings": "プライバシー", "NextDelete": "次回の削除まで", + "PluginDescription": "ユーザーのプライバシーを増強し、Piwik インスタンス プライバシーを地域の法律に準拠させます。", "PurgeNow": "今DBを削除する", "PurgeNowConfirm": "データベースから永久にデータを削除しようとしています。続けますか?", "PurgingData": "データを削除しています...", + "RecommendedForPrivacy": "プライバシー推奨", "ReportsDataSavedEstimate": "データベースサイズ", "SaveSettingsBeforePurge": "データ削除設定を変更しました。削除が開始される前に保存してください。", "SeeAlsoOurOfficialGuidePrivacy": "私達のオフィシャルガイドもご確認ください。%sWeb Analytics Privacy%s", diff --git a/plugins/PrivacyManager/lang/ko.json b/plugins/PrivacyManager/lang/ko.json index ff0c89f872..84987a600a 100644 --- a/plugins/PrivacyManager/lang/ko.json +++ b/plugins/PrivacyManager/lang/ko.json @@ -2,6 +2,7 @@ "PrivacyManager": { "AnonymizeIpDescription": "완전히 확인 된 IP 주소를 추적하지 않도록하려면 \"예\"를 선택합니다.", "AnonymizeIpInlineHelp": "개인이 설정한 내부 규칙 및 지침에 따라 방문자의 IP주소의 마지막 바이트를 숨깁니다.", + "AnonymizeIpExtendedHelp": "유저들이 웹사이트에 접속할 때, Piwik는 모든 IP 주소(예: %s)를 사용하지 않고 일부를 숨깁니다(예: %s). IP 주소 익명 처리는 독일과 같이 특정 국가에서 프라이버시 법률에 따라 요구되는 것 중 하나입니다.", "AnonymizeIpMaskLengtDescription": "방문자의 IP 주소에서 몇 바이트를 숨기려면 선택하세요.", "AnonymizeIpMaskLength": "%s 바이트 - 예) %s", "CannotLockSoDeleteLogActions": "log_action 테이블을 비울 수 없습니다: '%s' MYSQL 사용자에게 LOCK 테이블 권한을 부여하세요.", @@ -12,6 +13,8 @@ "DeleteDataDescription": "데이터베이스 크기를 작게 유지하기 위해 기존 방문자의 로그와 생성된 보고서를 정기적으로 삭제하도록 Piwik을 설정할 수 있습니다.", "DeleteDataDescription2": "필요한 경우, 사전 처리된 보고서는 삭제하지 않고, 방문과 페이지뷰 및 전환 로그 데이터만 삭제할 수 있습니다. 또는 사전 처리된 보고서를 제거하고 로그 데이터를 저장할 수 있습니다.", "DeleteDataInterval": "오래된 데이터 삭제", + "DeleteOldVisitorLogs": "오래된 방문자 로그 삭제", + "DeleteOldArchivedReports": "오래된 아카이브된 보고서 삭제", "DeleteLogDescription2": "자동으로 로그 삭제를 활성화할 때 데이터가 손실되지 않도록 모든 이전의 일일 보고서가 처리되고 있는지 확인해야합니다.", "DeleteLogInfo": "다음 테이블의 로그가 삭제됩니다: %s", "DeleteLogsConfirm": "당신은 로그 데이터의 삭제를 사용하려고합니다. 이전 로그 데이터가 삭제되고 보고서가 이미 생성되어 있지 않은 경우에는 과거 이력 분석 데이터를 볼 수 없습니다. 정말 실행할까요?", @@ -24,12 +27,13 @@ "DeleteReportsInfo2": "\"%s\"가 유효하지 않은 경우, 요구되는 경우, 이전 보고서가 자동으로 다시 만들어집니다.", "DeleteReportsInfo3": "\"%s\"를 사용하면 데이터가 영구적으로 손실됩니다.", "DeleteReportsOlderThan": "오래된 보고서 삭제", + "DeleteSchedulingSettings": "오래된 데이터 삭제 계획하기", "DeleteDataSettings": "오래된 방문자 기록 및 보고서 삭제", "DoNotTrack_Description": "\"추적 않함\"기능은 사용자가 방문한 웹사이트 (분석 서비스, 광고 네트워크, 소셜 플랫폼 등)에 의한 추적 차단을 가능하게하는 기술과 정책 제언입니다.", "DoNotTrack_Disable": "\"추적하지 않음\"기능 지원 비활성화", "DoNotTrack_Disabled": "Piwik은 현재 브라우저에서 \"추적당하고 싶지 않음\"이 지정된 경우에도 모든 방문자를 추적하고 있습니다.", "DoNotTrack_DisabledMoreInfo": "방문자의 프라이버시를 존중하고 \"추적하지 않기\"기능 지원을 활성화하는 것이 좋습니다.", - "DoNotTrack_Enable": "\"추적 않함\"기능 활성화", + "DoNotTrack_Enable": "\"추적하지 않음\"기능 활성화", "DoNotTrack_Enabled": "당신은 현재 사용자의 프라이버시를 존중합니다. 브라보!", "DoNotTrack_EnabledMoreInfo": "브라우저에서 사용자가 \"추적당하고 싶지 않음\"을 설정한 경우, (\"추적하지 않음\"기능이 활성화되어 있으면) Piwik 이러한 방문자를 추적하지 않습니다.", "DoNotTrack_SupportDNTPreference": "\"추적하지 않음\" 지원 기능 설정", @@ -38,19 +42,25 @@ "GeolocationAnonymizeIpNote": "참고: 위치 정보는 대략 1바이트로 익명으로 처리되는 것과 같은 결과를 얻습니다. 2바이트 또는 이상이면 위치 정보가 정확하지 않을 수 있습니다.", "GetPurgeEstimate": "비운 추정치 받기", "KeepBasicMetrics": "기본 지표 유지 (방문, 페이지뷰, 반송률, 목표전환, 전자상거래 전환 등)", + "KeepDataFor": "모든 데이터를 유지합니다:", "KeepReportSegments": "위 유지되는 데이터는 보고서의 세그먼트에도 유지", "LastDelete": "마지막으로 삭제한 것은", "LeastDaysInput": "일 수는 %s보다 커야합니다.", - "LeastMonthsInput": "달은 %s보다 커야합니다.", + "LeastMonthsInput": "개월은 %s보다 커야합니다.", "MenuPrivacySettings": "개인 정보 보호", "NextDelete": "다음 제거까지", + "PluginDescription": "유저 프라이버시를 높이고, 또한 프라이버시와 관련한 국내 법률을 따르도록 만드세요.", "PurgeNow": "지금 DB 삭제", "PurgeNowConfirm": "영구적으로 데이터베이스의 데이터를 삭제하려고합니다. 계속 하시겠습니까?", "PurgingData": "데이터를 삭제합니다...", + "RecommendedForPrivacy": "프라이버시를 위해 권장함", "ReportsDataSavedEstimate": "데이터베이스 크기", "SaveSettingsBeforePurge": "데이터 삭제 설정을 변경했습니다. 삭제가 시작되기 전에 저장하세요.", + "SeeAlsoOurOfficialGuidePrivacy": "공식 가이드인 %sWeb Analytics Privacy%s을 참고해주세요.", "Teaser": "이 페이지에서는 기존의 법률에 따라, Piwik이 개인에 맞도록 정의할 수 있습니다: %s 익명 방문자의 IP 주소 %s, %s 자동으로 데이터베이스에서 오래된 방문자의 로그를 삭제 %s, %s 웹사이트를 탈퇴하는 방법을 제공 %s에 따름.", "TeaserHeadline": "개인 정보 보호 설정", + "UseAnonymizedIpForVisitEnrichment": "또한, 방문이 많을 때 익명화된 IP 주소를 사용합니다.", + "UseAnonymizedIpForVisitEnrichmentNote": "IP 주소와 공급자를 통하여 방문자의 지역 정보를 알아내는 것 같은 플러그인은 방문자의 메타데이터를 향상시킵니다. 기본적으로 이런 플러그인은 익명화된 IP 주소를 사용합니다. 만약 '아니오'를 선택할 경우, 익명처리되지 않은 전체 IP 주소를 대신 사용하게 되어 프라이버시는 낮추지만 대신 데이터 정확도를 높일 것입니다.", "UseAnonymizeIp": "익명 방문자의 IP 주소", "UseDeleteLog": "정기적으로 데이터베이스에서 오래된 방문자의 로그를 삭제함", "UseDeleteReports": "정기적으로 데이터베이스에서 오래된 보고서를 삭제" diff --git a/plugins/PrivacyManager/lang/tr.json b/plugins/PrivacyManager/lang/tr.json index 22d6df66b6..a863ba7e52 100644 --- a/plugins/PrivacyManager/lang/tr.json +++ b/plugins/PrivacyManager/lang/tr.json @@ -1,6 +1,13 @@ { "PrivacyManager": { "AnonymizeIpInlineHelp": "Yerel gizlilik kurallarına uymak için ziyaretçi IP adreslerini gizleyin.", + "CurrentDBSize": "Güncel veritabanı boyutu", + "DeleteOldVisitorLogs": "Eski ziyaretçi kayıtlarını sil", + "DeleteOldArchivedReports": "Arşivlenen eski raporları sil", + "DeleteMaxRowsNoLimit": "Limit yok", + "DeleteDataSettings": "Eski ziyaretçi kayıtlarını ve raporları sil", + "MenuPrivacySettings": "Gizlilik", + "ReportsDataSavedEstimate": "Veritabanı boyutu", "TeaserHeadline": "Gizlilik Ayarları" } }
\ No newline at end of file diff --git a/plugins/Provider/lang/ja.json b/plugins/Provider/lang/ja.json index 8f34448bfc..4ac0948932 100644 --- a/plugins/Provider/lang/ja.json +++ b/plugins/Provider/lang/ja.json @@ -1,6 +1,7 @@ { "Provider": { "ColumnProvider": "プロバイダ", + "PluginDescription": "ビジターのインターネットサービスプロバイダを報告します", "ProviderReportDocumentation": "このリポートは、ウェブサイトにアクセスするビジターが使っているインターネットサービスプロバイダを示しています。詳細については、プロバイダ名をクリックしてください。 %s Piwik がビジターのプロバイダを判別できない場合は、IPとして表示されます。", "WidgetProviders": "プロバイダ", "ProviderReportFooter": "未知のプロバイダーとは IP アドレスが検索できなかったことを意味します。" diff --git a/plugins/Provider/lang/sl.json b/plugins/Provider/lang/sl.json index c13e93cdc9..8f5a09e046 100644 --- a/plugins/Provider/lang/sl.json +++ b/plugins/Provider/lang/sl.json @@ -1,6 +1,8 @@ { "Provider": { "ColumnProvider": "Ponudnik", + "PluginDescription": "Poročilo o obiskovalčevem ponudniku dostopa do internetnih storitev.", + "ProviderReportDocumentation": "To poročilo prikazuje ponudnike dostopa do internetnih storitve prek katerih so obiskovalci dostopali do vaše spletne strani. Za podrobnosti lahko kliknete na ime posameznega ponudnika. %s Če Piwik ne more določiti obiskovalčevega ponudnika, je le-ta naveden kot IP naslov.", "WidgetProviders": "Ponudniki", "ProviderReportFooter": "Neznan ponudnik pomeni, da IP številke ni bilo mogoče povezati s ponudnikom." } diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking -Subproject 35288ec165fc085360ec77b441dc25f51e04cd5 +Subproject 972fb6cc1953f7c21439c901fb87ab471eaf6b6 diff --git a/plugins/Referrers/API.php b/plugins/Referrers/API.php index d1c08ac20c..befd651fbe 100644 --- a/plugins/Referrers/API.php +++ b/plugins/Referrers/API.php @@ -230,14 +230,14 @@ class API extends \Piwik\Plugin\API $dataTable = Archive::createDataTableFromArchive(Archiver::SEARCH_ENGINES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, $flat); if ($flat) { - $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', __NAMESPACE__ . '\getSearchEngineUrlFromName')); - $dataTable->filter('MetadataCallbackAddMetadata', array('url', 'logo', __NAMESPACE__ . '\getSearchEngineLogoFromUrl')); + $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return SearchEngine::getInstance()->getUrlFromName($url); })); + $dataTable->filter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return SearchEngine::getInstance()->getLogoFromUrl($url); })); $dataTable->filterSubtables('Piwik\Plugins\Referrers\DataTable\Filter\KeywordsFromSearchEngineId', array($dataTable)); } else { $dataTable->filter('AddSegmentByLabel', array('referrerName')); $dataTable->queueFilter('PrependSegment', array('referrerType==search;')); - $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', __NAMESPACE__ . '\getSearchEngineUrlFromName')); - $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', __NAMESPACE__ . '\getSearchEngineLogoFromUrl')); + $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return SearchEngine::getInstance()->getUrlFromName($url); })); + $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return SearchEngine::getInstance()->getLogoFromUrl($url); })); } return $dataTable; @@ -309,9 +309,9 @@ class API extends \Piwik\Plugin\API { $dataTable = Archive::createDataTableFromArchive(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, false); - $dataTable->filter('ColumnCallbackDeleteRow', array('label', function ($url) { return !isSocialUrl($url); })); - $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', __NAMESPACE__ . '\getSocialMainUrl')); - $dataTable->filter('GroupBy', array('label', __NAMESPACE__ . '\getSocialNetworkFromDomain')); + $dataTable->filter('ColumnCallbackDeleteRow', array('label', function ($url) { return !Social::getInstance()->isSocialUrl($url); })); + $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return Social::getInstance()->getMainUrl($url); })); + $dataTable->filter('GroupBy', array('label', function ($url) { return Social::getInstance()->getSocialNetworkFromDomain($url); })); $this->setSocialIdSubtables($dataTable); $this->removeSubtableMetadata($dataTable); @@ -320,7 +320,7 @@ class API extends \Piwik\Plugin\API $this->buildExpandedTableForFlattenGetSocials($idSite, $period, $date, $segment, $expanded, $dataTable); } - $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', __NAMESPACE__ . '\getSocialsLogoFromUrl')); + $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return Social::getInstance()->getLogoFromUrl($url); })); return $dataTable; } @@ -334,7 +334,7 @@ class API extends \Piwik\Plugin\API * @param string $date * @param bool|string $segment * @param bool|int $idSubtable This ID does not reference a real DataTable record. Instead, it - * is the array index of an item in the /core/DataFiles/Socials.php file. + * is the array index of an item in the Socials list file. * The urls are filtered by the social network at this index. * If false, no filtering is done and every social URL is returned. * @return DataTable @@ -344,7 +344,7 @@ class API extends \Piwik\Plugin\API $dataTable = $this->getDataTable(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = true); // get the social network domain referred to by $idSubtable - $socialNetworks = Common::getSocialUrls(); + $socialNetworks = Social::getInstance()->getDefinitions(); $social = false; if ($idSubtable !== false) { @@ -362,7 +362,7 @@ class API extends \Piwik\Plugin\API $dataTable->filter( 'ColumnCallbackDeleteRow', array('label', - function ($url) use ($social) { return !isSocialUrl($url, $social); } + function ($url) use ($social) { return !Social::getInstance()->isSocialUrl($url, $social); } ) ); @@ -428,7 +428,7 @@ class API extends \Piwik\Plugin\API /** * Sets the subtable IDs for the DataTable returned by getSocial. * - * The IDs are int indexes into the array in /core/DataFiles/Socials.php. + * The IDs are int indexes into the array in of defined socials. * * @param DataTable $dataTable */ @@ -443,7 +443,7 @@ class API extends \Piwik\Plugin\API $socialName = $row->getColumn('label'); $i = 1; // start at one because idSubtable=0 is equivalent to idSubtable=false - foreach (Common::getSocialUrls() as $name) { + foreach (Social::getInstance()->getDefinitions() as $name) { if ($name == $socialName) { $row->setNonLoadedSubtableId($i); break; @@ -491,7 +491,7 @@ class API extends \Piwik\Plugin\API { $urlsTable = Archive::createDataTableFromArchive(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, $flat = true); $urlsTable->filter('ColumnCallbackDeleteRow', array('label', function ($url) { - return !isSocialUrl($url); + return !Social::getInstance()->isSocialUrl($url); })); $urlsTable = $urlsTable->mergeSubtables(); @@ -504,7 +504,7 @@ class API extends \Piwik\Plugin\API $rows = $urlsTable->getRows(); foreach ($rows as $id => $urlsTableRow) { $url = $urlsTableRow->getColumn('label'); - if (isSocialUrl($url, $social)) { + if (Social::getInstance()->isSocialUrl($url, $social)) { $newTable->addRow($urlsTableRow); $urlsTable->deleteRow($id); } diff --git a/plugins/Referrers/Columns/Base.php b/plugins/Referrers/Columns/Base.php index 78fe27516c..a4da79d389 100644 --- a/plugins/Referrers/Columns/Base.php +++ b/plugins/Referrers/Columns/Base.php @@ -11,6 +11,9 @@ namespace Piwik\Plugins\Referrers\Columns; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; +use Piwik\Plugins\Referrers\SearchEngine AS SearchEngineDetection; +use Piwik\Plugins\SitesManager\SiteUrls; +use Piwik\Tracker\Cache; use Piwik\Tracker\PageUrl; use Piwik\Tracker\Request; use Piwik\Tracker\Visit; @@ -111,6 +114,14 @@ abstract class Base extends VisitDimension if (!$referrerDetected && !empty($this->referrerHost)) { $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_WEBSITE; $this->nameReferrerAnalyzed = Common::mb_strtolower($this->referrerHost); + + $urlsByHost = $this->getCachedUrlsByHostAndIdSite(); + + $directEntry = new SiteUrls(); + $path = $directEntry->getPathMatchingUrl($this->referrerUrlParse, $urlsByHost); + if (!empty($path) && $path !== '/') { + $this->nameReferrerAnalyzed .= rtrim($path, '/'); + } } $referrerInformation = array( @@ -139,7 +150,7 @@ abstract class Base extends VisitDimension */ protected function detectReferrerSearchEngine() { - $searchEngineInformation = UrlHelper::extractSearchEngineInformationFromUrl($this->referrerUrl); + $searchEngineInformation = SearchEngineDetection::getInstance()->extractInformationFromUrl($this->referrerUrl); /** * Triggered when detecting the search engine of a referrer URL. @@ -241,6 +252,17 @@ abstract class Base extends VisitDimension return true; } + private function getCachedUrlsByHostAndIdSite() + { + $cache = Cache::getCacheGeneral(); + + if (!empty($cache['allUrlsByHostAndIdSite'])) { + return $cache['allUrlsByHostAndIdSite']; + } + + return array(); + } + /** * We have previously tried to detect the campaign variables in the URL * so at this stage, if the referrer host is the current host, @@ -250,20 +272,32 @@ abstract class Base extends VisitDimension */ protected function detectReferrerDirectEntry() { - if (!empty($this->referrerHost)) { - // is the referrer host the current host? - if (isset($this->currentUrlParse['host'])) { - $currentHost = Common::mb_strtolower($this->currentUrlParse['host'], 'UTF-8'); - if ($currentHost == Common::mb_strtolower($this->referrerHost, 'UTF-8')) { - $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; - return true; - } - } - if (Visit::isHostKnownAliasHost($this->referrerHost, $this->idsite)) { + if (empty($this->referrerHost)) { + return false; + } + + $urlsByHost = $this->getCachedUrlsByHostAndIdSite(); + + $directEntry = new SiteUrls(); + $matchingSites = $directEntry->getIdSitesMatchingUrl($this->referrerUrlParse, $urlsByHost); + + if (isset($matchingSites) && is_array($matchingSites) && in_array($this->idsite, $matchingSites)) { + $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; + return true; + } elseif (isset($matchingSites)) { + return false; + } + + // fallback logic if the referrer domain is not known to any site to not break BC + if (isset($this->currentUrlParse['host'])) { + // this might be actually buggy if first thing tracked is eg an outlink and referrer is from that site + $currentHost = Common::mb_strtolower($this->currentUrlParse['host']); + if ($currentHost == Common::mb_strtolower($this->referrerHost)) { $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; return true; } } + return false; } @@ -277,7 +311,7 @@ abstract class Base extends VisitDimension // Set the Campaign keyword to the keyword found in the Referrer URL if any if (!empty($this->nameReferrerAnalyzed)) { - $referrerUrlInfo = UrlHelper::extractSearchEngineInformationFromUrl($this->referrerUrl); + $referrerUrlInfo = SearchEngineDetection::getInstance()->extractInformationFromUrl($this->referrerUrl); if (!empty($referrerUrlInfo['keywords'])) { $this->keywordReferrerAnalyzed = $referrerUrlInfo['keywords']; } @@ -448,7 +482,7 @@ abstract class Base extends VisitDimension protected function hasReferrerColumnChanged(Visitor $visitor, $information, $infoName) { - return Common::mb_strtolower($visitor->getVisitorColumn($infoName)) != $information[$infoName]; + return Common::mb_strtolower($visitor->getVisitorColumn($infoName)) != Common::mb_strtolower($information[$infoName]); } protected function doesLastActionHaveSameReferrer(Visitor $visitor, $referrerType) diff --git a/plugins/Referrers/Columns/Campaign.php b/plugins/Referrers/Columns/Campaign.php index ff2d5c2401..c29b622336 100644 --- a/plugins/Referrers/Columns/Campaign.php +++ b/plugins/Referrers/Columns/Campaign.php @@ -37,7 +37,7 @@ class Campaign extends Base /** * If we should create a new visit when the campaign changes, check if the campaign info changed and if so - * force the tracker to create a new visit. + * force the tracker to create a new visit.i * * @param Request $request * @param Visitor $visitor diff --git a/plugins/Referrers/Columns/Keyword.php b/plugins/Referrers/Columns/Keyword.php index 78605c9d94..b3bfc374b1 100644 --- a/plugins/Referrers/Columns/Keyword.php +++ b/plugins/Referrers/Columns/Keyword.php @@ -8,6 +8,7 @@ */ namespace Piwik\Plugins\Referrers\Columns; +use Piwik\Common; use Piwik\Piwik; use Piwik\Plugins\Referrers\Segment; use Piwik\Tracker\Request; @@ -44,7 +45,7 @@ class Keyword extends Base $information = $this->getReferrerInformationFromRequest($request); if (!empty($information['referer_keyword'])) { - return substr($information['referer_keyword'], 0, 255); + return Common::mb_substr($information['referer_keyword'], 0, 255); } return $information['referer_keyword']; diff --git a/plugins/Referrers/Columns/ReferrerName.php b/plugins/Referrers/Columns/ReferrerName.php index 7bdb0732a6..ded0a48f9e 100644 --- a/plugins/Referrers/Columns/ReferrerName.php +++ b/plugins/Referrers/Columns/ReferrerName.php @@ -8,6 +8,7 @@ */ namespace Piwik\Plugins\Referrers\Columns; +use Piwik\Common; use Piwik\Plugins\Referrers\Segment; use Piwik\Tracker\Request; use Piwik\Tracker\Visitor; @@ -38,10 +39,8 @@ class ReferrerName extends Base $information = $this->getReferrerInformationFromRequest($request); if (!empty($information['referer_name'])) { - - return substr($information['referer_name'], 0, 70); + return Common::mb_substr($information['referer_name'], 0, 70); } - return $information['referer_name']; } diff --git a/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php b/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php index 1688713a86..789a1b1a6a 100644 --- a/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php +++ b/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Referrers\DataTable\Filter; use Piwik\DataTable\BaseFilter; use Piwik\DataTable\Row; use Piwik\DataTable; +use Piwik\Plugins\Referrers\SearchEngine; class KeywordsFromSearchEngineId extends BaseFilter { @@ -47,7 +48,7 @@ class KeywordsFromSearchEngineId extends BaseFilter if (!empty($subTableRow)) { $searchEngineUrl = $subTableRow->getMetadata('url'); - $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik\Plugins\Referrers\getSearchEngineUrlFromKeywordAndUrl', array($searchEngineUrl))); + $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($keyword, $url) { return SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword($url, $keyword); }, array($searchEngineUrl))); $table->queueFilter(function (DataTable $table) { $row = $table->getRowFromId(DataTable::ID_SUMMARY_ROW); if ($row) { diff --git a/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php b/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php index feab8636af..6d395abba0 100644 --- a/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php +++ b/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Referrers\DataTable\Filter; use Piwik\DataTable\BaseFilter; use Piwik\DataTable\Row; use Piwik\DataTable; +use Piwik\Plugins\Referrers\SearchEngine; class SearchEnginesFromKeywordId extends BaseFilter { @@ -44,14 +45,14 @@ class SearchEnginesFromKeywordId extends BaseFilter { $idSubtable = $this->idSubtable ? : $table->getId(); - $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik\Plugins\Referrers\getSearchEngineUrlFromName')); - $table->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl')); + $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return SearchEngine::getInstance()->getUrlFromName($url); })); + $table->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return SearchEngine::getInstance()->getLogoFromUrl($url); })); // get the keyword and create the URL to the search result page $rootRow = $this->firstLevelKeywordTable->getRowFromIdSubDataTable($idSubtable); if ($rootRow) { $keyword = $rootRow->getColumn('label'); - $table->queueFilter('MetadataCallbackReplace', array('url', 'Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword', array($keyword))); + $table->queueFilter('MetadataCallbackReplace', array('url', function ($url, $keyword) { return SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword($url, $keyword); }, array($keyword))); $table->queueFilter(function (DataTable $table) { $row = $table->getRowFromId(DataTable::ID_SUMMARY_ROW); if ($row) { diff --git a/plugins/Referrers/Referrers.php b/plugins/Referrers/Referrers.php index ce8667929b..d262c79224 100644 --- a/plugins/Referrers/Referrers.php +++ b/plugins/Referrers/Referrers.php @@ -12,6 +12,7 @@ use Piwik\ArchiveProcessor; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; +use Piwik\Plugins\SitesManager\SiteUrls; /** * @see plugins/Referrers/functions.php @@ -31,9 +32,18 @@ class Referrers extends \Piwik\Plugin 'Insights.addReportToOverview' => 'addReportToInsightsOverview', 'Live.getAllVisitorDetails' => 'extendVisitorDetails', 'Request.getRenamedModuleAndAction' => 'renameDeprecatedModuleAndAction', + 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral' ); } + public function setTrackerCacheGeneral(&$cacheContent) + { + $siteUrls = new SiteUrls(); + $urls = $siteUrls->getAllCachedSiteUrls(); + + return $cacheContent['allUrlsByHostAndIdSite'] = $siteUrls->groupUrlsByHost($urls); + } + public function renameDeprecatedModuleAndAction(&$module, &$action) { if($module == 'Referers') { diff --git a/plugins/Referrers/SearchEngine.php b/plugins/Referrers/SearchEngine.php new file mode 100644 index 0000000000..14009a9029 --- /dev/null +++ b/plugins/Referrers/SearchEngine.php @@ -0,0 +1,485 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers; + +use Piwik\Cache; +use Piwik\Common; +use Piwik\Option; +use Piwik\Piwik; +use Piwik\Singleton; +use Piwik\UrlHelper; + +/** + * Contains methods to access search engine definition data. + */ +class SearchEngine extends Singleton +{ + const OPTION_STORAGE_NAME = 'SearchEngineDefinitions'; + + /** @var string location of definition file (relative to PIWIK_INCLUDE_PATH) */ + const DEFINITION_FILE = '/vendor/piwik/searchengine-and-social-list/SearchEngines.yml'; + + protected $definitionList = null; + + /** + * Returns list of search engines by URL + * + * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) ) + */ + public function getDefinitions() + { + $cache = Cache::getEagerCache(); + $cacheId = 'SearchEngine-' . self::OPTION_STORAGE_NAME; + + if ($cache->contains($cacheId)) { + $list = $cache->fetch($cacheId); + } else { + $list = $this->loadDefinitions(); + $cache->save($cacheId, $list); + } + + return $list; + } + + private function loadDefinitions() + { + if (empty($this->definitionList)) { + // Read first from the auto-updated list in database + $list = Option::get(self::OPTION_STORAGE_NAME); + + if ($list) { + $this->definitionList = unserialize(base64_decode($list)); + } else { + // Fallback to reading the bundled list + $yml = file_get_contents(PIWIK_INCLUDE_PATH . self::DEFINITION_FILE); + $this->definitionList = $this->loadYmlData($yml); + Option::set(self::OPTION_STORAGE_NAME, base64_encode(serialize($this->definitionList))); + } + } + + Piwik::postEvent('Referrer.addSearchEngineUrls', array(&$this->definitionList)); + + $this->convertLegacyDefinitions(); + + return $this->definitionList; + } + + /** + * @deprecated remove in 3.0 + */ + protected function convertLegacyDefinitions() + { + foreach ($this->definitionList as $url => $definition) { + if (!array_key_exists('name', $definition) && isset($definition[0]) && isset($definition[1])) { + $this->definitionList[$url] = array( + 'name' => $definition[0], + 'params' => $definition[1], + 'backlink' => @$definition[2], + 'charsets' => @$definition[3] + ); + } + } + + } + + /** + * Parses the given YML string and caches the resulting definitions + * + * @param string $yml + * @return array + */ + public function loadYmlData($yml) + { + $searchEngines = \Spyc::YAMLLoadString($yml); + + $this->definitionList = $this->transformData($searchEngines); + + return $this->definitionList; + } + + protected function transformData($searchEngines) + { + $urlToInfo = array(); + foreach ($searchEngines as $name => $info) { + foreach ($info as $urlDefinitions) { + foreach ($urlDefinitions['urls'] as $url) { + $searchEngineData = $urlDefinitions; + unset($searchEngineData['urls']); + $searchEngineData['name'] = $name; + $urlToInfo[$url] = $searchEngineData; + } + } + } + return $urlToInfo; + } + + /** + * Returns list of search engines by name + * + * @return array Array of ( searchEngineName => URL ) + */ + public function getNames() + { + $cacheId = 'SearchEngine.getSearchEngineNames'; + $cache = Cache::getTransientCache(); + $nameToUrl = $cache->fetch($cacheId); + + if (empty($nameToUrl)) { + $searchEngines = $this->getDefinitions(); + + $nameToUrl = array(); + foreach ($searchEngines as $url => $info) { + if (!isset($nameToUrl[$info['name']])) { + $nameToUrl[$info['name']] = $url; + } + } + $cache->save($cacheId, $nameToUrl); + } + + return $nameToUrl; + } + + /** + * Returns definitions for the given search engine host + * + * @param string $host + * @return array + */ + public function getDefinitionByHost($host) + { + $searchEngines = $this->getDefinitions(); + + if (!array_key_exists($host, $searchEngines)) { + return array(); + } + + return $searchEngines[$host]; + } + + /** + * Extracts a keyword from a raw not encoded URL. + * Will only extract keyword if a known search engine has been detected. + * Returns the keyword: + * - in UTF8: automatically converted from other charsets when applicable + * - strtolowered: "QUErY test!" will return "query test!" + * - trimmed: extra spaces before and after are removed + * + * The function returns false when a keyword couldn't be found. + * eg. if the url is "http://www.google.com/partners.html" this will return false, + * as the google keyword parameter couldn't be found. + * + * @see unit tests in /tests/core/Common.test.php + * @param string $referrerUrl URL referrer URL, eg. $_SERVER['HTTP_REFERER'] + * @return array|bool false if a keyword couldn't be extracted, + * or array( + * 'name' => 'Google', + * 'keywords' => 'my searched keywords') + */ + public function extractInformationFromUrl($referrerUrl) + { + $referrerParsed = @parse_url($referrerUrl); + $referrerHost = ''; + if (isset($referrerParsed['host'])) { + $referrerHost = $referrerParsed['host']; + } + if (empty($referrerHost)) { + return false; + } + // some search engines (eg. Bing Images) use the same domain + // as an existing search engine (eg. Bing), we must also use the url path + $referrerPath = ''; + if (isset($referrerParsed['path'])) { + $referrerPath = $referrerParsed['path']; + } + + $query = ''; + if (isset($referrerParsed['query'])) { + $query = $referrerParsed['query']; + } + + // Google Referrers URLs sometimes have the fragment which contains the keyword + if (!empty($referrerParsed['fragment'])) { + $query .= '&' . $referrerParsed['fragment']; + } + + $referrerHost = $this->getEngineHostFromUrl($referrerHost, $referrerPath, $query); + + if (empty($referrerHost)) { + return false; + } + + $definitions = $this->getDefinitionByHost($referrerHost); + + $searchEngineName = $definitions['name']; + $variableNames = $definitions['params']; + + $key = null; + if ($searchEngineName === 'Google Images' + || ($searchEngineName === 'Google' && strpos($referrerUrl, '/imgres') !== false) + ) { + if (strpos($query, '&prev') !== false) { + $query = urldecode(trim(UrlHelper::getParameterFromQueryString($query, 'prev'))); + $query = str_replace('&', '&', strstr($query, '?')); + } + $searchEngineName = 'Google Images'; + } elseif ($searchEngineName === 'Google' + && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0) + ) { + $keys = array(); + $key = UrlHelper::getParameterFromQueryString($query, 'as_q'); + if (!empty($key)) { + array_push($keys, $key); + } + $key = UrlHelper::getParameterFromQueryString($query, 'as_oq'); + if (!empty($key)) { + array_push($keys, str_replace('+', ' OR ', $key)); + } + $key = UrlHelper::getParameterFromQueryString($query, 'as_epq'); + if (!empty($key)) { + array_push($keys, "\"$key\""); + } + $key = UrlHelper::getParameterFromQueryString($query, 'as_eq'); + if (!empty($key)) { + array_push($keys, "-$key"); + } + $key = trim(urldecode(implode(' ', $keys))); + } + + if ($searchEngineName === 'Google') { + // top bar menu + $tbm = UrlHelper::getParameterFromQueryString($query, 'tbm'); + switch ($tbm) { + case 'isch': + $searchEngineName = 'Google Images'; + break; + case 'vid': + $searchEngineName = 'Google Video'; + break; + case 'shop': + $searchEngineName = 'Google Shopping'; + break; + } + } + + if (empty($key)) { + foreach ($variableNames as $variableName) { + if ($variableName[0] == '/') { + // regular expression match + if (preg_match($variableName, $referrerUrl, $matches)) { + $key = trim(urldecode($matches[1])); + break; + } + } else { + // search for keywords now &vname=keyword + $key = UrlHelper::getParameterFromQueryString($query, $variableName); + $key = trim(urldecode($key)); + + // Special cases: empty or no keywords + if (empty($key) + && ( + // Google search with no keyword + ($searchEngineName == 'Google' + && (empty($query) && (empty($referrerPath) || $referrerPath == '/') && empty($referrerParsed['fragment'])) + ) + + // Yahoo search with no keyword + || ($searchEngineName == 'Yahoo!' + && ($referrerParsed['host'] == 'r.search.yahoo.com') + ) + + // empty keyword parameter + || strpos($query, sprintf('&%s=', $variableName)) !== false + || strpos($query, sprintf('?%s=', $variableName)) !== false + + // search engines with no keyword + || $searchEngineName == 'Ixquick' + || $searchEngineName == 'Google Images' + || $searchEngineName == 'DuckDuckGo') + ) { + $key = false; + } + if (!empty($key) + || $key === false + ) { + break; + } + } + } + } + + // $key === false is the special case "No keyword provided" which is a Search engine match + if ($key === null || $key === '') { + return false; + } + + if (!empty($key)) { + if (!empty($definitions['charsets'])) { + $key = $this->convertCharset($key, $definitions['charsets']); + } + $key = Common::mb_strtolower($key); + } + + return array( + 'name' => $searchEngineName, + 'keywords' => $key, + ); + } + + protected function getEngineHostFromUrl($host, $path, $query) + { + $searchEngines = $this->getDefinitions(); + + $hostPattern = UrlHelper::getLossyUrl($host); + /* + * Try to get the best matching 'host' in definitions + * 1. check if host + path matches an definition + * 2. check if host only matches + * 3. check if host pattern + path matches + * 4. check if host pattern matches + * 5. special handling + */ + if (array_key_exists($host . $path, $searchEngines)) { + $host = $host . $path; + } elseif (array_key_exists($host, $searchEngines)) { + // no need to change host + } elseif (array_key_exists($hostPattern . $path, $searchEngines)) { + $host = $hostPattern . $path; + } elseif (array_key_exists($hostPattern, $searchEngines)) { + $host = $hostPattern; + } elseif (!array_key_exists($host, $searchEngines)) { + if (!strncmp($query, 'cx=partner-pub-', 15)) { + // Google custom search engine + $host = 'google.com/cse'; + } elseif (!strncmp($path, '/pemonitorhosted/ws/results/', 28)) { + // private-label search powered by InfoSpace Metasearch + $host = 'wsdsold.infospace.com'; + } elseif (strpos($host, '.images.search.yahoo.com') != false) { + // Yahoo! Images + $host = 'images.search.yahoo.com'; + } elseif (strpos($host, '.search.yahoo.com') != false) { + // Yahoo! + $host = 'search.yahoo.com'; + } else { + return false; + } + } + + return $host; + } + + /** + * Tries to convert the given string from one of the given charsets to UTF-8 + * @param string $string + * @param array $charsets + * @return string + */ + protected function convertCharset($string, $charsets) + { + if (function_exists('iconv') + && !empty($charsets) + ) { + $charset = $charsets[0]; + if (count($charsets) > 1 + && function_exists('mb_detect_encoding') + ) { + $charset = mb_detect_encoding($string, $charsets); + if ($charset === false) { + $charset = $charsets[0]; + } + } + + $newKey = @iconv($charset, 'UTF-8//IGNORE', $string); + if (!empty($newKey)) { + $string = $newKey; + } + } + + return $string; + } + + /** + * Return search engine URL by name + * + * @see core/DataFiles/SearchEnginges.php + * + * @param string $name + * @return string URL + */ + public function getUrlFromName($name) + { + $searchEngineNames = $this->getNames(); + if (isset($searchEngineNames[$name])) { + $url = 'http://' . $searchEngineNames[$name]; + } else { + $url = 'URL unknown!'; + } + return $url; + } + + /** + * Return search engine host in URL + * + * @param string $url + * @return string host + */ + private function getHostFromUrl($url) + { + if (strpos($url, '//')) { + $url = substr($url, strpos($url, '//') + 2); + } + if (($p = strpos($url, '/')) !== false) { + $url = substr($url, 0, $p); + } + return $url; + } + + + /** + * Return search engine logo path by URL + * + * @param string $url + * @return string path + * @see plugins/Referrers/images/searchEnginges/ + */ + public function getLogoFromUrl($url) + { + $pathInPiwik = 'plugins/Referrers/images/searchEngines/%s.png'; + $pathWithCode = sprintf($pathInPiwik, $this->getHostFromUrl($url)); + $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode; + if (file_exists($absolutePath)) { + return $pathWithCode; + } + return sprintf($pathInPiwik, 'xx'); + } + + /** + * Return search engine URL for URL and keyword + * + * @see core/DataFiles/SearchEnginges.php + * + * @param string $url Domain name, e.g., search.piwik.org + * @param string $keyword Keyword, e.g., web+analytics + * @return string URL, e.g., http://search.piwik.org/q=web+analytics + */ + public function getBackLinkFromUrlAndKeyword($url, $keyword) + { + if ($keyword === API::LABEL_KEYWORD_NOT_DEFINED) { + return 'http://piwik.org/faq/general/#faq_144'; + } + $keyword = urlencode($keyword); + $keyword = str_replace(urlencode('+'), urlencode(' '), $keyword); + $host = substr($url, strpos($url, '//') + 2); + $definition = $this->getDefinitionByHost($host); + if (empty($definition['backlink'])) { + return false; + } + $path = str_replace("{k}", $keyword, $definition['backlink']); + return $url . (substr($url, -1) != '/' ? '/' : '') . $path; + } +} diff --git a/plugins/Referrers/Social.php b/plugins/Referrers/Social.php new file mode 100644 index 0000000000..71badc3357 --- /dev/null +++ b/plugins/Referrers/Social.php @@ -0,0 +1,181 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers; +use Piwik\Cache; +use Piwik\Common; +use Piwik\Option; +use Piwik\Piwik; +use Piwik\Singleton; +use Piwik\UrlHelper; + +/** + * Contains methods to access search engine definition data. + */ +class Social extends Singleton +{ + const OPTION_STORAGE_NAME = 'SocialDefinitions'; + + /** @var string location of definition file (relative to PIWIK_INCLUDE_PATH) */ + const DEFINITION_FILE = '/vendor/piwik/searchengine-and-social-list/Socials.yml'; + + protected $definitionList = null; + + /** + * Returns list of search engines by URL + * + * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) ) + */ + public function getDefinitions() + { + $cache = Cache::getEagerCache(); + $cacheId = 'Social-' . self::OPTION_STORAGE_NAME; + + if ($cache->contains($cacheId)) { + $list = $cache->fetch($cacheId); + } else { + $list = $this->loadDefinitions(); + $cache->save($cacheId, $list); + } + + return $list; + } + + private function loadDefinitions() + { + if ($this->definitionList === null) { + // Read first from the auto-updated list in database + $list = Option::get(self::OPTION_STORAGE_NAME); + + if ($list) { + $this->definitionList = unserialize(base64_decode($list)); + } else { + // Fallback to reading the bundled list + $yml = file_get_contents(PIWIK_INCLUDE_PATH . self::DEFINITION_FILE); + $this->definitionList = $this->loadYmlData($yml); + Option::set(self::OPTION_STORAGE_NAME, base64_encode(serialize($this->definitionList))); + } + } + + Piwik::postEvent('Referrer.addSocialUrls', array(&$this->definitionList)); + + return $this->definitionList; + } + + /** + * Parses the given YML string and caches the resulting definitions + * + * @param string $yml + * @return array + */ + public function loadYmlData($yml) + { + $searchEngines = \Spyc::YAMLLoadString($yml); + + $this->definitionList = $this->transformData($searchEngines); + + return $this->definitionList; + } + + protected function transformData($socials) + { + $urlToName = array(); + foreach ($socials as $name => $urls) { + foreach ($urls as $url) { + $urlToName[$url] = $name; + } + } + return $urlToName; + } + + /** + * Returns true if a URL belongs to a social network, false if otherwise. + * + * @param string $url The URL to check. + * @param string|bool $socialName The social network's name to check for, or false to check + * for any. + * @return bool + */ + public function isSocialUrl($url, $socialName = false) + { + foreach ($this->getDefinitions() as $domain => $name) { + + if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url) && ($socialName === false || $name == $socialName)) { + + return true; + } + } + + return false; + } + + + /** + * Get's social network name from URL. + * + * @param string $url + * @return string + */ + public function getSocialNetworkFromDomain($url) + { + foreach ($this->getDefinitions() as $domain => $name) { + + if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) { + + return $name; + } + } + + return Piwik::translate('General_Unknown'); + } + + /** + * Returns the main url of the social network the given url matches + * + * @param string $url + * + * @return string + */ + public function getMainUrl($url) + { + $social = $this->getSocialNetworkFromDomain($url); + foreach ($this->getDefinitions() as $domain => $name) { + + if ($name == $social) { + + return $domain; + } + } + return $url; + } + + + /** + * Return social network logo path by URL + * + * @param string $domain + * @return string path + * @see plugins/Referrers/images/socials/ + */ + public function getLogoFromUrl($domain) + { + $social = $this->getSocialNetworkFromDomain($domain); + $socialNetworks = $this->getDefinitions(); + + $filePattern = 'plugins/Referrers/images/socials/%s.png'; + + $socialDomains = array_keys($socialNetworks, $social); + foreach ($socialDomains as $domain) { + if (file_exists(PIWIK_INCLUDE_PATH . '/' . sprintf($filePattern, $domain))) { + return sprintf($filePattern, $domain); + } + } + + return sprintf($filePattern, 'xx'); + } +} diff --git a/plugins/Referrers/Tasks.php b/plugins/Referrers/Tasks.php new file mode 100644 index 0000000000..da9cad1321 --- /dev/null +++ b/plugins/Referrers/Tasks.php @@ -0,0 +1,54 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers; + + +use Piwik\Http; +use Piwik\Option; + +class Tasks extends \Piwik\Plugin\Tasks +{ + public function schedule() + { + $this->weekly('updateSearchEngines'); + $this->weekly('updateSocials'); + } + + /** + * Update the search engine definitions + * + * @see https://github.com/piwik/searchengine-and-social-list + */ + public function updateSearchEngines() + { + $url = 'https://raw.githubusercontent.com/piwik/searchengine-and-social-list/master/SearchEngines.yml'; + $list = Http::sendHttpRequest($url, 30); + $searchEngines = SearchEngine::getInstance()->loadYmlData($list); + if (count($searchEngines) < 200) { + return; + } + Option::set(SearchEngine::OPTION_STORAGE_NAME, base64_encode(serialize($searchEngines))); + } + + /** + * Update the social definitions + * + * @see https://github.com/piwik/searchengine-and-social-list + */ + public function updateSocials() + { + $url = 'https://raw.githubusercontent.com/piwik/searchengine-and-social-list/master/Socials.yml'; + $list = Http::sendHttpRequest($url, 30); + $socials = Social::getInstance()->loadYmlData($list); + if (count($socials) < 50) { + return; + } + Option::set(Social::OPTION_STORAGE_NAME, base64_encode(serialize($socials))); + } +}
\ No newline at end of file diff --git a/plugins/Referrers/Visitor.php b/plugins/Referrers/Visitor.php index 308c7b08f8..b10b9b953d 100644 --- a/plugins/Referrers/Visitor.php +++ b/plugins/Referrers/Visitor.php @@ -62,7 +62,7 @@ class Visitor ) { $refUrl = @parse_url($this->details['referer_url']); if (isset($refUrl['host'])) { - $url = getSearchEngineUrlFromUrlAndKeyword('http://google.com', $this->getKeyword()); + $url = SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword('http://google.com', $this->getKeyword()); $url = str_replace('google.com', $refUrl['host'], $url); return $url; @@ -109,7 +109,7 @@ class Visitor && !empty($this->details['referer_name']) ) { - return getSearchEngineUrlFromName($this->details['referer_name']); + return SearchEngine::getInstance()->getUrlFromName($this->details['referer_name']); } return null; @@ -121,7 +121,7 @@ class Visitor if (!is_null($searchEngineUrl)) { - return getSearchEngineLogoFromUrl($searchEngineUrl); + return SearchEngine::getInstance()->getLogoFromUrl($searchEngineUrl); } return null; diff --git a/plugins/Referrers/functions.php b/plugins/Referrers/functions.php index e0fee30833..c91ebc816d 100644 --- a/plugins/Referrers/functions.php +++ b/plugins/Referrers/functions.php @@ -28,194 +28,6 @@ function getPathFromUrl($url) } /** - * Returns the main url of the social network the given url matches - * - * @param string $url - * - * @return string - */ -function getSocialMainUrl($url) -{ - $social = getSocialNetworkFromDomain($url); - foreach (Common::getSocialUrls() as $domain => $name) { - - if ($name == $social) { - - return $domain; - } - } - return $url; -} - -/** - * Get's social network name from URL. - * - * @param string $url - * @return string - */ -function getSocialNetworkFromDomain($url) -{ - foreach (Common::getSocialUrls() as $domain => $name) { - - if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) { - - return $name; - } - } - - return Piwik::translate('General_Unknown'); -} - -/** - * Returns true if a URL belongs to a social network, false if otherwise. - * - * @param string $url The URL to check. - * @param string|bool $socialName The social network's name to check for, or false to check - * for any. - * @return bool - */ -function isSocialUrl($url, $socialName = false) -{ - foreach (Common::getSocialUrls() as $domain => $name) { - - if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url) && ($socialName === false || $name == $socialName)) { - - return true; - } - } - - return false; -} - -/** - * Return social network logo path by URL - * - * @param string $domain - * @return string path - * @see plugins/Referrers/images/socials/ - */ -function getSocialsLogoFromUrl($domain) -{ - $social = getSocialNetworkFromDomain($domain); - $socialNetworks = Common::getSocialUrls(); - - $filePattern = 'plugins/Referrers/images/socials/%s.png'; - - foreach ($socialNetworks as $domainKey => $name) { - if ($social == $socialNetworks[$domainKey] && file_exists(PIWIK_INCLUDE_PATH . '/' . sprintf($filePattern, $domainKey))) { - return sprintf($filePattern, $domainKey); - } - } - - return sprintf($filePattern, 'xx'); -} - -/** - * Return search engine URL by name - * - * @see core/DataFiles/SearchEnginges.php - * - * @param string $name - * @return string URL - */ -function getSearchEngineUrlFromName($name) -{ - $searchEngineNames = Common::getSearchEngineNames(); - if (isset($searchEngineNames[$name])) { - $url = 'http://' . $searchEngineNames[$name]; - } else { - $url = 'URL unknown!'; - } - return $url; -} - -/** - * Return search engine host in URL - * - * @param string $url - * @return string host - */ -function getSearchEngineHostFromUrl($url) -{ - if (strpos($url, '//')) { - $url = substr($url, strpos($url, '//') + 2); - } - if (($p = strpos($url, '/')) !== false) { - $url = substr($url, 0, $p); - } - return $url; -} - -/** - * Return search engine logo path by URL - * - * @param string $url - * @return string path - * @see plugins/Referrers/images/searchEnginges/ - */ -function getSearchEngineLogoFromUrl($url) -{ - $pathInPiwik = 'plugins/Referrers/images/searchEngines/%s.png'; - $pathWithCode = sprintf($pathInPiwik, getSearchEngineHostFromUrl($url)); - $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode; - if (file_exists($absolutePath)) { - return $pathWithCode; - } - return sprintf($pathInPiwik, 'xx'); -} - -/** - * Return search engine host and path in URL - * - * @param string $url - * @return string host - */ -function getSearchEngineHostPathFromUrl($url) -{ - $url = substr($url, strpos($url, '//') + 2); - return $url; -} - -/** - * Return search engine URL for URL and keyword - * - * @see core/DataFiles/SearchEnginges.php - * - * @param string $url Domain name, e.g., search.piwik.org - * @param string $keyword Keyword, e.g., web+analytics - * @return string URL, e.g., http://search.piwik.org/q=web+analytics - */ -function getSearchEngineUrlFromUrlAndKeyword($url, $keyword) -{ - if ($keyword === API::LABEL_KEYWORD_NOT_DEFINED) { - return 'http://piwik.org/faq/general/#faq_144'; - } - $searchEngineUrls = Common::getSearchEngineUrls(); - $keyword = urlencode($keyword); - $keyword = str_replace(urlencode('+'), urlencode(' '), $keyword); - $path = @$searchEngineUrls[getSearchEngineHostPathFromUrl($url)][2]; - if (empty($path)) { - return false; - } - $path = str_replace("{k}", $keyword, $path); - return $url . (substr($url, -1) != '/' ? '/' : '') . $path; -} - -/** - * Return search engine URL for keyword and URL - * - * @see \Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword - * - * @param string $keyword Keyword, e.g., web+analytics - * @param string $url Domain name, e.g., search.piwik.org - * @return string URL, e.g., http://search.piwik.org/q=web+analytics - */ -function getSearchEngineUrlFromKeywordAndUrl($keyword, $url) -{ - return getSearchEngineUrlFromUrlAndKeyword($url, $keyword); -} - -/** * Return translated referrer type * * @param string $label @@ -223,7 +35,6 @@ function getSearchEngineUrlFromKeywordAndUrl($keyword, $url) */ function getReferrerTypeLabel($label) { - $indexTranslation = ''; switch ($label) { case Common::REFERRER_TYPE_DIRECT_ENTRY: $indexTranslation = 'Referrers_DirectEntry'; diff --git a/plugins/Referrers/lang/cs.json b/plugins/Referrers/lang/cs.json index 740d25a38b..052a054161 100644 --- a/plugins/Referrers/lang/cs.json +++ b/plugins/Referrers/lang/cs.json @@ -3,9 +3,9 @@ "AllReferrersReportDocumentation": "Toto hlášení zobrazuje všechny referrery v jednom uceleném hlášení, kde zobrazuje všechny webové stránky, klíčová slova a kampaně, které vaši návštěvníci při hledání vašich webových stránek použili.", "Campaigns": "Kampaně", "CampaignsDocumentation": "Návštěvníci, kteří přišli na vaše webové stránky jako výsledek kampaně. %s Pro více informací si prohlédněte hlášení %s.", - "CampaignsReportDocumentation": "Toto hlášení zobrazuje, jaké kampaně přivedly návštěvníky na vaše stránky. %s Pro více informací o sledování kampaní si %spřečtěte dokumentaci kampaní na piwik.org%s.", + "CampaignsReportDocumentation": "Toto hlášení zobrazuje, které kampaně přivedly návštěvníky na vaše stránky. %s Pro více informací o sledování kampaní si %spřečtěte dokumentaci kampaní na piwik.org%s.", "ColumnCampaign": "Kampaň", - "ColumnSearchEngine": "Vyhledávací stroj", + "ColumnSearchEngine": "Vyhledávač", "ColumnSocial": "Sociální síť", "ColumnWebsite": "Web stránky", "ColumnWebsitePage": "Web stránka", @@ -15,7 +15,7 @@ "DistinctCampaigns": "jedinečné kampaně", "DistinctKeywords": "jedinečná klíčová slova", "DistinctSearchEngines": "jedinečné vyhledávače", - "DistinctWebsites": "jedinečná weby", + "DistinctWebsites": "jedinečné weby", "EvolutionDocumentation": "Toto je přehled referrerů, které vedly návštěvníky na vaše webové stránky.", "EvolutionDocumentationMoreInfo": "Pro více informací o rozdílných typech referrerů nahlédněte do dokumentace tabulky %s.", "Keywords": "Klíčová slova", @@ -26,13 +26,13 @@ "Referrers": "Doporučení", "ReferrersOverview": "Přehled referrerů", "ReferrerTypes": "Typy referrerů", - "SearchEngines": "Vyhledávacích strojů", + "SearchEngines": "Vyhledávače", "SearchEnginesDocumentation": "Návštěvník byl na vaše stránky odkázán vyhledávačem. %s Pro více informací si prohlédněte hlášení %s.", "SearchEnginesReportDocumentation": "Toto hlášení zobrazuje, které vyhledávače odkázali návštěvníky na vaše stránky. %s Kliknutím na řádek v tabulce zobrazíte, co uživatelé na daném vyhledávači hledali.", - "SocialFooterMessage": "Toto je podmnožina hlášení webových stránek zobrazeného vlevo. Filtruje ostatní stránky, takže můžete přímo porovnat referrery ze sociálních sítí.", + "SocialFooterMessage": "Toto je podmnožina hlášení zobrazeného vlevo. Filtruje ostatní stránky, takže můžete přímo porovnat referrery ze sociálních sítí.", "Socials": "Sociální sítě", "SocialsReportDocumentation": "Toto hlášení zobrazuje, jaké sociální sítě přivedly návštěvníky na vaše stránky. <br\/> Kliknutím na Kliknutím na řádek tabulky zobrazíte stránky sociální sítě, ze kterých návštěvníci přišli.", - "SubmenuSearchEngines": "Vyhledávače & klíčová slova", + "SubmenuSearchEngines": "Vyhledávače a klíčová slova", "SubmenuWebsites": "Web", "Type": "Typ refereru", "TypeCampaigns": "%s z kampaní", diff --git a/plugins/Referrers/lang/ko.json b/plugins/Referrers/lang/ko.json index f5f309c895..28c132386a 100644 --- a/plugins/Referrers/lang/ko.json +++ b/plugins/Referrers/lang/ko.json @@ -20,10 +20,12 @@ "EvolutionDocumentationMoreInfo": "다른 참조 유형에 대한 자세한 내용은 %s 테이블의 문서를 참조하세요.", "Keywords": "검색어", "KeywordsReportDocumentation": "이 보고서는 사용자가 웹사이트를 방문하는 데 사용하는 검색어를 나타냅니다. %s 테이블의 행을 클릭하면 검색어에 조회된 검색 엔진의 분포를 볼 수 있습니다.", + "PluginDescription": "리퍼터 데이터 보고서: 검색 엔진, 키워드, 웹사이트, 캠페인, 소셜 미디어, 직접 입력", "Referrer": "리퍼러", "ReferrerName": "리퍼러 이름", "Referrers": "참조", "ReferrersOverview": "리퍼러 개요", + "ReferrerTypes": "리퍼러 타입", "SearchEngines": "검색엔진", "SearchEnginesDocumentation": "검색 엔진을 참조하여 웹사이트로 접속한 방문자입니다. 상세 보고서는 %s 여기 %s에 있습니다.", "SearchEnginesReportDocumentation": "이 보고서는 어떤 검색 엔진으로 웹사이트에 사용자가 유입되었는 지를 보여줍니다. %s 테이블의 행을 클릭하면 사용자가 특정 검색 엔진을 사용하여 무엇을 찾고 있었는지를 확인할 수 있습니다.", @@ -47,6 +49,7 @@ "WidgetExternalWebsites": "외부 웹사이트 목록", "WidgetGetAll": "모든 리퍼러", "WidgetSocials": "소셜 네트워크의 목록", - "WidgetTopKeywordsForPages": "패지 URL의 상위 키워드" + "WidgetTopKeywordsForPages": "패지 URL의 상위 키워드", + "XPercentOfVisits": "%s 번" } }
\ No newline at end of file diff --git a/plugins/Referrers/tests/Integration/Columns/ReferrerNameTest.php b/plugins/Referrers/tests/Integration/Columns/ReferrerNameTest.php new file mode 100644 index 0000000000..db34a25c46 --- /dev/null +++ b/plugins/Referrers/tests/Integration/Columns/ReferrerNameTest.php @@ -0,0 +1,130 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Referrers\tests\Integration\Columns; + +use Piwik\Common; +use Piwik\Plugins\Referrers\Columns\ReferrerName; +use Piwik\Plugins\Referrers\Columns\ReferrerType; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Cache; +use Piwik\Tracker\Request; +use Piwik\Tracker\Visit\VisitProperties; +use Piwik\Tracker\Visitor; + +/** + * @group Referrers + * @group ReferrerNameTest + * @group ReferrerName + * @group Plugins + */ +class ReferrerNameTest extends IntegrationTestCase +{ + /** + * @var ReferrerType + */ + private $referrerName; + private $idSite1 = 1; + private $idSite2 = 2; + private $idSite3 = 3; + + public function setUp() + { + parent::setUp(); + + Cache::clearCacheGeneral(); + + $date = '2012-01-01 00:00:00'; + $ecommerce = false; + + Fixture::createWebsite($date, $ecommerce, $name = 'test1', $url = 'http://piwik.org/foo/bar'); + Fixture::createWebsite($date, $ecommerce, $name = 'test2', $url = 'http://piwik.org/'); + Fixture::createWebsite($date, $ecommerce, $name = 'test3', $url = 'http://piwik.pro/'); + + $this->referrerName = new ReferrerName(); + } + + public function tearDown() + { + // clean up your test here if needed + Cache::clearCacheGeneral(); + + parent::tearDown(); + } + + /** + * @dataProvider getReferrerUrls + */ + public function test_onNewVisit_shouldDetectCorrectReferrerType($expectedType, $idSite, $url, $referrerUrl) + { + $request = $this->getRequest(array('idsite' => $idSite, 'url' => $url, 'urlref' => $referrerUrl)); + $type = $this->referrerName->onNewVisit($request, $this->getNewVisitor(), $action = null); + + $this->assertSame($expectedType, $type); + } + + public function getReferrerUrls() + { + $url = 'http://piwik.org/foo/bar'; + $referrer = 'http://piwik.org'; + + $directEntryReferrerName = ''; + + return array( + // domain matches but path does not match for idsite1 + array('piwik.org', $this->idSite1, $url, $referrer), + array('piwik.org', $this->idSite1, $url, $referrer . '/'), + // idSite2 matches any piwik.org path so this is a direct entry for it + array($directEntryReferrerName, $this->idSite2, $url, $referrer), + array($directEntryReferrerName, $this->idSite2, $url, $referrer . '/'), + // idSite3 has different domain so it is coming from different website + array('piwik.org', $this->idSite3, $url, $referrer), + array('piwik.org', $this->idSite3, $url, $referrer . '/'), + + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/foo/bar/baz'), + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/foo/bar/baz/'), + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/foo/bar/baz?x=5'), + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/fOo/BaR/baz?x=5'), + // /not/xyz belongs to different website + array('piwik.org', $this->idSite1, $url, $referrer . '/not/xyz'), + array($directEntryReferrerName, $this->idSite2, $url, $referrer . '/not/xyz'), + + // /foo/bar/baz belongs to different website + array('piwik.org/foo/bar', $this->idSite2, $url, $referrer . '/foo/bar/baz'), + array('piwik.org/foo/bar', $this->idSite3, $url, $referrer . '/foo/bar'), + array('piwik.org/foo/bar', $this->idSite3, $url, $referrer . '/fOo/BaR'), + + // should detect campaign independent of domain / path + array('test', $this->idSite1, $url . '?pk_campaign=test', $referrer), + array('testfoobar', $this->idSite2, $url . '?pk_campaign=testfoobar', $referrer), + array('test', $this->idSite3, $url . '?pk_campaign=test', $referrer), + + array('Google', $this->idSite3, $url, 'http://google.com/search?q=piwik'), + + // testing case for backwards compatibility where url has same domain as urlref but the domain is not known to any website + array($directEntryReferrerName, $this->idSite3, 'http://example.com/foo', 'http://example.com/bar'), + array($directEntryReferrerName, $this->idSite3, 'http://example.com/foo', 'http://example.com'), + array($directEntryReferrerName, $this->idSite3, 'http://example.com', 'http://example.com/bar'), + + // testing case where domain of referrer is not known to any site but neither is the URL, url != urlref + array('example.com', $this->idSite3, 'http://example.org', 'http://example.com/bar'), + ); + } + + private function getRequest($params) + { + return new Request($params); + } + + private function getNewVisitor() + { + return new Visitor(new VisitProperties()); + } + +} diff --git a/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php new file mode 100644 index 0000000000..5925dbc5c0 --- /dev/null +++ b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php @@ -0,0 +1,128 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Referrers\tests\Integration\Columns; + +use Piwik\Common; +use Piwik\Plugins\Referrers\Columns\ReferrerType; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Cache; +use Piwik\Tracker\Request; +use Piwik\Tracker\Visit\VisitProperties; +use Piwik\Tracker\Visitor; + +/** + * @group Referrers + * @group ReferrerTypeTest + * @group ReferrerType + * @group Plugins + */ +class ReferrerTypeTest extends IntegrationTestCase +{ + /** + * @var ReferrerType + */ + private $referrerType; + private $idSite1 = 1; + private $idSite2 = 2; + private $idSite3 = 3; + + public function setUp() + { + parent::setUp(); + + Cache::clearCacheGeneral(); + + $date = '2012-01-01 00:00:00'; + $ecommerce = false; + + Fixture::createWebsite($date, $ecommerce, $name = 'test1', $url = 'http://piwik.org/foo/bar'); + Fixture::createWebsite($date, $ecommerce, $name = 'test2', $url = 'http://piwik.org/'); + Fixture::createWebsite($date, $ecommerce, $name = 'test3', $url = 'http://piwik.pro/'); + + $this->referrerType = new ReferrerType(); + } + + public function tearDown() + { + // clean up your test here if needed + Cache::clearCacheGeneral(); + + parent::tearDown(); + } + + /** + * @dataProvider getReferrerUrls + */ + public function test_onNewVisit_shouldDetectCorrectReferrerType($expectedType, $idSite, $url, $referrerUrl) + { + $request = $this->getRequest(array('idsite' => $idSite, 'url' => $url, 'urlref' => $referrerUrl)); + $type = $this->referrerType->onNewVisit($request, $this->getNewVisitor(), $action = null); + + $this->assertSame($expectedType, $type); + } + + public function getReferrerUrls() + { + $url = 'http://piwik.org/foo/bar'; + $referrer = 'http://piwik.org'; + + // $expectedType, $idSite, $url, $referrerUrl + return array( + // domain matches but path does not match for idsite1 + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer), + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer . '/'), + // idSite2 matches any piwik.org path so this is a direct entry for it + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer . '/'), + // idSite3 has different domain so it is coming from different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer), + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/'), + + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz/'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz?x=5'), + // /not/xyz belongs to different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer . '/not/xyz'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer . '/not/xyz'), + + // /foo/bar/baz belongs to different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite2, $url, $referrer . '/foo/bar/baz'), + + // website as it is from different domain anyway + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/foo/bar/baz'), + + // should detect campaign independent of domain / path + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite1, $url . '?pk_campaign=test', $referrer), + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite2, $url . '?pk_campaign=test', $referrer), + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite3, $url . '?pk_campaign=test', $referrer), + + array(Common::REFERRER_TYPE_SEARCH_ENGINE, $this->idSite3, $url, 'http://google.com/search?q=piwik'), + + // testing case for backwards compatibility where url has same domain as urlref but the domain is not known to any website + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com/foo', 'http://example.com/bar'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com/foo', 'http://example.com'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com', 'http://example.com/bar'), + + // testing case where domain of referrer is not known to any site but neither is the URL, url != urlref + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, 'http://example.org', 'http://example.com/bar'), + ); + } + + private function getRequest($params) + { + return new Request($params); + } + + private function getNewVisitor() + { + return new Visitor(new VisitProperties()); + } + +} diff --git a/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml b/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml index edc1bd40ff..58601dc41b 100644 --- a/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml +++ b/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml @@ -874,7 +874,7 @@ <result prettyDate="Tuesday, February 2, 2010"/> <result prettyDate="Wednesday, February 3, 2010"/> <result prettyDate="Thursday, February 4, 2010"> - <row> + <row> <idsubdatatable>2</idsubdatatable> </row> <row> diff --git a/plugins/Referrers/tests/Unit/ReferrersTest.php b/plugins/Referrers/tests/Unit/ReferrersTest.php index 909e6bf65e..e5dea866e9 100644 --- a/plugins/Referrers/tests/Unit/ReferrersTest.php +++ b/plugins/Referrers/tests/Unit/ReferrersTest.php @@ -11,217 +11,15 @@ namespace Piwik\Plugins\Referrers\tests; use Piwik\DataTable; use Piwik\DataTable\Row; use Piwik\Period; +use Piwik\Plugins\Referrers\SearchEngine; require_once PIWIK_INCLUDE_PATH . '/plugins/Referrers/Referrers.php'; +/** + * @group Plugin + */ class ReferrersTest extends \PHPUnit_Framework_TestCase { - /** - * Dataprovider serving all search engine data - */ - public function getSearchEngines() - { - include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php'; - - $searchEngines = array(); - foreach ($GLOBALS['Piwik_SearchEngines'] as $url => $searchEngine) { - $searchEngines[] = array($url, $searchEngine); - } - return $searchEngines; - } - - /** - * search engine has at least one keyword - * - * @group Plugins - * - * @dataProvider getSearchEngines - */ - public function testMissingSearchEngineKeyword($url, $searchEngine) - { - // Get list of search engines and first appearing URL - static $searchEngines = array(); - - $name = parse_url('http://' . $url); - if (!array_key_exists($searchEngine[0], $searchEngines)) { - $searchEngines[$searchEngine[0]] = $url; - - $this->assertTrue(!empty($searchEngine[1]), $name['host']); - } - } - - /** - * search engine is defined in DataFiles/SearchEngines.php but there's no favicon - * - * @group Plugins - * - * @dataProvider getSearchEngines - */ - public function testMissingSearchEngineIcons($url, $searchEngine) - { - // Get list of existing favicons - $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); - - // Get list of search engines and first appearing URL - static $searchEngines = array(); - - $name = parse_url('http://' . $url); - if (!array_key_exists($searchEngine[0], $searchEngines)) { - $searchEngines[$searchEngine[0]] = $url; - - $this->assertTrue(in_array($name['host'] . '.png', $favicons), $name['host']); - } - } - - /** - * favicon exists but there's no corresponding search engine defined in DataFiles/SearchEngines.php - * - * @group Plugins - */ - public function testObsoleteSearchEngineIcons() - { - include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php'; - - // Get list of search engines and first appearing URL - $searchEngines = array(); - foreach ($GLOBALS['Piwik_SearchEngines'] as $url => $searchEngine) { - $name = parse_url('http://' . $url); - if (!array_key_exists($name['host'], $searchEngines)) { - $searchEngines[$name['host']] = true; - } - } - - // Get list of existing favicons - $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); - foreach ($favicons as $name) { - if ($name[0] == '.' || strpos($name, 'xx.') === 0) { - continue; - } - - $host = substr($name, 0, -4); - $this->assertTrue(array_key_exists($host, $searchEngines), $host); - } - } - - /** - * get search engine host from url - * - * @group Plugins - */ - public function testGetSearchEngineHostFromUrl() - { - $data = array( - 'http://www.google.com/cse' => array('www.google.com', 'www.google.com/cse'), - 'http://www.google.com' => array('www.google.com', 'www.google.com'), - ); - - foreach ($data as $url => $expected) { - $this->assertEquals($expected[0], \Piwik\Plugins\Referrers\getSearchEngineHostFromUrl($url)); - $this->assertEquals($expected[1], \Piwik\Plugins\Referrers\getSearchEngineHostPathFromUrl($url)); - } - } - - /** - * Dataprovider for testGetSearchEngineUrlFromUrlAndKeyword - */ - public function getSearchEngineUrlFromUrlAndKeywordTestData() - { - return array( - array('http://apollo.lv/portal/search/', 'piwik', 'http://apollo.lv/portal/search/?cof=FORID%3A11&q=piwik&search_where=www'), - array('http://bing.com/images/search', 'piwik', 'http://bing.com/images/search/?q=piwik'), - array('http://google.com', 'piwik', 'http://google.com/search?q=piwik'), - ); - } - - /** - * get search engine url from name and keyword - * - * @group Plugins - * - * @dataProvider getSearchEngineUrlFromUrlAndKeywordTestData - */ - public function testGetSearchEngineUrlFromUrlAndKeyword($url, $keyword, $expected) - { - include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php'; - $this->assertEquals($expected, \Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword($url, $keyword)); - } - - /** - * Dataprovider for getSocialNetworkFromDomainTestData - */ - public function getSocialNetworkFromDomainTestData() - { - return array( - array('http://www.facebook.com', 'Facebook'), - array('http://www.facebook.com/piwik', 'Facebook'), - array('http://m.facebook.com', 'Facebook'), - array('https://m.facebook.com', 'Facebook'), - array('m.facebook.com', 'Facebook'), - array('http://lastfm.com.tr', 'Last.fm'), - array('http://t.co/test', 'Twitter'), - array('http://xxt.co/test', \Piwik\Piwik::translate('General_Unknown')), - array('asdfasdfadsf.com', \Piwik\Piwik::translate('General_Unknown')), - array('http://xwayn.com', \Piwik\Piwik::translate('General_Unknown')), - array('http://live.com/test', \Piwik\Piwik::translate('General_Unknown')), - ); - } - - /** - * @group Plugins - * - * @dataProvider getSocialNetworkFromDomainTestData - */ - public function testGetSocialNetworkFromDomain($url, $expected) - { - include PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - $this->assertEquals($expected, \Piwik\Plugins\Referrers\getSocialNetworkFromDomain($url)); - } - - public function getSocialsLogoFromUrlTestData() - { - return array( - array('http://www.facebook.com', 'facebook.com.png'), - array('www.facebook.com', 'facebook.com.png',), - array('http://lastfm.com.tr', 'last.fm.png'), - array('http://asdfasdf.org/test', 'xx.png'), - array('http://www.google.com', 'xx.png'), - ); - } - - /** - * @group Plugins - * - * @dataProvider getSocialsLogoFromUrlTestData - */ - public function testGetSocialsLogoFromUrl($url, $expected) - { - include PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - $this->assertContains($expected, \Piwik\Plugins\Referrers\getSocialsLogoFromUrl($url)); - } - - public function isSocialUrlTestData() - { - return array( - array('http://www.facebook.com', 'Facebook', true), - array('http://www.facebook.com', 'Twitter', false), - array('http://m.facebook.com', false, true), - array('http://lastfm.com.tr', 'Last.fm', true), - array('http://asdfasdf.org/test', false, false), - array('http://asdfasdf.com/test', 'Facebook', false), - ); - } - - /** - * @group Plugins - * - * @dataProvider isSocialUrlTestData - */ - public function testIsSocialUrl($url, $assumedSocial, $expected) - { - include PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - $this->assertEquals($expected, \Piwik\Plugins\Referrers\isSocialUrl($url, $assumedSocial)); - } - public function removeUrlProtocolTestData() { return array( diff --git a/plugins/Referrers/tests/Unit/SearchEngineTest.php b/plugins/Referrers/tests/Unit/SearchEngineTest.php new file mode 100644 index 0000000000..e9f0c926ea --- /dev/null +++ b/plugins/Referrers/tests/Unit/SearchEngineTest.php @@ -0,0 +1,173 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Referrers\tests; + +use Piwik\Plugins\Referrers\SearchEngine; +use Spyc; + +/** + * @group SearchEngine + */ +class SearchEngineTest extends \PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + // inject definitions to avoid database usage + $yml = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . SearchEngine::DEFINITION_FILE); + SearchEngine::getInstance()->loadYmlData($yml); + parent::setUpBeforeClass(); + } + + public function getSearchEngineUrls() + { + return Spyc::YAMLLoad(PIWIK_PATH_TEST_TO_ROOT .'/tests/resources/extractSearchEngineInformationFromUrlTests.yml'); + } + + /** + * @dataProvider getSearchEngineUrls + */ + public function testExtractInformationFromUrl($url, $engine, $keywords) + { + $returnedValue = SearchEngine::getInstance()->extractInformationFromUrl($url); + + $expectedValue = false; + + if (!empty($engine)) { + $expectedValue = array('name' => $engine, 'keywords' => $keywords); + } + + $this->assertEquals($expectedValue, $returnedValue); + } + + public function testSearchEnginesDefinedCorrectly() + { + $searchEngines = array(); + foreach (SearchEngine::getInstance()->getDefinitions() as $host => $info) { + if (isset($info['backlink']) && $info['backlink'] !== false) { + $this->assertTrue(strrpos($info['backlink'], "{k}") !== false, $host . " search URL is not defined correctly, must contain the macro {k}"); + } + + if (!array_key_exists($info['name'], $searchEngines)) { + $searchEngines[$info['name']] = true; + + $this->assertTrue(strpos($host, '{}') === false, $host . " search URL is the master record and should not contain {}"); + } + + if (isset($info['charsets']) && $info['charsets'] !== false) { + $this->assertTrue(is_array($info['charsets']) || is_string($info['charsets']), $host . ' charsets must be either a string or an array'); + + if (is_string($info['charsets'])) { + $this->assertTrue(trim($info['charsets']) !== '', $host . ' charsets cannot be an empty string'); + $this->assertTrue(strpos($info['charsets'], ' ') === false, $host . ' charsets cannot contain spaces'); + + } + + if (is_array($info['charsets'])) { + $this->assertTrue(count($info['charsets']) > 0, $host . ' charsets cannot be an empty array'); + $this->assertTrue(strpos(serialize($info['charsets']), '""') === false, $host . ' charsets in array cannot be empty stringss'); + $this->assertTrue(strpos(serialize($info['charsets']), ' ') === false, $host . ' charsets in array cannot contain spaces'); + } + } + } + } + + /** + * Dataprovider for testGetBackLinkFromUrlAndKeyword + */ + public function getBackLinkFromUrlAndKeywordTestData() + { + return array( + array('http://apollo.lv/portal/search/', 'piwik', 'http://apollo.lv/portal/search/?cof=FORID%3A11&q=piwik&search_where=www'), + array('http://bing.com/images/search', 'piwik', 'http://bing.com/images/search/?q=piwik'), + array('http://google.com', 'piwik', 'http://google.com/search?q=piwik'), + ); + } + + /** + * get search engine url from name and keyword + * + * @dataProvider getBackLinkFromUrlAndKeywordTestData + */ + public function testGetBackLinkFromUrlAndKeyword($url, $keyword, $expected) + { + $this->assertEquals($expected, SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword($url, $keyword)); + } + + /** + * Dataprovider serving all search engine data + */ + public function getAllSearchEngines() + { + $yml = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . SearchEngine::DEFINITION_FILE); + SearchEngine::getInstance()->loadYmlData($yml); + $searchEngines = array(); + foreach (SearchEngine::getInstance()->getDefinitions() as $url => $searchEngine) { + $searchEngines[] = array($url, $searchEngine); + } + return $searchEngines; + } + + /** + * search engine has at least one keyword + * + * @dataProvider getAllSearchEngines + */ + public function testMissingSearchEngineKeyword($url, $searchEngine) + { + $name = parse_url('http://' . $url); + $this->assertTrue(!empty($searchEngine['params']), $name['host']); + } + + /** + * search engine is defined but there's no favicon + * + * @dataProvider getAllSearchEngines + */ + public function testMissingSearchEngineIcons($url, $searchEngine) + { + // Get list of existing favicons + $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); + + // Get list of search engines and first appearing URL + static $searchEngines = array(); + + $name = parse_url('http://' . $url); + if (!array_key_exists($searchEngine['name'], $searchEngines)) { + $searchEngines[$searchEngine['name']] = $url; + + $this->assertTrue(in_array($name['host'] . '.png', $favicons), $name['host']); + } + } + + /** + * favicon exists but there's no corresponding search engine defined + */ + public function testObsoleteSearchEngineIcons() + { + // Get list of search engines and first appearing URL + $searchEngines = array(); + foreach (SearchEngine::getInstance()->getDefinitions() as $url => $searchEngine) { + $name = parse_url('http://' . $url); + if (!array_key_exists($name['host'], $searchEngines)) { + $searchEngines[$name['host']] = true; + } + } + + // Get list of existing favicons + $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); + foreach ($favicons as $name) { + if ($name[0] == '.' || strpos($name, 'xx.') === 0) { + continue; + } + + $host = substr($name, 0, -4); + $this->assertTrue(array_key_exists($host, $searchEngines), $host); + } + } +}
\ No newline at end of file diff --git a/plugins/Referrers/tests/Unit/SocialTest.php b/plugins/Referrers/tests/Unit/SocialTest.php new file mode 100644 index 0000000000..8e8f35e0f9 --- /dev/null +++ b/plugins/Referrers/tests/Unit/SocialTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Referrers\tests; + +use Piwik\Plugins\Referrers\Social; +use Spyc; + +/** + * @group Social + * @group Plugins + */ +class SocialTest extends \PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + // inject definitions to avoid database usage + $yml = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . Social::DEFINITION_FILE); + Social::getInstance()->loadYmlData($yml); + parent::setUpBeforeClass(); + } + + public function isSocialUrlTestData() + { + return array( + array('http://www.facebook.com', 'Facebook', true), + array('http://www.facebook.com', 'Twitter', false), + array('http://m.facebook.com', false, true), + array('http://lastfm.com.tr', 'Last.fm', true), + array('http://asdfasdf.org/test', false, false), + array('http://asdfasdf.com/test', 'Facebook', false), + ); + } + + /** + * @dataProvider isSocialUrlTestData + */ + public function testIsSocialUrl($url, $assumedSocial, $expected) + { + $this->assertEquals($expected, Social::getInstance()->isSocialUrl($url, $assumedSocial)); + } + + + /** + * Dataprovider for getSocialNetworkFromDomainTestData + */ + public function getSocialNetworkFromDomainTestData() + { + return array( + array('http://www.facebook.com', 'Facebook'), + array('http://www.facebook.com/piwik', 'Facebook'), + array('http://m.facebook.com', 'Facebook'), + array('https://m.facebook.com', 'Facebook'), + array('m.facebook.com', 'Facebook'), + array('http://lastfm.com.tr', 'Last.fm'), + array('http://t.co/test', 'Twitter'), + array('http://xxt.co/test', \Piwik\Piwik::translate('General_Unknown')), + array('asdfasdfadsf.com', \Piwik\Piwik::translate('General_Unknown')), + array('http://xwayn.com', \Piwik\Piwik::translate('General_Unknown')), + array('http://live.com/test', \Piwik\Piwik::translate('General_Unknown')), + ); + } + + /** + * @dataProvider getSocialNetworkFromDomainTestData + */ + public function testGetSocialNetworkFromDomain($url, $expected) + { + $this->assertEquals($expected, Social::getInstance()->getSocialNetworkFromDomain($url)); + } + + public function getLogoFromUrlTestData() + { + return array( + array('http://www.facebook.com', 'facebook.com.png'), + array('www.facebook.com', 'facebook.com.png',), + array('http://lastfm.com.tr', 'last.fm.png'), + array('http://asdfasdf.org/test', 'xx.png'), + array('http://www.google.com', 'xx.png'), + ); + } + + /** + * @group Plugins + * + * @dataProvider getLogoFromUrlTestData + */ + public function testGetLogoFromUrl($url, $expected) + { + $this->assertContains($expected, Social::getInstance()->getLogoFromUrl($url)); + } +}
\ No newline at end of file diff --git a/plugins/Resolution/lang/ko.json b/plugins/Resolution/lang/ko.json index d76113b7af..19be5c511d 100644 --- a/plugins/Resolution/lang/ko.json +++ b/plugins/Resolution/lang/ko.json @@ -3,6 +3,7 @@ "ColumnConfiguration": "구성", "ColumnResolution": "해상도", "Configurations": "구성", + "PluginDescription": "방문자의 화면 해상도를 기록\/보고합니다.", "Resolutions": "해상도", "WidgetGlobalVisitors": "글로벌 방문자 구성", "WidgetGlobalVisitorsDocumentation": "방문자의 가장 일반적인 사용 환경에 대한 보고서입니다. 운영 체제, 브라우저 종류와 화면 해상도의 조합으로 표시합니다.", diff --git a/plugins/SEO/Metric/Alexa.php b/plugins/SEO/Metric/Alexa.php index 87c5785d36..e8f7956241 100644 --- a/plugins/SEO/Metric/Alexa.php +++ b/plugins/SEO/Metric/Alexa.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -42,7 +43,7 @@ class Alexa implements MetricsProvider $value = null; } - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://alexa.com'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://alexa.com'); $link = self::LINK . urlencode($domain); return array( diff --git a/plugins/SEO/Metric/Bing.php b/plugins/SEO/Metric/Bing.php index e85d585c13..71553ffc6e 100644 --- a/plugins/SEO/Metric/Bing.php +++ b/plugins/SEO/Metric/Bing.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -46,7 +47,7 @@ class Bing implements MetricsProvider $pageCount = null; } - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://bing.com'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://bing.com'); return array( new Metric('bing-index', 'SEO_Bing_IndexedPages', $pageCount, $logo, null, null, 'General_Pages') diff --git a/plugins/SEO/Metric/Dmoz.php b/plugins/SEO/Metric/Dmoz.php index d21be1e5ca..c7b8860a89 100644 --- a/plugins/SEO/Metric/Dmoz.php +++ b/plugins/SEO/Metric/Dmoz.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -53,7 +54,7 @@ class Dmoz implements MetricsProvider $value = null; } - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://dmoz.org'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://dmoz.org'); return array( new Metric('dmoz', 'SEO_Dmoz', $value, $logo) diff --git a/plugins/SEO/Metric/Google.php b/plugins/SEO/Metric/Google.php index cec1d96af9..7abd82b371 100644 --- a/plugins/SEO/Metric/Google.php +++ b/plugins/SEO/Metric/Google.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -37,7 +38,7 @@ class Google implements MetricsProvider $pageCount = $this->fetchIndexedPagesCount($domain); $pageRank = $this->fetchPageRank($domain); - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://google.com'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://google.com'); return array( new Metric('google-index', 'SEO_Google_IndexedPages', $pageCount, $logo, null, null, 'General_Pages'), diff --git a/plugins/SEO/lang/ja.json b/plugins/SEO/lang/ja.json index e0496470df..e4459709a2 100644 --- a/plugins/SEO/lang/ja.json +++ b/plugins/SEO/lang/ja.json @@ -1,5 +1,6 @@ { "SEO": { + "PluginDescription": "このプラグインは SEO メトリクスを抽出し、表示します。: Alexa web ランキング, Google ページランク,インデックスされたページ数やバックリンクの数。", "AlexaRank": "Alexa ランク", "Bing_IndexedPages": "Bing インデックスページ", "Dmoz": "DMOZ エントリー", diff --git a/plugins/SEO/lang/ko.json b/plugins/SEO/lang/ko.json index 55f71074cb..de4e2b2bd6 100644 --- a/plugins/SEO/lang/ko.json +++ b/plugins/SEO/lang/ko.json @@ -1,5 +1,6 @@ { "SEO": { + "PluginDescription": "이 플러그인은 Alexa 웹 랭킹, 구글 페이지랭크, 색인된 페이지 수, 선택된 웹사이트의 백링크와 같은 SEO 측정 기준을 추출하고 보여줍니다.", "AlexaRank": "Alexa 랭크", "Bing_IndexedPages": "Bing에 색인된 페이지", "Dmoz": "DMOZ 항목", diff --git a/plugins/ScheduledReports/config/tcpdf_config.php b/plugins/ScheduledReports/config/tcpdf_config.php index 0fd19a1232..26b33b7da7 100644 --- a/plugins/ScheduledReports/config/tcpdf_config.php +++ b/plugins/ScheduledReports/config/tcpdf_config.php @@ -13,7 +13,7 @@ use Piwik\Container\StaticContainer; * */ -define('K_PATH_MAIN', PIWIK_VENDOR_PATH . '/tecnick.com/tcpdf/'); +define('K_PATH_MAIN', PIWIK_VENDOR_PATH . '/tecnickcom/tcpdf/'); $pathTmpTCPDF = StaticContainer::get('path.tmp') . '/tcpdf/'; diff --git a/plugins/ScheduledReports/lang/cs.json b/plugins/ScheduledReports/lang/cs.json index 836eb47133..caa1cb992e 100644 --- a/plugins/ScheduledReports/lang/cs.json +++ b/plugins/ScheduledReports/lang/cs.json @@ -4,7 +4,7 @@ "AggregateReportsFormat_GraphsOnly": "Pouze zobrazit grafy (ne tabulky hlášení)", "AggregateReportsFormat_TablesAndGraphs": "Zobrazit tabulky hlášení a grafy pro všechna hlášení", "AggregateReportsFormat_TablesOnly": "(Výchozí) zobrazit tabulky hlášení (grafy pouze pro klíčová měření)", - "AlsoSendReportToTheseEmails": "Odesílat hlášení také na tyto e-maily (jeden e-mail na řádek):", + "AlsoSendReportToTheseEmails": "Odesílat hlášení také na tyto emaily (jeden email na řádek):", "AreYouSureDeleteReport": "Opravdu chcete odstranit toto hlášení a jeho plán?", "CancelAndReturnToReports": "Zrušit a %svrátit se k seznamu hlášení%s", "CreateAndScheduleReport": "Vytvořit a naplánovat hlášení", @@ -13,7 +13,7 @@ "DescriptionOnFirstPage": "Popis hlášení bude zobrazen na první jeho straně.", "DisplayFormat_TablesOnly": "Pouze zobrazit tabulky (žádné grafy)", "EmailHello": "Ahoj,", - "EmailReports": "Poslat hlášení na e-mail", + "EmailReports": "Poslat hlášení na email", "EmailSchedule": "Plánování zasílání hlášení", "EvolutionGraph": "Zobrazit historické grafy pro %s nejvyšších hodnot", "FrontPage": "Hlavní strana", @@ -34,15 +34,15 @@ "ReportsIncluded": "Zahrnuté statistiky", "ReportType": "Odeslat hlášení pomocí", "ReportUpdated": "Hlášení aktualizováno", - "Segment_Deletion_Error": "Tento segment nemůže být smazán nebo zneviditelněn, protože se používá k vytváření e-mailových hlášení %s. Zkuste to znovu poté, co ho z těchto hlášení odstraníte.", - "Segment_Help": "Můžete zvolit existující segment, který bude aplikován na data v tomto e-mailovém hlášení. Segmenty můžete vytvářet a upravovat vlastní segmenty na vaší nástěnce %s(klikněte zde pro otevření)%s, pak klikněte na box \"%s\", pak \"%s\".", + "Segment_Deletion_Error": "Tento segment nemůže být smazán nebo zneviditelněn, protože se používá k vytváření emailových hlášení %s. Zkuste to znovu poté, co ho z těchto hlášení odstraníte.", + "Segment_Help": "Můžete zvolit existující segment, který bude aplikován na data v tomto emailovém hlášení. Segmenty můžete vytvářet a upravovat vlastní segmenty na vaší nástěnce %s(klikněte zde pro otevření)%s, pak klikněte na box \"%s\", pak \"%s\".", "SegmentAppliedToReports": "Segment %s je aplikován na hlášení.", "SendReportNow": "Odeslat hlášení ihned", "SendReportTo": "Odeslat hlášení", "SentToMe": "Odeslat hlášení mně", "TableOfContent": "Seznam hlášení", "ThereIsNoReportToManage": "Pro stránky %s neexistuje žádné hlášení, které lze spravovat", - "TopLinkTooltip": "Vytvářejte vlastní hlášení, která budou automaticky doručena na vaši nebo zákazníkovu e-mailovou adresu.", + "TopLinkTooltip": "Vytvářejte vlastní hlášení, která budou automaticky doručena na vaši nebo zákazníkovu emailovou adresu.", "TopOfReport": "Zpět nahoru", "UpdateReport": "Aktualizovat hlášení", "WeeklyScheduleHelp": "Týdenní plán: hlášení bude odesláno každé pondělí" diff --git a/plugins/ScheduledReports/lang/it.json b/plugins/ScheduledReports/lang/it.json index 5f9e17ee17..bf796fd1c3 100644 --- a/plugins/ScheduledReports/lang/it.json +++ b/plugins/ScheduledReports/lang/it.json @@ -25,7 +25,7 @@ "PiwikReports": "Report di Piwik", "PleaseFindAttachedFile": "Potete trovare nel file allegato il tuo %1$s report per %2$s.", "SentFromX": "Inviato da %s", - "PleaseFindBelow": "Di seguito trovi il tuo %1$s report per %2$s.", + "PleaseFindBelow": "Di seguito trovi il tuo report %1$s per %2$s.", "PluginDescription": "Crea dei report personalizzati e li programma per l'invio tramite email ogni giorno, settimana o mese a una o più persone. Sono supportati svariati formati (html, pdf, csv, immagini).", "ReportFormat": "Formato report", "ReportHour": "Invia report alle %s", diff --git a/plugins/ScheduledReports/lang/ja.json b/plugins/ScheduledReports/lang/ja.json index 15e24efd81..677a778017 100644 --- a/plugins/ScheduledReports/lang/ja.json +++ b/plugins/ScheduledReports/lang/ja.json @@ -24,9 +24,11 @@ "Pagination": "%s の %s ページ", "PiwikReports": "Piwik リポート", "PleaseFindAttachedFile": "%2$s の %1$s のリポートを添付しています。", + "SentFromX": "%s から送信されます。", "PleaseFindBelow": "%2$s の %1$s のリポートをお届けします。", "PluginDescription": "カスタム レポートを作成し、毎日、毎週、毎月等、 1 つまたは複数の人にメールで送信するようにスケジュールします。サポートされているレポート形式 ( html、pdf、csv、画像)。", "ReportFormat": "リポートのフォーマット", + "ReportHour": "%s 時にレポートを送信します", "ReportIncludeNWebsites": "このレポートは、現在利用可能な %s ウェブサイトから、少なくとも 1 訪問以上ある全ウェブサイトの主なメトリクスを含みます。", "ReportSent": "送信されたレポート", "ReportsIncluded": "リポートに含める情報", diff --git a/plugins/ScheduledReports/lang/tr.json b/plugins/ScheduledReports/lang/tr.json index 818cb4378b..04e359c5a4 100644 --- a/plugins/ScheduledReports/lang/tr.json +++ b/plugins/ScheduledReports/lang/tr.json @@ -1,8 +1,12 @@ { "ScheduledReports": { + "AggregateReportsFormat": "Ekran seçenekleri (isteğe bağlı)", "CreateReport": "Rapor Oluştur", "EmailHello": "Merhaba,", "EmailReports": "Raporları epostala", + "ReportFormat": "Rapor Formatı", + "ReportUpdated": "Rapor güncellendi", + "SendReportNow": "Raporu şimdi gönder", "TableOfContent": "Rapor listesi", "TopOfReport": "Başa dön", "UpdateReport": "Raporu Güncelle" diff --git a/plugins/SecurityInfo b/plugins/SecurityInfo -Subproject 65c7b5a1693b99dc97b78df015df1f606b2ef8b +Subproject ffbc301bf148481b7896f685847ada3bc9734b5 diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php index 9107935e73..a46be068bc 100644 --- a/plugins/SegmentEditor/SegmentEditor.php +++ b/plugins/SegmentEditor/SegmentEditor.php @@ -28,6 +28,7 @@ class SegmentEditor extends \Piwik\Plugin 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'Template.nextToCalendar' => 'getSegmentEditorHtml', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', ); } @@ -85,4 +86,9 @@ class SegmentEditor extends \Piwik\Plugin { return Config::getInstance()->General['allow_adding_segments_for_all_websites'] == 1; } + + public function getClientSideTranslationKeys(&$translationKeys) + { + $translationKeys[] = 'SegmentEditor_CustomSegment'; + } } diff --git a/plugins/SegmentEditor/SegmentFormatter.php b/plugins/SegmentEditor/SegmentFormatter.php new file mode 100644 index 0000000000..19838d46d5 --- /dev/null +++ b/plugins/SegmentEditor/SegmentFormatter.php @@ -0,0 +1,147 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\SegmentEditor; + +use Exception; +use Piwik\Config; +use Piwik\Db; +use Piwik\Piwik; +use Piwik\Segment; +use Piwik\Segment\SegmentExpression; + +/** + */ +class SegmentFormatter +{ + /** + * @var SegmentList + */ + private $segmentList; + + private $matchesMetric = array( + SegmentExpression::MATCH_EQUAL => 'General_OperationEquals', + SegmentExpression::MATCH_NOT_EQUAL => 'General_OperationNotEquals', + SegmentExpression::MATCH_LESS_OR_EQUAL => 'General_OperationAtMost', + SegmentExpression::MATCH_GREATER_OR_EQUAL => 'General_OperationAtLeast', + SegmentExpression::MATCH_LESS => 'General_OperationLessThan', + SegmentExpression::MATCH_GREATER => 'General_OperationGreaterThan', + ); + + private $matchesDimension = array( + SegmentExpression::MATCH_EQUAL => 'General_OperationIs', + SegmentExpression::MATCH_NOT_EQUAL => 'General_OperationIsNot', + SegmentExpression::MATCH_CONTAINS => 'General_OperationContains', + SegmentExpression::MATCH_DOES_NOT_CONTAIN => 'General_OperationDoesNotContain', + SegmentExpression::MATCH_STARTS_WITH => 'General_OperationStartsWith', + SegmentExpression::MATCH_ENDS_WITH => 'General_OperationEndsWith' + ); + + private $operators = array( + SegmentExpression::BOOL_OPERATOR_AND => 'General_And', + SegmentExpression::BOOL_OPERATOR_OR => 'General_Or', + SegmentExpression::BOOL_OPERATOR_END => '', + ); + + public function __construct(SegmentList $segmentList) + { + $this->segmentList = $segmentList; + } + + public function getHumanReadable($segmentString, $idSite) + { + if (empty($segmentString)) { + return Piwik::translate('SegmentEditor_DefaultAllVisits'); + } + + try { + $segment = new SegmentExpression(urldecode($segmentString)); + $expressions = $segment->parseSubExpressions(); + } catch (Exception $e) { + $segment = new SegmentExpression($segmentString); + $expressions = $segment->parseSubExpressions(); + } + + $readable = ''; + foreach ($expressions as $expression) { + $operator = $expression[SegmentExpression::INDEX_BOOL_OPERATOR]; + $operand = $expression[SegmentExpression::INDEX_OPERAND]; + $name = $operand[SegmentExpression::INDEX_OPERAND_NAME]; + + $segment = $this->segmentList->findSegment($name, $idSite); + + if (empty($segment)) { + throw new Exception(sprintf("The segment '%s' does not exist.", $name)); + } + + $readable .= $segment['name'] . ' '; + $readable .= $this->getTranslationForComparison($operand, $segment['type']) . ' '; + $readable .= $this->getFormattedValue($operand); + $readable .= $this->getTranslationForBoolOperator($operator) . ' '; + } + + $readable = trim($readable); + + return $readable; + } + + private function getTranslationForComparison($operand, $segmentType) + { + $operator = $operand[SegmentExpression::INDEX_OPERAND_OPERATOR]; + + $translation = $operator; + + if ($operator === SegmentExpression::MATCH_IS_NULL_OR_EMPTY) { + return Piwik::translate('SegmentEditor_SegmentOperatorIsNullOrEmpty'); + } + + if ($operator === SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY) { + return Piwik::translate('SegmentEditor_SegmentOperatorIsNotNullNorEmpty'); + } + + if ($segmentType === 'dimension' && !empty($this->matchesDimension[$operator])) { + $translation = Piwik::translate($this->matchesDimension[$operator]); + } + if ($segmentType === 'metric' && !empty($this->matchesMetric[$operator])) { + $translation = Piwik::translate($this->matchesMetric[$operator]); + } + + return strtolower($translation); + } + + private function getFormattedValue($operand) + { + $operator = $operand[SegmentExpression::INDEX_OPERAND_OPERATOR]; + + if ($operator === SegmentExpression::MATCH_IS_NULL_OR_EMPTY + || $operator === SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY) { + return ''; + } + + $value = $operand[SegmentExpression::INDEX_OPERAND_VALUE]; + + if (empty($value)) { + $value = ''; + } + + return '"' . $value . '" '; + } + + private function getTranslationForBoolOperator($operator) + { + $translation = ''; + + if (!empty($this->operators[$operator])) { + $translation = Piwik::translate($this->operators[$operator]); + } elseif (!empty($operator)) { + $translation = $operator; + } + + return $translation; + } +} diff --git a/plugins/SegmentEditor/SegmentList.php b/plugins/SegmentEditor/SegmentList.php new file mode 100644 index 0000000000..e7094d4793 --- /dev/null +++ b/plugins/SegmentEditor/SegmentList.php @@ -0,0 +1,32 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\SegmentEditor; + +use Piwik\API\Request; +use Piwik\Config; +use Piwik\Db; + +/** + */ +class SegmentList +{ + public function findSegment($segmentName, $idSite) + { + $segments = Request::processRequest('API.getSegmentsMetadata', array( + 'idSites' => array($idSite), + )); + + foreach ($segments as $segment) { + if ($segment['segment'] == $segmentName && !empty($segmentName)) { + return $segment; + } + } + } + +} diff --git a/plugins/SegmentEditor/SegmentSelectorControl.php b/plugins/SegmentEditor/SegmentSelectorControl.php index e37354e1cd..72be071e5e 100644 --- a/plugins/SegmentEditor/SegmentSelectorControl.php +++ b/plugins/SegmentEditor/SegmentSelectorControl.php @@ -8,8 +8,10 @@ */ namespace Piwik\Plugins\SegmentEditor; +use Piwik\API\Request; use Piwik\Common; use Piwik\Config; +use Piwik\Container\StaticContainer; use Piwik\Piwik; use Piwik\Plugins\API\API as APIMetadata; use Piwik\View\UIControl; @@ -37,13 +39,17 @@ class SegmentSelectorControl extends UIControl $this->selectedSegment = Common::getRequestVar('segment', false, 'string'); + $formatter = StaticContainer::get('Piwik\Plugins\SegmentEditor\SegmentFormatter'); + $this->segmentDescription = $formatter->getHumanReadable(Request::getRawSegmentFromRequest(), $this->idSite); + $this->isAddingSegmentsForAllWebsitesEnabled = SegmentEditor::isAddingSegmentsForAllWebsitesEnabled(); $segments = APIMetadata::getInstance()->getSegmentsMetadata($this->idSite); + $visitTitle = Piwik::translate('General_Visit'); $segmentsByCategory = array(); foreach ($segments as $segment) { - if ($segment['category'] == Piwik::translate('General_Visit') + if ($segment['category'] == $visitTitle && ($segment['type'] == 'metric' && $segment['segment'] != 'visitIp') ) { $metricsLabel = Piwik::translate('General_Metrics'); @@ -52,7 +58,6 @@ class SegmentSelectorControl extends UIControl } $segmentsByCategory[$segment['category']][] = $segment; } - uksort($segmentsByCategory, array($this, 'sortSegmentCategories')); $this->createRealTimeSegmentsIsEnabled = Config::getInstance()->General['enable_create_realtime_segments']; $this->segmentsByCategory = $segmentsByCategory; @@ -95,15 +100,6 @@ class SegmentSelectorControl extends UIControl return (bool) $savedSegment['auto_archive']; } - public function sortSegmentCategories($a, $b) - { - // Custom Variables last - if ($a == Piwik::translate('CustomVariables_CustomVariables')) { - return 1; - } - return 0; - } - private function getTranslations() { $translationKeys = array( @@ -115,6 +111,8 @@ class SegmentSelectorControl extends UIControl 'General_OperationGreaterThan', 'General_OperationContains', 'General_OperationDoesNotContain', + 'General_OperationStartsWith', + 'General_OperationEndsWith', 'General_OperationIs', 'General_OperationIsNot', 'General_OperationContains', diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index 0e47bd7d19..2ffd7dac57 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -49,6 +49,8 @@ Segmentation = (function($) { self.availableMatches["dimension"]["!="] = self.translations['General_OperationIsNot']; self.availableMatches["dimension"]["=@"] = self.translations['General_OperationContains']; self.availableMatches["dimension"]["!@"] = self.translations['General_OperationDoesNotContain']; + self.availableMatches["dimension"]["=^"] = self.translations['General_OperationStartsWith']; + self.availableMatches["dimension"]["=$"] = self.translations['General_OperationEndsWith']; segmentation.prototype.setAvailableSegments = function (segments) { this.availableSegments = segments; @@ -83,7 +85,7 @@ Segmentation = (function($) { var name = $(foundItems).first().find("span.segname").text(); title.text(name); } else { - title.text("Custom Segment"); + title.text(_pk_translate('SegmentEditor_CustomSegment')); } segmentationTitle.html(title); } @@ -264,7 +266,7 @@ Segmentation = (function($) { }; var findAndExplodeByMatch = function(metric){ - var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">"]; + var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">", "=^", "=$"]; var newMetric = {}; var minPos = metric.length; var match, index; diff --git a/plugins/SegmentEditor/lang/cs.json b/plugins/SegmentEditor/lang/cs.json index 36dd2084b3..c5752de9e2 100644 --- a/plugins/SegmentEditor/lang/cs.json +++ b/plugins/SegmentEditor/lang/cs.json @@ -7,13 +7,14 @@ "AutoArchivePreProcessed": "Segmentovaná hlášení jsou předzpracována rychleji (vyžadují cron)", "AutoArchiveRealTime": "Segmentovaná hlášení jsou zpracována v reálném čase", "ChooseASegment": "Zvolte segment", + "CurrentlySelectedSegment": "Aktuálně vybraný segment: %s", "DataAvailableAtLaterDate": "Vaše segmentovaná analytická hlášení budou dostupná později. Omlouváme se za tuto nepříjemnost.", "DefaultAllVisits": "Všechny návštěvy", "DragDropCondition": "Přetáhněte podmínku", "LoadingSegmentedDataMayTakeSomeTime": "Zpracování segmentovaných dat návštěvníků může pár minut trvat...", "OperatorAND": "AND", "OperatorOR": "OR", - "SaveAndApply": "Uložit & použít", + "SaveAndApply": "Uložit a použít", "SegmentDisplayedAllWebsites": "Všechny webové stránky", "SegmentDisplayedThisWebsiteOnly": "Pouze tyto webové stránky", "SegmentIsDisplayedForWebsite": "A zobrazený po", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "Jinak můžete nastavení změnit v souboru %s, nebo můžete upravit tento segment a zvolit %s.", "YouMustBeLoggedInToCreateSegments": "Pro vytváření a úpravu vlastních segmentů návštěvníků musíte být přihlášen.", "YouDontHaveAccessToCreateSegments": "Pro vytváření a úpravu segmentů nemáte požadovanou přístupovou úroveň.", - "AddingSegmentForAllWebsitesDisabled": "Přidávání segmentů pro všechny stránky bylo zakázáno." + "AddingSegmentForAllWebsitesDisabled": "Přidávání segmentů pro všechny stránky bylo zakázáno.", + "SegmentXIsAUnionOf": "%s je propojení těchto segmentů:", + "CustomSegment": "Vlastní segment", + "SegmentOperatorIsNullOrEmpty": "je nulový nebo prázdný", + "SegmentOperatorIsNotNullNorEmpty": "není nulový nebo prázdný" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/el.json b/plugins/SegmentEditor/lang/el.json index e0a9f14a6a..72d3659543 100644 --- a/plugins/SegmentEditor/lang/el.json +++ b/plugins/SegmentEditor/lang/el.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "οι αναφορές τμημάτων προεπεξεργάζονται (γρηγορότερα, απαιτείται το archive.php με cron)", "AutoArchiveRealTime": "οι αναφορές τμημάτων επεξεργάζονται σε πραγματικό χρόνο", "ChooseASegment": "Επιλέξτε ένα τμήμα", + "CurrentlySelectedSegment": "Επιλεγμένο τμήμα αυτή τη στιγμή: %s", "DataAvailableAtLaterDate": "Οι τμηματικές αναφορές αναλυτικών θα είναι αργότερα διαθέσιμες. Ζητούμε συγγνώμη για την ταλαιπωρία.", "DefaultAllVisits": "Όλες οι επισκέψεις", "DragDropCondition": "Συνθήκη με Σύρε & Άσε", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "Εναλλακτικά, μπορείτε να αλλάξετε τη ρύθμιση στο αρχείο ρυθμίσεων (%s), ή να τροποποιήσετε το Τμήμα αυτό και να επιλέξετε '%s'.", "YouMustBeLoggedInToCreateSegments": "Θα πρέπει να έχετε κάνει είσοδο για να δημιουργήσετε και να επεξεργαστείτε προσαρμοσμένα τμήματα επισκεπτών.", "YouDontHaveAccessToCreateSegments": "Δεν διαθέτετε την απαιτούμενη πρόσβαση ασφαλείας για να δημιουργείτε και να τροποποιείτε τμήματα.", - "AddingSegmentForAllWebsitesDisabled": "Η προσθήκη τμημάτων έχει απενεργοποιηθεί για όλους τους ιστοτόπους." + "AddingSegmentForAllWebsitesDisabled": "Η προσθήκη τμημάτων έχει απενεργοποιηθεί για όλους τους ιστοτόπους.", + "SegmentXIsAUnionOf": "Το %s είναι η ένωση αυτών των τμημάτων:", + "CustomSegment": "Προσαρμοσμένο τμήμα", + "SegmentOperatorIsNullOrEmpty": "είναι άκυρο (null) ή κενό", + "SegmentOperatorIsNotNullNorEmpty": "δεν είναι άκυρο (null) ή άδειο" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/en.json b/plugins/SegmentEditor/lang/en.json index 46bdf5fef2..8651afeecd 100644 --- a/plugins/SegmentEditor/lang/en.json +++ b/plugins/SegmentEditor/lang/en.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "segmented reports are pre-processed (faster, requires cron)", "AutoArchiveRealTime": "segmented reports are processed in real time", "ChooseASegment": "Choose a segment", + "CurrentlySelectedSegment": "Currently selected segment: %s", "DataAvailableAtLaterDate": "Your segmented analytics reports will be available later. We apologize for the inconvenience.", "DefaultAllVisits": "All visits", "DragDropCondition": "Drag & Drop condition", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "Alternatively you may change the setting in the config file (%s), or edit this Segment and choose '%s'.", "YouMustBeLoggedInToCreateSegments": "You must be logged in to create and edit custom visitor segments.", "YouDontHaveAccessToCreateSegments": "You don't have the required access level to create and edit segments.", - "AddingSegmentForAllWebsitesDisabled": "Adding segments for all websites has been disabled." + "AddingSegmentForAllWebsitesDisabled": "Adding segments for all websites has been disabled.", + "SegmentXIsAUnionOf": "%s is a union of these segments:", + "CustomSegment": "Custom Segment", + "SegmentOperatorIsNullOrEmpty": "is null or empty", + "SegmentOperatorIsNotNullNorEmpty": "is not null nor empty" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/it.json b/plugins/SegmentEditor/lang/it.json index 63b0ec0ce2..28b4b9f3ed 100644 --- a/plugins/SegmentEditor/lang/it.json +++ b/plugins/SegmentEditor/lang/it.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "i reports segmentati sono pre-elaborati (più veloce, richiede un cron-job)", "AutoArchiveRealTime": "i reports segmentati sono elaborati in tempo reale", "ChooseASegment": "Scegli un segmento", + "CurrentlySelectedSegment": "Segmento attualmente selezionato: %s", "DataAvailableAtLaterDate": "I report segmentati delle statistiche saranno disponibili più tardi. Ci scusiamo per l'inconveniente.", "DefaultAllVisits": "Tutte le visite", "DragDropCondition": "Copia & Incolla condizione", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "In alternativa, puoi cambiare le impostazioni nel file di configurazione (%s) o modificare questo Segmento e scegliere '%s'.", "YouMustBeLoggedInToCreateSegments": "Devi avere effettuato l'accesso per creare e modificare i segmenti personalizzati dei visitatori.", "YouDontHaveAccessToCreateSegments": "Non hai un livello d'accesso adeguato per creare e modificare i segmenti.", - "AddingSegmentForAllWebsitesDisabled": "L'aggiunta di segmenti per tutti i siti è stata disabilitata." + "AddingSegmentForAllWebsitesDisabled": "L'aggiunta di segmenti per tutti i siti è stata disabilitata.", + "SegmentXIsAUnionOf": "%s è un'unione di questi segmenti:", + "CustomSegment": "Segmento Speciale", + "SegmentOperatorIsNullOrEmpty": "è nullo o vuoto", + "SegmentOperatorIsNotNullNorEmpty": "non è nullo nè vuoto" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/ja.json b/plugins/SegmentEditor/lang/ja.json index 8c65a9c7cf..0156de56ab 100644 --- a/plugins/SegmentEditor/lang/ja.json +++ b/plugins/SegmentEditor/lang/ja.json @@ -1,5 +1,6 @@ { "SegmentEditor": { + "PluginDescription": "セグメントエディターでカスタム ユーザー セグメントを作成、編集できます。", "AddANDorORCondition": "%s の条件を追加", "AddNewSegment": "新しいセグメントを追加", "AreYouSureDeleteSegment": "このセグメントを削除してもよろしいですか?", @@ -25,6 +26,7 @@ "YouMayChangeSetting": "別の方法としては設定ファイル (%s) で設定を変更するか、このセグメント '%s' を選択し編集することができます。", "YouMustBeLoggedInToCreateSegments": "ビジターのカスタムセグメントの作成と編集にはログインが必要です。", "YouDontHaveAccessToCreateSegments": "セグメントの作成および編集に必要なレベルのアクセス権限を持っていません。", - "AddingSegmentForAllWebsitesDisabled": "全ウェブサイトに対するセグメントは追加できませんでした。" + "AddingSegmentForAllWebsitesDisabled": "全ウェブサイトに対するセグメントは追加できませんでした。", + "CustomSegment": "カスタムセグメント" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/pt-br.json b/plugins/SegmentEditor/lang/pt-br.json index a5f8df59f2..eb7a8919d2 100644 --- a/plugins/SegmentEditor/lang/pt-br.json +++ b/plugins/SegmentEditor/lang/pt-br.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "Relatórios segmentados são pré-processados (Para agilizar, é necessário usar o cron em: archive.php)", "AutoArchiveRealTime": "Relatórios segmentados são processados em tempo real", "ChooseASegment": "Escolha um segmento", + "CurrentlySelectedSegment": "Segmento atualmente selecionado: %s", "DataAvailableAtLaterDate": "Os seus relatórios de análise segmentados estarão disponíveis mais tarde. Pedimos desculpas pela inconveniência.", "DefaultAllVisits": "Todas as visitas", "DragDropCondition": "Condição Drag & Drop", @@ -23,8 +24,13 @@ "ThisSegmentIsVisibleTo": "Este segmento é visível para:", "VisibleToAllUsers": "Todos os Usuários", "VisibleToMe": "mim", + "YouMayChangeSetting": "Alternativamente, você pode alterar a configuração no arquivo de configuração (%s), ou editar este Segmento e escolher %s.", "YouMustBeLoggedInToCreateSegments": "Você precisa estar logado para criar e editar segmentos personalizados de visitantes.", "YouDontHaveAccessToCreateSegments": "Você não tem o nível de acesso necessário para criar e editar segmentos.", - "AddingSegmentForAllWebsitesDisabled": "Adicionar segmentos para todos os sites foi desativado." + "AddingSegmentForAllWebsitesDisabled": "Adicionar segmentos para todos os sites foi desativado.", + "SegmentXIsAUnionOf": "%s é uma união destes segmentos:", + "CustomSegment": "Segmento Personalizado", + "SegmentOperatorIsNullOrEmpty": "está nulo ou vazio", + "SegmentOperatorIsNotNullNorEmpty": "não está nulo nem vazio" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/tr.json b/plugins/SegmentEditor/lang/tr.json new file mode 100644 index 0000000000..3541941a61 --- /dev/null +++ b/plugins/SegmentEditor/lang/tr.json @@ -0,0 +1,8 @@ +{ + "SegmentEditor": { + "OperatorAND": "VE", + "OperatorOR": "VEYA", + "SaveAndApply": "Kaydet & Uygula", + "VisibleToAllUsers": "tüm kullanıcılar" + } +}
\ No newline at end of file diff --git a/plugins/SegmentEditor/templates/_segmentSelector.twig b/plugins/SegmentEditor/templates/_segmentSelector.twig index 7a8019d37e..dc269dc8b9 100644 --- a/plugins/SegmentEditor/templates/_segmentSelector.twig +++ b/plugins/SegmentEditor/templates/_segmentSelector.twig @@ -1,5 +1,5 @@ <div class="SegmentEditor" style="display:none;"> - <div class="segmentationContainer listHtml" title="{{ 'SegmentEditor_ChooseASegment'|translate|e('html_attr') }}"> + <div class="segmentationContainer listHtml" title="{{ 'SegmentEditor_ChooseASegment'|translate|e('html_attr') }}. {{ 'SegmentEditor_CurrentlySelectedSegment'|translate(segmentDescription)|e('html_attr') }}"> <a class="title"><span class="icon icon-segment"></span><span class="segmentationTitle"></span></a> <div class="dropdown dropdown-body"> <div class="segmentFilterContainer"> @@ -61,6 +61,8 @@ <option value=">">{{ 'General_OperationGreaterThan'|translate }}</option> <option value="=@">{{ 'General_OperationContains'|translate }}</option> <option value="!@">{{ 'General_OperationDoesNotContain'|translate }}</option> + <option value="=^">{{ 'General_OperationStartsWith'|translate }}</option> + <option value="=$">{{ 'General_OperationEndsWith'|translate }}</option> </select> </div> <div class="segment-input metricValueBlock"> @@ -99,7 +101,17 @@ <a class="metric_category" href="#">{{ category }}</a> <ul style="display:none;"> {% for segmentInCategory in segmentsInCategory %} - <li data-metric="{{ segmentInCategory.segment }}"><a class="ddmetric" href="#">{{ segmentInCategory.name }}</a></li> + {% set title = segmentInCategory.name %} + {% if segmentInCategory.unionOfSegments is defined and segmentInCategory.unionOfSegments %} + {% set title = 'SegmentEditor_SegmentXIsAUnionOf'|translate(title) %} + {% for unionSegment in segmentInCategory.unionOfSegments %} + {% set title = title ~ ' ' ~ unionSegment %} + {% if not loop.last %} + {% set title = title ~ ',' %} + {% endif %} + {% endfor %} + {% endif %} + <li data-metric="{{ segmentInCategory.segment }}" title="{{ title|e('html_attr') }}"><a class="ddmetric" href="#">{{ segmentInCategory.name }}</a></li> {% endfor %} </ul> </li> diff --git a/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php b/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php new file mode 100644 index 0000000000..ca37e0775b --- /dev/null +++ b/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\SegmentEditor\tests\Integration; + +use Piwik\Plugins\SegmentEditor\SegmentFormatter; +use Piwik\Plugins\SegmentEditor\SegmentList; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Translate; +use Exception; + +/** + * @group SegmentFormatterTest + * @group SegmentFormatter + * @group SegmentEditor + * @group Plugins + */ +class SegmentFormatterTest extends IntegrationTestCase +{ + /** + * @var SegmentFormatter + */ + private $formatter; + + private $idSite; + + public function setUp() + { + parent::setUp(); + + $this->idSite = Fixture::createWebsite('2012-01-01 00:00:00'); + $this->formatter = new SegmentFormatter(new SegmentList()); + + Translate::loadAllTranslations(); + } + + public function tearDown() + { + Translate::reset(); + } + + public function test_getHumanReadable_noSegmentGiven_ShouldReturnDefaultSegment() + { + $readable = $this->formatter->getHumanReadable($segment = '', $this->idSite); + $this->assertSame('All visits', $readable); + } + + public function test_getHumanReadable_ShouldTranslateAMetric() + { + $readable = $this->formatter->getHumanReadable($segment = 'visitCount>5', $this->idSite); + $this->assertSame('Number of visits greater than "5"', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'visitCount==5', $this->idSite); + $this->assertSame('Number of visits equals "5"', $readable); + } + + public function test_getHumanReadable_ShouldTranslateADimension() + { + $readable = $this->formatter->getHumanReadable($segment = 'resolution=@1024', $this->idSite); + $this->assertSame('Resolution contains "1024"', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'resolution==1024x768', $this->idSite); + $this->assertSame('Resolution is "1024x768"', $readable); + } + + public function test_getHumanReadable_ShouldCombineMultipleSegmentDefinitionsWithBooleanOperator() + { + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=1.0;browserEngine=$Trident', $this->idSite); + $this->assertSame('Browser version is not "1.0" and Browser engine ends with "Trident"', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=1.0,browserEngine=$Trident', $this->idSite); + $this->assertSame('Browser version is not "1.0" or Browser engine ends with "Trident"', $readable); + } + + public function test_getHumanReadable_ShouldHandleAMissingValue() + { + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion==', $this->idSite); + $this->assertSame('Browser version is null or empty', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=', $this->idSite); + $this->assertSame('Browser version is not null nor empty', $readable); + } + + public function test_getHumanReadable_ShouldHandleAUrlDecodedSegment() + { + $readable = $this->formatter->getHumanReadable($segment = 'pageUrl%3D%40piwik%2CvisitId!%3D1', $this->idSite); + $this->assertSame('Page URL contains "piwik" or Visit ID is not "1"', $readable); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The segment 'noTexisTinG' does not exist + */ + public function test_getHumanReadable_ShouldThrowAnException_IfTheGivenSegmentNameDoesNotExist() + { + $this->formatter->getHumanReadable($segment = 'noTexisTinG==1.0', $this->idSite); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The segment 'pageUrl=!1.0' is not valid. + */ + public function test_getHumanReadable_ShouldThrowAnException_IfSegmentCannotBeParsedBecauseOfInvalidFormat() + { + $invalidOperator = '=!'; + $this->formatter->getHumanReadable($segment = 'pageUrl' . $invalidOperator . '1.0', $this->idSite); + } + +} diff --git a/plugins/SegmentEditor/tests/Integration/SegmentListTest.php b/plugins/SegmentEditor/tests/Integration/SegmentListTest.php new file mode 100644 index 0000000000..3a90bb95ee --- /dev/null +++ b/plugins/SegmentEditor/tests/Integration/SegmentListTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\SegmentEditor\tests\Integration; + +use Piwik\Plugins\SegmentEditor\SegmentList; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Exception; + +/** + * @group SegmentListTest + * @group SegmentList + * @group SegmentEditor + * @group Plugins + */ +class SegmentListTest extends IntegrationTestCase +{ + /** + * @var SegmentList + */ + private $list; + + private $idSite; + + public function setUp() + { + parent::setUp(); + + $this->idSite = Fixture::createWebsite('2012-01-01 00:00:00'); + $this->list = new SegmentList(); + } + + public function test_findSegment_shouldFindSegmentByName_IfNameExists() + { + $segmentName = 'pageUrl'; + + $segment = $this->list->findSegment($segmentName, $this->idSite); + $this->assertInternalType('array', $segment); + $this->assertSame($segmentName, $segment['segment']); + } + + public function test_findSegment_shouldNotFindSegmentByName_IfNameDoesNotExist() + { + $segment = $this->list->findSegment('aNyNotExisTinGSegmEnt', $this->idSite); + $this->assertNull($segment); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage checkUserHasViewAccess + */ + public function test_findSegment_ShouldThrowException_IfNotEnoughPermission() + { + FakeAccess::clearAccess($superUser = false, array(1)); + + $segment = $this->list->findSegment('pageUrl', 999); + $this->assertNull($segment); + } + + public function provideContainerConfig() + { + return array( + 'Piwik\Access' => new FakeAccess() + ); + } + +} diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 4a67c45c2c..739af7d5e9 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -585,7 +585,7 @@ class API extends \Piwik\Plugin\API } if (!empty($settings)) { - $this->validateMeasurableSettings($bind['type'], $settings); + $this->validateMeasurableSettings(0, $bind['type'], $settings); } $idSite = $this->getModel()->createSite($bind); @@ -611,9 +611,9 @@ class API extends \Piwik\Plugin\API return (int) $idSite; } - private function validateMeasurableSettings($idType, $settings) + private function validateMeasurableSettings($idSite, $idType, $settings) { - $measurableSettings = new MeasurableSettings(0, $idType); + $measurableSettings = new MeasurableSettings($idSite, $idType); foreach ($measurableSettings->getSettingsForCurrentUser() as $measurableSetting) { $name = $measurableSetting->getName(); @@ -645,6 +645,7 @@ class API extends \Piwik\Plugin\API { Site::clearCache(); Cache::regenerateCacheWebsiteAttributes($idSite); + Cache::clearCacheGeneral(); SiteUrls::clearSitesCache(); } @@ -1184,7 +1185,7 @@ class API extends \Piwik\Plugin\API } if (!empty($settings)) { - $this->validateMeasurableSettings(Site::getTypeFor($idSite), $settings); + $this->validateMeasurableSettings($idSite, Site::getTypeFor($idSite), $settings); } $this->getModel()->updateSite($bind, $idSite); diff --git a/plugins/SitesManager/Model.php b/plugins/SitesManager/Model.php index 93ddb01d8f..96062b608a 100644 --- a/plugins/SitesManager/Model.php +++ b/plugins/SitesManager/Model.php @@ -286,6 +286,22 @@ class Model return $urls; } + /** + * Returns the list of alias URLs registered for the given idSite. + * The website ID must be valid when calling this method! + * + * @param int $idSite + * @return array list of alias URLs + */ + public function getAllKnownUrlsForAllSites() + { + $db = $this->getDb(); + $mainUrls = $db->fetchAll("SELECT idsite, main_url as url FROM " . Common::prefixTable("site")); + $aliasUrls = $db->fetchAll("SELECT idsite, url FROM " . Common::prefixTable("site_url")); + + return array_merge($mainUrls, $aliasUrls); + } + public function updateSite($site, $idSite) { $idSite = (int) $idSite; diff --git a/plugins/SitesManager/SiteUrls.php b/plugins/SitesManager/SiteUrls.php index a1fac67a89..33e2e4844a 100644 --- a/plugins/SitesManager/SiteUrls.php +++ b/plugins/SitesManager/SiteUrls.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\SitesManager; use Piwik\Cache; +use Piwik\Common; class SiteUrls { @@ -19,6 +20,117 @@ class SiteUrls self::getCache()->delete(self::$cacheId); } + /** + * Groups all URLs by host, path and idsite. + * + * @param array $urls An array containing URLs by idsite, + * eg array(array($idSite = 1 => array('apache.piwik', 'apache2.piwik'), 2 => array(), ...)) + * as returned by {@link getAllCachedSiteUrls()} and {@link getAllSiteUrls} + * @return array All urls grouped by host => path => idSites. Path having the most '/' will be listed first + * array( + 'apache.piwik' => array( + '/test/two' => $idsite = array(3), + '/test' => $idsite = array(1), + '/' => $idsite = array(2), + ), + 'test.apache.piwik' => array( + '/test/two' => $idsite = array(3), + '/test' => $idsite = array(1), + '/' => $idsite = array(2, 3), + ), + ); + */ + public function groupUrlsByHost($siteUrls) + { + if (empty($siteUrls)) { + return array(); + } + + $allUrls = array(); + + foreach ($siteUrls as $idSite => $urls) { + $idSite = (int) $idSite; + foreach ($urls as $url) { + $urlParsed = @parse_url($url); + + if ($urlParsed === false || !isset($urlParsed['host'])) { + continue; + } + + $host = $this->toCanonicalHost($urlParsed['host']); + $path = $this->getCanonicalPathFromParsedUrl($urlParsed); + + if (!isset($allUrls[$host])) { + $allUrls[$host] = array(); + } + + if (!isset($allUrls[$host][$path])) { + $allUrls[$host][$path] = array(); + } + + if (!in_array($idSite, $allUrls[$host][$path])) { + $allUrls[$host][$path][] = $idSite; + } + } + } + + foreach ($allUrls as $host => $paths) { + uksort($paths, array($this, 'sortByPathDepth')); + $allUrls[$host] = $paths; + } + + return $allUrls; + } + + public function getIdSitesMatchingUrl($parsedUrl, $urlsGroupedByHost) + { + if (empty($parsedUrl['host'])) { + return null; + } + + $urlHost = $this->toCanonicalHost($parsedUrl['host']); + $urlPath = $this->getCanonicalPathFromParsedUrl($parsedUrl); + + $matchingSites = null; + if (isset($urlsGroupedByHost[$urlHost])) { + $paths = $urlsGroupedByHost[$urlHost]; + + foreach ($paths as $path => $idSites) { + if (0 === strpos($urlPath, $path)) { + $matchingSites = $idSites; + break; + } + } + + if (!isset($matchingSites) && isset($paths['/'])) { + $matchingSites = $paths['/']; + } + } + + return $matchingSites; + } + + public function getPathMatchingUrl($parsedUrl, $urlsGroupedByHost) + { + if (empty($parsedUrl['host'])) { + return null; + } + + $urlHost = $this->toCanonicalHost($parsedUrl['host']); + $urlPath = $this->getCanonicalPathFromParsedUrl($parsedUrl); + + $matchingSites = null; + if (isset($urlsGroupedByHost[$urlHost])) { + $paths = $urlsGroupedByHost[$urlHost]; + + foreach ($paths as $path => $idSites) { + if (0 === strpos($urlPath, $path)) { + return $path; + } + } + } + } + public function getAllCachedSiteUrls() { $cache = $this->getCache(); @@ -35,23 +147,66 @@ class SiteUrls public function getAllSiteUrls() { $model = new Model(); - $siteIds = $model->getSitesId(); - $siteUrls = array(); + $siteUrls = $model->getAllKnownUrlsForAllSites(); - if (empty($siteIds)) { + if (empty($siteUrls)) { return array(); } - foreach ($siteIds as $siteId) { - $siteId = (int) $siteId; - $siteUrls[$siteId] = $model->getSiteUrlsFromId($siteId); + $urls = array(); + foreach ($siteUrls as $siteUrl) { + $siteId = (int) $siteUrl['idsite']; + + if (!isset($urls[$siteId])) { + $urls[$siteId] = array(); + } + + $urls[$siteId][] = $siteUrl['url']; } - return $siteUrls; + return $urls; } private static function getCache() { return Cache::getLazyCache(); } + + private function sortByPathDepth($pathA, $pathB) + { + // list first the paths with most '/' , and list path = '/' last + $numSlashA = substr_count($pathA, '/'); + $numSlashB = substr_count($pathB, '/'); + + if ($numSlashA === $numSlashB) { + return -1 * strcmp($pathA, $pathB); + } + + return $numSlashA > $numSlashB ? -1 : 1; + } + + private function toCanonicalHost($host) + { + $host = Common::mb_strtolower($host); + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } + + private function getCanonicalPathFromParsedUrl($urlParsed) + { + $path = '/'; + + if (isset($urlParsed['path'])) { + $path = Common::mb_strtolower($urlParsed['path']); + if (!Common::stringEndsWith($path, '/')) { + $path .= '/'; + } + } + + return $path; + } + } diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php index 4b686e2bde..ca3f2757b9 100644 --- a/plugins/SitesManager/SitesManager.php +++ b/plugins/SitesManager/SitesManager.php @@ -114,8 +114,11 @@ class SitesManager extends \Piwik\Plugin { $idSite = (int) $idSite; + $urls = API::getInstance()->getSiteUrlsFromId($idSite); + // add the 'hosts' entry in the website array - $array['hosts'] = $this->getTrackerHosts($idSite); + $array['urls'] = $urls; + $array['hosts'] = $this->getTrackerHosts($urls); $website = API::getInstance()->getSiteFromId($idSite); $array['exclude_unknown_urls'] = $website['exclude_unknown_urls']; @@ -252,9 +255,8 @@ class SitesManager extends \Piwik\Plugin * @param int $idSite * @return array */ - private function getTrackerHosts($idSite) + private function getTrackerHosts($urls) { - $urls = API::getInstance()->getSiteUrlsFromId($idSite); $hosts = array(); foreach ($urls as $url) { $url = parse_url($url); diff --git a/plugins/SitesManager/config/config.php b/plugins/SitesManager/config/config.php deleted file mode 100644 index 0855c39329..0000000000 --- a/plugins/SitesManager/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\SitesManager\Tracker\SitesManagerRequestProcessor'), - )), - -); diff --git a/plugins/SitesManager/lang/cs.json b/plugins/SitesManager/lang/cs.json index 014c9f88f2..6854eadf75 100644 --- a/plugins/SitesManager/lang/cs.json +++ b/plugins/SitesManager/lang/cs.json @@ -68,13 +68,13 @@ "SiteWithoutDataTitle": "Zatím nebyla zaznamenána žádná data", "SiteWithoutDataDescription": "Pro tuto stránku nebyla zatím zaznamenána žádná analytická data.", "SiteWithoutDataSetupTracking": "Prosím, nastavte %1$ssledovací javascriptový kód %2$s na vašich webových stránkách, a pak stránku obnovte.", - "SuperUserAccessCan": "eUživatel se super uživatelským přístupem může také %sspecifikovat globální nastavení%s pro nové webové stránky.", + "SuperUserAccessCan": "Uživatel se super uživatelským přístupem může také %sspecifikovat globální nastavení%s pro nové webové stránky.", "Timezone": "Časová zóna", "TrackingSiteSearch": "Sledování interního vyhledávání na stránkách", "TrackingTags": "Zaznamenávací tagy pro %s", "Urls": "URL", "UTCTimeIs": "UTC čas je %s", - "OnlyMatchedUrlsAllowed": "Sledovat návštěvy a akce pouze tehdy, když URL akce začíná jednou z výše uvedených URL.", + "OnlyMatchedUrlsAllowed": "Sledovat návštěvy a akce pouze tehdy, pokud URL akce začíná jednou z výše uvedených URL.", "OnlyMatchedUrlsAllowedHelp": "Pokud je povoleno, Piwik bude sledovat interní akce pouze tehdy, když je URL stránky jednou ze známých URL vašich webových stránek. To zabrání lidem, aby zahltili analýzu URL jiných stránek.", "WebsitesManagement": "Nastavení Web sídel", "XManagement": "Spravovat %s", diff --git a/plugins/SitesManager/lang/fr.json b/plugins/SitesManager/lang/fr.json index 283f77c0f3..2372187f6c 100644 --- a/plugins/SitesManager/lang/fr.json +++ b/plugins/SitesManager/lang/fr.json @@ -74,8 +74,8 @@ "TrackingTags": "Code de suivi pour %s", "Urls": "URLs", "UTCTimeIs": "L'heure UTC est %s.", - "OnlyMatchedUrlsAllowed": "Effectue le suivit des visites et actions uniquement quand l'URL d'action commence avec une des URL's ci-dessus.", - "OnlyMatchedUrlsAllowedHelp": "Quand activé, Piwik va effectuer le suivit des actions uniquement quand l'URL de la page est une des URL's de votre site web. Ceci empêche les gens de spammer vos données d'analyse avec des URL's d'autres sites web.", + "OnlyMatchedUrlsAllowed": "Effectue le suivi des visites et actions uniquement quand l'URL d'action commence avec une des URLs ci-dessus.", + "OnlyMatchedUrlsAllowedHelp": "Quand activé, Piwik va effectuer le suivi des actions uniquement quand l'URL de la page est une des URLs de votre site web. Ceci empêche les gens de spammer vos données d'analyse avec des URLs d'autres sites web.", "WebsitesManagement": "Gestion des sites", "XManagement": "Gérer %s", "ChooseMeasurableTypeHeadline": "Que voudriez-vous mesurer ?", diff --git a/plugins/SitesManager/lang/ja.json b/plugins/SitesManager/lang/ja.json index 56cc2b4697..b1d3a73a83 100644 --- a/plugins/SitesManager/lang/ja.json +++ b/plugins/SitesManager/lang/ja.json @@ -1,6 +1,7 @@ { "SitesManager": { "AddSite": "新しいサイトの追加", + "AddMeasurable": "新規の測定対象を追加", "AdvancedTimezoneSupportNotFound": "このサーバーの PHP では拡張タイムゾーンがサポートされていません(PHP>=5.2 でサポート)。 手作業で UTC オフセットを指定します。", "AliasUrlHelp": "これは推奨されますが、必須ではありません。 ビジターがこのウェブサイトのアクセスに使用するさまざまな URL を指定するには、1行に1つの URL を入力します。 ウェブサイトの URL エイリアスは、[参照元]>[ウェブサイト]のリポートには表示されません。 URL の 'www' の有無は、Piwik が両方とも考慮するため、指定する必要がないことに注意してください。", "ChangingYourTimezoneWillOnlyAffectDataForward": "タイムゾーンの変更は、今後のデータにのみ反映され、過去のデータには適用されません。", @@ -49,6 +50,7 @@ "OnlyOneSiteAtTime": "一度にウェブサイトは一つだけ編集できます。ウェブサイト %s への変更を保存するか、キャンセルしてください。", "PiwikOffersEcommerceAnalytics": "Piwikは、高度なeコマース分析、トラッキング&リポーティング、が可能になります。%s eコマース分析 %s について詳しく知る。", "PiwikWillAutomaticallyExcludeCommonSessionParameters": "一般的なセッションパラメータ(%s)は、Piwik が自動的に除外します。", + "PluginDescription": "Web サイト管理では、新しい web サイトを追加し、既存の web サイトを編集することができます。", "SearchCategoryDesc": "Piwik は、各内部サイト内検索キーワードに対する検索カテゴリを追跡することもできます。", "SearchCategoryLabel": "カテゴリーパラメーター", "SearchCategoryParametersDesc": "検索カテゴリを指定するクエリパラメーターリストを、コンマ区切りで入力できます。", @@ -72,7 +74,11 @@ "TrackingTags": "%s 用トラッキングタグ", "Urls": "URL", "UTCTimeIs": "UTC 時間は %s です。", + "OnlyMatchedUrlsAllowed": "アクション URL は、上記のいずれかの URLで始まる場合にのみビジットやアクションを追跡します。", + "OnlyMatchedUrlsAllowedHelp": "有効にすると、Piwik はページ URL があなたのウェブサイトの URL として知られたものである時に内部のアクションを追跡します。これは他のウェブサイトの URL を使用して分析をスパムすることから人々を防ぎます。", "WebsitesManagement": "ウェブサイトの管理", + "XManagement": "管理 %s", + "ChooseMeasurableTypeHeadline": "何を測定しますか?", "YouCurrentlyHaveAccessToNWebsites": "現在 %s つのウェブサイトに接続しています。", "YourCurrentIpAddressIs": "あなたの現在の IP アドレスは %s です。" } diff --git a/plugins/SitesManager/tests/Integration/ModelTest.php b/plugins/SitesManager/tests/Integration/ModelTest.php index 45ebce2be8..7291ac346f 100644 --- a/plugins/SitesManager/tests/Integration/ModelTest.php +++ b/plugins/SitesManager/tests/Integration/ModelTest.php @@ -56,10 +56,56 @@ class ModelTest extends IntegrationTestCase $this->assertSame(array('website', 'universal', 'mobileapp'), $this->model->getUsedTypeIds()); } - private function createMeasurable($type) + public function test_getAllKnownUrlsForAllSites_shouldReturnAllUrls() { - Fixture::createWebsite('2015-01-01 00:00:00', - $ecommerce = 0, $siteName = false, $siteUrl = false, + $idSite = $this->createMeasurable('website', 'http://apache.piwik'); + $this->model->insertSiteUrl($idSite, 'http://example.apache.piwik'); + $this->model->insertSiteUrl($idSite, 'http://example.org'); + + $idSite2 = $this->createMeasurable('website'); + $this->model->insertSiteUrl($idSite2, 'http://example.org'); + $this->model->insertSiteUrl($idSite2, 'http://example.com'); + + $idSite3 = $this->createMeasurable('website', 'http://example.pro'); + + $expected = array( + array( + 'idsite' => $idSite, + 'url' => 'http://apache.piwik' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://piwik.net' + ), + array( + 'idsite' => $idSite3, + 'url' => 'http://example.pro' + ), + array( + 'idsite' => $idSite, + 'url' => 'http://example.apache.piwik' + ), + array( + 'idsite' => $idSite, + 'url' => 'http://example.org' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://example.com' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://example.org' + ) + + ); + $this->assertEquals($expected, $this->model->getAllKnownUrlsForAllSites()); + } + + private function createMeasurable($type, $siteUrl = false) + { + return Fixture::createWebsite('2015-01-01 00:00:00', + $ecommerce = 0, $siteName = false, $siteUrl, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $timezone = null, $type); } diff --git a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php index f9362ae2f4..3db34199fb 100644 --- a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php +++ b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php @@ -119,6 +119,176 @@ class SiteUrlsTest extends IntegrationTestCase $this->assertEquals($urlsToFake, $actual); } + public function test_groupUrlsByHost_shouldReturnEmptyArray_WhenNoUrlsGiven() + { + $this->assertSame(array(), $this->siteUrls->groupUrlsByHost(array())); + $this->assertSame(array(), $this->siteUrls->groupUrlsByHost(null)); + } + + public function test_groupUrlsByHost_shouldGroupByHost_WithOneSiteAndDifferentDomains_shouldRemoveWwwAndDefaultToPathSlash() + { + $idSite = 1; + $oneSite = array( + $idSite => array( + 'http://apache.piwik', + 'http://www.example.com', // should remove www. + 'https://example.org', // should handle https or other protocol + 'http://apache.piwik/', // same as initial one but with slash at the end, should not add idsite twice + 'http://third.www.com' // should not remove www. in the middle of a domain + ) + ); + + $expected = array( + 'apache.piwik' => array('/' => array($idSite)), + 'example.com' => array('/' => array($idSite)), + 'example.org' => array('/' => array($idSite)), + 'third.www.com' => array('/' => array($idSite)), + ); + + $this->assertSame($expected, $this->siteUrls->groupUrlsByHost($oneSite)); + } + + public function test_groupUrlsByHost_shouldGroupByHost_WithDifferentDomainsAndPathsShouldListPathByNumberOfDirectoriesAndConvertToLowerCase() + { + $idSite = 1; + $idSite2 = 2; + $idSite3 = 3; + $idSite4 = 4; + $idSite5 = 5; + + $urls = array( + $idSite => array( + 'http://apache.piwik/test', 'http://apache.piWik', 'http://apache.piwik/foo/bAr/', 'http://apache.piwik/Foo/SECOND' + ), + $idSite2 => array( + 'http://apache.piwik/test/', 'http://example.oRg', 'http://apache.piwik/foo/secOnd' + ), + $idSite3 => array( + 'http://apache.piwik/', 'http://apache.piwik/third', 'http://exampLe.com', 'http://example.org/foo/test/two' + ), + $idSite4 => array(), + $idSite5 => array('invalidUrl', 'ftp://example.org/'), + ); + + $expected = array( + 'apache.piwik' => array( + '/foo/second/' => array($idSite, $idSite2), + '/foo/bar/' => array($idSite), + '/third/' => array($idSite3), + '/test/' => array($idSite, $idSite2), + '/' => array($idSite, $idSite3) + ), + 'example.org' => array( + '/foo/test/two/' => array($idSite3), + '/' => array($idSite2, $idSite5) + ), + 'example.com' => array( + '/' => array($idSite3) + ), + ); + + $this->assertSame($expected, $this->siteUrls->groupUrlsByHost($urls)); + } + + /** + * @dataProvider getTestIdSitesMatchingUrl + */ + public function test_getIdSitesMatchingUrl($expectedMatchSites, $parsedUrl) + { + $urlsGroupedByHost = array( + 'apache.piwik' => array( + '/foo/second/' => array(2), + '/foo/sec/' => array(4), + '/foo/bar/' => array(1), + '/third/' => array(3), + '/test/' => array(1, 2), + '/' => array(1, 3) + ), + 'example.org' => array( + '/foo/test/two/' => array(3), + '/foo/second/' => array(6), + '/' => array(2, 5) + ), + 'example.com' => array( + '/' => array(3) + ), + ); + $matchedSites = $this->siteUrls->getIdSitesMatchingUrl($parsedUrl, $urlsGroupedByHost); + + $this->assertSame($expectedMatchSites, $matchedSites); + } + + public function getTestIdSitesMatchingUrl() + { + return array( + array(array(1,3), array('host' => 'apache.piwik')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => 'nomatch')), // no other URL matches a site so we fall back to domain match + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/nomatch')), + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second')), + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second/')), // it shouldn't matter if slash is at end or not + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second/test')), // it should find best match + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec/test')), // it should not use /foo/second for these + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec/')), + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/foo')), + array(array(2,5), array('host' => 'example.org')), + array(array(2,5), array('host' => 'example.org', 'path' => '/')), + array(array(2,5), array('host' => 'example.org', 'path' => 'any/nonmatching/path')), + array(array(6), array('host' => 'example.org', 'path' => '/foo/second')), + array(array(6), array('host' => 'example.org', 'path' => '/foo/second/test')), + array(array(3), array('host' => 'example.com')), + array(null, array('host' => 'example.pro')), + array(null, array('host' => 'example.pro', 'path' => '/any')), + ); + } + + /** + * @dataProvider getTestPathMatchingUrl + */ + public function test_getPathMatchingUrl($expectedMatchSites, $parsedUrl) + { + $urlsGroupedByHost = array( + 'apache.piwik' => array( + '/foo/second/' => array(2), + '/foo/sec/' => array(4), + '/foo/bar/' => array(1), + '/third/' => array(3), + '/test/' => array(1, 2), + '/' => array(1, 3) + ), + 'example.org' => array( + '/foo/test/two/' => array(3), + '/foo/second/' => array(6), + '/' => array(2, 5) + ), + ); + $matchedSites = $this->siteUrls->getPathMatchingUrl($parsedUrl, $urlsGroupedByHost); + + $this->assertSame($expectedMatchSites, $matchedSites); + } + + public function getTestPathMatchingUrl() + { + return array( + array('/', array('host' => 'apache.piwik')), + array('/', array('host' => 'apache.piwik', 'path' => '/')), + array('/', array('host' => 'apache.piwik', 'path' => '')), + array(null, array('host' => 'test.piwik')), + array(null, array('host' => 'test.apache.piwik')), // we do not match subdomains, only exact domain match + + array('/foo/bar/', array('host' => 'apache.piwik', 'path' => '/foo/bar')), + array('/foo/bar/', array('host' => 'apache.piwik', 'path' => '/foo/bar/')), + array('/foo/bar/', array('host' => 'apache.piwik', 'path' => '/foo/bar/baz/')), + array('/', array('host' => 'apache.piwik', 'path' => '/foo/baz/bar/')), + array('/third/', array('host' => 'apache.piwik', 'path' => '/third/bar/baz/')), + + array('/foo/second/', array('host' => 'example.org', 'path' => '/foo/second/')), + array('/', array('host' => 'example.org', 'path' => '/foo/secon')), + array(null, array('host' => 'example.pro', 'path' => '/foo/second/')), + ); + } + private function assertSiteUrls($expectedUrls) { $urls = $this->siteUrls->getAllSiteUrls(); diff --git a/plugins/TasksTimetable b/plugins/TasksTimetable -Subproject 32915362e3f90f8e98929108ccc13695a820c2f +Subproject 67c1119ff385f153439e81555d7d3e4a799ebe5 diff --git a/plugins/TestRunner/Commands/GenerateTravisYmlFile.php b/plugins/TestRunner/Commands/GenerateTravisYmlFile.php index 5e1ac7d181..a99c3ae4f0 100644 --- a/plugins/TestRunner/Commands/GenerateTravisYmlFile.php +++ b/plugins/TestRunner/Commands/GenerateTravisYmlFile.php @@ -39,7 +39,10 @@ class GenerateTravisYmlFile extends ConsoleCommand ->addOption('php-versions', null, InputOption::VALUE_OPTIONAL, "List of PHP versions to test against, ie, 5.4,5.5,5.6. Defaults to: 5.3.3,5.4,5.5,5.6.") ->addOption('dump', null, InputOption::VALUE_REQUIRED, "Debugging option. Saves the output .travis.yml to the specified file.") - ->addOption('repo-root-dir', null, InputOption::VALUE_REQUIRED, "Path to the repo for whom a .travis.yml file will be generated for."); + ->addOption('repo-root-dir', null, InputOption::VALUE_REQUIRED, "Path to the repo for whom a .travis.yml file will be generated for.") + ->addOption('force-php-tests', null, InputOption::VALUE_NONE, "Forces the presence of the PHP tests jobs for plugin builds.") + ->addOption('force-ui-tests', null, InputOption::VALUE_NONE, "Forces the presence of the UI tests jobs for plugin builds.") + ->addOption('sudo-false', null, InputOption::VALUE_NONE, "If supplied, the .travis.yml file will use travis' new infrastructure."); } protected function execute(InputInterface $input, OutputInterface $output) diff --git a/plugins/TestRunner/Commands/TestsRun.php b/plugins/TestRunner/Commands/TestsRun.php index 08e456da19..8a12c07e3b 100644 --- a/plugins/TestRunner/Commands/TestsRun.php +++ b/plugins/TestRunner/Commands/TestsRun.php @@ -47,10 +47,6 @@ class TestsRun extends ConsoleCommand // bin is the composer executeable directory, where all vendors (should) place their executables $command = PIWIK_VENDOR_PATH . '/bin/phpunit'; - if (version_compare(PHP_VERSION, '5.4.0', '<')) { - $command = 'php -dzend.enable_gc=0 ' . $command; - } - if (!$this->isCoverageEnabled($options) && $this->isXdebugLoaded()) { $message = 'Did you know? You can run tests faster by disabling xdebug'; if($this->isXdebugCodeCoverageEnabled()) { diff --git a/plugins/TestRunner/Commands/TestsRunUI.php b/plugins/TestRunner/Commands/TestsRunUI.php index faf436a8af..3a9a06c080 100644 --- a/plugins/TestRunner/Commands/TestsRunUI.php +++ b/plugins/TestRunner/Commands/TestsRunUI.php @@ -26,7 +26,7 @@ class TestsRunUI extends ConsoleCommand \nRun one spec: \n./console tests:run-ui UIIntegrationTest "); - $this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by comma, for instance core,integration', array()); + $this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by a space, for instance UIIntegrationTest ', array()); $this->addOption("persist-fixture-data", null, InputOption::VALUE_NONE, "Persist test data in a database and do not execute tear down."); $this->addOption('keep-symlinks', null, InputOption::VALUE_NONE, "Keep recursive directory symlinks so test pages can be viewed in a browser."); $this->addOption('print-logs', null, InputOption::VALUE_NONE, "Print webpage logs even if tests succeed."); @@ -35,6 +35,9 @@ class TestsRunUI extends ConsoleCommand $this->addOption('plugin', null, InputOption::VALUE_REQUIRED, "Execute all tests for a plugin."); $this->addOption('core', null, InputOption::VALUE_NONE, "Execute only tests for Piwik core & core plugins."); $this->addOption('skip-delete-assets', null, InputOption::VALUE_NONE, "Skip deleting of merged assets (will speed up a test run, but not by a lot)."); + $this->addOption('screenshot-repo', null, InputOption::VALUE_NONE, "For tests"); + $this->addOption('store-in-ui-tests-repo', null, InputOption::VALUE_NONE, "For tests"); + $this->addOption('extra-options', null, InputOption::VALUE_REQUIRED, "Extra options to pass to phantomjs."); } protected function execute(InputInterface $input, OutputInterface $output) @@ -48,6 +51,9 @@ class TestsRunUI extends ConsoleCommand $plugin = $input->getOption('plugin'); $skipDeleteAssets = $input->getOption('skip-delete-assets'); $core = $input->getOption('core'); + $extraOptions = $input->getOption('extra-options'); + $storeInUiTestsRepo = $input->getOption('store-in-ui-tests-repo'); + $screenshotRepo = $input->getOption('screenshot-repo'); if (!$skipDeleteAssets) { AssetManager::getInstance()->removeMergedAssets(); @@ -84,6 +90,18 @@ class TestsRunUI extends ConsoleCommand $options[] = "--core"; } + if ($storeInUiTestsRepo) { + $options[] = "--store-in-ui-tests-repo"; + } + + if ($screenshotRepo) { + $options[] = "--screenshot-repo"; + } + + if ($extraOptions) { + $options[] = $extraOptions; + } + $options = implode(" ", $options); $specs = implode(" ", $specs); @@ -93,7 +111,9 @@ class TestsRunUI extends ConsoleCommand $output->writeln('Executing command: <info>' . $cmd . '</info>'); $output->writeln(''); - passthru($cmd); + passthru($cmd, $returnCode); + + return $returnCode; } /** diff --git a/plugins/TestRunner/Commands/TestsSetupFixture.php b/plugins/TestRunner/Commands/TestsSetupFixture.php index 510578cb76..565a81122a 100644 --- a/plugins/TestRunner/Commands/TestsSetupFixture.php +++ b/plugins/TestRunner/Commands/TestsSetupFixture.php @@ -154,7 +154,7 @@ class TestsSetupFixture extends ConsoleCommand private function createSymbolicLinksForUITests() { // make sure symbolic links exist (phantomjs doesn't support symlink-ing yet) - foreach (array('libs', 'plugins', 'tests', 'piwik.js') as $linkName) { + foreach (array('libs', 'plugins', 'tests', 'misc', 'piwik.js') as $linkName) { $linkPath = PIWIK_INCLUDE_PATH . '/tests/PHPUnit/proxy/' . $linkName; if (!file_exists($linkPath)) { symlink(PIWIK_INCLUDE_PATH . '/' . $linkName, $linkPath); @@ -192,10 +192,7 @@ class TestsSetupFixture extends ConsoleCommand ); foreach ($optionsToOverride as $configOption => $value) { if ($value) { - $configOverride = $testingEnvironment->configOverride; - $configOverride['database_tests'][$configOption] = $configOverride['database'][$configOption] = $value; - $testingEnvironment->configOverride = $configOverride; - + $testingEnvironment->overrideConfig('database_tests', $configOption, $value); Config::getInstance()->database[$configOption] = $value; } } @@ -212,6 +209,7 @@ class TestsSetupFixture extends ConsoleCommand throw new \Exception("Cannot find fixture class '$fixtureClass'."); } + /** @var Fixture $fixture */ $fixture = new $fixtureClass(); $fixture->printToScreen = true; @@ -233,6 +231,8 @@ class TestsSetupFixture extends ConsoleCommand $fixture->extraPluginsToLoad = explode(',', $extraPluginsToLoad); } + $fixture->extraDiEnvironments = array('ui-test'); + return $fixture; } diff --git a/plugins/Transitions/javascripts/transitions.js b/plugins/Transitions/javascripts/transitions.js index cb98e3d045..8461c89069 100644 --- a/plugins/Transitions/javascripts/transitions.js +++ b/plugins/Transitions/javascripts/transitions.js @@ -17,8 +17,12 @@ function DataTable_RowActions_Transitions(dataTable) { DataTable_RowActions_Transitions.prototype = new DataTable_RowAction; /** Static helper method to launch transitions from anywhere */ -DataTable_RowActions_Transitions.launchForUrl = function (url) { - broadcast.propagateNewPopoverParameter('RowAction', 'Transitions:url:' + url); +DataTable_RowActions_Transitions.launchForUrl = function (url, segment) { + var value = 'Transitions:url:' + url; + if (segment) { + value += ':segment:' + segment; + } + broadcast.propagateNewPopoverParameter('RowAction', value); }; DataTable_RowActions_Transitions.isPageUrlReport = function (module, action) { @@ -30,19 +34,25 @@ DataTable_RowActions_Transitions.isPageTitleReport = function (module, action) { return module == 'Actions' && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch'); }; +DataTable_RowActions_Transitions.registeredReports = []; +DataTable_RowActions_Transitions.registerReport = function (handler) { + DataTable_RowActions_Transitions.registeredReports.push(handler); +} + DataTable_RowActions_Transitions.prototype.trigger = function (tr, e, subTableLabel) { - var link = tr.find('> td:first > a').attr('href'); - link = $('<textarea>').html(link).val(); // remove html entities - - var module = this.dataTable.param.module; - var action = this.dataTable.param.action; - if (DataTable_RowActions_Transitions.isPageUrlReport(module, action)) { - this.openPopover('url:' + link); - } else if (DataTable_RowActions_Transitions.isPageTitleReport(module, action)) { - DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]); - } else { - alert('Transitions can\'t be used on this report.'); + var i = 0; + for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) { + var report = DataTable_RowActions_Transitions.registeredReports[i]; + if (report + && report.trigger + && report.isAvailableOnReport + && report.isAvailableOnReport(this.dataTable.param)) { + report.trigger.apply(this, arguments); + return; + } } + + alert('Transitions can\'t be used on this report.'); }; DataTable_RowActions_Transitions.prototype.performAction = function (label, tr, e) { @@ -57,6 +67,15 @@ DataTable_RowActions_Transitions.prototype.performAction = function (label, tr, }; DataTable_RowActions_Transitions.prototype.doOpenPopover = function (link) { + var posSegment = (link+'').indexOf(':segment:'); + var segment = null; + + // handle and remove ':segment:$SEGMENT' from link + if (posSegment && posSegment > 0) { + segment = link.substring(posSegment + (':segment:'.length)); + link = link.substring(0, posSegment); + } + var parts = link.split(':'); if (parts.length < 2) { return; @@ -67,9 +86,9 @@ DataTable_RowActions_Transitions.prototype.doOpenPopover = function (link) { var actionName = parts.join(':'); if (this.transitions === null) { - this.transitions = new Piwik_Transitions(actionType, actionName, this); + this.transitions = new Piwik_Transitions(actionType, actionName, this, segment); } else { - this.transitions.reset(actionType, actionName); + this.transitions.reset(actionType, actionName, segment); } this.transitions.showPopover(); }; @@ -93,10 +112,17 @@ DataTable_RowActions_Registry.register({ }, isAvailableOnReport: function (dataTableParams) { - return ( - DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) || - DataTable_RowActions_Transitions.isPageTitleReport(dataTableParams.module, dataTableParams.action) - ); + var i = 0; + for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) { + var report = DataTable_RowActions_Transitions.registeredReports[i]; + if (report + && report.isAvailableOnReport + && report.isAvailableOnReport(dataTableParams)) { + return true; + } + } + + return false; }, isAvailableOnRow: function (dataTableParams, tr) { @@ -104,11 +130,18 @@ DataTable_RowActions_Registry.register({ // not available on groups (i.e. folders) return false; } - if (DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) - && !tr.find('> td:first span.label').parent().is('a')) { - // not on page url without link (i.e. "Page URL not defined") - return false; + + var i = 0; + for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) { + var report = DataTable_RowActions_Transitions.registeredReports[i]; + if (report + && report.isAvailableOnRow + && report.isAvailableOnReport + && report.isAvailableOnReport(dataTableParams)) { + return report.isAvailableOnRow(dataTableParams, tr); + } } + return true; } @@ -118,8 +151,8 @@ DataTable_RowActions_Registry.register({ // TRANSITIONS IMPLEMENTATION // -function Piwik_Transitions(actionType, actionName, rowAction) { - this.reset(actionType, actionName); +function Piwik_Transitions(actionType, actionName, rowAction, segment) { + this.reset(actionType, actionName, segment); this.rowAction = rowAction; this.ajax = new Piwik_Transitions_Ajax(); @@ -129,9 +162,10 @@ function Piwik_Transitions(actionType, actionName, rowAction) { this.rightGroups = ['followingPages', 'followingSiteSearches', 'downloads', 'outlinks']; } -Piwik_Transitions.prototype.reset = function (actionType, actionName) { +Piwik_Transitions.prototype.reset = function (actionType, actionName, segment) { this.actionType = actionType; this.actionName = actionName; + this.segment = segment; this.popover = null; this.canvas = null; @@ -179,7 +213,7 @@ Piwik_Transitions.prototype.showPopover = function () { } // load the data - self.model.loadData(self.actionType, self.actionName, function () { + self.model.loadData(self.actionType, self.actionName, self.segment, function () { if (typeof Piwik_Transitions.popoverHtml == 'undefined') { // html not there yet callbackForHtml = bothLoaded; @@ -1249,7 +1283,7 @@ Piwik_Transitions_Model.prototype.htmlLoaded = function () { }; }; -Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, callback) { +Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, segment, callback) { var self = this; this.pageviews = 0; @@ -1287,11 +1321,16 @@ Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, c this.date = ''; - this.ajax.callApi('Transitions.getTransitionsForAction', { - actionType: actionType, - actionName: actionName, - expanded: 1 - }, + var params = { + actionType: actionType, + actionName: actionName, + expanded: 1 + }; + if (segment) { + params.segment = segment; + } + + this.ajax.callApi('Transitions.getTransitionsForAction', params, function (report) { self.date = report.date; diff --git a/plugins/Transitions/lang/ko.json b/plugins/Transitions/lang/ko.json index 468c039e52..1acc59cff6 100644 --- a/plugins/Transitions/lang/ko.json +++ b/plugins/Transitions/lang/ko.json @@ -16,6 +16,7 @@ "NoDataForAction": "%s 데이터 없음", "NoDataForActionDetails": "특정 동작은 %s을 하는 기간 동안 페이지뷰가 일어나지 않거나 유효하지 않습니다.", "OutgoingTraffic": "나가는 트래픽", + "PluginDescription": "새로운 변화에 대한 보고서 내 각 페이지 URL에 대한 이전 및 다음의 보고서는 새 아이콘을 통한 동작 보고서에서 확인할 수 있습니다.", "ShareOfAllPageviews": "이 페이지의 페이지뷰 %s (전체 페이지뷰 수 %s)", "ToFollowingPages": "내부 페이지로", "ToFollowingPagesInline": "%s 내부 페이지", diff --git a/plugins/TreemapVisualization b/plugins/TreemapVisualization -Subproject 9640640f7b49374790330699c9985634ecb9c0a +Subproject e487d14390a4b99504a38ba6237705c10317b12 diff --git a/plugins/UserCountry/lang/cs.json b/plugins/UserCountry/lang/cs.json index d39b67ea08..821069f8d4 100644 --- a/plugins/UserCountry/lang/cs.json +++ b/plugins/UserCountry/lang/cs.json @@ -6,7 +6,7 @@ "CannotFindPeclGeoIPDb": "Nepodařilo se najít databázi zemí, regionů nebo měst pro GEOIP modul PECl. Ujistěte se, že vaše GEOIP databáze je umísěna v adresáři %1$s a je pojmenována %2$s nebo %3$s, jinak si jí GEOIP modul PECl nevšimne.", "CannotListContent": "Nepodařilo se vypsat obsah pro %1$s: %2$s", "CannotLocalizeLocalIP": "IP adresa %s je místní a enemůže být geolokována.", - "CannotSetupGeoIPAutoUpdating": "Vypadá to, že vaše GEOIP databáze ukládáte vně Piwiku (víme to, protože v podadresáři misc žádné databáze nejsou, ale gEOIP funguje). Piwik nemůže automaticky aktualizovat GEOIP databázi, když není uložena v podadresáři misc.", + "CannotSetupGeoIPAutoUpdating": "Vypadá to, že svoje GEOIP databáze ukládáte vně Piwiku (víme to, protože v podadresáři misc žádné databáze nejsou, ale gEOIP funguje). Piwik nemůže automaticky aktualizovat GEOIP databázi, pokud není uložena v podadresáři misc.", "CannotUnzipDatFile": "Nepodařilo se rozbalit dat soubor v %1$s: %2$s", "City": "Město", "CityAndCountry": "%1$s, %2$s", @@ -84,7 +84,7 @@ "Region": "Region", "SetupAutomaticUpdatesOfGeoIP": "Nastavit automatické aktualizace GeoIP databází", "SubmenuLocations": "Umístění", - "TestIPLocatorFailed": "Piwik se pokusil zjistit místění námé IP adresy %1$s, ale server odpověděl %2$s. Kdyby byl tento poskytovatel nastaven správně, odpověděl by %3$s.", + "TestIPLocatorFailed": "Piwik se pokusil zjistit místění známé IP adresy %1$s, ale server odpověděl %2$s. Pokud by byl tento poskytovatel nastaven správně, odpověděl by %3$s.", "ThisUrlIsNotAValidGeoIPDB": "Stažený soubor není platná GeoIP databáze. Zkontrolujte URL, nebo soubor stáhněte ručně.", "ToGeolocateOldVisits": "Pokud chcete získat data o umístění pro staré návštěvy, použijte skript popisovaný %1$szde%2$s.", "UnsupportedArchiveType": "Nalezen nepodporovaný typ archivu %1$s.", diff --git a/plugins/UserCountry/lang/id.json b/plugins/UserCountry/lang/id.json index 5a49080dfc..efc27e6e38 100644 --- a/plugins/UserCountry/lang/id.json +++ b/plugins/UserCountry/lang/id.json @@ -9,12 +9,14 @@ "CannotSetupGeoIPAutoUpdating": "Tampaknya Anda menyimpan basidata GeoIP Anda di luar Piwik (kami dapat katakan sejak tidak ada basisdata di subdirektori misc, tapi GeoIP Anda berkerja). Piwik tidak dapat otomatis memperbarui basisdata GeoIP Anda bila berada di luar direktori misc.", "CannotUnzipDatFile": "Tidak dapat melakukan unzip berkas dat di %1$s: %2$s", "City": "Kota", + "CityAndCountry": "%1$s, %2$s", "Continent": "Benua", "Country": "Negara", "country_a1": "Wali Anonim", "country_a2": "Penyedia Satelit", "country_cat": "Masyarakat berbahasa Katalan", "country_o1": "Negara Lain", + "country_ti": "Tibet", "CurrentLocationIntro": "Berdasar penyedia ini, lokasi Anda adalah", "DefaultLocationProviderDesc1": "Lokasi penyedia asali menebak negara pengunjung dari bahasa yang digunakan.", "DefaultLocationProviderDesc2": "Ini sangat tidak teliti, serta %1$skami menyarankan memasang dan menggunakan %2$sGeoIP%3$s.%4$s", @@ -23,6 +25,7 @@ "DownloadNewDatabasesEvery": "Perbarui basisdata setiap", "FatalErrorDuringDownload": "Galat fatal terjadi ketika mengunduh berkas ini. Kemungkinan ada yang salah dengan sambungan internet Anda, dengan basisdata GeoIP yang Anda unduh, atau Piwik. Silakan coba mengunduh dan memasang secara manual.", "FoundApacheModules": "Piwik menemukan modul Apache berikut", + "FromDifferentCities": "kota berbeda", "GeoIPCannotFindMbstringExtension": "Tidak dapat menemukan fungsi %1$s. Harap pastikan bahwa ekstensi %2$s telah terpasang dan termuat.", "GeoIPDatabases": "Basisdata GeoIP", "GeoIPDocumentationSuffix": "Agar dapat melihat data untuk laporan ini, Anda harus memasang GeoIP di tab pengelola Lokasi-Geo. Basisdata GeoIP %1$sMaxmind%2$s komersial lebih teliti daripada yang gratis. Untuk melihat seberapa teliti mereka, klik %3$sdi sini%4$s.", diff --git a/plugins/UserCountry/lang/ja.json b/plugins/UserCountry/lang/ja.json index f5d3a08214..303595ae43 100644 --- a/plugins/UserCountry/lang/ja.json +++ b/plugins/UserCountry/lang/ja.json @@ -16,6 +16,7 @@ "country_a2": "衛星プロバイダ", "country_cat": "カタロニア語圏のコミュニティ", "country_o1": "その他の国", + "country_ti": "チベット", "CurrentLocationIntro": "このプロバイダーによると、あなたの現在地は", "DefaultLocationProviderDesc1": "デフォルトロケーションプロバイダーでは、ビジターの国は使用言語に基づいて推測されます。", "DefaultLocationProviderDesc2": "これは正確ではありません。%1$swe recommend installing and using %2$sGeoIP%3$s。%4$s", diff --git a/plugins/UserCountry/lang/ko.json b/plugins/UserCountry/lang/ko.json index 13a2f18b53..0ef8232311 100644 --- a/plugins/UserCountry/lang/ko.json +++ b/plugins/UserCountry/lang/ko.json @@ -2,38 +2,44 @@ "UserCountry": { "AssumingNonApache": "아파치 웹서버가 아니어서 apache_get_modules을 찾을 수 없습니다.", "CannotFindGeoIPDatabaseInArchive": "%2$s tar 압축파일에서 %1$s 파일을 찾을 수 없습니다!", - "CannotFindGeoIPServerVar": "%s에 변수가 설정되어 있지 않습니다. 서버가 올바르게 구성되 않았을 수 있습니다.", + "CannotFindGeoIPServerVar": "%s에 변수가 설정되어 있지 않습니다. 서버가 올바르게 구성되지 않았을 수 있습니다.", "CannotFindPeclGeoIPDb": "GeoIP PECL 모듈에서 국가, 지역 또는 도시 데이터베이스를 찾을 수 없습니다. GeoIP 데이터베이스가 %1$s에 위치하고 파일 이름이 %2$s 또는 %3$s인지 확인하세요. 틀린 부분이 있다면 PECL 모듈을 인식하지 못합니다.", "CannotListContent": "%1$s에 대한 내용을 나열 할 수 없습니다: %2$s", "CannotLocalizeLocalIP": "IP 주소 %s는 로컬 주소이기 때문에 위치를 추적할 수 없습니다.", - "CannotSetupGeoIPAutoUpdating": "Piwik의 외부에서 GeoIP 데이터베이스를 저장한 것 같군요 (하위 디렉토리인 misc에 데이터베이스가 없지만, 당신의 GeoIP가 잘 작동하고 있어요) 이 파일들이 misc 디렉토리의 밖에 있는 경우, Piwik는 자동으로 GeoIP 데이터베이스를 업데이트할 수 없습니다.", + "CannotSetupGeoIPAutoUpdating": "Piwik의 외부에서 GeoIP 데이터베이스를 저장한 것 같군요 (하위 디렉토리인 misc에 데이터베이스가 없지만, 당신의 GeoIP가 잘 작동하고 있어요). 이 파일들이 misc 디렉토리의 밖에 있는 경우, Piwik는 자동으로 GeoIP 데이터베이스를 업데이트할 수 없습니다.", "CannotUnzipDatFile": "%1$s의 DAT 파일 압축을 풀 수 없습니다: %2$s", "City": "도시", + "CityAndCountry": "%1$s, %2$s", "Continent": "대륙", "Country": "국가", "country_a1": "익명 프록시", "country_a2": "위성 공급자", "country_cat": "카탈로니아 지역의 커뮤니티", "country_o1": "기타 국가", + "country_ti": "티베트", "CurrentLocationIntro": "이 공급자에 따르면, 현재 위치는", "DefaultLocationProviderDesc1": "방문자의 국가에 기반하는 언어에서 추측하는 기본 위치 공급자입니다.", "DefaultLocationProviderDesc2": "이것은 매우 부정확합니다. 그래서 %1$s우리는 %2$sGeoIP%3$s를 설치하여 사용하는 것을 추천합니다.%4$s", + "DefaultLocationProviderExplanation": "당신은 현재 기본 위치 공급자를 사용하고 있어, Piwik는 방문자의 위치를 그들이 사용하는 언어에 기반하여 추측하고 있습니다. %1$s이 문서%2$s에서 어떻게 더 정확한 위치를 얻어낼 수 있는지 얘기하고 있습니다.", "DistinctCountries": "%s개 국가", "DownloadingDb": "다운로드 중 %s", "DownloadNewDatabasesEvery": "모든 데이터베이스 업데이트", "FatalErrorDuringDownload": "파일을 다운로드하는 동안 치명적인 오류가 발생했습니다. Piwik에서 GeoIP 데이터베이스 다운로드하는데 인터넷 연결에 문제가 있을 수 있습니다, 직접 다운로드해서 수동으로 설치해 보세요.", "FoundApacheModules": "Piwik은 다음의 아파치 모듈을 찾을 수 없음", - "GeoIPCannotFindMbstringExtension": "%1$s 함수를 찾을 수 없습니다. %2$s 확장이 설치되었고 로드되었는지 확인하세요.", + "FromDifferentCities": "다른 도시", + "GeoIPCannotFindMbstringExtension": "%1$s 함수를 찾을 수 없습니다. %2$s 확장이 설치 및 로드되었는지 확인하세요.", "GeoIPDatabases": "GeoIP 데이터베이스", "GeoIPDocumentationSuffix": "이 보고서에 대한 데이터를 볼 수 있도록 당신은 위치 정보관리 탭에서 GeoIP를 설정해야합니다. 상업용 %1$sMaxmind%2$s GeoIP 데이터베이스는 무료로 버전보다 더 정확합니다. 정확도를 높이려면 %3$s공유%4$s를 클릭하세요.", "GeoIPImplHasAccessTo": "이 GeoIP 구현에서 접근하는 데이터베이스 유형은", + "GeoIPIncorrectDatabaseFormat": "당신이 사용하는 GeoIP 데이터베이스는 올바른 포멧을 가지고 있지 않는 듯싶습니다. 아마도 손상된 것으로 보입니다. 바이너리 버전을 사용하는지 확인하시고 다른 복사본으로 바꿔 다시 진행해보세요.", "GeoIpLocationProviderDesc_Pecl1": "이 위치 공급자는 PECL 모듈과 GeoIP 데이터베이스를 사용하여 정확하고 효율적으로 방문자의 위치를 결정합니다.", - "GeoIpLocationProviderDesc_Pecl2": "제한이 없는 공급자입니다. 그래서 우리는 이것이 사용되는 것을 추천합니다.", + "GeoIpLocationProviderDesc_Pecl2": "제한이 없는 공급자입니다. 그래서 우리는 이를 사용하는 것을 추천합니다.", "GeoIpLocationProviderDesc_Php1": "이 위치 공급자는 서버에 설치 및 구성을 요구하지 않기 때문에 가장 간단합니다 (공유 호스팅에 적합!). GeoIP 데이터베이스 및 MaxMind의 PHP API를 사용하여 정확하게 방문자의 위치를 결정합니다.", "GeoIpLocationProviderDesc_Php2": "웹사이트 트래픽이 높은 경우에 이 위치 공급자는 너무 느립니다. 이 경우는 %1$sPECL 확장%2$s이나 %3$s서버 모듈%4$s을 설치하는 것이 좋습니다.", "GeoIpLocationProviderDesc_ServerBased1": "이 위치 공급자는 HTTP 서버에 설치되는 GeoIP 모듈을 사용합니다. 이 것은 빠르고 정확하지만, %1$s일반 브라우저의 추적만 사용할 수 있습니다.%2$s", "GeoIpLocationProviderDesc_ServerBased2": "로그 파일을 가져오거나 IP 주소 설정을 필요로하는 경우 %1$sPECL GeoIP 구현 (권장)%2$s 또는 %3$sPHP GeoIP 구현%4$s을 사용합니다.", "GeoIpLocationProviderDesc_ServerBasedAnonWarn": "참고: IP를 익명화하면 공급자가 보고있는 위치에 아무런 영향을 미치지 않습니다. IP를 익명화 하지 않으면, 개인 정보 보호법을 위반이 될수 있으므로 주의하세요.", + "GeoIpLocationProviderNotRecomnended": "지리적 위치 플러그인은 동작하지만, 현재 당신은 추천하는 공급자를 사용하고 있지 않습니다.", "GeoIPNoServerVars": "Piwik은 GeoIP %s 변수를 찾을 수 없습니다.", "GeoIPPeclCustomDirNotSet": "%s PHP ini 옵션이 설정되어 있지 않습니다.", "GeoIPServerVarsFound": "Piwik은 다음의 GeoIP %s 변수를 감지함", @@ -52,7 +58,7 @@ "HowToInstallNginxModule": "Nginx에 GeoIP 모듈을 설치하려면 어떻게해야합니까?", "HowToSetupGeoIP": "GeoIP로 정확한 위치 정보를 설정하는 방법", "HowToSetupGeoIP_Step1": "%3$sMaxMind%4$s에서 GeoLite 도시 데이터베이스를 %1$s다운로드%2$s 합니다.", - "HowToSetupGeoIP_Step2": "이 파일의 압축을 풀고 %1$s 파일을 Piwik 하위 디렉토리인 %2$smisc%3$s로 복사합니다 (FTP나 SSH를 이용하여).", + "HowToSetupGeoIP_Step2": "(FTP나 SSH를 이용하여) 이 파일의 압축을 풀고 %1$s 파일을 Piwik 하위 디렉토리인 %2$smisc%3$s로 복사합니다.", "HowToSetupGeoIP_Step3": "화면을 새로 고침합니다. %1$sGeoIP (PHP)%2$s 공급자가 %3$s설치됨%4$s으로 나타날 것입니다. 이것을 선택합니다.", "HowToSetupGeoIP_Step4": "작업 완료! 이제 Piwik은 GeoIP를 사용하도록 설정되었으며, 이것은 방문자의 정확한 국가와 도시 지역 정보를 볼 수 있음을 의미합니다.", "HowToSetupGeoIPIntro": "위치 정보를 정확하게 설정하지 않으면 방문자의 위치 정보 및 이와 관련한 유용한 기능이 표시되지 않습니다. 다음 방법을 참고하여 어서 이 기능을 사용하세요:", @@ -64,7 +70,7 @@ "Latitude": "위도", "Location": "위치", "LocationDatabase": "위치 데이터베이스", - "LocationDatabaseHint": "위치 데이터베이스 중 하나 국가, 지역 또는 도시 데이터베이스입니다.", + "LocationDatabaseHint": "위치 데이터베이스는 국가, 지역 또는 도시 데이터베이스를 말합니다.", "LocationProvider": "위치 공급자", "Longitude": "경도", "NoDataForGeoIPReport1": "이 보고서에 대한 데이터가 없습니다. 어떠한 위치 데이터도 사용할 수 없거나, 방문자의 IP 주소에서 지역정보를 찾을 수 없기 때문입니다.", @@ -74,6 +80,7 @@ "PeclGeoIPNoDBDir": "PECL 모듈이 %1$s 경로에서 데이터베이스를 찾았지만, 디렉토리가 존재하지 않습니다. 디렉토리를 만들고 여기에 GeoIP 데이터베이스를 추가하세요. 대안으로, php.ini 파일에 올바른 디렉토리를 %2$s 경로를 설정할 수 있습니다.", "PeclGeoLiteError": "%1$s에 있는 GeoIP 데이터베이스 이름은 %2$s입니다. 안타깝게도, PECL 모듈은 이 이름으로 인식하지 않습니다. 이름을 %3$s로 변경해 주세요.", "PiwikNotManagingGeoIPDBs": "Piwik은 현재 모든 GeoIP 데이터베이스를 관리하지 않습니다.", + "PluginDescription": "방문자 위치 보고서: 국가, 지역, 도시, 지리적 좌표(위도\/경도)", "Region": "지역", "SetupAutomaticUpdatesOfGeoIP": "GeoIP 데이터베이스의 자동 업데이트 설정", "SubmenuLocations": "위치", @@ -81,7 +88,11 @@ "ThisUrlIsNotAValidGeoIPDB": "다운로드 한 파일은 유효한 GeoIP 데이터베이스가 아닙니다. URL을 다시 확인하거나 수동으로 파일을 다운로드하기 바랍니다.", "ToGeolocateOldVisits": "기존의 방문의 위치 데이터를 얻으려면 %1$s이 곳%2$s에 있는 스크립트 설명을 사용하세요.", "UnsupportedArchiveType": "지원되지 않는 압축 형식 %1$s 입니다.", + "UpdaterHasNotBeenRun": "업데이터가 한 번도 실행되지 않았습니다.", + "UpdaterIsNotScheduledToRun": "향후 실행할 계획이 없습니다.", + "UpdaterScheduledForNextRun": "다음 cron core:archive 명령 수행시 실행됩니다.", "UpdaterWasLastRun": "%s에 마지막 업데이트 확인", + "UpdaterWillRunNext": "%s에 실행이 계획되어 있습니다.", "WidgetLocation": "방문자 위치" } }
\ No newline at end of file diff --git a/plugins/UserCountry/lang/tr.json b/plugins/UserCountry/lang/tr.json index 58b4148923..16fa632c49 100644 --- a/plugins/UserCountry/lang/tr.json +++ b/plugins/UserCountry/lang/tr.json @@ -4,11 +4,13 @@ "Continent": "Kıta", "Country": "Ülke", "country_o1": "Diğer Ükle", + "DownloadingDb": "Karşıdan Yükleme %s", "Geolocation": "Coğrafi Lokasyon", "GeolocationPageDesc": "Bu sayfada Piwik'in ziyaretçi lokasyonunu nasıl bulacağını ayarlayabilirsiniz.", "Latitude": "Enlem", "Location": "Konum", "Longitude": "Boylam", + "Organization": "Organizasyon", "Region": "Bölge", "SubmenuLocations": "Konumlar" } diff --git a/plugins/UserCountryMap/lang/id.json b/plugins/UserCountryMap/lang/id.json index dc6cddf743..84fce004d8 100644 --- a/plugins/UserCountryMap/lang/id.json +++ b/plugins/UserCountryMap/lang/id.json @@ -1,5 +1,6 @@ { "UserCountryMap": { + "PluginDescription": "Pengaya ini menyediakan gawit Peta Pengunjung dan Peta Waktu-Nyata. Catatan: Membutuhkan pengaya NegaraPengguna diaktifkan.", "AndNOthers": "dan %s lain", "Cities": "Kita", "Countries": "Negara", @@ -17,6 +18,8 @@ "ShowingVisits": "Kunjungan Lokasi-Geo terakhir", "Unlocated": "<b>%s<\/b> %p kunjungan dari %c lokasi-geo tidak diketahui.", "VisitorMap": "Peta Pengunjung", - "WorldWide": "Seluruh Dunia" + "WorldWide": "Seluruh Dunia", + "WithUnknownRegion": "%s dengan wilayah tidak dikenal", + "WithUnknownCity": "%s dengan kota tidak dikenal" } }
\ No newline at end of file diff --git a/plugins/UserCountryMap/lang/ja.json b/plugins/UserCountryMap/lang/ja.json index ef083246b5..55de4296c1 100644 --- a/plugins/UserCountryMap/lang/ja.json +++ b/plugins/UserCountryMap/lang/ja.json @@ -1,5 +1,6 @@ { "UserCountryMap": { + "PluginDescription": "このプラグインでは、ビジターマップとリアルタイムマップウィジェットを提供します。注:利用可能なユーザーカントリープラグインが必要。", "AndNOthers": "と、%s ほか", "Cities": "都市", "Countries": "国", diff --git a/plugins/UserCountryMap/lang/ko.json b/plugins/UserCountryMap/lang/ko.json index e21ff537a5..d599797a57 100644 --- a/plugins/UserCountryMap/lang/ko.json +++ b/plugins/UserCountryMap/lang/ko.json @@ -1,5 +1,6 @@ { "UserCountryMap": { + "PluginDescription": "이 플러그인은 방문자 지도 및 실시간 지도 위젯을 제공합니다. 주의: UserCountry 플러그인이 활성화 되어 있어야 합니다.", "AndNOthers": ", 기타 %s", "Cities": "도시", "Countries": "국가", @@ -17,6 +18,8 @@ "ShowingVisits": "최근 방문의 지리적 위치", "Unlocated": "<b>%s<\/b> %c 방문에서 %p의 지리적 위치를 찾을 수 없습니다.", "VisitorMap": "방문자 지도", - "WorldWide": "전세계" + "WorldWide": "전세계", + "WithUnknownRegion": "%s 알 수 없는 지역", + "WithUnknownCity": "%s 알 수 없는 도시" } }
\ No newline at end of file diff --git a/plugins/UserLanguage/lang/id.json b/plugins/UserLanguage/lang/id.json index 7a9cc30d70..8d359ebf78 100644 --- a/plugins/UserLanguage/lang/id.json +++ b/plugins/UserLanguage/lang/id.json @@ -1,6 +1,7 @@ { "UserLanguage": { "BrowserLanguage": "Bahasa Peramban", - "LanguageCode": "Kode Bahasa" + "LanguageCode": "Kode Bahasa", + "PluginDescription": "Laporan bahasa yang digunakan oleh peramban pengguna" } }
\ No newline at end of file diff --git a/plugins/UserLanguage/lang/ko.json b/plugins/UserLanguage/lang/ko.json index 5cd9ae7a9f..9b50bdaf2a 100644 --- a/plugins/UserLanguage/lang/ko.json +++ b/plugins/UserLanguage/lang/ko.json @@ -1,6 +1,7 @@ { "UserLanguage": { "BrowserLanguage": "브라우저 언어", - "LanguageCode": "언어 코드" + "LanguageCode": "언어 코드", + "PluginDescription": "방문자의 브라우저를 통해 언어 기록" } }
\ No newline at end of file diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php index 105d795036..37e13fcefa 100644 --- a/plugins/UsersManager/Controller.php +++ b/plugins/UsersManager/Controller.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\UsersManager; use Exception; +use Piwik\API\Request; use Piwik\API\ResponseBuilder; use Piwik\Common; use Piwik\Container\StaticContainer; @@ -19,7 +20,6 @@ use Piwik\Plugin\ControllerAdmin; use Piwik\Plugins\LanguagesManager\API as APILanguagesManager; use Piwik\Plugins\LanguagesManager\LanguagesManager; use Piwik\Plugins\Login\SessionInitializer; -use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\Plugins\UsersManager\API as APIUsersManager; use Piwik\SettingsPiwik; use Piwik\Site; @@ -56,7 +56,7 @@ class Controller extends ControllerAdmin $view = new View('@UsersManager/index'); - $IdSitesAdmin = APISitesManager::getInstance()->getSitesIdWithAdminAccess(); + $IdSitesAdmin = Request::processRequest('SitesManager.getSitesIdWithAdminAccess'); $idSiteSelected = 1; if (count($IdSitesAdmin) > 0) { @@ -70,7 +70,7 @@ class Controller extends ControllerAdmin } else { $defaultReportSiteName = Site::getNameFor($idSiteSelected); try { - $usersAccessByWebsite = APIUsersManager::getInstance()->getUsersAccessFromSite($idSiteSelected); + $usersAccessByWebsite = Request::processRequest('UsersManager.getUsersAccessFromSite', array('idSite' => $idSiteSelected)); } catch (NoAccessException $e) { return $this->noAdminAccessToWebsite($idSiteSelected, $defaultReportSiteName, $e->getMessage()); } @@ -78,7 +78,7 @@ class Controller extends ControllerAdmin // we dont want to display the user currently logged so that the user can't change his settings from admin to view... $currentlyLogged = Piwik::getCurrentUserLogin(); - $usersLogin = APIUsersManager::getInstance()->getUsersLogin(); + $usersLogin = Request::processRequest('UsersManager.getUsersLogin'); foreach ($usersLogin as $login) { if (!isset($usersAccessByWebsite[$login])) { $usersAccessByWebsite[$login] = 'noaccess'; @@ -105,7 +105,7 @@ class Controller extends ControllerAdmin if (Piwik::isUserHasSomeAdminAccess()) { $view->showLastSeen = true; - $users = APIUsersManager::getInstance()->getUsers(); + $users = Request::processRequest('UsersManager.getUsers'); foreach ($users as $index => $user) { $usersAliasByLogin[$user['login']] = $user['alias']; @@ -131,7 +131,8 @@ class Controller extends ControllerAdmin $view->usersAliasByLogin = $usersAliasByLogin; $view->usersCount = count($users) - 1; $view->usersAccessByWebsite = $usersAccessByWebsite; - $websites = APISitesManager::getInstance()->getSitesWithAdminAccess(); + + $websites = Request::processRequest('SitesManager.getSitesWithAdminAccess'); uasort($websites, array('Piwik\Plugins\UsersManager\Controller', 'orderByName')); $view->websites = $websites; $this->setBasicVariablesView($view); @@ -228,7 +229,7 @@ class Controller extends ControllerAdmin $view = new View('@UsersManager/userSettings'); $userLogin = Piwik::getCurrentUserLogin(); - $user = APIUsersManager::getInstance()->getUser($userLogin); + $user = Request::processRequest('UsersManager.getUser', array('userLogin' => $userLogin)); $view->userAlias = $user['alias']; $view->userEmail = $user['email']; @@ -259,6 +260,7 @@ class Controller extends ControllerAdmin $view->languages = APILanguagesManager::getInstance()->getAvailableLanguageNames(); $view->currentLanguageCode = LanguagesManager::getLanguageCodeForCurrentUser(); + $view->currentTimeformat = LanguagesManager::uses12HourClockForCurrentUser(); $view->ignoreCookieSet = IgnoreCookie::isIgnoreCookieFound(); $view->piwikHost = Url::getCurrentHost(); $this->setBasicVariablesView($view); @@ -309,11 +311,13 @@ class Controller extends ControllerAdmin $userLogin = 'anonymous'; // Which websites are available to the anonymous users? - $anonymousSitesAccess = APIUsersManager::getInstance()->getSitesAccessFromUser($userLogin); + + $anonymousSitesAccess = Request::processRequest('UsersManager.getSitesAccessFromUser', array('userLogin' => $userLogin)); $anonymousSites = array(); foreach ($anonymousSitesAccess as $info) { $idSite = $info['site']; - $site = APISitesManager::getInstance()->getSiteFromId($idSite); + + $site = Request::processRequest('SitesManager.getSiteFromId', array('idSite' => $idSite)); // Work around manual website deletion if (!empty($site)) { $anonymousSites[$idSite] = $site; @@ -322,7 +326,7 @@ class Controller extends ControllerAdmin $view->anonymousSites = $anonymousSites; // Which report is displayed by default to the anonymous user? - $anonymousDefaultReport = APIUsersManager::getInstance()->getUserPreference($userLogin, APIUsersManager::PREFERENCE_DEFAULT_REPORT); + $anonymousDefaultReport = Request::processRequest('UsersManager.getUserPreference', array('userLogin' => $userLogin, 'preferenceName' => APIUsersManager::PREFERENCE_DEFAULT_REPORT)); if ($anonymousDefaultReport === false) { if (empty($anonymousSites)) { $anonymousDefaultReport = Piwik::getLoginPluginName(); @@ -379,12 +383,14 @@ class Controller extends ControllerAdmin $defaultReport = Common::getRequestVar('defaultReport'); $defaultDate = Common::getRequestVar('defaultDate'); $language = Common::getRequestVar('language'); + $timeFormat = Common::getRequestVar('timeformat'); $userLogin = Piwik::getCurrentUserLogin(); $this->processPasswordChange($userLogin); LanguagesManager::setLanguageForSession($language); APILanguagesManager::getInstance()->setLanguageForUser($userLogin, $language); + APILanguagesManager::getInstance()->set12HourClockForUser($userLogin, $timeFormat); APIUsersManager::getInstance()->setUserPreference($userLogin, APIUsersManager::PREFERENCE_DEFAULT_REPORT, diff --git a/plugins/UsersManager/javascripts/usersSettings.js b/plugins/UsersManager/javascripts/usersSettings.js index 809b1309b7..1708e64bf7 100644 --- a/plugins/UsersManager/javascripts/usersSettings.js +++ b/plugins/UsersManager/javascripts/usersSettings.js @@ -38,6 +38,7 @@ function sendUserSettingsAJAX() { postParams.defaultReport = defaultReport; postParams.defaultDate = defaultDate; postParams.language = $('#userSettingsTable #language').val(); + postParams.timeformat = $('#userSettingsTable #timeformat').val(); var ajaxHandler = new ajaxHelper(); ajaxHandler.addParams({ diff --git a/plugins/UsersManager/lang/cs.json b/plugins/UsersManager/lang/cs.json index 3fd20a14e2..80aab2ca4c 100644 --- a/plugins/UsersManager/lang/cs.json +++ b/plugins/UsersManager/lang/cs.json @@ -5,7 +5,7 @@ "AllWebsites": "Všechny weby", "AnonymousUser": "Anonymní uživatel", "AnonymousUserHasViewAccess": "Poznámka: %1$s uživatel má právo k přístupu k %2$s", - "AnonymousUserHasViewAccess2": "Vaše analytická hlášení a informace o návštěvnících je veřejně dostupná.", + "AnonymousUserHasViewAccess2": "Vaše analytická hlášení a informace o návštěvnících jsou veřejně dostupná.", "ApplyToAllWebsites": "Použít na všechny weby", "ChangeAllConfirm": "Jste si jistí, že chcete změnit '%s' oprávnění pro všechny weby?", "ChangePasswordConfirm": "Změna hesla také změní uživatelův token_auth. Opravdu chcete pokračovat?", @@ -15,8 +15,8 @@ "ConfirmProhibitMySuperUserAccess": "%s, opravdu chcete odstranit svůjvlastn í super uživatelský přístup? Přijdete o všechna oprávnění a o přístup k datům stránek a budete odhlášen z Piwiku.", "ConfirmProhibitOtherUsersSuperUserAccess": "Opravdu chcete odstranit super uživatelský přístup pro %s? Tento uživatel přijde o všechna práva a o přístup ke všem stránkám. Nezapomeňte mu povolit přístup k těm, které potřebuje, je-li to nutné.", "DeleteConfirm": "Jste si jistí, že chcete vymazat uživatele %s?", - "Email": "E-mail", - "EmailYourAdministrator": "%1$sPošlete administrátorovi e-mail o tomto problému%2$s.", + "Email": "Email", + "EmailYourAdministrator": "%1$sPošlete administrátorovi email o tomto problému%2$s.", "ExceptionAccessValues": "Parametr přístupu musí mít jednu z následujících hodnot: [ %s ]", "ExceptionAdminAnonymous": "Nemůžete dát 'admin' přístup 'anonymous' uživateli.", "ExceptionDeleteDoesNotExist": "Uživatel '%s' neexistuje a proto nemůže být vymazán.", @@ -61,7 +61,7 @@ "User": "Uživatel", "UsersManagement": "Správa uživatelů", "UsersManagementMainDescription": "Vytvořte nové uživatele, nebo aktualizujte existující. Níže můžete nastavit jejich oprávnění.", - "WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess": "Když uživatelé nejsou přihlášení do Piwiku, můžou mít přístup k", + "WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess": "Když uživatelé nejsou přihlášeni do Piwiku, mohou přistupovat k", "YourUsernameCannotBeChanged": "Vaše uživatelské jméno nemůže být změněno", "YourVisitsAreIgnoredOnDomain": "%sVaše návštěvy jsou vynechávány Piwikem na %s %s (cookie pro vynechání byla nalezena ve vašem prohlížeči).", "YourVisitsAreNotIgnored": "%sVaše návštěvy nejsou vynechávány Piwikem%s (cookie pro vynechání nebyla nalezena ve vašem prohlížeči)." diff --git a/plugins/UsersManager/lang/ja.json b/plugins/UsersManager/lang/ja.json index 6f1b01a556..2d1bf2acb5 100644 --- a/plugins/UsersManager/lang/ja.json +++ b/plugins/UsersManager/lang/ja.json @@ -42,8 +42,11 @@ "MenuAnonymousUserSettings": "anonymous ユーザーの設定", "MenuUsers": "ユーザー", "MenuUserSettings": "ユーザーの設定", + "MenuPersonal": "個人用", + "PersonalSettings": "個人設定", "NoteNoAnonymousUserAccessSettingsWontBeUsed2": "注) 匿名ユーザーがアクセスできるウェブサイトをお持ちでないため、このセクションでは設定の変更ができません。", "NoUsersExist": "まだユーザーがいません。", + "PluginDescription": "ユーザー管理では、新しいユーザーの追加、既存ユーザーの編集、および web サイトの管理や閲覧の権限を設定できます。", "PrivAdmin": "管理", "PrivNone": "権限なし", "PrivView": "ビュー", diff --git a/plugins/UsersManager/templates/index.twig b/plugins/UsersManager/templates/index.twig index e2fb24dc9a..6bf12d01e0 100644 --- a/plugins/UsersManager/templates/index.twig +++ b/plugins/UsersManager/templates/index.twig @@ -15,6 +15,7 @@ {% endset %} <div piwik-siteselector + show-selected-site="true" class="sites_autocomplete" siteid="{{ idSiteSelected }}" sitename="{{ defaultReportSiteName }}" diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig index 57e04dff85..6b075eda86 100644 --- a/plugins/UsersManager/templates/userSettings.twig +++ b/plugins/UsersManager/templates/userSettings.twig @@ -45,6 +45,14 @@ </div> <div class="form-group"> + <label for="timeformat">{{ 'General_TimeFormat'|translate }}</label> + <select name="timeformat" id="timeformat"> + <option value="1" {% if currentTimeformat == 1 %}selected="selected"{% endif %} title="{{ 'General_12HourClock'|translate }}">{{ 'General_12HourClock'|translate }}</option> + <option value="0" {% if currentTimeformat == 0 %}selected="selected"{% endif %} title="{{ 'General_24HourClock'|translate }}">{{ 'General_24HourClock'|translate }}</option> + </select> + </div> + + <div class="form-group"> <label>{{ 'UsersManager_ReportToLoadByDefault'|translate }}</label> <label class="radio"> <input id="defaultReportRadioAll" type="radio" value="MultiSites" @@ -57,6 +65,7 @@ {{ 'General_DashboardForASpecificWebsite'|translate }} </label> <div piwik-siteselector + show-selected-site="true" class="sites_autocomplete" siteid="{{ defaultReportIdSite }}" sitename="{{ defaultReportSiteName }}" diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php index a6f9971480..550212d9e3 100644 --- a/plugins/VisitFrequency/API.php +++ b/plugins/VisitFrequency/API.php @@ -45,7 +45,6 @@ class API extends \Piwik\Plugin\API 'segment' => $segment, 'columns' => implode(',', $columns), 'format' => 'original', - 'serialize' => 0, // tests set this to 1 'format_metrics' => 0 ); diff --git a/plugins/VisitFrequency/lang/id.json b/plugins/VisitFrequency/lang/id.json index 4149ae29fc..d44b555ef2 100644 --- a/plugins/VisitFrequency/lang/id.json +++ b/plugins/VisitFrequency/lang/id.json @@ -10,6 +10,8 @@ "ColumnReturningVisits": "Kunjungan Kembali", "ColumnSumVisitLengthReturning": "Jumlah waktu digunakan oleh pengunjung kembali (dalam detik)", "ColumnUniqueReturningVisitors": "Pengnjung kembali unik", + "ColumnReturningUsers": "Penguna kembali", + "PluginDescription": "Laporan matriks mengenai pengunjung baru dan pengunjung kembali.", "ReturnActions": "%s tindakan tiap kunjungan kembali", "ReturnAverageVisitDuration": "%s rerata waktu kunjungan pengunjung kembali", "ReturnAvgActions": "%s tindakan tiap kunjungan kembali", diff --git a/plugins/VisitFrequency/lang/ko.json b/plugins/VisitFrequency/lang/ko.json index 4192687557..7a5880db1c 100644 --- a/plugins/VisitFrequency/lang/ko.json +++ b/plugins/VisitFrequency/lang/ko.json @@ -10,6 +10,8 @@ "ColumnReturningVisits": "돌아온 방문 수", "ColumnSumVisitLengthReturning": "리피터의 총 머문 시간 (초)", "ColumnUniqueReturningVisitors": "고유한 돌아온 방문자", + "ColumnReturningUsers": "돌아온 사용자", + "PluginDescription": "처음 방문한 방문자와 다시 돌아온 방문자에 대한", "ReturnActions": "%s 돌아온 방문의 활동", "ReturnAverageVisitDuration": "%s 돌아온 방문자의 평균 방문 시간", "ReturnAvgActions": "%s 돌아온 방문당 활동", @@ -17,8 +19,8 @@ "ReturningVisitDocumentation": "돌아온 방문은 (신규 방문과는 대조적으로), 적어도 한 번은 이전에 웹 사이트를 방문한 사람에 의해 이루어집니다.", "ReturningVisitsDocumentation": "돌아온 방문 개요입니다.", "ReturnVisits": "%s 돌아온 방문", - "SubmenuFrequency": "프리퀸시", + "SubmenuFrequency": "빈도", "WidgetGraphReturning": "돌아온 방문 수 그래프", - "WidgetOverview": "프리퀸시 개요" + "WidgetOverview": "빈도 개요" } }
\ No newline at end of file diff --git a/plugins/VisitTime/lang/cs.json b/plugins/VisitTime/lang/cs.json index ab1e8150f7..c49f2bc6a6 100644 --- a/plugins/VisitTime/lang/cs.json +++ b/plugins/VisitTime/lang/cs.json @@ -6,13 +6,13 @@ "LocalTime": "Návštěvy podle lokálního času", "NHour": "%sh", "PluginDescription": "Hlásí místní a serverový čas, kdy vaši návštěvníci zobrazují stránky nebo aplikaci.", - "ServerTime": "Návštěvy podle serverového času", + "ServerTime": "Návštěvy podle času na serveru", "SubmenuTimes": "Časy", "VisitsByDayOfWeek": "Návštěvy podle dnů v týdnu", "WidgetByDayOfWeekDocumentation": "Tento graf zobrazuje, kolik návštěv obdržela vaše stránka každý den v týdnu.", "WidgetLocalTime": "Návštěvy podle lokálního času", "WidgetLocalTimeDocumentation": "Tento graf ukazuje čas v %s návštěvníkovo časové zóně %s během jěho návštěvy.", - "WidgetServerTime": "Návštěvy podle serverového času", + "WidgetServerTime": "Návštěvy podle času na serveru", "WidgetServerTimeDocumentation": "Tento graf ukazuje jaký čas byl na %s serveru časové zóny %s během návstěvy." } }
\ No newline at end of file diff --git a/plugins/VisitTime/lang/ko.json b/plugins/VisitTime/lang/ko.json index c83bd88aac..216933c951 100644 --- a/plugins/VisitTime/lang/ko.json +++ b/plugins/VisitTime/lang/ko.json @@ -5,6 +5,7 @@ "DayOfWeek": "요일", "LocalTime": "현지 시간 기준 방문 수", "NHour": "%s시", + "PluginDescription": "방문자가 웹사이트나 앱을 볼 때 현지 시간 및 서버 시간을 알려줍니다.", "ServerTime": "서버 시간 기준 방문 수", "SubmenuTimes": "방문시간", "VisitsByDayOfWeek": "요일별 방문수", diff --git a/plugins/VisitorGenerator b/plugins/VisitorGenerator -Subproject 6ef251bbe316882c010408180c293af3b2a2ef2 +Subproject c3abd090cf8a89894ce427b56312920f794e612 diff --git a/plugins/VisitorInterest/lang/cs.json b/plugins/VisitorInterest/lang/cs.json index 151191809e..6ee78b7b7e 100644 --- a/plugins/VisitorInterest/lang/cs.json +++ b/plugins/VisitorInterest/lang/cs.json @@ -8,17 +8,17 @@ "NPages": "%s stránek", "OnePage": "1 stránka", "PluginDescription": "Hlásí zájem vašich návštěvníků: počet zobrazených stránek, čas na stránkách, dny od poslední návštěvy aj.", - "VisitNum": "Číslo návstěvníka", - "VisitsByDaysSinceLast": "Návstěv po dnech od poslední návštěvy", + "VisitNum": "Číslo návštěvníka", + "VisitsByDaysSinceLast": "Návštěvy po dnech od posledního navštívení", "visitsByVisitCount": "Návstěvy podle pořadí", - "VisitsPerDuration": "Návštěv za dobu návštěvy", - "VisitsPerNbOfPages": "Návštěv za počet stránek", + "VisitsPerDuration": "Doba zobrazení na návštěvníka", + "VisitsPerNbOfPages": "Počet stránek na návštěvníka", "WidgetLengths": "Doba návštěv", "WidgetLengthsDocumentation": "V tomto hlášení vidíte, kolik návštěv mělo určitou celkovou délku. Nejdříve je toto hlášení zobrazeno jako tag cloud, častější doby jsou větším písmem.", "WidgetPages": "Stránek za návštěvu", "WidgetPagesDocumentation": "V tomto hlášení je zobrazeno, kolik návštěv provedlo určité množství zobrazení stránek. Nejdříve je toto hlášení zobrazeno jako tag cloud, častější čísla mají větší písmo.", "WidgetVisitsByDaysSinceLast": "Návštěvy podle dnů od poslední návštěvy", - "WidgetVisitsByDaysSinceLastDocumentation": "V hlášení můžete vidět kolik návstěv bylo od návstěvníků, kteří již v minulosti po několika dnech stránku navstívili.", + "WidgetVisitsByDaysSinceLastDocumentation": "V tomto hlášení vidíte kolik návštěv provedli návštěvníci, kteří v minulosti stránku navštívili opakovaně.", "WidgetVisitsByNumDocumentation": "V tomto hlášení vidíte, kolik návštěv bylo n-tou návštěvou, t. j. návštěvníky, kteří navštívili vaše stránky aspoň n-krát." } }
\ No newline at end of file diff --git a/plugins/VisitorInterest/lang/ja.json b/plugins/VisitorInterest/lang/ja.json index fab4023ba2..521c594e90 100644 --- a/plugins/VisitorInterest/lang/ja.json +++ b/plugins/VisitorInterest/lang/ja.json @@ -7,6 +7,7 @@ "Engagement": "利用状況", "NPages": "%s ページ", "OnePage": "1 ページ", + "PluginDescription": "ビジターの関心についてのレポート:閲覧したページ数、ウェブサイトに費やされた時間、最後の訪問以来の日数、その他", "VisitNum": "訪問回数", "VisitsByDaysSinceLast": "最後の訪問からの日数別のビジット", "visitsByVisitCount": "訪問回数別のビジット", diff --git a/plugins/VisitorInterest/lang/ko.json b/plugins/VisitorInterest/lang/ko.json index be803fec11..75b9e8c7ce 100644 --- a/plugins/VisitorInterest/lang/ko.json +++ b/plugins/VisitorInterest/lang/ko.json @@ -7,6 +7,7 @@ "Engagement": "이용상황", "NPages": "%s페이지", "OnePage": "1페이지", + "PluginDescription": "방문자의 관심에 대한 보고서: 읽은 페이지 수, 웹사이트 방문 시간, 지난 마지막 방문으로부터 며칠 등", "VisitNum": "방문 횟수", "VisitsByDaysSinceLast": "마지막 방문일로부터 방문", "visitsByVisitCount": "방문 빈도", diff --git a/plugins/VisitsSummary/lang/cs.json b/plugins/VisitsSummary/lang/cs.json index 54a409e23d..004245c9e4 100644 --- a/plugins/VisitsSummary/lang/cs.json +++ b/plugins/VisitsSummary/lang/cs.json @@ -6,7 +6,7 @@ "GenerateTime": "%s sekund k vygenerování stránky", "MaxNbActions": "%s maximální počet akcí na návštěvu", "NbActionsDescription": "%s akcí (zobrazení stránek, stažení a odchodů)", - "NbActionsPerVisit": "%s akcí za návštěvu", + "NbActionsPerVisit": "%s akcí (zobrazení stránek, stažení, odkazů a vyhledávání) na návštěvu", "NbDownloadsDescription": "%s stažení", "NbKeywordsDescription": "%s unikátní klíčová slova", "NbOutlinksDescription": "%s externích odkazů", diff --git a/plugins/VisitsSummary/lang/ko.json b/plugins/VisitsSummary/lang/ko.json index c7b22f205b..ec21c7d19e 100644 --- a/plugins/VisitsSummary/lang/ko.json +++ b/plugins/VisitsSummary/lang/ko.json @@ -1,5 +1,6 @@ { "VisitsSummary": { + "AverageGenerationTime": "%s 평균 페이지 생성 시간", "AverageVisitDuration": "%s 방문의 평균 지속 시간", "GenerateQueries": "%s 쿼리 실행됨", "GenerateTime": "페이지 생성에 %s 초 걸림", @@ -16,6 +17,7 @@ "NbUniquePageviewsDescription": "%s 고유 페이지뷰", "NbUniqueVisitors": "%s 고유 방문자", "NbVisitsBounced": "%s 반송률 (첫 페이지에서 이탈)", + "PluginDescription": "보고서 기본 분석 측정 기준: 방문횟수, 고유 방문자, 활동 횟수, 반송률 등", "VisitsSummary": "방문 개요", "VisitsSummaryDocumentation": "방문 추이 개요입니다.", "WidgetLastVisits": "최근 방문 그래프", diff --git a/plugins/WebsiteMeasurable/lang/ja.json b/plugins/WebsiteMeasurable/lang/ja.json new file mode 100644 index 0000000000..51f760f6ac --- /dev/null +++ b/plugins/WebsiteMeasurable/lang/ja.json @@ -0,0 +1,7 @@ +{ + "WebsiteMeasurable": { + "Website": "ウェブサイト", + "Websites": "ウェブサイト", + "WebsiteDescription": "Web サイトは、通常単一の web ドメインから提供される web ページで構成されています。" + } +}
\ No newline at end of file diff --git a/plugins/WebsiteMeasurable/lang/ko.json b/plugins/WebsiteMeasurable/lang/ko.json new file mode 100644 index 0000000000..769d1388fe --- /dev/null +++ b/plugins/WebsiteMeasurable/lang/ko.json @@ -0,0 +1,7 @@ +{ + "WebsiteMeasurable": { + "Website": "웹사이트", + "Websites": "웹사이트", + "WebsiteDescription": "웹 페이지가 포함된 웹사이트는 대체로 하나의 웹 도메인에서 제공됩니다." + } +}
\ No newline at end of file diff --git a/plugins/Widgetize/lang/nb.json b/plugins/Widgetize/lang/nb.json index a448e5258f..659ed7e34f 100644 --- a/plugins/Widgetize/lang/nb.json +++ b/plugins/Widgetize/lang/nb.json @@ -1,5 +1,6 @@ { "Widgetize": { - "OpenInNewWindow": "Åpne i nytt vindu" + "OpenInNewWindow": "Åpne i nytt vindu", + "TopLinkTooltip": "Eksporter Piwik-rapporter som widgeter og bygg dem inn kontrollpanelet i din app som en iframe." } }
\ No newline at end of file diff --git a/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php b/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php index b516c85d65..227e45bd4a 100644 --- a/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php +++ b/tests/PHPUnit/Fixtures/ManySitesImportedLogsWithXssAttempts.php @@ -57,6 +57,11 @@ class ManySitesImportedLogsWithXssAttempts extends ManySitesImportedLogs self::createWebsite($this->dateTime, $ecommerce = 0, $siteName = 'Piwik test two', $siteUrl = 'http://example-site-two.com'); } + + if (!self::siteCreated($idSite = 3)) { + self::createWebsite($this->dateTime, $ecommerce = 0, $siteName = 'Piwik test three', + $siteUrl = 'http://example-site-three.com'); + } } public function addAnnotations() diff --git a/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php b/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php index 9db794269d..ce454b38d2 100644 --- a/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php +++ b/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php @@ -265,9 +265,6 @@ class ManyVisitsWithGeoIP extends Fixture public static function unsetLocationProvider() { - // also fails on other PHP, is it really needed? - return; - try { LocationProvider::setCurrentProvider('default'); } catch(Exception $e) { diff --git a/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php b/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php index dd2a2c773c..9fbd6f986d 100644 --- a/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php +++ b/tests/PHPUnit/Fixtures/ManyVisitsWithMockLocationProvider.php @@ -33,6 +33,8 @@ class ManyVisitsWithMockLocationProvider extends Fixture $this->setUpWebsitesAndGoals(); $this->setMockLocationProvider(); $this->trackVisits(); + + ManyVisitsWithGeoIP::unsetLocationProvider(); } public function tearDown() diff --git a/tests/PHPUnit/Fixtures/OmniFixture.php b/tests/PHPUnit/Fixtures/OmniFixture.php index dd680e9085..cd6fc0fb34 100644 --- a/tests/PHPUnit/Fixtures/OmniFixture.php +++ b/tests/PHPUnit/Fixtures/OmniFixture.php @@ -32,11 +32,29 @@ class OmniFixture extends Fixture public $fixtures = array(); + private function requireAllFixtures() + { + $fixturesToLoad = array( + '/tests/PHPUnit/Fixtures/*.php', + '/tests/UI/Fixtures/*.php', + '/plugins/*/tests/Fixtures/*.php', + '/plugins/*/Test/Fixtures/*.php', + ); + + foreach($fixturesToLoad as $fixturePath) { + foreach (glob(PIWIK_INCLUDE_PATH . $fixturePath) as $file) { + require_once $file; + } + } + } + /** * Constructor. */ public function __construct() { + $this->requireAllFixtures(); + $date = $this->month . '-01'; $classes = get_declared_classes(); @@ -97,6 +115,8 @@ class OmniFixture extends Fixture public function setUp() { foreach ($this->fixtures as $fixture) { + echo "Setting up " . get_class($fixture) . "...\n"; + $fixture->setUp(); } @@ -111,6 +131,8 @@ class OmniFixture extends Fixture public function tearDown() { foreach ($this->fixtures as $fixture) { + echo "Tearing down " . get_class($fixture) . "...\n"; + $fixture->tearDown(); } } diff --git a/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php b/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php index 8ba755fabd..a32129c93b 100644 --- a/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php +++ b/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php @@ -36,10 +36,9 @@ class SomeVisitsCustomVariablesCampaignsNotHeuristics extends Fixture private function setPiwikEnvironmentOverrides() { - $configOverride = $this->getTestEnvironment()->configOverride; - $configOverride['Tracker']['create_new_visit_when_website_referrer_changes'] = 1; - $this->getTestEnvironment()->configOverride = $configOverride; - $this->getTestEnvironment()->save(); + $env = $this->getTestEnvironment(); + $env->overrideConfig('Tracker', 'create_new_visit_when_website_referrer_changes', 1); + $env->save(); } private function setUpWebsitesAndGoals() diff --git a/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php b/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php index 8983d1eea5..d2740b8aba 100644 --- a/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php +++ b/tests/PHPUnit/Fixtures/TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers.php @@ -23,7 +23,7 @@ class TwoSitesManyVisitsOverSeveralDaysWithSearchEngineReferrers extends Fixture public $keywords = array( 'free > proprietary', // testing a keyword containing > 'peace "," not war', // testing a keyword containing , - 'justice )(&^#%$ NOT corruption!', + 'justice )(&^#%$ NOT \'" corruption!', ); public function setUp() diff --git a/tests/PHPUnit/Fixtures/UITestFixture.php b/tests/PHPUnit/Fixtures/UITestFixture.php index 93ae92a6dd..623e7e388d 100644 --- a/tests/PHPUnit/Fixtures/UITestFixture.php +++ b/tests/PHPUnit/Fixtures/UITestFixture.php @@ -24,6 +24,7 @@ use Piwik\Plugins\UsersManager\API as UsersManagerAPI; use Piwik\Plugins\SitesManager\API as SitesManagerAPI; use Piwik\Tests\Framework\Fixture; use Piwik\Plugins\VisitsSummary\API as VisitsSummaryAPI; +use Piwik\Config as PiwikConfig; /** * Fixture for UI tests. @@ -44,7 +45,9 @@ class UITestFixture extends SqlDump parent::setUp(); + self::resetPluginsInstalledConfig(); self::updateDatabase(); + self::installAndActivatePlugins($this->getTestEnvironment()); // make sure site has an early enough creation date (for period selector tests) Db::get()->update(Common::prefixTable("site"), diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php index acd2f5c9f4..f421ba5658 100644 --- a/tests/PHPUnit/Framework/Fixture.php +++ b/tests/PHPUnit/Framework/Fixture.php @@ -107,6 +107,7 @@ class Fixture extends \PHPUnit_Framework_Assert public $testCaseClass = false; public $extraPluginsToLoad = array(); + public $extraDiEnvironments = array(); public $testEnvironment = null; @@ -141,6 +142,11 @@ class Fixture extends \PHPUnit_Framework_Assert return 'python'; } + public static function getTestRootUrl() + { + return self::getRootUrl() . 'tests/PHPUnit/proxy/'; + } + public function loginAsSuperUser() { /** @var Auth $auth */ @@ -175,7 +181,7 @@ class Fixture extends \PHPUnit_Framework_Assert return $id; } - return Config::getInstance()->database_tests['dbname']; + return self::getConfig()->database_tests['dbname']; } public function performSetUp($setupEnvironmentOnly = false) @@ -196,6 +202,7 @@ class Fixture extends \PHPUnit_Framework_Assert $testEnv->testCaseClass = $this->testCaseClass; $testEnv->fixtureClass = get_class($this); $testEnv->dbName = $this->dbName; + $testEnv->extraDiEnvironments = $this->extraDiEnvironments; foreach ($this->extraTestEnvVars as $name => $value) { $testEnv->$name = $value; @@ -206,7 +213,7 @@ class Fixture extends \PHPUnit_Framework_Assert $this->createEnvironmentInstance(); if ($this->dbName === false) { // must be after test config is created - $this->dbName = Config::getInstance()->database['dbname']; + $this->dbName = self::getConfig()->database['dbname']; } try { @@ -223,21 +230,19 @@ class Fixture extends \PHPUnit_Framework_Assert Tracker::disconnectCachedDbConnection(); // reconnect once we're sure the database exists - Config::getInstance()->database['dbname'] = $this->dbName; + self::getConfig()->database['dbname'] = $this->dbName; Db::createDatabaseObject(); Db::get()->query("SET wait_timeout=28800;"); DbHelper::createTables(); - Manager::getInstance()->unloadPlugins(); + self::getPluginManager()->unloadPlugins(); } catch (Exception $e) { static::fail("TEST INITIALIZATION FAILED: " . $e->getMessage() . "\n" . $e->getTraceAsString()); } - include "DataFiles/SearchEngines.php"; - include "DataFiles/Socials.php"; include "DataFiles/Providers.php"; if (!$this->isFixtureSetUp()) { @@ -249,11 +254,12 @@ class Fixture extends \PHPUnit_Framework_Assert Cache::deleteTrackerCache(); - static::loadAllPlugins($this->getTestEnvironment(), $this->testCaseClass, $this->extraPluginsToLoad); + self::resetPluginsInstalledConfig(); + $testEnvironment = $this->getTestEnvironment(); + static::loadAllPlugins($testEnvironment, $this->testCaseClass, $this->extraPluginsToLoad); self::updateDatabase(); - - self::installAndActivatePlugins(); + self::installAndActivatePlugins($testEnvironment); $_GET = $_REQUEST = array(); $_SERVER['HTTP_REFERER'] = ''; @@ -277,7 +283,7 @@ class Fixture extends \PHPUnit_Framework_Assert APILanguageManager::getInstance()->setLanguageForUser('superUserLogin', 'en'); } - SettingsPiwik::overwritePiwikUrl(self::getRootUrl() . 'tests/PHPUnit/proxy/'); + SettingsPiwik::overwritePiwikUrl(self::getTestRootUrl()); if ($setupEnvironmentOnly) { return; @@ -359,10 +365,26 @@ class Fixture extends \PHPUnit_Framework_Assert $_GET = $_REQUEST = array(); Translate::reset(); - Config::getInstance()->Plugins; // make sure Plugins exists in a config object for next tests that use Plugin\Manager + self::getConfig()->Plugins; // make sure Plugins exists in a config object for next tests that use Plugin\Manager // since Plugin\Manager uses getFromGlobalConfig which doesn't init the config object } + protected static function resetPluginsInstalledConfig() + { + $config = self::getConfig(); + $installed = $config->PluginsInstalled; + $installed['PluginsInstalled'] = array(); + $config->PluginsInstalled = $installed; + } + + protected static function rememberCurrentlyInstalledPluginsAcrossRequests(TestingEnvironmentVariables $testEnvironment) + { + $plugins = self::getPluginManager()->getInstalledPluginsName(); + + $testEnvironment->overrideConfig('PluginsInstalled', 'PluginsInstalled', $plugins); + $testEnvironment->save(); + } + /** * @param \Piwik\Tests\Framework\TestingEnvironmentVariables|null $testEnvironment Ignored. * @param bool|false $testCaseClass Ignored. @@ -371,12 +393,12 @@ class Fixture extends \PHPUnit_Framework_Assert public static function loadAllPlugins(TestingEnvironmentVariables $testEnvironment = null, $testCaseClass = false, $extraPluginsToLoad = array()) { DbHelper::createTables(); - Plugin\Manager::getInstance()->loadActivatedPlugins(); + self::getPluginManager()->loadActivatedPlugins(); } - public static function installAndActivatePlugins() + public static function installAndActivatePlugins(TestingEnvironmentVariables $testEnvironment) { - $pluginsManager = Manager::getInstance(); + $pluginsManager = self::getPluginManager(); // Install plugins $messages = $pluginsManager->installLoadedPlugins(); @@ -393,19 +415,35 @@ class Fixture extends \PHPUnit_Framework_Assert } $pluginsManager->loadPluginTranslations(); + + self::rememberCurrentlyInstalledPluginsAcrossRequests($testEnvironment); + } + + private static function getPluginManager() + { + return Manager::getInstance(); + } + + private static function getConfig() + { + return Config::getInstance(); } public static function unloadAllPlugins() { try { - $manager = Manager::getInstance(); + $manager = self::getPluginManager(); $plugins = $manager->getLoadedPlugins(); foreach ($plugins as $plugin) { $plugin->uninstall(); } - Manager::getInstance()->unloadPlugins(); + + $manager->unloadPlugins(); } catch (Exception $e) { } + + self::resetPluginsInstalledConfig(); + self::rememberCurrentlyInstalledPluginsAcrossRequests(new TestingEnvironmentVariables()); } /** @@ -455,6 +493,7 @@ class Fixture extends \PHPUnit_Framework_Assert // Clear the memory Website cache Site::clearCache(); + Cache::deleteCacheWebsiteAttributes($idSite); return $idSite; } @@ -466,13 +505,19 @@ class Fixture extends \PHPUnit_Framework_Assert */ public static function getRootUrl() { - $piwikUrl = Config::getInstance()->tests['http_host']; - $piwikUri = Config::getInstance()->tests['request_uri']; + $config = self::getConfig(); + $piwikUrl = $config->tests['http_host']; + $piwikUri = $config->tests['request_uri']; + $piwikPort = $config->tests['port']; if($piwikUri == '@REQUEST_URI@') { throw new Exception("Piwik is mis-configured. Remove (or fix) the 'request_uri' entry below [tests] section in your config.ini.php. "); } + if (!empty($piwikPort)) { + $piwikUrl = $piwikUrl . ':' . $piwikPort; + } + if (strpos($piwikUrl, 'http://') !== 0) { $piwikUrl = 'http://' . $piwikUrl . '/'; } @@ -508,7 +553,7 @@ class Fixture extends \PHPUnit_Framework_Assert */ public static function getTrackerUrl() { - return self::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php'; + return self::getTestRootUrl() . 'piwik.php'; } /** @@ -815,7 +860,7 @@ class Fixture extends \PHPUnit_Framework_Assert $cmd = $python . ' "' . PIWIK_INCLUDE_PATH . '/misc/log-analytics/import_logs.py" ' # script loc . '-ddd ' // debug - . '--url="' . self::getRootUrl() . 'tests/PHPUnit/proxy/" ' # proxy so that piwik uses test config files + . '--url="' . self::getTestRootUrl() . '" ' # proxy so that piwik uses test config files ; foreach ($options as $name => $values) { @@ -860,7 +905,7 @@ class Fixture extends \PHPUnit_Framework_Assert */ public static function connectWithoutDatabase() { - $dbConfig = Config::getInstance()->database; + $dbConfig = self::getConfig()->database; $oldDbName = $dbConfig['dbname']; $dbConfig['dbname'] = null; @@ -878,7 +923,7 @@ class Fixture extends \PHPUnit_Framework_Assert public function dropDatabase($dbName = null) { - $dbName = $dbName ?: $this->dbName ?: Config::getInstance()->database_tests['dbname']; + $dbName = $dbName ?: $this->dbName ?: self::getConfig()->database_tests['dbname']; $this->log("Dropping database '$dbName'..."); diff --git a/tests/PHPUnit/Framework/Mock/LocationProvider.php b/tests/PHPUnit/Framework/Mock/LocationProvider.php index 545f71130e..3423c53a7b 100755 --- a/tests/PHPUnit/Framework/Mock/LocationProvider.php +++ b/tests/PHPUnit/Framework/Mock/LocationProvider.php @@ -27,6 +27,11 @@ class LocationProvider extends CountryLocationProvider if (isset($this->ipToLocations[$ip])) { $result = $this->ipToLocations[$ip]; } else { + if (!isset(self::$locations[$this->currentLocation])) { + throw new \Exception("Unknown location index in mock LocationProvider {$this->currentLocation}. This " + . "shouldn't ever happen, it is likely something is using the mock LocationProvider when it should be using a real one."); + } + $result = self::$locations[$this->currentLocation]; $this->currentLocation = ($this->currentLocation + 1) % count(self::$locations); diff --git a/tests/PHPUnit/Framework/TestRequest/Response.php b/tests/PHPUnit/Framework/TestRequest/Response.php index 6d729ca941..8a3ed5d39e 100644 --- a/tests/PHPUnit/Framework/TestRequest/Response.php +++ b/tests/PHPUnit/Framework/TestRequest/Response.php @@ -11,6 +11,7 @@ namespace Piwik\Tests\Framework\TestRequest; use Piwik\API\Request; use PHPUnit_Framework_Assert as Asserts; use Exception; +use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\TestCase\SystemTestCase; /** @@ -114,6 +115,7 @@ class Response $apiResponse = $this->normalizeDecimalFields($apiResponse); $apiResponse = $this->normalizeEncodingPhp533($apiResponse); $apiResponse = $this->normalizeSpaces($apiResponse); + $apiResponse = $this->replacePiwikUrl($apiResponse); return $apiResponse; } @@ -258,4 +260,18 @@ class Response { return $this->removeXmlFields($apiResponse, array('idsubdatatable_in_db')); } + + /** + * To allow tests to pass no matter what port Piwik is on, we replace the test URL w/ another + * one in the response. We don't remove the URL outright, because then we would not be able + * to detect regressions where the root URL went missing. + * + * @param $apiResponse + * @return mixed + * @throws Exception + */ + private function replacePiwikUrl($apiResponse) + { + return str_replace(Fixture::getRootUrl(), "http://example.com/piwik/", $apiResponse); + } } diff --git a/tests/PHPUnit/Framework/TestingEnvironmentManipulator.php b/tests/PHPUnit/Framework/TestingEnvironmentManipulator.php index be1c17f959..df6a011af5 100644 --- a/tests/PHPUnit/Framework/TestingEnvironmentManipulator.php +++ b/tests/PHPUnit/Framework/TestingEnvironmentManipulator.php @@ -190,7 +190,12 @@ class TestingEnvironmentManipulator implements EnvironmentManipulator public function getExtraEnvironments() { - return array('test'); + $result = array('test'); + + $extraEnvironments = $this->vars->extraDiEnvironments ?: array(); + $result = array_merge($result, $extraEnvironments); + + return $result; } private function getPluginsToLoadDuringTest() @@ -234,4 +239,4 @@ class TestingEnvironmentManipulator implements EnvironmentManipulator . "Is the namespace correct? Is the file in the correct folder?"); } } -}
\ No newline at end of file +} diff --git a/tests/PHPUnit/Framework/TestingEnvironmentVariables.php b/tests/PHPUnit/Framework/TestingEnvironmentVariables.php index 4b33a7fe07..60942d2510 100644 --- a/tests/PHPUnit/Framework/TestingEnvironmentVariables.php +++ b/tests/PHPUnit/Framework/TestingEnvironmentVariables.php @@ -39,6 +39,45 @@ class TestingEnvironmentVariables return isset($this->behaviorOverrideProperties[$name]); } + /** + * Overrides a config entry. + * + * You can use this method either to set one specific config value `overrideConfig(group, name, value)` + * or you can set a whole group of values `overrideConfig(group, valueObject)`. + * + * @param string $group Eg 'General', 'log', or any other config group name + * @param string|array $name The name of the config within the group you want to overwrite. If you want to overwrite + * the whole group just leave `$value` empty and instead provide an array of key/value pairs + * here. + * @param string|int|array|null $value The value you want to set for the given config. + * @throws \Exception if no name is set + */ + public function overrideConfig($group, $name, $value = null) + { + if (empty($name) && !is_array($name)) { + throw new \Exception('No name set that needs to be overwritten'); + } + + $config = $this->configOverride; + + if (empty($config)) { + $config = array(); + } + + if (!isset($value) && is_array($name)) { + $config[$group] = $name; + $this->configOverride = $config; + return; + } + + if (!isset($config[$group])) { + $config[$group] = array(); + } + + $config[$group][$name] = $value; + $this->configOverride = $config; + } + public function save() { $includePath = __DIR__ . '/../../..'; diff --git a/tests/PHPUnit/Integration/DbTest.php b/tests/PHPUnit/Integration/DbTest.php index c21a277061..9a5595e29a 100644 --- a/tests/PHPUnit/Integration/DbTest.php +++ b/tests/PHPUnit/Integration/DbTest.php @@ -52,7 +52,7 @@ class DbTest extends IntegrationTestCase $this->assertInstanceOf($expectedClass, $db); $result = $db->fetchOne('SELECT @@SESSION.sql_mode'); - $expected = 'NO_AUTO_VALUE_ON_ZERO,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'; + $expected = 'NO_AUTO_VALUE_ON_ZERO'; $this->assertSame($expected, $result); } diff --git a/tests/PHPUnit/Integration/Plugin/ManagerTest.php b/tests/PHPUnit/Integration/Plugin/ManagerTest.php index 924edecf00..e05e3a021e 100644 --- a/tests/PHPUnit/Integration/Plugin/ManagerTest.php +++ b/tests/PHPUnit/Integration/Plugin/ManagerTest.php @@ -15,6 +15,7 @@ use Piwik\Plugin; use Piwik\Settings\Storage; use Piwik\Cache as PiwikCache; use Piwik\Tests\Integration\Settings\IntegrationTestCase; +use Piwik\Widget\WidgetsList; /** * @group Plugin @@ -96,7 +97,7 @@ class ManagerTest extends IntegrationTestCase list($controller, $module, $action) = explode('.', $hook); try { - $resolver = new ControllerResolver(StaticContainer::getContainer()); + $resolver = new ControllerResolver(StaticContainer::getContainer(), new Plugin\Widgets($this->manager)); $params = array(); $controller = $resolver->getController($module, $action, $params); } catch (\Exception $e) { @@ -109,6 +110,36 @@ class ManagerTest extends IntegrationTestCase } } + /** + * @dataProvider getPluginNameProvider + */ + public function test_isValidPluginName($expectedIsValid, $pluginName) + { + $valid = $this->manager->isValidPluginName($pluginName); + $this->assertSame($expectedIsValid, $valid); + } + + public function getPluginNameProvider() + { + return array( + array(true, 'a'), + array(true, 'a0'), + array(true, 'pluginNameTest'), + array(true, 'PluginNameTest'), + array(true, 'PluginNameTest92323232eerwrwere938'), + array(false, ''), + array(false, '0'), + array(false, '0a'), + array(false, 'a.'), + array(false, 'a-'), + array(false, 'a_'), + array(false, 'a-ererer'), + array(false, 'a_ererer'), + array(false, '..'), + array(false, '/'), + ); + } + private function getCacheForTrackerPlugins() { return PiwikCache::getEagerCache(); diff --git a/tests/PHPUnit/Integration/ReleaseCheckListTest.php b/tests/PHPUnit/Integration/ReleaseCheckListTest.php index b33a7c1949..58970199f0 100644 --- a/tests/PHPUnit/Integration/ReleaseCheckListTest.php +++ b/tests/PHPUnit/Integration/ReleaseCheckListTest.php @@ -528,35 +528,35 @@ class ReleaseCheckListTest extends \PHPUnit_Framework_TestCase 'vendor/mnapoli/php-di/website', 'vendor/mnapoli/php-di/news', 'vendor/mnapoli/php-di/doc', - 'vendor/tecnick.com/tcpdf/examples', - 'vendor/tecnick.com/tcpdf/CHANGELOG.txt', + 'vendor/tecnickcom/tcpdf/examples', + 'vendor/tecnickcom/tcpdf/CHANGELOG.txt', 'vendor/guzzle/guzzle/docs/', // deleted fonts folders - 'vendor/tecnick.com/tcpdf/fonts/ae_fonts_2.0', - 'vendor/tecnick.com/tcpdf/fonts/dejavu-fonts-ttf-2.33', - 'vendor/tecnick.com/tcpdf/fonts/dejavu-fonts-ttf-2.34', - 'vendor/tecnick.com/tcpdf/fonts/freefont-20100919', - 'vendor/tecnick.com/tcpdf/fonts/freefont-20120503', + 'vendor/tecnickcom/tcpdf/fonts/ae_fonts_2.0', + 'vendor/tecnickcom/tcpdf/fonts/dejavu-fonts-ttf-2.33', + 'vendor/tecnickcom/tcpdf/fonts/dejavu-fonts-ttf-2.34', + 'vendor/tecnickcom/tcpdf/fonts/freefont-20100919', + 'vendor/tecnickcom/tcpdf/fonts/freefont-20120503', // In the package script, there is a trailing * so any font matching will be deleted - 'vendor/tecnick.com/tcpdf/fonts/freemon', - 'vendor/tecnick.com/tcpdf/fonts/cid', - 'vendor/tecnick.com/tcpdf/fonts/courier', - 'vendor/tecnick.com/tcpdf/fonts/aefurat', - 'vendor/tecnick.com/tcpdf/fonts/dejavusansb', - 'vendor/tecnick.com/tcpdf/fonts/dejavusansi', - 'vendor/tecnick.com/tcpdf/fonts/dejavusansmono', - 'vendor/tecnick.com/tcpdf/fonts/dejavusanscondensed', - 'vendor/tecnick.com/tcpdf/fonts/dejavusansextralight', - 'vendor/tecnick.com/tcpdf/fonts/dejavuserif', - 'vendor/tecnick.com/tcpdf/fonts/freesansi', - 'vendor/tecnick.com/tcpdf/fonts/freesansb', - 'vendor/tecnick.com/tcpdf/fonts/freeserifb', - 'vendor/tecnick.com/tcpdf/fonts/freeserifi', - 'vendor/tecnick.com/tcpdf/fonts/pdf', - 'vendor/tecnick.com/tcpdf/fonts/times', - 'vendor/tecnick.com/tcpdf/fonts/uni2cid', + 'vendor/tecnickcom/tcpdf/fonts/freemon', + 'vendor/tecnickcom/tcpdf/fonts/cid', + 'vendor/tecnickcom/tcpdf/fonts/courier', + 'vendor/tecnickcom/tcpdf/fonts/aefurat', + 'vendor/tecnickcom/tcpdf/fonts/dejavusansb', + 'vendor/tecnickcom/tcpdf/fonts/dejavusansi', + 'vendor/tecnickcom/tcpdf/fonts/dejavusansmono', + 'vendor/tecnickcom/tcpdf/fonts/dejavusanscondensed', + 'vendor/tecnickcom/tcpdf/fonts/dejavusansextralight', + 'vendor/tecnickcom/tcpdf/fonts/dejavuserif', + 'vendor/tecnickcom/tcpdf/fonts/freesansi', + 'vendor/tecnickcom/tcpdf/fonts/freesansb', + 'vendor/tecnickcom/tcpdf/fonts/freeserifb', + 'vendor/tecnickcom/tcpdf/fonts/freeserifi', + 'vendor/tecnickcom/tcpdf/fonts/pdf', + 'vendor/tecnickcom/tcpdf/fonts/times', + 'vendor/tecnickcom/tcpdf/fonts/uni2cid', ); return $this->isFilePathFoundInArray($file, $filesDeletedFromPackage); diff --git a/tests/PHPUnit/Integration/ReportTest.php b/tests/PHPUnit/Integration/ReportTest.php index 3475cee9cf..036516bd91 100644 --- a/tests/PHPUnit/Integration/ReportTest.php +++ b/tests/PHPUnit/Integration/ReportTest.php @@ -364,7 +364,8 @@ class ReportTest extends IntegrationTestCase 'format' => 'original', 'module' => 'API', 'method' => 'ExampleReport.getExampleReport', - 'format_metrics' => 'bc' + 'format_metrics' => 'bc', + 'serialize' => '0' ) )->willReturn("result"); Proxy::setSingletonInstance($proxyMock); @@ -387,7 +388,8 @@ class ReportTest extends IntegrationTestCase 'format' => 'original', 'module' => 'API', 'method' => 'Referrers.getSearchEnginesFromKeywordId', - 'format_metrics' => 'bc' + 'format_metrics' => 'bc', + 'serialize' => '0' ) )->willReturn("result"); Proxy::setSingletonInstance($proxyMock); diff --git a/tests/PHPUnit/Integration/SegmentTest.php b/tests/PHPUnit/Integration/SegmentTest.php index 9179c6d61a..eccd355ee3 100644 --- a/tests/PHPUnit/Integration/SegmentTest.php +++ b/tests/PHPUnit/Integration/SegmentTest.php @@ -94,8 +94,7 @@ class SegmentTest extends IntegrationTestCase // test multiple column segments array('customVariableName==abc;customVariableValue==def', array( - 'where' => ' ((log_visit.custom_var_k1 = ?) OR (log_visit.custom_var_k2 = ?) OR (log_visit.custom_var_k3 = ?) OR (log_visit.custom_var_k4 = ?) OR (log_visit.custom_var_k5 = ?))' - . ' AND ((log_visit.custom_var_v1 = ?) OR (log_visit.custom_var_v2 = ?) OR (log_visit.custom_var_v3 = ?) OR (log_visit.custom_var_v4 = ?) OR (log_visit.custom_var_v5 = ?)) ', + 'where' => ' (log_visit.custom_var_k1 = ? OR log_visit.custom_var_k2 = ? OR log_visit.custom_var_k3 = ? OR log_visit.custom_var_k4 = ? OR log_visit.custom_var_k5 = ?) AND (log_visit.custom_var_v1 = ? OR log_visit.custom_var_v2 = ? OR log_visit.custom_var_v3 = ? OR log_visit.custom_var_v4 = ? OR log_visit.custom_var_v5 = ? )', 'bind' => array( 'abc', 'abc', 'abc', 'abc', 'abc', 'def', 'def', 'def', 'def', 'def', @@ -162,7 +161,7 @@ class SegmentTest extends IntegrationTestCase $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } - public function test_getSelectQuery_whenJoinVisitOnAction() + public function test_getSelectQuery_whenJoinVisitOnLogLinkVisitAction() { $select = '*'; $from = 'log_link_visit_action'; @@ -226,7 +225,7 @@ class SegmentTest extends IntegrationTestCase $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } - public function test_getSelectQuery_whenJoinConversionOnAction() + public function test_getSelectQuery_whenJoinConversionOnLogLinkVisitAction() { $select = '*'; $from = 'log_link_visit_action'; @@ -372,11 +371,106 @@ class SegmentTest extends IntegrationTestCase $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } + public function test_getSelectQuery_whenJoinLogLinkVisitActionOnActionOnVisit_WithSameTableAlias() + { + $actionType = 3; + $idSite = 1; + $select = 'log_link_visit_action.custom_dimension_1, + log_action.name as url, + sum(log_link_visit_action.time_spent) as `13`, + sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`'; + $from = array( + 'log_link_visit_action', + array('table' => 'log_visit', 'joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'), + array('table' => 'log_action', 'joinOn' => 'log_link_visit_action.idaction_url = log_action.idaction'), + 'log_visit' + ); + $where = 'log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ?'; + $bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite); + + $segment = 'actionType==' . $actionType; + $segment = new Segment($segment, $idSites = array()); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $logVisitTable = Common::prefixTable('log_visit'); + $logActionTable = Common::prefixTable('log_action'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $expected = array( + "sql" => " + SELECT log_link_visit_action.custom_dimension_1, + log_action.name as url, + sum(log_link_visit_action.time_spent) as `13`, + sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6` + FROM $logLinkVisitActionTable AS log_link_visit_action + LEFT JOIN $logVisitTable AS log_visit + ON log_visit.idvisit = log_link_visit_action.idvisit + LEFT JOIN $logActionTable AS log_action + ON log_link_visit_action.idaction_url = log_action.idaction + WHERE ( log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ? ) + AND ( log_action.type = ? )", + "bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType)); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + + public function test_getSelectQuery_whenJoinLogLinkVisitActionOnActionOnVisit_WithSameTableAliasButDifferentJoin() + { + $actionType = 3; + $idSite = 1; + $select = 'log_link_visit_action.custom_dimension_1, + log_action.name as url, + sum(log_link_visit_action.time_spent) as `13`, + sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`'; + $from = array( + 'log_link_visit_action', + array('table' => 'log_visit', 'joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'), + array('table' => 'log_action', 'joinOn' => 'log_link_visit_action.idaction_name = log_action.idaction') + ); + $where = 'log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ?'; + $bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite); + + $segment = 'actionType==' . $actionType; + $segment = new Segment($segment, $idSites = array()); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $logVisitTable = Common::prefixTable('log_visit'); + $logActionTable = Common::prefixTable('log_action'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $expected = array( + "sql" => " + SELECT log_link_visit_action.custom_dimension_1, + log_action.name as url, + sum(log_link_visit_action.time_spent) as `13`, + sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6` + FROM $logLinkVisitActionTable AS log_link_visit_action + LEFT JOIN $logVisitTable AS log_visit + ON log_visit.idvisit = log_link_visit_action.idvisit + LEFT JOIN $logActionTable AS log_action + ON (log_link_visit_action.idaction_name = log_action.idaction AND log_link_visit_action.idaction_url = log_action.idaction) + WHERE ( log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ? ) + AND ( log_action.type = ? )", + "bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType)); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + /** * visit is joined on action, then conversion is joined * make sure that conversion is joined on action not visit */ - public function test_getSelectQuery_whenJoinVisitAndConversionOnAction() + public function test_getSelectQuery_whenJoinVisitAndConversionOnLogLinkVisitAction() { $select = '*'; $from = 'log_link_visit_action'; @@ -446,7 +540,201 @@ class SegmentTest extends IntegrationTestCase $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } - public function test_getSelectQuery_whenJoinConversionOnAction_segmentUsesPageUrl() + public function test_getSelectQuery_whenJoinVisitOnAction() + { + $actionType = 3; + $idSite = 1; + $select = 'count(distinct log_visit.idvisitor) AS `1`, + count(*) AS `2`, + sum(log_visit.visit_total_actions) AS `3`'; + $from = 'log_visit'; + $where = 'log_visit.visit_last_action_time >= ? + AND log_visit.visit_last_action_time <= ? + AND log_visit.idsite IN (?)'; + $bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite); + + $segment = 'actionType==' . $actionType; + $segment = new Segment($segment, $idSites = array()); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $logVisitTable = Common::prefixTable('log_visit'); + $logActionTable = Common::prefixTable('log_action'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $expected = array( + "sql" => " + SELECT count(distinct log_inner.idvisitor) AS `1`, count(*) AS `2`, sum(log_inner.visit_total_actions) AS `3` FROM ( SELECT log_visit.idvisitor, log_visit.visit_total_actions + FROM $logVisitTable AS log_visit + LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action + ON log_link_visit_action.idvisit = log_visit.idvisit + LEFT JOIN $logActionTable AS log_action + ON log_link_visit_action.idaction_url = log_action.idaction + WHERE ( log_visit.visit_last_action_time >= ? + AND log_visit.visit_last_action_time <= ? + AND log_visit.idsite IN (?) ) + AND ( log_action.type = ? ) + GROUP BY log_visit.idvisit + ORDER BY NULL ) AS log_inner", + "bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType)); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + + public function test_getSelectQuery_whenJoinLogLinkVisitActionOnActionOnVisit() + { + $actionType = 3; + $idSite = 1; + $select = 'log_link_visit_action.custom_dimension_1, + actionAlias.name as url, + sum(log_link_visit_action.time_spent) as `13`, + sum(case visitAlias.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`'; + $from = array( + 'log_link_visit_action', + array('table' => 'log_visit', 'tableAlias' => 'visitAlias', 'joinOn' => 'visitAlias.idvisit = log_link_visit_action.idvisit'), + array('table' => 'log_action', 'tableAlias' => 'actionAlias', 'joinOn' => 'log_link_visit_action.idaction_url = actionAlias.idaction') + ); + $where = 'log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ?'; + $bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite); + + $segment = 'actionType==' . $actionType; + $segment = new Segment($segment, $idSites = array()); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $logVisitTable = Common::prefixTable('log_visit'); + $logActionTable = Common::prefixTable('log_action'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $expected = array( + "sql" => " + SELECT log_link_visit_action.custom_dimension_1, + actionAlias.name as url, + sum(log_link_visit_action.time_spent) as `13`, + sum(case visitAlias.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6` + FROM $logLinkVisitActionTable AS log_link_visit_action + LEFT JOIN $logVisitTable AS visitAlias + ON visitAlias.idvisit = log_link_visit_action.idvisit + LEFT JOIN $logActionTable AS actionAlias + ON log_link_visit_action.idaction_url = actionAlias.idaction + LEFT JOIN $logActionTable AS log_action + ON log_link_visit_action.idaction_url = log_action.idaction + WHERE ( log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ? ) + AND ( log_action.type = ? )", + "bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType)); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + + public function test_getSelectQuery_whenJoinLogLinkVisitActionOnAction() + { + $actionType = 3; + $idSite = 1; + $select = 'log_link_visit_action.custom_dimension_1, + sum(log_link_visit_action.time_spent) as `13`'; + $from = 'log_link_visit_action'; + $where = 'log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ?'; + $bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite); + + $segment = 'actionType==' . $actionType; + $segment = new Segment($segment, $idSites = array()); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $logActionTable = Common::prefixTable('log_action'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $expected = array( + "sql" => " + SELECT log_link_visit_action.custom_dimension_1, sum(log_link_visit_action.time_spent) as `13` + FROM $logLinkVisitActionTable AS log_link_visit_action + LEFT JOIN $logActionTable AS log_action + ON log_link_visit_action.idaction_url = log_action.idaction + WHERE ( log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND log_link_visit_action.idsite = ? ) + AND ( log_action.type = ? )", + "bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType)); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + + public function test_getSelectQuery_whenJoinConversionOnAction() + { + $actionType = 3; + $idSite = 1; + $select = 'log_conversion.idgoal AS `idgoal`, + log_conversion.custom_dimension_1 AS `custom_dimension_1`, + count(*) AS `1`, + count(distinct log_conversion.idvisit) AS `3`,'; + $from = 'log_conversion'; + $where = 'log_conversion.server_time >= ? + AND log_conversion.server_time <= ? + AND log_conversion.idsite IN (?)'; + $bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite); + + $segment = 'actionType==' . $actionType; + $segment = new Segment($segment, $idSites = array()); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $logConversionsTable = Common::prefixTable('log_conversion'); + $logActionTable = Common::prefixTable('log_action'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $expected = array( + "sql" => " + SELECT log_conversion.idgoal AS `idgoal`, log_conversion.custom_dimension_1 AS `custom_dimension_1`, count(*) AS `1`, count(distinct log_conversion.idvisit) AS `3`, + FROM $logConversionsTable AS log_conversion + LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action + ON log_conversion.idvisit = log_link_visit_action.idvisit + LEFT JOIN $logActionTable AS log_action + ON log_link_visit_action.idaction_url = log_action.idaction + WHERE ( log_conversion.server_time >= ? + AND log_conversion.server_time <= ? + AND log_conversion.idsite IN (?) ) + AND ( log_action.type = ? )", + "bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType)); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + + public function test_getSelectQuery_whenUnionOfSegmentsAreUsed() + { + $select = 'log_visit.*'; + $from = 'log_visit'; + $where = false; + $bind = array(); + + $segment = 'actionUrl=@myTestUrl'; + $segment = new Segment($segment, $idSites = array()); + + $logVisitTable = Common::prefixTable('log_visit'); + $logLinkVisitActionTable = Common::prefixTable('log_link_visit_action'); + + $query = $segment->getSelectQuery($select, $from, $where, $bind); + + $expected = array( + "sql" => " SELECT log_inner.* FROM ( + SELECT log_visit.* FROM $logVisitTable AS log_visit + LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action + ON log_link_visit_action.idvisit = log_visit.idvisit + WHERE (( log_link_visit_action.idaction_url IN (SELECT idaction FROM log_action WHERE ( name LIKE CONCAT('%', ?, '%') AND type = 1 )) ) + OR ( log_link_visit_action.idaction_url IN (SELECT idaction FROM log_action WHERE ( name LIKE CONCAT('%', ?, '%') AND type = 3 )) ) + OR ( log_link_visit_action.idaction_url IN (SELECT idaction FROM log_action WHERE ( name LIKE CONCAT('%', ?, '%') AND type = 2 )) ) ) + GROUP BY log_visit.idvisit ORDER BY NULL ) AS log_inner", + "bind" => array('myTestUrl', 'myTestUrl', 'myTestUrl')); + + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); + } + + public function test_getSelectQuery_whenJoinConversionOnLogLinkVisitAction_segmentUsesPageUrl() { $this->insertPageUrlAsAction('example.com/anypage'); $this->insertPageUrlAsAction('example.com/anypage_bis'); diff --git a/tests/PHPUnit/Integration/Tracker/VisitTest.php b/tests/PHPUnit/Integration/Tracker/VisitTest.php index e686146f73..f4bc1fd5ed 100644 --- a/tests/PHPUnit/Integration/Tracker/VisitTest.php +++ b/tests/PHPUnit/Integration/Tracker/VisitTest.php @@ -118,15 +118,37 @@ class VisitTest extends IntegrationTestCase 'http://x.com' => true, )), array(array('http://test.com', 'http://sub.test2.com'), true, array( - 'http://sub.test.com' => true, - 'http://sub.sub.test.com' => true, + 'http://sub.test.com' => false, // we do not match subdomains + 'http://sub.sub.test.com' => false, 'http://subtest.com' => false, 'http://test.com.org' => false, + 'http://test2.com' => false, 'http://sub.test2.com' => true, - 'http://x.sub.test2.com' => true, + 'http://test.com' => true, + 'http://x.sub.test2.com' => false, 'http://xsub.test2.com' => false, 'http://sub.test2.com.org' => false, )), + array(array('http://test.com/path', 'http://test2.com/sub/dir'), true, array( + 'http://test.com/path' => true, // test matching path + 'http://test.com/path/' => true, + 'http://test.com/path/test' => true, + + 'http://test.com/path1' => false, + 'http://test.com/' => false, + 'http://test.com' => false, + 'http://test.com/foo' => false, + 'http://sub.test.com/path' => false, // we still do not match subdomains + + 'http://test2.com/sub/dir' => true, + 'http://test2.com/sub/dir/' => true, + 'http://test2.com/sub/dir/test' => true, + + 'http://test2.com/sub/foo/' => false, + 'http://test2.com/sub/' => false, + 'http://test2.com/' => false, + 'http://test2.com/dir/sub' => false, + )), ); } @@ -142,7 +164,7 @@ class VisitTest extends IntegrationTestCase 'rec' => 1, 'url' => $url ))); - $this->assertEquals($isTracked, !$visitExclude->isExcluded()); + $this->assertEquals($isTracked, !$visitExclude->isExcluded(), $url . ' is not returning expected result'); } } diff --git a/tests/PHPUnit/System/AutoSuggestAPITest.php b/tests/PHPUnit/System/AutoSuggestAPITest.php index 62a0175d01..c1404e5757 100644 --- a/tests/PHPUnit/System/AutoSuggestAPITest.php +++ b/tests/PHPUnit/System/AutoSuggestAPITest.php @@ -17,7 +17,6 @@ use Piwik\Plugins\CustomVariables\Columns\CustomVariableValue; use Piwik\Plugins\CustomVariables\Model; use Piwik\Tests\Framework\TestCase\SystemTestCase; use Piwik\Tests\Fixtures\ManyVisitsWithGeoIP; -use Piwik\Tests\Framework\Fixture; use Piwik\Tracker\Cache; use Piwik\Cache as PiwikCache; diff --git a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php index 74dbb05d73..12a60f3821 100644 --- a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php +++ b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php @@ -13,6 +13,7 @@ use Piwik\Plugins\VisitFrequency\API as VisitFrequencyApi; use Piwik\Tests\Framework\TestCase\SystemTestCase; use Piwik\Tests\Fixtures\SqlDump; use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestingEnvironmentVariables; /** * Tests that Piwik 2.0 works w/ data from Piwik 1.12. @@ -32,6 +33,7 @@ class BackwardsCompatibility1XTest extends SystemTestCase // note: not sure why I have to manually install plugin \Piwik\Plugin\Manager::getInstance()->loadPlugin('CustomAlerts')->install(); + \Piwik\Plugin\Manager::getInstance()->loadPlugin('CustomDimensions')->install(); $result = Fixture::updateDatabase(); if ($result === false) { diff --git a/tests/PHPUnit/System/TrackerTest.php b/tests/PHPUnit/System/TrackerTest.php index 461b0a2385..417b40f72a 100644 --- a/tests/PHPUnit/System/TrackerTest.php +++ b/tests/PHPUnit/System/TrackerTest.php @@ -180,9 +180,7 @@ class TrackerTest extends IntegrationTestCase public function test_scheduledTasks_CanBeRunThroughTracker_WithOutputIncluded_IfDebugQueryParamUsed() { $environment = $this->setScheduledTasksToRunInTracker(); - $config = $environment->configOverride; - $config['log']['log_writers'] = array('screen'); - $environment->configOverride = $config; + $environment->overrideConfig('log', 'log_writers', array('screen')); $environment->save(); $urlToTest = $this->getSimpleTrackingUrl() . '&debug=1'; @@ -298,7 +296,8 @@ class TrackerTest extends IntegrationTestCase $testingEnvironment = new \Piwik\Tests\Framework\TestingEnvironmentVariables(); $testingEnvironment->testCaseClass = 'Piwik\Tests\System\TrackerTest'; $testingEnvironment->addScheduledTask = true; - $testingEnvironment->configOverride = array('Tracker' => array('scheduled_tasks_min_interval' => 1, 'debug_on_demand' => 1)); + $testingEnvironment->overrideConfig('Tracker', array('scheduled_tasks_min_interval' => 1, 'debug_on_demand' => 1)); + $testingEnvironment->overrideConfig('log', array()); $testingEnvironment->save(); return $testingEnvironment; diff --git a/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentContainsTest.php b/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentContainsTest.php index 92a4f2dde2..bad8cc45b3 100755 --- a/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentContainsTest.php +++ b/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentContainsTest.php @@ -48,6 +48,13 @@ class TwoVisitsWithCustomVariablesSegmentContainsTest extends SystemTestCase array("pageTitle=@Profile pa", '_SegmentPageTitleContains', $api), array("pageUrl!@user/profile", '_SegmentPageUrlExcludes', $api), array("pageTitle!@Profile pa", '_SegmentPageTitleExcludes', $api), + // starts with + array('pageUrl=^example.org/home', '_SegmentPageUrlStartsWith', array('Actions.getPageUrls')), + array('pageTitle=^Profile pa', '_SegmentPageTitleStartsWith', array('Actions.getPageTitles')), + + // ends with + array('pageUrl=$er/profile', '_SegmentPageUrlEndsWith', array('Actions.getPageUrls')), + array('pageTitle=$page', '_SegmentPageTitleEndsWith', array('Actions.getPageTitles')), ); foreach ($segmentsToTest as $segment) { diff --git a/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchNONETest.php b/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchNONETest.php index 3667f7f65b..482b0d12d8 100755 --- a/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchNONETest.php +++ b/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchNONETest.php @@ -64,10 +64,13 @@ class TwoVisitsWithCustomVariablesSegmentMatchNONETest extends SystemTestCase if ($segment == 'visitEcommerceStatus') { $value = 'none'; } + if ($segment == 'actionType') { + $value = 'pageviews'; + } $matchNone = $segment . '!=' . $value; // deviceType != campaign matches ALL visits, but we want to match None - if($segment == 'deviceType') { + if ($segment == 'deviceType') { $matchNone = $segment . '==car%20browser'; } $segmentExpression[] = $matchNone; diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionType__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionType__API.getSuggestedValuesForSegment.xml new file mode 100644 index 0000000000..cff62022a9 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionType__API.getSuggestedValuesForSegment.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row>pageviews</row> + <row>contents</row> + <row>sitesearches</row> + <row>events</row> + <row>outlinks</row> + <row>downloads</row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionType__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionType__VisitsSummary.get_range.xml new file mode 100644 index 0000000000..f3bee672d6 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionType__VisitsSummary.get_range.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <nb_visits>35</nb_visits> + <nb_actions>95</nb_actions> + <nb_visits_converted>35</nb_visits_converted> + <bounce_count>18</bounce_count> + <sum_visit_length>27557</sum_visit_length> + <max_actions>5</max_actions> + <bounce_rate>51%</bounce_rate> + <nb_actions_per_visit>2.7</nb_actions_per_visit> + <avg_time_on_site>787</avg_time_on_site> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionUrl__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionUrl__API.getSuggestedValuesForSegment.xml new file mode 100644 index 0000000000..2e03969e6d --- /dev/null +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionUrl__API.getSuggestedValuesForSegment.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row>http://piwik.net/grue/lair</row> + <row>http://piwik.net/space/quest/iv</row> + <row>http://example-outlink.org/1.html</row> + <row>http://example.org/path/file1.zip</row> + <row>http://example.org/path/file0.zip</row> + <row>http://example-outlink.org/0.html</row> + <row>http://example.org/path/file2.zip</row> + <row>http://example.org/path/file3.zip</row> + <row>http://example-outlink.org/3.html</row> + <row>http://example-outlink.org/2.html</row> + <row>http://example.org/path/file4.zip</row> + <row>http://example.org/path/file5.zip</row> + <row>http://example.org/path/file8.zip</row> + <row>http://example-outlink.org/6.html</row> + <row>http://example-outlink.org/7.html</row> + <row>http://example-outlink.org/5.html</row> + <row>http://example-outlink.org/4.html</row> + <row>http://example.org/path/file7.zip</row> + <row>http://example-outlink.org/8.html</row> + <row>http://example.org/path/file6.zip</row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionUrl__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionUrl__VisitsSummary.get_range.xml new file mode 100644 index 0000000000..7ace3fcbe7 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionUrl__VisitsSummary.get_range.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <nb_visits>18</nb_visits> + <nb_actions>18</nb_actions> + <nb_visits_converted>18</nb_visits_converted> + <bounce_count>18</bounce_count> + <sum_visit_length>0</sum_visit_length> + <max_actions>1</max_actions> + <bounce_rate>100%</bounce_rate> + <nb_actions_per_visit>1</nb_actions_per_visit> + <avg_time_on_site>0</avg_time_on_site> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableName__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableName__API.getSuggestedValuesForSegment.xml index b3e5836d32..9c10ae8b37 100644 --- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableName__API.getSuggestedValuesForSegment.xml +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableName__API.getSuggestedValuesForSegment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" ?> <result> - <row>Cvar 5 name</row> <row>Cvar 1 name</row> + <row>Cvar 5 name</row> </result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariablePageValue__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariablePageValue__API.getSuggestedValuesForSegment.xml index fb3a6b6413..0ab1e3a74c 100644 --- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariablePageValue__API.getSuggestedValuesForSegment.xml +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariablePageValue__API.getSuggestedValuesForSegment.xml @@ -1,22 +1,22 @@ <?xml version="1.0" encoding="utf-8" ?> <result> <row>CAT</row> + <row>Cvar5 PAGE value is 0</row> <row>Cvar5 PAGE value is 1</row> <row>Cvar2 PAGE value is 1</row> <row>Cvar2 PAGE value is 0</row> - <row>Cvar5 PAGE value is 0</row> - <row>Cvar5 PAGE value is 3</row> <row>Cvar2 PAGE value is 3</row> - <row>Cvar2 PAGE value is 2</row> <row>Cvar5 PAGE value is 2</row> - <row>Cvar5 PAGE value is 4</row> + <row>Cvar5 PAGE value is 3</row> + <row>Cvar2 PAGE value is 2</row> + <row>Cvar2 PAGE value is 5</row> <row>Cvar2 PAGE value is 4</row> - <row>Cvar2 PAGE value is 7</row> - <row>Cvar5 PAGE value is 8</row> - <row>Cvar2 PAGE value is 8</row> - <row>Cvar5 PAGE value is 7</row> <row>Cvar2 PAGE value is 6</row> - <row>Cvar2 PAGE value is 5</row> - <row>Cvar5 PAGE value is 6</row> + <row>Cvar5 PAGE value is 8</row> <row>Cvar5 PAGE value is 5</row> + <row>Cvar5 PAGE value is 6</row> + <row>Cvar5 PAGE value is 4</row> + <row>Cvar5 PAGE value is 7</row> + <row>Cvar2 PAGE value is 8</row> + <row>Cvar2 PAGE value is 7</row> </result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableValue__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableValue__API.getSuggestedValuesForSegment.xml index 662ed617b1..6be328a177 100644 --- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableValue__API.getSuggestedValuesForSegment.xml +++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_customVariableValue__API.getSuggestedValuesForSegment.xml @@ -1,21 +1,21 @@ <?xml version="1.0" encoding="utf-8" ?> <result> - <row>Cvar5 value is 1</row> - <row>Cvar1 value is 0</row> <row>Cvar1 value is 1</row> + <row>Cvar1 value is 0</row> + <row>Cvar5 value is 1</row> <row>Cvar5 value is 0</row> <row>Cvar1 value is 3</row> - <row>Cvar5 value is 3</row> - <row>Cvar5 value is 2</row> <row>Cvar1 value is 2</row> - <row>Cvar5 value is 4</row> + <row>Cvar5 value is 2</row> + <row>Cvar5 value is 3</row> <row>Cvar1 value is 4</row> <row>Cvar5 value is 7</row> - <row>Cvar1 value is 7</row> - <row>Cvar5 value is 8</row> - <row>Cvar1 value is 6</row> + <row>Cvar1 value is 5</row> <row>Cvar5 value is 6</row> + <row>Cvar1 value is 7</row> <row>Cvar5 value is 5</row> - <row>Cvar1 value is 5</row> + <row>Cvar5 value is 8</row> <row>Cvar1 value is 8</row> + <row>Cvar5 value is 4</row> + <row>Cvar1 value is 6</row> </result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml index 38fec6a150..c2c4ac7f78 100644 --- a/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml +++ b/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml @@ -9,7 +9,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>21</pageId> <eventCategory>Movie</eventCategory> @@ -114,7 +114,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>20</pageId> <eventCategory>Movie</eventCategory> @@ -219,7 +219,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>16</pageId> <eventCategory>Movie</eventCategory> @@ -233,7 +233,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>17</pageId> <eventCategory>Movie</eventCategory> @@ -247,7 +247,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>19</pageId> <eventCategory>Movie</eventCategory> @@ -261,7 +261,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>22</pageId> <eventCategory>Movie</eventCategory> @@ -290,7 +290,7 @@ <row> <type>event</type> <url>http://example.org/finishedMovie</url> - <pageIdAction>23</pageIdAction> + <pageIdAction>25</pageIdAction> <pageId>24</pageId> <eventCategory>event category Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long ---> SHOULD APPEAR IN TEST OUTPUT NOT TRUNCATED <---</eventCategory> @@ -397,7 +397,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>18</pageId> <eventCategory>Movie</eventCategory> @@ -526,7 +526,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>2</pageId> <eventCategory>Music</eventCategory> @@ -546,7 +546,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>3</pageId> <eventCategory>Music</eventCategory> @@ -566,7 +566,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>4</pageId> <eventCategory>Music</eventCategory> @@ -586,7 +586,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>5</pageId> <eventCategory>Music</eventCategory> @@ -606,7 +606,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>6</pageId> <eventCategory>Music</eventCategory> @@ -626,7 +626,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>7</pageId> <eventCategory>Music</eventCategory> @@ -647,7 +647,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>8</pageId> <eventCategory>Music</eventCategory> @@ -669,7 +669,7 @@ <type>action</type> <url>http://example.org/movies</url> <pageTitle>Movie Theater</pageTitle> - <pageIdAction>12</pageIdAction> + <pageIdAction>13</pageIdAction> <pageId>9</pageId> <generationTime>0.67s</generationTime> @@ -681,7 +681,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>10</pageId> <eventCategory>Movie</eventCategory> @@ -695,7 +695,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>11</pageId> <eventCategory>Movie</eventCategory> @@ -709,7 +709,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>12</pageId> <eventCategory>Movie</eventCategory> @@ -723,7 +723,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>13</pageId> <eventCategory>Movie</eventCategory> @@ -737,7 +737,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>14</pageId> <eventCategory>Movie</eventCategory> @@ -751,7 +751,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>15</pageId> <eventCategory>Movie</eventCategory> @@ -857,7 +857,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>45</pageId> <eventCategory>Movie</eventCategory> @@ -958,7 +958,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>44</pageId> <eventCategory>Movie</eventCategory> @@ -1059,7 +1059,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>40</pageId> <eventCategory>Movie</eventCategory> @@ -1073,7 +1073,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>41</pageId> <eventCategory>Movie</eventCategory> @@ -1087,7 +1087,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>43</pageId> <eventCategory>Movie</eventCategory> @@ -1101,7 +1101,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>46</pageId> <eventCategory>Movie</eventCategory> @@ -1130,7 +1130,7 @@ <row> <type>event</type> <url>http://example.org/finishedMovie</url> - <pageIdAction>23</pageIdAction> + <pageIdAction>25</pageIdAction> <pageId>48</pageId> <eventCategory>event category Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long ---> SHOULD APPEAR IN TEST OUTPUT NOT TRUNCATED <---</eventCategory> @@ -1233,7 +1233,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>42</pageId> <eventCategory>Movie</eventCategory> @@ -1358,7 +1358,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>26</pageId> <eventCategory>Music</eventCategory> @@ -1378,7 +1378,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>27</pageId> <eventCategory>Music</eventCategory> @@ -1398,7 +1398,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>28</pageId> <eventCategory>Music</eventCategory> @@ -1418,7 +1418,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>29</pageId> <eventCategory>Music</eventCategory> @@ -1438,7 +1438,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>30</pageId> <eventCategory>Music</eventCategory> @@ -1458,7 +1458,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>31</pageId> <eventCategory>Music</eventCategory> @@ -1479,7 +1479,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>32</pageId> <eventCategory>Music</eventCategory> @@ -1501,7 +1501,7 @@ <type>action</type> <url>http://example.org/movies</url> <pageTitle>Movie Theater</pageTitle> - <pageIdAction>12</pageIdAction> + <pageIdAction>13</pageIdAction> <pageId>33</pageId> <generationTime>0.67s</generationTime> @@ -1513,7 +1513,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>34</pageId> <eventCategory>Movie</eventCategory> @@ -1527,7 +1527,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>35</pageId> <eventCategory>Movie</eventCategory> @@ -1541,7 +1541,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>36</pageId> <eventCategory>Movie</eventCategory> @@ -1555,7 +1555,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>37</pageId> <eventCategory>Movie</eventCategory> @@ -1569,7 +1569,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>38</pageId> <eventCategory>Movie</eventCategory> @@ -1583,7 +1583,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>39</pageId> <eventCategory>Movie</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml index 38fec6a150..c2c4ac7f78 100644 --- a/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml @@ -9,7 +9,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>21</pageId> <eventCategory>Movie</eventCategory> @@ -114,7 +114,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>20</pageId> <eventCategory>Movie</eventCategory> @@ -219,7 +219,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>16</pageId> <eventCategory>Movie</eventCategory> @@ -233,7 +233,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>17</pageId> <eventCategory>Movie</eventCategory> @@ -247,7 +247,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>19</pageId> <eventCategory>Movie</eventCategory> @@ -261,7 +261,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>22</pageId> <eventCategory>Movie</eventCategory> @@ -290,7 +290,7 @@ <row> <type>event</type> <url>http://example.org/finishedMovie</url> - <pageIdAction>23</pageIdAction> + <pageIdAction>25</pageIdAction> <pageId>24</pageId> <eventCategory>event category Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long ---> SHOULD APPEAR IN TEST OUTPUT NOT TRUNCATED <---</eventCategory> @@ -397,7 +397,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>18</pageId> <eventCategory>Movie</eventCategory> @@ -526,7 +526,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>2</pageId> <eventCategory>Music</eventCategory> @@ -546,7 +546,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>3</pageId> <eventCategory>Music</eventCategory> @@ -566,7 +566,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>4</pageId> <eventCategory>Music</eventCategory> @@ -586,7 +586,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>5</pageId> <eventCategory>Music</eventCategory> @@ -606,7 +606,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>6</pageId> <eventCategory>Music</eventCategory> @@ -626,7 +626,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>7</pageId> <eventCategory>Music</eventCategory> @@ -647,7 +647,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>8</pageId> <eventCategory>Music</eventCategory> @@ -669,7 +669,7 @@ <type>action</type> <url>http://example.org/movies</url> <pageTitle>Movie Theater</pageTitle> - <pageIdAction>12</pageIdAction> + <pageIdAction>13</pageIdAction> <pageId>9</pageId> <generationTime>0.67s</generationTime> @@ -681,7 +681,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>10</pageId> <eventCategory>Movie</eventCategory> @@ -695,7 +695,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>11</pageId> <eventCategory>Movie</eventCategory> @@ -709,7 +709,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>12</pageId> <eventCategory>Movie</eventCategory> @@ -723,7 +723,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>13</pageId> <eventCategory>Movie</eventCategory> @@ -737,7 +737,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>14</pageId> <eventCategory>Movie</eventCategory> @@ -751,7 +751,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>15</pageId> <eventCategory>Movie</eventCategory> @@ -857,7 +857,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>45</pageId> <eventCategory>Movie</eventCategory> @@ -958,7 +958,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>44</pageId> <eventCategory>Movie</eventCategory> @@ -1059,7 +1059,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>40</pageId> <eventCategory>Movie</eventCategory> @@ -1073,7 +1073,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>41</pageId> <eventCategory>Movie</eventCategory> @@ -1087,7 +1087,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>43</pageId> <eventCategory>Movie</eventCategory> @@ -1101,7 +1101,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>46</pageId> <eventCategory>Movie</eventCategory> @@ -1130,7 +1130,7 @@ <row> <type>event</type> <url>http://example.org/finishedMovie</url> - <pageIdAction>23</pageIdAction> + <pageIdAction>25</pageIdAction> <pageId>48</pageId> <eventCategory>event category Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long Extremely long ---> SHOULD APPEAR IN TEST OUTPUT NOT TRUNCATED <---</eventCategory> @@ -1233,7 +1233,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>42</pageId> <eventCategory>Movie</eventCategory> @@ -1358,7 +1358,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>26</pageId> <eventCategory>Music</eventCategory> @@ -1378,7 +1378,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>27</pageId> <eventCategory>Music</eventCategory> @@ -1398,7 +1398,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>28</pageId> <eventCategory>Music</eventCategory> @@ -1418,7 +1418,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>29</pageId> <eventCategory>Music</eventCategory> @@ -1438,7 +1438,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>30</pageId> <eventCategory>Music</eventCategory> @@ -1458,7 +1458,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>31</pageId> <eventCategory>Music</eventCategory> @@ -1479,7 +1479,7 @@ <row> <type>event</type> <url>http://example.org/webradio</url> - <pageIdAction>2</pageIdAction> + <pageIdAction>3</pageIdAction> <pageId>32</pageId> <eventCategory>Music</eventCategory> @@ -1501,7 +1501,7 @@ <type>action</type> <url>http://example.org/movies</url> <pageTitle>Movie Theater</pageTitle> - <pageIdAction>12</pageIdAction> + <pageIdAction>13</pageIdAction> <pageId>33</pageId> <generationTime>0.67s</generationTime> @@ -1513,7 +1513,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>34</pageId> <eventCategory>Movie</eventCategory> @@ -1527,7 +1527,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>35</pageId> <eventCategory>Movie</eventCategory> @@ -1541,7 +1541,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>36</pageId> <eventCategory>Movie</eventCategory> @@ -1555,7 +1555,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>37</pageId> <eventCategory>Movie</eventCategory> @@ -1569,7 +1569,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>38</pageId> <eventCategory>Movie</eventCategory> @@ -1583,7 +1583,7 @@ <row> <type>event</type> <url>http://example.org/movies</url> - <pageIdAction>12</pageIdAction> + <pageIdAction>14</pageIdAction> <pageId>39</pageId> <eventCategory>Movie</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_FlattenReports__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_FlattenReports__CustomVariables.getCustomVariables_day.xml index c2c0a93fff..6d6e6128de 100644 --- a/tests/PHPUnit/System/expected/test_FlattenReports__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_FlattenReports__CustomVariables.getCustomVariables_day.xml @@ -10,11 +10,23 @@ <sum_visit_length>6</sum_visit_length> <bounce_count>0</bounce_count> <nb_visits_converted>0</nb_visits_converted> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <is_aggregate>1</is_aggregate> </row> <row> <label>CustomVarPage</label> <nb_actions>18</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <is_aggregate>1</is_aggregate> </row> <row> @@ -68,6 +80,12 @@ <sum_visit_length>1</sum_visit_length> <bounce_count>0</bounce_count> <nb_visits_converted>0</nb_visits_converted> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <is_aggregate>1</is_aggregate> </row> <row> @@ -90,6 +108,12 @@ <row> <label>CustomVarPage</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <is_aggregate>1</is_aggregate> </row> </result> diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml new file mode 100644 index 0000000000..7a397597a0 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>url</value> + <name>Page URL</name> + </row> + <row> + <value>urlparam</value> + <name>Page URL Parameter</name> + </row> + <row> + <value>action_name</value> + <name>Page Title</name> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml new file mode 100644 index 0000000000..149cf8b31c --- /dev/null +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>visit</value> + <name>Visit</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>0</supportsExtractions> + </row> + <row> + <value>action</value> + <name>Action</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>1</supportsExtractions> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml new file mode 100644 index 0000000000..c234bed59e --- /dev/null +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result />
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getCustomVariables_month.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getCustomVariables_month.xml index dc1a11ab2b..4582327ecc 100644 --- a/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getCustomVariables_month.xml +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getCustomVariables_month.xml @@ -3,6 +3,16 @@ <row> <label>HTTP-code</label> <nb_actions>43</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + <row> + <scope>page</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==HTTP-code</segment> <subtable> <row> @@ -61,6 +71,12 @@ <revenue>25</revenue> <sum_daily_nb_uniq_visitors>7</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Not-Bot</segment> <subtable> <row> @@ -151,6 +167,12 @@ <revenue>10</revenue> <sum_daily_nb_uniq_visitors>3</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>1</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==User+Name</segment> <subtable> <row> @@ -196,6 +218,12 @@ <row> <label>Generation Time</label> <nb_actions>4</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Generation+Time</segment> <subtable> <row> @@ -221,6 +249,12 @@ <row> <label>Windows Status Code</label> <nb_actions>4</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Windows+Status+Code</segment> <subtable> <row> @@ -267,6 +301,12 @@ <revenue>5</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Bot</segment> <subtable> <row> @@ -308,6 +348,12 @@ <revenue>5</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Forum+status</segment> <subtable> <row> @@ -349,6 +395,12 @@ <revenue>5</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getUsagesOfSlots.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getUsagesOfSlots.xml new file mode 100644 index 0000000000..4e74cbbfdf --- /dev/null +++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomVariables.getUsagesOfSlots.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <scope>visit</scope> + <index>1</index> + <usages> + <row> + <name>Domain landed</name> + <nb_visits>12</nb_visits> + <nb_actions>16</nb_actions> + </row> + <row> + <name>Not-Bot</name> + <nb_visits>7</nb_visits> + <nb_actions>10</nb_actions> + </row> + <row> + <name>User Name</name> + <nb_visits>3</nb_visits> + <nb_actions>5</nb_actions> + </row> + <row> + <name>Bot</name> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </usages> + </row> + <row> + <scope>visit</scope> + <index>2</index> + <usages> + <row> + <name>Demo language</name> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </usages> + </row> + <row> + <scope>visit</scope> + <index>3</index> + <usages> + <row> + <name>Forum status</name> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </usages> + </row> + <row> + <scope>visit</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>5</index> + <usages> + <row> + <name>VisitorType</name> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </usages> + </row> + <row> + <scope>page</scope> + <index>1</index> + <usages> + <row> + <name>HTTP-code</name> + <nb_visits>0</nb_visits> + <nb_actions>69</nb_actions> + </row> + <row> + <name>Generation Time</name> + <nb_visits>0</nb_visits> + <nb_actions>4</nb_actions> + </row> + </usages> + </row> + <row> + <scope>page</scope> + <index>2</index> + <usages> + <row> + <name>Windows Status Code</name> + <nb_visits>0</nb_visits> + <nb_actions>4</nb_actions> + </row> + </usages> + </row> + <row> + <scope>page</scope> + <index>3</index> + <usages> + <row> + <name>HTTP-code</name> + <nb_visits>0</nb_visits> + <nb_actions>69</nb_actions> + </row> + </usages> + </row> + <row> + <scope>page</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>5</index> + <usages> + </usages> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_1__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_1__Live.getLastVisitsDetails_month.xml index b1ce633309..23daf4bf06 100644 --- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_1__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_1__Live.getLastVisitsDetails_month.xml @@ -62,7 +62,7 @@ <type>download</type> <url>http://example.org/path/file8.zip</url> <pageTitle /> - <pageIdAction>46</pageIdAction> + <pageIdAction>47</pageIdAction> <pageId>48</pageId> <timeSpent>180</timeSpent> @@ -74,7 +74,7 @@ <type>outlink</type> <url>http://example-outlink.org/8.html</url> <pageTitle /> - <pageIdAction>47</pageIdAction> + <pageIdAction>48</pageIdAction> <pageId>49</pageId> <timeSpent>180</timeSpent> @@ -85,7 +85,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>50</pageId> <eventCategory>Cat8</eventCategory> @@ -360,7 +360,7 @@ <type>download</type> <url>http://example.org/path/file7.zip</url> <pageTitle /> - <pageIdAction>41</pageIdAction> + <pageIdAction>42</pageIdAction> <pageId>42</pageId> <timeSpent>180</timeSpent> @@ -372,7 +372,7 @@ <type>outlink</type> <url>http://example-outlink.org/7.html</url> <pageTitle /> - <pageIdAction>42</pageIdAction> + <pageIdAction>43</pageIdAction> <pageId>43</pageId> <timeSpent>180</timeSpent> @@ -383,7 +383,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>44</pageId> <eventCategory>Cat7</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_2__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_2__Live.getLastVisitsDetails_month.xml index 40321dd60b..69d9272531 100644 --- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_2__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_offsetAndLimit_2__Live.getLastVisitsDetails_month.xml @@ -191,7 +191,7 @@ <type>download</type> <url>http://example.org/path/file6.zip</url> <pageTitle /> - <pageIdAction>36</pageIdAction> + <pageIdAction>37</pageIdAction> <pageId>37</pageId> <timeSpent>180</timeSpent> @@ -203,7 +203,7 @@ <type>outlink</type> <url>http://example-outlink.org/6.html</url> <pageTitle /> - <pageIdAction>37</pageIdAction> + <pageIdAction>38</pageIdAction> <pageId>38</pageId> <timeSpent>180</timeSpent> @@ -214,7 +214,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>39</pageId> <eventCategory>Cat6</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml index 0f01c7764b..0323bd788c 100644 --- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml @@ -173,7 +173,7 @@ <type>download</type> <url>http://example.org/path/file8.zip</url> <pageTitle /> - <pageIdAction>46</pageIdAction> + <pageIdAction>47</pageIdAction> <pageId>48</pageId> <timeSpent>180</timeSpent> @@ -185,7 +185,7 @@ <type>outlink</type> <url>http://example-outlink.org/8.html</url> <pageTitle /> - <pageIdAction>47</pageIdAction> + <pageIdAction>48</pageIdAction> <pageId>49</pageId> <timeSpent>180</timeSpent> @@ -196,7 +196,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>50</pageId> <eventCategory>Cat8</eventCategory> @@ -471,7 +471,7 @@ <type>download</type> <url>http://example.org/path/file7.zip</url> <pageTitle /> - <pageIdAction>41</pageIdAction> + <pageIdAction>42</pageIdAction> <pageId>42</pageId> <timeSpent>180</timeSpent> @@ -483,7 +483,7 @@ <type>outlink</type> <url>http://example-outlink.org/7.html</url> <pageTitle /> - <pageIdAction>42</pageIdAction> + <pageIdAction>43</pageIdAction> <pageId>43</pageId> <timeSpent>180</timeSpent> @@ -494,7 +494,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>44</pageId> <eventCategory>Cat7</eventCategory> @@ -787,7 +787,7 @@ <type>download</type> <url>http://example.org/path/file6.zip</url> <pageTitle /> - <pageIdAction>36</pageIdAction> + <pageIdAction>37</pageIdAction> <pageId>37</pageId> <timeSpent>180</timeSpent> @@ -799,7 +799,7 @@ <type>outlink</type> <url>http://example-outlink.org/6.html</url> <pageTitle /> - <pageIdAction>37</pageIdAction> + <pageIdAction>38</pageIdAction> <pageId>38</pageId> <timeSpent>180</timeSpent> @@ -810,7 +810,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>39</pageId> <eventCategory>Cat6</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml index 0f01c7764b..0323bd788c 100644 --- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml @@ -173,7 +173,7 @@ <type>download</type> <url>http://example.org/path/file8.zip</url> <pageTitle /> - <pageIdAction>46</pageIdAction> + <pageIdAction>47</pageIdAction> <pageId>48</pageId> <timeSpent>180</timeSpent> @@ -185,7 +185,7 @@ <type>outlink</type> <url>http://example-outlink.org/8.html</url> <pageTitle /> - <pageIdAction>47</pageIdAction> + <pageIdAction>48</pageIdAction> <pageId>49</pageId> <timeSpent>180</timeSpent> @@ -196,7 +196,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>50</pageId> <eventCategory>Cat8</eventCategory> @@ -471,7 +471,7 @@ <type>download</type> <url>http://example.org/path/file7.zip</url> <pageTitle /> - <pageIdAction>41</pageIdAction> + <pageIdAction>42</pageIdAction> <pageId>42</pageId> <timeSpent>180</timeSpent> @@ -483,7 +483,7 @@ <type>outlink</type> <url>http://example-outlink.org/7.html</url> <pageTitle /> - <pageIdAction>42</pageIdAction> + <pageIdAction>43</pageIdAction> <pageId>43</pageId> <timeSpent>180</timeSpent> @@ -494,7 +494,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>44</pageId> <eventCategory>Cat7</eventCategory> @@ -787,7 +787,7 @@ <type>download</type> <url>http://example.org/path/file6.zip</url> <pageTitle /> - <pageIdAction>36</pageIdAction> + <pageIdAction>37</pageIdAction> <pageId>37</pageId> <timeSpent>180</timeSpent> @@ -799,7 +799,7 @@ <type>outlink</type> <url>http://example-outlink.org/6.html</url> <pageTitle /> - <pageIdAction>37</pageIdAction> + <pageIdAction>38</pageIdAction> <pageId>38</pageId> <timeSpent>180</timeSpent> @@ -810,7 +810,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>39</pageId> <eventCategory>Cat6</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml index 4b4f490f44..4665c485f0 100644 --- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml @@ -173,7 +173,7 @@ <type>download</type> <url>http://example.org/path/file8.zip</url> <pageTitle /> - <pageIdAction>46</pageIdAction> + <pageIdAction>47</pageIdAction> <pageId>48</pageId> <timeSpent>180</timeSpent> @@ -185,7 +185,7 @@ <type>outlink</type> <url>http://example-outlink.org/8.html</url> <pageTitle /> - <pageIdAction>47</pageIdAction> + <pageIdAction>48</pageIdAction> <pageId>49</pageId> <timeSpent>180</timeSpent> @@ -196,7 +196,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>50</pageId> <eventCategory>Cat8</eventCategory> @@ -471,7 +471,7 @@ <type>download</type> <url>http://example.org/path/file7.zip</url> <pageTitle /> - <pageIdAction>41</pageIdAction> + <pageIdAction>42</pageIdAction> <pageId>42</pageId> <timeSpent>180</timeSpent> @@ -483,7 +483,7 @@ <type>outlink</type> <url>http://example-outlink.org/7.html</url> <pageTitle /> - <pageIdAction>42</pageIdAction> + <pageIdAction>43</pageIdAction> <pageId>43</pageId> <timeSpent>180</timeSpent> @@ -494,7 +494,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>44</pageId> <eventCategory>Cat7</eventCategory> @@ -787,7 +787,7 @@ <type>download</type> <url>http://example.org/path/file6.zip</url> <pageTitle /> - <pageIdAction>36</pageIdAction> + <pageIdAction>37</pageIdAction> <pageId>37</pageId> <timeSpent>180</timeSpent> @@ -799,7 +799,7 @@ <type>outlink</type> <url>http://example-outlink.org/6.html</url> <pageTitle /> - <pageIdAction>37</pageIdAction> + <pageIdAction>38</pageIdAction> <pageId>38</pageId> <timeSpent>180</timeSpent> @@ -810,7 +810,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>39</pageId> <eventCategory>Cat6</eventCategory> @@ -1085,7 +1085,7 @@ <type>download</type> <url>http://example.org/path/file5.zip</url> <pageTitle /> - <pageIdAction>31</pageIdAction> + <pageIdAction>32</pageIdAction> <pageId>31</pageId> <timeSpent>180</timeSpent> @@ -1097,7 +1097,7 @@ <type>outlink</type> <url>http://example-outlink.org/5.html</url> <pageTitle /> - <pageIdAction>32</pageIdAction> + <pageIdAction>33</pageIdAction> <pageId>32</pageId> <timeSpent>180</timeSpent> @@ -1108,7 +1108,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>33</pageId> <eventCategory>Cat5</eventCategory> @@ -1401,7 +1401,7 @@ <type>download</type> <url>http://example.org/path/file4.zip</url> <pageTitle /> - <pageIdAction>26</pageIdAction> + <pageIdAction>27</pageIdAction> <pageId>26</pageId> <timeSpent>180</timeSpent> @@ -1413,7 +1413,7 @@ <type>outlink</type> <url>http://example-outlink.org/4.html</url> <pageTitle /> - <pageIdAction>27</pageIdAction> + <pageIdAction>28</pageIdAction> <pageId>27</pageId> <timeSpent>180</timeSpent> @@ -1424,7 +1424,7 @@ <row> <type>event</type> <url>http://piwik.net/space/quest/iv</url> - <pageIdAction>4</pageIdAction> + <pageIdAction>8</pageIdAction> <pageId>28</pageId> <eventCategory>Cat4</eventCategory> diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml new file mode 100644 index 0000000000..7a397597a0 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>url</value> + <name>Page URL</name> + </row> + <row> + <value>urlparam</value> + <name>Page URL Parameter</name> + </row> + <row> + <value>action_name</value> + <name>Page Title</name> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml new file mode 100644 index 0000000000..149cf8b31c --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>visit</value> + <name>Visit</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>0</supportsExtractions> + </row> + <row> + <value>action</value> + <name>Action</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>1</supportsExtractions> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml new file mode 100644 index 0000000000..c234bed59e --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result />
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomVariables.getUsagesOfSlots.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomVariables.getUsagesOfSlots.xml new file mode 100644 index 0000000000..81163427e5 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomVariables.getUsagesOfSlots.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <scope>visit</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>5</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>5</index> + <usages> + </usages> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml index 8202b69b04..3ecc645c62 100644 --- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml @@ -58,7 +58,7 @@ <reportMetadata> <row> - <idsubdatatable>2078</idsubdatatable> + <idsubdatatable>5119</idsubdatatable> </row> </reportMetadata> <reportTotal> diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml index 450961285b..96248d9ba3 100644 --- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml @@ -67,7 +67,7 @@ <reportMetadata> <row> - <idsubdatatable>2082</idsubdatatable> + <idsubdatatable>5123</idsubdatatable> </row> </reportMetadata> <reportTotal> diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_withCookieSupport__CustomVariables.getUsagesOfSlots.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_withCookieSupport__CustomVariables.getUsagesOfSlots.xml new file mode 100644 index 0000000000..81163427e5 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_withCookieSupport__CustomVariables.getUsagesOfSlots.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <scope>visit</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>5</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>5</index> + <usages> + </usages> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml index cc9433a13a..73cc01ca74 100644 --- a/tests/PHPUnit/System/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml @@ -10,7 +10,7 @@ <url>http://piwik.net/</url> <pageTitle /> <pageIdAction>1</pageIdAction> - <serverTimePretty>Apr 7, 2013 10:00:00 AM</serverTimePretty> + <serverTimePretty>Apr 7, 2013 10:00:00</serverTimePretty> <pageId>1</pageId> <customVariables> <row> @@ -91,10 +91,10 @@ <plugins /> <pluginsIcons /> <serverTimestamp>1365328800</serverTimestamp> - <serverTimePretty>10:00:00 AM</serverTimePretty> + <serverTimePretty>10:00:00</serverTimePretty> <serverDatePretty>Sunday, April 7, 2013</serverDatePretty> <serverDatePrettyFirstAction>Sunday, April 7, 2013</serverDatePrettyFirstAction> - <serverTimePrettyFirstAction>10:00:00 AM</serverTimePrettyFirstAction> + <serverTimePrettyFirstAction>10:00:00</serverTimePrettyFirstAction> </row> <row> <idSite>1</idSite> @@ -106,7 +106,7 @@ <url>http://piwik.net/</url> <pageTitle /> <pageIdAction>1</pageIdAction> - <serverTimePretty>Apr 6, 2013 11:00:00 AM</serverTimePretty> + <serverTimePretty>Apr 6, 2013 11:00:00</serverTimePretty> <pageId>2</pageId> <customVariables> <row> @@ -187,10 +187,10 @@ <plugins /> <pluginsIcons /> <serverTimestamp>1365246000</serverTimestamp> - <serverTimePretty>11:00:00 AM</serverTimePretty> + <serverTimePretty>11:00:00</serverTimePretty> <serverDatePretty>Saturday, April 6, 2013</serverDatePretty> <serverDatePrettyFirstAction>Saturday, April 6, 2013</serverDatePrettyFirstAction> - <serverTimePrettyFirstAction>11:00:00 AM</serverTimePrettyFirstAction> + <serverTimePrettyFirstAction>11:00:00</serverTimePrettyFirstAction> </row> <row> <idSite>1</idSite> @@ -202,7 +202,7 @@ <url>http://piwik.net/</url> <pageTitle /> <pageIdAction>1</pageIdAction> - <serverTimePretty>Apr 5, 2013 12:00:00 PM</serverTimePretty> + <serverTimePretty>Apr 5, 2013 12:00:00</serverTimePretty> <pageId>3</pageId> <customVariables> <row> @@ -283,9 +283,9 @@ <plugins /> <pluginsIcons /> <serverTimestamp>1365163200</serverTimestamp> - <serverTimePretty>12:00:00 PM</serverTimePretty> + <serverTimePretty>12:00:00</serverTimePretty> <serverDatePretty>Friday, April 5, 2013</serverDatePretty> <serverDatePrettyFirstAction>Friday, April 5, 2013</serverDatePrettyFirstAction> - <serverTimePrettyFirstAction>12:00:00 PM</serverTimePrettyFirstAction> + <serverTimePrettyFirstAction>12:00:00</serverTimePrettyFirstAction> </row> </result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_LabelReservedCharactersHierarchical__API.getRowEvolution_day.xml b/tests/PHPUnit/System/expected/test_RowEvolution_LabelReservedCharactersHierarchical__API.getRowEvolution_day.xml index 7295efb518..3b8bde9065 100644 --- a/tests/PHPUnit/System/expected/test_RowEvolution_LabelReservedCharactersHierarchical__API.getRowEvolution_day.xml +++ b/tests/PHPUnit/System/expected/test_RowEvolution_LabelReservedCharactersHierarchical__API.getRowEvolution_day.xml @@ -249,7 +249,7 @@ <change>-100%</change> </nb_visits_1> <nb_visits_2> - <name>Google - justice )(&^#%$ not corruption! (Visits)</name> + <name>Google - justice )(&^#%$ not &#039;" corruption! (Visits)</name> <min>0</min> <max>1</max> </nb_visits_2> diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_flatFilters__Referrers.getSearchEngines_month.xml b/tests/PHPUnit/System/expected/test_RowEvolution_flatFilters__Referrers.getSearchEngines_month.xml index e1f0382dda..d97e75695c 100644 --- a/tests/PHPUnit/System/expected/test_RowEvolution_flatFilters__Referrers.getSearchEngines_month.xml +++ b/tests/PHPUnit/System/expected/test_RowEvolution_flatFilters__Referrers.getSearchEngines_month.xml @@ -14,7 +14,7 @@ <logo>plugins/Referrers/images/searchEngines/google.com.png</logo> </row> <row> - <label>Google - justice )(&^#%$ not corruption!</label> + <label>Google - justice )(&^#%$ not &#039;" corruption!</label> <nb_visits>8</nb_visits> <nb_actions>8</nb_actions> <max_actions>1</max_actions> @@ -23,7 +23,7 @@ <nb_visits_converted>0</nb_visits_converted> <sum_daily_nb_uniq_visitors>8</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> - <url>http://google.com/search?q=justice+%29%28%26%5E%23%25%24+not+corruption%21</url> + <url>http://google.com/search?q=justice+%29%28%26%5E%23%25%24+not+%27%22+corruption%21</url> <logo>plugins/Referrers/images/searchEngines/google.com.png</logo> </row> <row> diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_multipleDates_lastNoData__API.getRowEvolution_month.xml b/tests/PHPUnit/System/expected/test_RowEvolution_multipleDates_lastNoData__API.getRowEvolution_month.xml index ff4f69a79d..5730859763 100644 --- a/tests/PHPUnit/System/expected/test_RowEvolution_multipleDates_lastNoData__API.getRowEvolution_month.xml +++ b/tests/PHPUnit/System/expected/test_RowEvolution_multipleDates_lastNoData__API.getRowEvolution_month.xml @@ -33,7 +33,7 @@ <change>-100%</change> </nb_visits_0> <nb_visits_1> - <name>justice )(&^#%$ not corruption! (Visits)</name> + <name>justice )(&^#%$ not &#039;" corruption! (Visits)</name> <min>0</min> <max>8</max> <change>-100%</change> diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_day.xml index 9a056e8786..5b6b0bc255 100644 --- a/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_day.xml @@ -6,6 +6,12 @@ <label>_pk_scount</label> <nb_visits>4</nb_visits> <nb_actions>6</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <subtable> <row> @@ -24,6 +30,12 @@ <label>_pk_scat</label> <nb_visits>2</nb_visits> <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <subtable> <row> @@ -39,6 +51,12 @@ <label>_pk_scount</label> <nb_visits>3</nb_visits> <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <subtable> <row> @@ -62,6 +80,12 @@ <label>_pk_scat</label> <nb_visits>2</nb_visits> <nb_actions>2</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <subtable> <row> @@ -89,6 +113,12 @@ <label>_pk_scount</label> <nb_visits>1</nb_visits> <nb_actions>2</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <subtable> <row> @@ -102,6 +132,12 @@ <label>_pk_scat</label> <nb_visits>1</nb_visits> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <subtable> <row> @@ -130,6 +166,12 @@ <sum_visit_length>541</sum_visit_length> <bounce_count>0</bounce_count> <nb_visits_converted>0</nb_visits_converted> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==test+cvar+name</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_month.xml index 50b6b4821e..76eae7c657 100644 --- a/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_month.xml +++ b/tests/PHPUnit/System/expected/test_SiteSearch_AllSites__CustomVariables.getCustomVariables_month.xml @@ -7,6 +7,12 @@ <nb_visits>7</nb_visits> <nb_actions>9</nb_actions> <sum_daily_nb_uniq_visitors>7</sum_daily_nb_uniq_visitors> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <subtable> <row> @@ -34,6 +40,12 @@ <nb_visits>4</nb_visits> <nb_actions>5</nb_actions> <sum_daily_nb_uniq_visitors>4</sum_daily_nb_uniq_visitors> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <subtable> <row> @@ -71,6 +83,12 @@ <nb_visits>1</nb_visits> <nb_actions>2</nb_actions> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <subtable> <row> @@ -86,6 +104,12 @@ <nb_visits>1</nb_visits> <nb_actions>1</nb_actions> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <subtable> <row> @@ -116,6 +140,12 @@ <nb_visits_converted>0</nb_visits_converted> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==test+cvar+name</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml index 8428ead295..bd42dba25a 100644 --- a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml +++ b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml @@ -112,11 +112,23 @@ <reportMetadata> <result prettyDate="Sunday, January 3, 2010"> <row> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <idsubdatatable>3173</idsubdatatable> </row> <row> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <idsubdatatable>3172</idsubdatatable> @@ -124,11 +136,23 @@ </result> <result prettyDate="Monday, January 4, 2010"> <row> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <idsubdatatable>3176</idsubdatatable> </row> <row> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <idsubdatatable>3175</idsubdatatable> diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml index a849b60fd7..4d86dedf9b 100644 --- a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml +++ b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml @@ -81,11 +81,23 @@ <reportMetadata> <result prettyDate="January 2010"> <row> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pk_scount</segment> <idsubdatatable>3198</idsubdatatable> </row> <row> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pk_scat</segment> <idsubdatatable>3197</idsubdatatable> diff --git a/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_day.xml b/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_day.xml index 362ecce292..865d25db4e 100644 --- a/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_day.xml +++ b/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_day.xml @@ -25,7 +25,7 @@ <loops>2</loops> <pageviews>18</pageviews> <entries>4</entries> - <exits>6</exits> + <exits>7</exits> </pageMetrics> <followingPages> <row> @@ -38,7 +38,7 @@ </row> <row> <label>Others</label> - <referrals>4</referrals> + <referrals>3</referrals> </row> </followingPages> <followingSiteSearches> diff --git a/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_month.xml b/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_month.xml index 639a8ffa93..7e7b381757 100644 --- a/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_month.xml +++ b/tests/PHPUnit/System/expected/test_Transitions__Transitions.getTransitionsForPageUrl_month.xml @@ -25,7 +25,7 @@ <loops>2</loops> <pageviews>21</pageviews> <entries>4</entries> - <exits>7</exits> + <exits>9</exits> </pageMetrics> <followingPages> <row> @@ -38,7 +38,7 @@ </row> <row> <label>Others</label> - <referrals>5</referrals> + <referrals>3</referrals> </row> </followingPages> <followingSiteSearches> diff --git a/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_day.xml b/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_day.xml index c6b559a21c..6233153f1c 100644 --- a/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_day.xml +++ b/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_day.xml @@ -25,7 +25,7 @@ <loops>2</loops> <pageviews>18</pageviews> <entries>4</entries> - <exits>6</exits> + <exits>7</exits> </pageMetrics> <followingPages> <row> @@ -44,10 +44,6 @@ <label>example.org/page3.html</label> <referrals>1</referrals> </row> - <row> - <label>example.org/page/search.html</label> - <referrals>1</referrals> - </row> </followingPages> <followingSiteSearches> <row> diff --git a/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_month.xml b/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_month.xml index dd4aea11a7..c782fbb761 100644 --- a/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_month.xml +++ b/tests/PHPUnit/System/expected/test_Transitions_noLimit__Transitions.getTransitionsForPageUrl_month.xml @@ -25,7 +25,7 @@ <loops>2</loops> <pageviews>21</pageviews> <entries>4</entries> - <exits>7</exits> + <exits>9</exits> </pageMetrics> <followingPages> <row> @@ -41,10 +41,6 @@ <referrals>2</referrals> </row> <row> - <label>example.org/page/search.html</label> - <referrals>2</referrals> - </row> - <row> <label>example.org/page3.html</label> <referrals>1</referrals> </row> diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html index 3ffd998150..cfb6f4e333 100644 --- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html +++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html @@ -5,7 +5,7 @@ </head> <body style="font-family: dejavusans; color: rgb(13,13,13);line-height: 1.33;"> -<a id="reportTop" rel="noreferrer" target="_blank" href="http://localhost/tests/PHPUnit/proxy/"><img title="Go to Piwik" border="0" alt="Piwik" src='http://localhost/tests/PHPUnit/proxy/plugins/Morpheus/images/logo-header.png'/></a> +<a id="reportTop" rel="noreferrer" target="_blank" href="http://example.com/piwik/tests/PHPUnit/proxy/"><img title="Go to Piwik" border="0" alt="Piwik" src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/Morpheus/images/logo-header.png'/></a> <h1 style="font-weight:normal; color: rgb(13,13,13); font-size: 24pt;"> Site 1 @@ -2562,7 +2562,42 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Unique returning visitors </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/cookie.gif'> + + Cookie </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 11 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 100% + </td> + </tr> + + <tr style=";line-height: 22px;"> + <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/flash.gif'> + + Flash </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 11 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 100% + </td> + </tr> + + <tr style="background-color: rgb(242,242,242);line-height: 22px;"> + <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/java.gif'> + + Java </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 11 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 2 </td> @@ -2570,7 +2605,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Returning Users </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/director.gif'> + + Director </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 0 </td> @@ -2578,7 +2619,16 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Returning Visits </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/gears.gif'> + + Gears </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 9 </td> @@ -2586,7 +2636,16 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Actions by Returning Visits </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/pdf.gif'> + + Pdf </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 41 </td> @@ -2594,7 +2653,16 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Maximum actions in one returning visit </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/quicktime.gif'> + + Quicktime </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 5 </td> @@ -2602,7 +2670,16 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Bounce Rate for Returning Visits </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/realplayer.gif'> + + Realplayer </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 11% </td> @@ -2610,7 +2687,16 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Avg. Actions per Returning Visit </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/silverlight.gif'> + + Silverlight </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 4.6 </td> @@ -2618,7 +2704,16 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Avg. Duration of a Returning Visit (in sec) </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/windowsmedia.gif'> + + Windowsmedia </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 00:13:21 </td> @@ -3574,7 +3669,13 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD 14h </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> + + Unknown </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 0 </td> @@ -3597,7 +3698,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD 15h </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/fr.png'> + + France </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 0 </td> @@ -3666,7 +3773,13 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD 18h </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> + + Unknown </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 0 </td> @@ -3689,7 +3802,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD 19h </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> + + Unknown </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 0 </td> @@ -4507,7 +4626,22 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Pageviews </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/screens/unknown.gif'> + + Unknown </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 8 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 40 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 5 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 43 </td> @@ -4531,7 +4665,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Unique Downloads </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/screens/normal.gif'> + + Desktop </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 0 </td> @@ -4610,7 +4750,13 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD second visitor </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/UNK.gif'> + + Unknown </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 8 </td> @@ -4627,7 +4773,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD first page view </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/FF.gif'> + + Firefox </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 2 </td> @@ -4635,6 +4787,40 @@ 2 </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD +======= + 1 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 00:00:00 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 100% + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0% + </td> + </tr> + + <tr style="background-color: rgb(242,242,242);line-height: 22px;"> + <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/OP.gif'> + + Opera </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 1 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 1 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 1 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 00:00:00 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +>>>>>>> master 100% </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -4675,7 +4861,19 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Checkout </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/brand/Unknown.ico'> + + Unknown </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 11 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 43 + </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 8 </td> @@ -4746,7 +4944,13 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Websites </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/UNK.gif'> + + Unknown </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 6 </td> @@ -4769,7 +4973,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Campaigns </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/FF.gif'> + + Firefox 3.6 </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 4 </td> @@ -4792,7 +5002,13 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD Direct Entry </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/OP.gif'> + + Opera 9.63 </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 1 </td> @@ -4852,7 +5068,13 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD referrer.com </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/UNK.gif'> + + Unknown </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 6 </td> @@ -4875,7 +5097,13 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD goal-matching-url-parameter </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/WIN.gif'> + + Windows </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 4 </td> @@ -4945,7 +5173,38 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> +<<<<<<< HEAD referrer.com </td> +======= + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/UNK.gif'> + + Unknown </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 8 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 40 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 5 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 00:15:01 + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0% + </td> + <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + 0% + </td> + </tr> + + <tr style=";line-height: 22px;"> + <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/WIN.gif'> + + Windows XP </td> +>>>>>>> master <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> 6 </td> diff --git a/tests/PHPUnit/System/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/System/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml index c728abec68..b83a3d071d 100644 --- a/tests/PHPUnit/System/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml +++ b/tests/PHPUnit/System/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml @@ -9,7 +9,7 @@ <url>http://example.org/index.htm</url> <pageTitle>incredible title!</pageTitle> <pageIdAction>2</pageIdAction> - <serverTimePretty>Mar 6, 2010 11:22:33 AM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 11:22:33</serverTimePretty> <pageId>1</pageId> <icon /> <timestamp>1267874553</timestamp> @@ -28,7 +28,7 @@ <url>http://example.org/index2.htm</url> <pageTitle>incredible title!</pageTitle> <pageIdAction>3</pageIdAction> - <serverTimePretty>Mar 6, 2010 11:25:33 AM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 11:25:33</serverTimePretty> <pageId>2</pageId> <timeSpent>180</timeSpent> <timeSpentPretty>3 min 0s</timeSpentPretty> @@ -40,7 +40,7 @@ <url>http://example.org/index3.htm</url> <pageTitle>incredible title!</pageTitle> <pageIdAction>4</pageIdAction> - <serverTimePretty>Mar 6, 2010 11:28:33 AM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 11:28:33</serverTimePretty> <pageId>3</pageId> <icon /> <timestamp>1267874913</timestamp> @@ -59,7 +59,7 @@ <url>http://example.org/no-user-id-set-but-should-appear-in-user-id-visit</url> <pageTitle>no User Id set but it should appear in email@example.com!</pageTitle> <pageIdAction>6</pageIdAction> - <serverTimePretty>Mar 6, 2010 1:16:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 13:16:33</serverTimePretty> <pageId>4</pageId> <timeSpent>360</timeSpent> <timeSpentPretty>6 min 0s</timeSpentPretty> @@ -71,7 +71,7 @@ <url>http://example.org/index.htm</url> <pageTitle>incredible title!</pageTitle> <pageIdAction>2</pageIdAction> - <serverTimePretty>Mar 6, 2010 1:22:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 13:22:33</serverTimePretty> <pageId>5</pageId> <timeSpent>360</timeSpent> <timeSpentPretty>6 min 0s</timeSpentPretty> @@ -83,7 +83,7 @@ <url>http://example.org/index.htm</url> <pageTitle>second page</pageTitle> <pageIdAction>2</pageIdAction> - <serverTimePretty>Mar 6, 2010 1:28:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 13:28:33</serverTimePretty> <pageId>6</pageId> <icon /> <timestamp>1267882113</timestamp> @@ -102,7 +102,7 @@ <url>http://example.org/index.htm</url> <pageTitle>a new user id was set -> new visit</pageTitle> <pageIdAction>2</pageIdAction> - <serverTimePretty>Mar 6, 2010 1:34:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 13:34:33</serverTimePretty> <pageId>7</pageId> <icon /> <timestamp>1267882473</timestamp> @@ -121,7 +121,7 @@ <url>http://example.org/home</url> <pageTitle>pageview - should not be tracked by our user id but in a new visit</pageTitle> <pageIdAction>10</pageIdAction> - <serverTimePretty>Mar 6, 2010 4:28:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 16:28:33</serverTimePretty> <pageId>10</pageId> <icon /> <timestamp>1267892913</timestamp> @@ -140,7 +140,7 @@ <url>http://example.org/home</url> <pageTitle>same user id was set -> this is the same unique user</pageTitle> <pageIdAction>10</pageIdAction> - <serverTimePretty>Mar 6, 2010 4:22:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 16:22:33</serverTimePretty> <pageId>8</pageId> <timeSpent>360</timeSpent> <timeSpentPretty>6 min 0s</timeSpentPretty> @@ -152,7 +152,7 @@ <url>http://example.org/home</url> <pageTitle>second pageview - by this user id</pageTitle> <pageIdAction>10</pageIdAction> - <serverTimePretty>Mar 6, 2010 4:28:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 16:28:33</serverTimePretty> <pageId>9</pageId> <timeSpent>720</timeSpent> <timeSpentPretty>12 min 0s</timeSpentPretty> @@ -165,7 +165,7 @@ <goalId>1</goalId> <revenue>0</revenue> <goalPageId /> - <serverTimePretty>Mar 6, 2010 4:34:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 16:34:33</serverTimePretty> <url>http://example.org/home</url> <icon>plugins/Morpheus/images/goal.png</icon> <timestamp>1267893273</timestamp> @@ -174,7 +174,7 @@ <type>ecommerceAbandonedCart</type> <revenue>10000000000</revenue> <items>1</items> - <serverTimePretty>Mar 6, 2010 4:40:33 PM</serverTimePretty> + <serverTimePretty>Mar 6, 2010 16:40:33</serverTimePretty> <itemDetails> <row> <itemSKU>sku-007-PRISM</itemSKU> @@ -201,7 +201,7 @@ <url>http://example.org/index.htm</url> <pageTitle>Page view by email@example.com</pageTitle> <pageIdAction>2</pageIdAction> - <serverTimePretty>Mar 14, 2010 11:22:33 AM</serverTimePretty> + <serverTimePretty>Mar 14, 2010 11:22:33</serverTimePretty> <pageId>11</pageId> <icon /> <timestamp>1268565753</timestamp> @@ -220,7 +220,7 @@ <url>http://example.org/index.htm</url> <pageTitle>A page view by new-user-id@one-weeklater</pageTitle> <pageIdAction>2</pageIdAction> - <serverTimePretty>Mar 14, 2010 11:46:33 AM</serverTimePretty> + <serverTimePretty>Mar 14, 2010 11:46:33</serverTimePretty> <pageId>12</pageId> <icon /> <timestamp>1268567193</timestamp> diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml index ded012ba51..1a9ff120e6 100644 --- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml @@ -67,6 +67,13 @@ <permission>1</permission> </row> <row> + <type>metric</type> + <category>Actions</category> + <name>Action Type</name> + <segment>actionType</segment> + <acceptedValues>A type of action, such as: pageviews, contents, sitesearches, events, outlinks, downloads</acceptedValues> + </row> + <row> <type>dimension</type> <category>Visit Location</category> <name>City</name> @@ -306,6 +313,13 @@ <category>Custom Variables</category> <name>Custom Variable name (scope visit)</name> <segment>customVariableName</segment> + <unionOfSegments> + <row>customVariableName1</row> + <row>customVariableName2</row> + <row>customVariableName3</row> + <row>customVariableName4</row> + <row>customVariableName5</row> + </unionOfSegments> </row> <row> <type>dimension</type> @@ -342,6 +356,13 @@ <category>Custom Variables</category> <name>Custom Variable name (scope page)</name> <segment>customVariablePageName</segment> + <unionOfSegments> + <row>customVariablePageName1</row> + <row>customVariablePageName2</row> + <row>customVariablePageName3</row> + <row>customVariablePageName4</row> + <row>customVariablePageName5</row> + </unionOfSegments> </row> <row> <type>dimension</type> @@ -378,6 +399,13 @@ <category>Custom Variables</category> <name>Custom Variable value (scope page)</name> <segment>customVariablePageValue</segment> + <unionOfSegments> + <row>customVariablePageValue1</row> + <row>customVariablePageValue2</row> + <row>customVariablePageValue3</row> + <row>customVariablePageValue4</row> + <row>customVariablePageValue5</row> + </unionOfSegments> </row> <row> <type>dimension</type> @@ -414,6 +442,13 @@ <category>Custom Variables</category> <name>Custom Variable value (scope visit)</name> <segment>customVariableValue</segment> + <unionOfSegments> + <row>customVariableValue1</row> + <row>customVariableValue2</row> + <row>customVariableValue3</row> + <row>customVariableValue4</row> + <row>customVariableValue5</row> + </unionOfSegments> </row> <row> <type>dimension</type> @@ -448,6 +483,17 @@ <row> <type>dimension</type> <category>Actions</category> + <name>Action URL</name> + <segment>actionUrl</segment> + <unionOfSegments> + <row>pageUrl</row> + <row>downloadUrl</row> + <row>outlinkUrl</row> + </unionOfSegments> + </row> + <row> + <type>dimension</type> + <category>Actions</category> <name>Clicked URL</name> <segment>outlinkUrl</segment> </row> diff --git a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems__CustomVariables.getCustomVariables_day.xml index 1d0ee64c8d..caf12153e5 100755 --- a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems__CustomVariables.getCustomVariables_day.xml @@ -33,6 +33,12 @@ </goals> <nb_conversions>3</nb_conversions> <revenue>3121.11</revenue> + <slots> + <row> + <scope>visit</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==ValueIsZero</segment> <subtable> <row> @@ -104,6 +110,12 @@ </goals> <nb_conversions>3</nb_conversions> <revenue>3121.11</revenue> + <slots> + <row> + <scope>visit</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -146,6 +158,12 @@ <label>_pkc</label> <nb_visits>7</nb_visits> <nb_actions>12</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==_pkc</segment> <subtable> <row> @@ -191,6 +209,12 @@ <label>_pkn</label> <nb_visits>6</nb_visits> <nb_actions>11</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==_pkn</segment> <subtable> <row> @@ -216,6 +240,12 @@ <label>_pks</label> <nb_visits>6</nb_visits> <nb_actions>11</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==_pks</segment> <subtable> <row> @@ -265,6 +295,12 @@ </goals> <nb_conversions>2</nb_conversions> <revenue>3111.11</revenue> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==VisitorName</segment> <subtable> <row> @@ -302,6 +338,12 @@ <label>_pkp</label> <nb_visits>8</nb_visits> <nb_actions>8</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==_pkp</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html index b81c8a37d0..b2825b88ee 100644 --- a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html +++ b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html @@ -5,7 +5,7 @@ </head> <body style="font-family: dejavusans; color: rgb(13,13,13);line-height: 1.33;"> -<a id="reportTop" rel="noreferrer" target="_blank" href="http://localhost/tests/PHPUnit/proxy/"><img title="Go to Piwik" border="0" alt="Piwik" src='http://localhost/tests/PHPUnit/proxy/plugins/Morpheus/images/logo-header.png'/></a> +<a id="reportTop" rel="noreferrer" target="_blank" href="http://example.com/piwik/tests/PHPUnit/proxy/"><img title="Go to Piwik" border="0" alt="Piwik" src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/Morpheus/images/logo-header.png'/></a> <h1 style="font-weight:normal; color: rgb(13,13,13); font-size: 24pt;"> Piwik test @@ -696,7 +696,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicesDetection/images/screens/normal.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/screens/normal.gif'> Desktop </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -818,7 +818,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicesDetection/images/brand/Unknown.ico'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/brand/Unknown.ico'> Unknown </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -940,7 +940,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/WIN.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/WIN.gif'> Windows XP </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1002,7 +1002,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/FF.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/FF.gif'> Firefox </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1064,7 +1064,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/FF.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/browsers/FF.gif'> Firefox 3.6 </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1186,7 +1186,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/WIN.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicesDetection/images/os/WIN.gif'> Windows </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1296,7 +1296,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/cookie.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/cookie.gif'> Cookie </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1309,7 +1309,7 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/flash.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/flash.gif'> Flash </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1322,7 +1322,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/java.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/java.gif'> Java </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1335,7 +1335,7 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/director.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/director.gif'> Director </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1348,7 +1348,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/gears.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/gears.gif'> Gears </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1361,7 +1361,7 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/pdf.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/pdf.gif'> Pdf </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1374,7 +1374,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/quicktime.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/quicktime.gif'> Quicktime </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1387,7 +1387,7 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/realplayer.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/realplayer.gif'> Realplayer </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1400,7 +1400,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/silverlight.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/silverlight.gif'> Silverlight </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1413,7 +1413,7 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/windowsmedia.gif'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/DevicePlugins/images/plugins/windowsmedia.gif'> Windowsmedia </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1463,7 +1463,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/pl.png'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/pl.png'> Poland </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1488,7 +1488,7 @@ <tr style=";line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/fr.png'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/fr.png'> France </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1610,7 +1610,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> Unknown </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> @@ -1755,7 +1755,7 @@ <tr style="background-color: rgb(242,242,242);line-height: 22px;"> <td style="font-size: 13px; border-right: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> - <img src='http://localhost/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> + <img src='http://example.com/piwik/tests/PHPUnit/proxy/plugins/UserCountry/images/flags/xx.png'> Unknown </td> <td style="font-size: 13px; border-left: 1px solid rgb(217,217,217); padding: 5px 0 5px 5px;"> diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml new file mode 100644 index 0000000000..7a397597a0 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>url</value> + <name>Page URL</name> + </row> + <row> + <value>urlparam</value> + <name>Page URL Parameter</name> + </row> + <row> + <value>action_name</value> + <name>Page Title</name> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml new file mode 100644 index 0000000000..149cf8b31c --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>visit</value> + <name>Visit</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>0</supportsExtractions> + </row> + <row> + <value>action</value> + <name>Action</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>1</supportsExtractions> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml new file mode 100644 index 0000000000..c234bed59e --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result />
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomVariables.getUsagesOfSlots.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomVariables.getUsagesOfSlots.xml new file mode 100644 index 0000000000..81163427e5 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomVariables.getUsagesOfSlots.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <scope>visit</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>5</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>5</index> + <usages> + </usages> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml new file mode 100644 index 0000000000..7a397597a0 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>url</value> + <name>Page URL</name> + </row> + <row> + <value>urlparam</value> + <name>Page URL Parameter</name> + </row> + <row> + <value>action_name</value> + <name>Page Title</name> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml new file mode 100644 index 0000000000..149cf8b31c --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <value>visit</value> + <name>Visit</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>0</supportsExtractions> + </row> + <row> + <value>action</value> + <name>Action</name> + <numSlotsAvailable>5</numSlotsAvailable> + <numSlotsUsed>0</numSlotsUsed> + <numSlotsLeft>5</numSlotsLeft> + <supportsExtractions>1</supportsExtractions> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml new file mode 100644 index 0000000000..c234bed59e --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result />
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomVariables.getUsagesOfSlots.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomVariables.getUsagesOfSlots.xml new file mode 100644 index 0000000000..81163427e5 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_noVisit__CustomVariables.getUsagesOfSlots.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <scope>visit</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>visit</scope> + <index>5</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>1</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>2</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>3</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>4</index> + <usages> + </usages> + </row> + <row> + <scope>page</scope> + <index>5</index> + <usages> + </usages> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__CustomVariables.getCustomVariables_range.xml b/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__CustomVariables.getCustomVariables_range.xml index 595575819f..ed926db9f7 100644 --- a/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__CustomVariables.getCustomVariables_range.xml +++ b/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__CustomVariables.getCustomVariables_range.xml @@ -23,6 +23,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -88,6 +94,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -129,6 +141,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -155,6 +173,16 @@ <row> <label>Status user</label> <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==Status+user</segment> <subtable> <row> @@ -189,6 +217,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Othercustom+value+which+should+be+truncated+abcdefghijklmnopqrstuvwxyz</segment> <subtable> <row> @@ -215,6 +249,12 @@ <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -228,6 +268,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI_pagesegment__CustomVariables.getCustomVariables_range.xml b/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI_pagesegment__CustomVariables.getCustomVariables_range.xml index 595575819f..ed926db9f7 100644 --- a/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI_pagesegment__CustomVariables.getCustomVariables_range.xml +++ b/tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI_pagesegment__CustomVariables.getCustomVariables_range.xml @@ -23,6 +23,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -88,6 +94,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -129,6 +141,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -155,6 +173,16 @@ <row> <label>Status user</label> <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==Status+user</segment> <subtable> <row> @@ -189,6 +217,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Othercustom+value+which+should+be+truncated+abcdefghijklmnopqrstuvwxyz</segment> <subtable> <row> @@ -215,6 +249,12 @@ <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -228,6 +268,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_reportLimiting__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting__CustomVariables.getCustomVariables_day.xml index d436493f49..6ee02592b7 100644 --- a/tests/PHPUnit/System/expected/test_reportLimiting__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_reportLimiting__CustomVariables.getCustomVariables_day.xml @@ -3,6 +3,12 @@ <row> <label>liked</label> <nb_actions>20</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==liked</segment> <subtable> <row> @@ -20,6 +26,12 @@ <row> <label>name</label> <nb_actions>20</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==name</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_rankingQuery__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_rankingQuery__CustomVariables.getCustomVariables_day.xml index d436493f49..6ee02592b7 100644 --- a/tests/PHPUnit/System/expected/test_reportLimiting_rankingQuery__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_reportLimiting_rankingQuery__CustomVariables.getCustomVariables_day.xml @@ -3,6 +3,12 @@ <row> <label>liked</label> <nb_actions>20</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==liked</segment> <subtable> <row> @@ -20,6 +26,12 @@ <row> <label>name</label> <nb_actions>20</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==name</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageTitleEndsWith__Actions.getPageTitles_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageTitleEndsWith__Actions.getPageTitles_day.xml new file mode 100644 index 0000000000..38eb716dff --- /dev/null +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageTitleEndsWith__Actions.getPageTitles_day.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label> Homepage</label> + <nb_visits>2</nb_visits> + <nb_uniq_visitors>2</nb_uniq_visitors> + <nb_hits>2</nb_hits> + <sum_time_spent>360</sum_time_spent> + <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors> + <entry_nb_visits>1</entry_nb_visits> + <entry_nb_actions>3</entry_nb_actions> + <entry_sum_visit_length>364</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>180</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>50%</exit_rate> + </row> + <row> + <label> Profile page</label> + <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> + <nb_hits>1</nb_hits> + <sum_time_spent>0</sum_time_spent> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>0%</exit_rate> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageTitleStartsWith__Actions.getPageTitles_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageTitleStartsWith__Actions.getPageTitles_day.xml new file mode 100644 index 0000000000..7cfe228274 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageTitleStartsWith__Actions.getPageTitles_day.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label> Profile page</label> + <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> + <nb_hits>1</nb_hits> + <sum_time_spent>0</sum_time_spent> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>0%</exit_rate> + </row> + <row> + <label> Profile page for user *_)%</label> + <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> + <nb_hits>1</nb_hits> + <sum_time_spent>0</sum_time_spent> + <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>100%</exit_rate> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageUrlEndsWith__Actions.getPageUrls_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageUrlEndsWith__Actions.getPageUrls_day.xml new file mode 100644 index 0000000000..c64e18155a --- /dev/null +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageUrlEndsWith__Actions.getPageUrls_day.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label>user</label> + <nb_visits>1</nb_visits> + <nb_hits>2</nb_hits> + <sum_time_spent>0</sum_time_spent> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>100%</exit_rate> + <subtable> + <row> + <label>/profile</label> + <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> + <nb_hits>2</nb_hits> + <sum_time_spent>0</sum_time_spent> + <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>100%</exit_rate> + <url>http://example.org/user/profile</url> + </row> + </subtable> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageUrlStartsWith__Actions.getPageUrls_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageUrlStartsWith__Actions.getPageUrls_day.xml new file mode 100644 index 0000000000..21450c0dd4 --- /dev/null +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_SegmentPageUrlStartsWith__Actions.getPageUrls_day.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label>/homepage</label> + <nb_visits>2</nb_visits> + <nb_uniq_visitors>2</nb_uniq_visitors> + <nb_hits>2</nb_hits> + <sum_time_spent>0</sum_time_spent> + <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors> + <entry_nb_visits>1</entry_nb_visits> + <entry_nb_actions>3</entry_nb_actions> + <entry_sum_visit_length>364</entry_sum_visit_length> + <entry_bounce_count>0</entry_bounce_count> + <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors> + <exit_nb_visits>1</exit_nb_visits> + <avg_time_on_page>0</avg_time_on_page> + <bounce_rate>0%</bounce_rate> + <exit_rate>50%</exit_rate> + <url>http://example.org/homepage</url> + <segment>pageUrl==http%3A%2F%2Fexample.org%2Fhomepage</segment> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_day.xml index 7de013ab81..893b862eda 100644 --- a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_day.xml @@ -24,6 +24,12 @@ </goals> <nb_conversions>3</nb_conversions> <revenue>1000</revenue> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -86,6 +92,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -125,6 +137,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -150,6 +168,16 @@ <row> <label>Status user</label> <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==Status+user</segment> <subtable> <row> @@ -181,6 +209,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>1000</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Othercustom+value+which+should+be+truncated+abcdefghijklmnopqrstuvwxyz</segment> <subtable> <row> @@ -206,6 +240,12 @@ <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -218,6 +258,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> @@ -230,6 +276,12 @@ <row> <label>var1</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==var1</segment> <subtable> <row> @@ -242,6 +294,12 @@ <row> <label>var2</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==var2</segment> <subtable> <row> @@ -254,6 +312,12 @@ <row> <label>var3</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==var3</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_week.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_week.xml index 5ad33104c9..ed85668d5c 100644 --- a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_week.xml +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables__CustomVariables.getCustomVariables_week.xml @@ -25,6 +25,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -90,6 +96,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -131,6 +143,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -157,6 +175,16 @@ <row> <label>Status user</label> <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==Status+user</segment> <subtable> <row> @@ -191,6 +219,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Othercustom+value+which+should+be+truncated+abcdefghijklmnopqrstuvwxyz</segment> <subtable> <row> @@ -217,6 +251,12 @@ <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -230,6 +270,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> @@ -243,6 +289,12 @@ <row> <label>var1</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==var1</segment> <subtable> <row> @@ -256,6 +308,12 @@ <row> <label>var2</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==var2</segment> <subtable> <row> @@ -269,6 +327,12 @@ <row> <label>var3</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==var3</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_day.xml index eec12ff442..1bfb23612e 100644 --- a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_day.xml @@ -24,6 +24,12 @@ </goals> <nb_conversions>3</nb_conversions> <revenue>1000</revenue> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -86,6 +92,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -109,6 +121,33 @@ </subtable> </row> <row> + <label>Status user</label> + <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> + <segment>customVariableName==Status+user</segment> + <subtable> + <row> + <label>looking at "profile page"</label> + <nb_visits>2</nb_visits> + <nb_actions>2</nb_actions> + </row> + <row> + <label>Loggedin</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> <label>Value will be VERY long and truncated</label> <nb_visits>1</nb_visits> <nb_actions>3</nb_actions> @@ -125,6 +164,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -148,23 +193,6 @@ </subtable> </row> <row> - <label>Status user</label> - <nb_actions>3</nb_actions> - <segment>customVariableName==Status+user</segment> - <subtable> - <row> - <label>looking at "profile page"</label> - <nb_visits>2</nb_visits> - <nb_actions>2</nb_actions> - </row> - <row> - <label>Loggedin</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> <label>Othercustom value which should be truncated abcdefghijklmnopqrstuvwxyz</label> <nb_visits>2</nb_visits> <nb_actions>2</nb_actions> @@ -181,6 +209,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>1000</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Othercustom+value+which+should+be+truncated+abcdefghijklmnopqrstuvwxyz</segment> <subtable> <row> @@ -206,6 +240,12 @@ <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -218,6 +258,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_week.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_week.xml index fe4627f8e5..4041ca8eac 100644 --- a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_week.xml +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_week.xml @@ -25,6 +25,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -90,6 +96,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -114,6 +126,35 @@ </subtable> </row> <row> + <label>Status user</label> + <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> + <segment>customVariableName==Status+user</segment> + <subtable> + <row> + <label>looking at "profile page"</label> + <nb_visits>2</nb_visits> + <nb_actions>2</nb_actions> + <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> + </row> + <row> + <label>Loggedin</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> + </row> + </subtable> + </row> + <row> <label>Value will be VERY long and truncated</label> <nb_visits>1</nb_visits> <nb_actions>3</nb_actions> @@ -131,6 +172,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -155,25 +202,6 @@ </subtable> </row> <row> - <label>Status user</label> - <nb_actions>3</nb_actions> - <segment>customVariableName==Status+user</segment> - <subtable> - <row> - <label>looking at "profile page"</label> - <nb_visits>2</nb_visits> - <nb_actions>2</nb_actions> - <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> - </row> - <row> - <label>Loggedin</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> - </row> - </subtable> - </row> - <row> <label>Othercustom value which should be truncated abcdefghijklmnopqrstuvwxyz</label> <nb_visits>2</nb_visits> <nb_actions>2</nb_actions> @@ -191,6 +219,12 @@ <revenue>1000</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Othercustom+value+which+should+be+truncated+abcdefghijklmnopqrstuvwxyz</segment> <subtable> <row> @@ -217,6 +251,12 @@ <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -230,6 +270,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_day.xml index 38cbd40db8..428f0180ae 100644 --- a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_day.xml +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_day.xml @@ -19,6 +19,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -42,6 +48,33 @@ </subtable> </row> <row> + <label>Status user</label> + <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> + <segment>customVariableName==Status+user</segment> + <subtable> + <row> + <label>looking at "profile page"</label> + <nb_visits>2</nb_visits> + <nb_actions>2</nb_actions> + </row> + <row> + <label>Loggedin</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> <label>Value will be VERY long and truncated</label> <nb_visits>1</nb_visits> <nb_actions>3</nb_actions> @@ -58,6 +91,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -102,6 +141,12 @@ </goals> <nb_conversions>2</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -138,25 +183,14 @@ </subtable> </row> <row> - <label>Status user</label> - <nb_actions>3</nb_actions> - <segment>customVariableName==Status+user</segment> - <subtable> - <row> - <label>looking at "profile page"</label> - <nb_visits>2</nb_visits> - <nb_actions>2</nb_actions> - </row> - <row> - <label>Loggedin</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -169,6 +203,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> diff --git a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_week.xml b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_week.xml index 7557e0aa02..4c0da8f95e 100644 --- a/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_week.xml +++ b/tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_week.xml @@ -20,6 +20,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE</segment> <subtable> <row> @@ -44,6 +50,35 @@ </subtable> </row> <row> + <label>Status user</label> + <nb_actions>3</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> + <segment>customVariableName==Status+user</segment> + <subtable> + <row> + <label>looking at "profile page"</label> + <nb_visits>2</nb_visits> + <nb_actions>2</nb_actions> + <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> + </row> + <row> + <label>Loggedin</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> + </row> + </subtable> + </row> + <row> <label>Value will be VERY long and truncated</label> <nb_visits>1</nb_visits> <nb_actions>3</nb_actions> @@ -61,6 +96,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Value+will+be+VERY+long+and+truncated</segment> <subtable> <row> @@ -107,6 +148,12 @@ <revenue>0</revenue> <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <sum_daily_nb_users>0</sum_daily_nb_users> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==VisitorType</segment> <subtable> <row> @@ -144,27 +191,14 @@ </subtable> </row> <row> - <label>Status user</label> - <nb_actions>3</nb_actions> - <segment>customVariableName==Status+user</segment> - <subtable> - <row> - <label>looking at "profile page"</label> - <nb_visits>2</nb_visits> - <nb_actions>2</nb_actions> - <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> - </row> - <row> - <label>Loggedin</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> - </row> - </subtable> - </row> - <row> <label>Language</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Language</segment> <subtable> <row> @@ -178,6 +212,12 @@ <row> <label>SET WITH EMPTY VALUE PAGE SCOPE</label> <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==SET+WITH+EMPTY+VALUE+PAGE+SCOPE</segment> <subtable> <row> diff --git a/tests/PHPUnit/Unit/CommonTest.php b/tests/PHPUnit/Unit/CommonTest.php index 8aa85ed550..08931b6bfe 100644 --- a/tests/PHPUnit/Unit/CommonTest.php +++ b/tests/PHPUnit/Unit/CommonTest.php @@ -102,14 +102,6 @@ class CommonTest extends PHPUnit_Framework_TestCase */ public function testSanitizeInputValues($input, $output) { - if (version_compare(PHP_VERSION, '5.4') < 0) { - $this->assertTrue(@set_magic_quotes_runtime(1)); - $this->assertEquals(1, @get_magic_quotes_runtime()); - $this->assertEquals($output, Common::sanitizeInputValues($input)); - - $this->assertTrue(@set_magic_quotes_runtime(0)); - $this->assertEquals(0, @get_magic_quotes_runtime()); - } $this->assertEquals($output, Common::sanitizeInputValues($input)); } @@ -465,39 +457,4 @@ class CommonTest extends PHPUnit_Framework_TestCase { $this->assertEquals($expected, Common::extractLanguageCodeFromBrowserLanguage($browserLanguage, $validLanguages), "test with {$browserLanguage} failed, expected {$expected}"); } - - public function testSearchEnginesDefinedCorrectly() - { - include "DataFiles/SearchEngines.php"; - - $searchEngines = array(); - foreach ($GLOBALS['Piwik_SearchEngines'] as $host => $info) { - if (isset($info[2]) && $info[2] !== false) { - $this->assertTrue(strrpos($info[2], "{k}") !== false, $host . " search URL is not defined correctly, must contain the macro {k}"); - } - - if (!array_key_exists($info[0], $searchEngines)) { - $searchEngines[$info[0]] = true; - - $this->assertTrue(strpos($host, '{}') === false, $host . " search URL is the master record and should not contain {}"); - } - - if (isset($info[3]) && $info[3] !== false) { - $this->assertTrue(is_array($info[3]) || is_string($info[3]), $host . ' encoding must be either a string or an array'); - - if (is_string($info[3])) { - $this->assertTrue(trim($info[3]) !== '', $host . ' encoding cannot be an empty string'); - $this->assertTrue(strpos($info[3], ' ') === false, $host . ' encoding cannot contain spaces'); - - } - - if (is_array($info[3])) { - $this->assertTrue(count($info[3]) > 0, $host . ' encodings cannot be an empty array'); - $this->assertTrue(strpos(serialize($info[3]), '""') === false, $host . ' encodings in array cannot be empty stringss'); - $this->assertTrue(strpos(serialize($info[3]), ' ') === false, $host . ' encodings in array cannot contain spaces'); - } - } - } - } - } diff --git a/tests/PHPUnit/Unit/DataTable/Renderer/CSVTest.php b/tests/PHPUnit/Unit/DataTable/Renderer/CSVTest.php index c6fd94e406..057f500d18 100644 --- a/tests/PHPUnit/Unit/DataTable/Renderer/CSVTest.php +++ b/tests/PHPUnit/Unit/DataTable/Renderer/CSVTest.php @@ -96,6 +96,22 @@ class DataTable_Renderer_CSVTest extends \PHPUnit_Framework_TestCase return $table; } + protected function _getDataTableHavingAnArrayInRowMetadata() + { + $array = array( + array(Row::COLUMNS => array('label' => 'sub1', 'count' => 1)), + array(Row::COLUMNS => array('label' => 'sub2', 'count' => 2), Row::METADATA => array('test' => 'render')), + array(Row::COLUMNS => array('label' => 'sub3', 'count' => 2), Row::METADATA => array('test' => 'renderMe', 'testArray' => 'ignore')), + array(Row::COLUMNS => array('label' => 'sub4', 'count' => 6), Row::METADATA => array('testArray' => array('do not render'))), + array(Row::COLUMNS => array('label' => 'sub5', 'count' => 2), Row::METADATA => array('testArray' => 'do ignore', 'mymeta' => 'should be rendered')), + array(Row::COLUMNS => array('label' => 'sub6', 'count' => 3), Row::METADATA => array('mymeta' => 'renderrrrrr')), + ); + + $table = new DataTable(); + $table->addRowsFromArray($array); + + return $table; + } public function testCSVTest1() { @@ -186,6 +202,25 @@ class DataTable_Renderer_CSVTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $actual); } + public function testCSVTest7_shouldNotRenderMetadataThatContainsAnArray() + { + $dataTable = $this->_getDataTableHavingAnArrayInRowMetadata(); + $render = new Csv(); + $render->setTable($dataTable); + $render->convertToUnicode = false; + + // the column "testArray" should be ignored and not rendered, all other columns should be assigned correctly + $expected = "label,count,metadata_test,metadata_mymeta +sub1,1,, +sub2,2,render, +sub3,2,renderMe, +sub4,6,, +sub5,2,,should be rendered +sub6,3,,renderrrrrr"; + $rendered = $render->render(); + $this->assertEquals($expected, $rendered); + } + /** * DATA OF DATATABLE_ARRAY * ------------------------- diff --git a/tests/PHPUnit/Unit/DataTable/RowTest.php b/tests/PHPUnit/Unit/DataTable/RowTest.php index e18806d2d5..bc9c49bb84 100644 --- a/tests/PHPUnit/Unit/DataTable/RowTest.php +++ b/tests/PHPUnit/Unit/DataTable/RowTest.php @@ -362,6 +362,65 @@ class RowTest extends \PHPUnit_Framework_TestCase $this->assertTrue($this->row->hasColumn('test')); } + public function test_sumRowMetadata_shouldSumMetadataAccordingToAggregationOperations() + { + $this->row->setColumn('nb_visits', 10); + $this->row->setMetadata('my_sum', 5); + $this->row->setMetadata('my_max', 4); + $this->row->setMetadata('my_array', array(array('test' => 1, 'value' => 1), array('test' => 2, 'value' => 2))); + + + $row = $this->getTestRowWithNoSubDataTable(); + $row->setColumn('nb_visits', 15); + $row->setMetadata('my_sum', 7); + $row->setMetadata('my_max', 2); + $row->setMetadata('my_array', array(array('test' => 3, 'value' => 3), array('test' => 2, 'value' => 2))); + + + $aggregations = array( + 'nosuchcolumn' => 'max', // this metadata name does not exist and should be ignored + 'my_sum' => 'sum', + 'my_max' => 'max', + 'my_array' => 'uniquearraymerge' + ); + $this->row->sumRowMetadata($row, $aggregations); + + $metadata = $this->row->getMetadata(); + $expected = array( + 'my_sum' => 12, + 'my_max' => 4, + 'my_array' => array(array('test' => 1, 'value' => 1), array('test' => 2, 'value' => 2), array('test' => 3, 'value' => 3)) + ); + $this->assertSame($expected, $metadata); + } + + public function test_sumRowMetadata_uniquearraymergeShouldUseArrayFromOtherRow_IfNoMetadataForThisRowSpecified() + { + $row = $this->getTestRowWithNoSubDataTable(); + $arrayValue = array(array('test' => 3, 'value' => 3), array('test' => 2, 'value' => 2)); + $row->setMetadata('my_array', $arrayValue); + + $aggregations = array('my_array' => 'uniquearraymerge'); + + $this->row->sumRowMetadata($row, $aggregations); + + $this->assertSame(array('my_array' => $arrayValue), $this->row->getMetadata()); + } + + public function test_sumRowMetadata_uniquearraymergeShouldUseArrayFromThisRow_IfNoMetadataForOtherRowSpecified() + { + $row = $this->getTestRowWithNoSubDataTable(); + + $arrayValue = array(array('test' => 3, 'value' => 3), array('test' => 2, 'value' => 2)); + $this->row->setMetadata('my_array', $arrayValue); + + $aggregations = array('my_array' => 'uniquearraymerge'); + + $this->row->sumRowMetadata($row, $aggregations); + + $this->assertSame(array('my_array' => $arrayValue), $this->row->getMetadata()); + } + private function assertColumnSavesValue($expectedValue, $columnName, $valueToSet) { $this->row->setColumn($columnName, $valueToSet); diff --git a/tests/PHPUnit/Unit/DateTest.php b/tests/PHPUnit/Unit/DateTest.php index ba18ce1e70..ee37aba1eb 100644 --- a/tests/PHPUnit/Unit/DateTest.php +++ b/tests/PHPUnit/Unit/DateTest.php @@ -9,8 +9,10 @@ namespace Piwik\Tests\Unit; use Exception; +use Piwik\Container\StaticContainer; use Piwik\Date; use Piwik\SettingsServer; +use Piwik\Translate; /** */ @@ -331,4 +333,34 @@ class DateTest extends \PHPUnit_Framework_TestCase $this->assertTrue($date->isLeapYear()); } } + + + public function getLocalizedLongStrings() + { + return array( + array('en', false, '2000-01-01 16:05:52', '16:05:52'), + array('de', false, '2000-01-01 16:05:52', '16:05:52'), + array('en', true, '2000-01-01 16:05:52', '4:05:52 PM'), + array('de', true, '2000-01-01 04:05:52', '4:05:52 vorm.'), + array('zh-tw', true, '2000-01-01 04:05:52', '上午4:05:52'), + array('lt', true, '2000-01-01 16:05:52', '04:05:52 popiet'), + array('ar', true, '2000-01-01 04:05:52', '4:05:52 ص'), + ); + } + + /** + * @group Core + * @dataProvider getLocalizedLongStrings + */ + public function testGetLocalizedTimeFormats($language, $use12HourClock, $time, $shouldBe) + { + Translate::loadAllTranslations(); + StaticContainer::get('Piwik\Translation\Translator')->setCurrentLanguage($language); + StaticContainer::get('Piwik\Intl\Data\Provider\DateTimeFormatProvider')->forceTimeFormat($use12HourClock); + + $date = Date::factory($time); + + $this->assertEquals($shouldBe, $date->getLocalized(Date::TIME_FORMAT)); + Translate::reset(); + } } diff --git a/tests/PHPUnit/Unit/IPTest.php b/tests/PHPUnit/Unit/IPTest.php index 2225144557..139687f619 100644 --- a/tests/PHPUnit/Unit/IPTest.php +++ b/tests/PHPUnit/Unit/IPTest.php @@ -10,11 +10,12 @@ namespace Piwik\Tests\Unit; -use Piwik\Common; use Piwik\Config; use Piwik\IP; -use Piwik\Tests\Framework\Mock\TestConfig; +/** + * @group Core + */ class IPTest extends \PHPUnit_Framework_TestCase { /** @@ -83,7 +84,6 @@ class IPTest extends \PHPUnit_Framework_TestCase /** * @dataProvider getIpFromHeaderTestData - * @group Core */ public function testGetIpFromHeader($description, $test) { @@ -111,8 +111,6 @@ class IPTest extends \PHPUnit_Framework_TestCase } /** - * @group Core - * * @dataProvider getIpTestData */ public function testGetNonProxyIpFromHeader($ip) @@ -121,8 +119,6 @@ class IPTest extends \PHPUnit_Framework_TestCase } /** - * @group Core - * * @dataProvider getIpTestData */ public function testGetNonProxyIpFromHeader2($ip) @@ -134,8 +130,6 @@ class IPTest extends \PHPUnit_Framework_TestCase } /** - * @group Core - * * @dataProvider getIpTestData */ public function testGetNonProxyIpFromHeader3($ip) @@ -155,6 +149,20 @@ class IPTest extends \PHPUnit_Framework_TestCase } /** + * See https://github.com/piwik/piwik/issues/8721 + */ + public function testGetNonProxyIpFromHeader4_ShouldReturnDefaultIp_IfDefaultIpIsGivenMultipleTimes() + { + // 1.1.1.1 is a trusted proxy + $_SERVER['REMOTE_ADDR'] = '1.1.1.1'; + $_SERVER['HTTP_X_FORWARDED_FOR'] = $_SERVER['REMOTE_ADDR']; + $_SERVER['HTTP_CF_CONNECTING_IP'] = $_SERVER['REMOTE_ADDR']; + + $this->assertEquals('1.1.1.1', IP::getNonProxyIpFromHeader('1.1.1.1', array('HTTP_X_FORWARDED_FOR', 'HTTP_CF_CONNECTING_IP'))); + unset($_SERVER['HTTP_CF_CONNECTING_IP']); + } + + /** * Dataprovider for testGetLastIpFromList */ public function getLastIpFromListTestData() @@ -170,8 +178,6 @@ class IPTest extends \PHPUnit_Framework_TestCase } /** - * @group Core - * * @dataProvider getLastIpFromListTestData */ public function testGetLastIpFromList($csv, $expected) @@ -182,4 +188,10 @@ class IPTest extends \PHPUnit_Framework_TestCase // with excluded Ips $this->assertEquals($expected, IP::getLastIpFromList($csv . ', 10.10.10.10', array('10.10.10.10'))); } + + public function testGetLastIpFromList_shouldReturnAnEmptyString_IfMultipleIpsAreGivenButAllAreExcluded() + { + // with excluded Ips + $this->assertEquals('', IP::getLastIpFromList('10.10.10.10, 10.10.10.10', array('10.10.10.10'))); + } } diff --git a/tests/PHPUnit/Unit/Metrics/FormatterTest.php b/tests/PHPUnit/Unit/Metrics/FormatterTest.php index 0bcef84c5f..d9ccc18c6d 100644 --- a/tests/PHPUnit/Unit/Metrics/FormatterTest.php +++ b/tests/PHPUnit/Unit/Metrics/FormatterTest.php @@ -190,9 +190,9 @@ class FormatterTest extends \PHPUnit_Framework_TestCase array(100, array('1 min 40s', '00:01:40')), array(3600, array('1 hours 0 min', '01:00:00')), array(3700, array('1 hours 1 min', '01:01:40')), - array(86400 + 3600 * 10, array('1 days 10 hours', '34:00:00')), - array(86400 * 365, array('365 days 0 hours', '8760:00:00')), - array((86400 * (365.25 + 10)), array('1 years 10 days', '9006:00:00')), + array(86400 + 3600 * 10, array('1 days 10 hours', '1 days 10:00:00')), + array(86400 * 365, array('365 days 0 hours', '365 days 00:00:00')), + array((86400 * (365.25 + 10)), array('1 years 10 days', '375 days 06:00:00')), array(1.342, array('1.34s', '00:00:01.34')), array(.342, array('0.34s', '00:00:00.34')), array(.02, array('0.02s', '00:00:00.02')), @@ -202,7 +202,7 @@ class FormatterTest extends \PHPUnit_Framework_TestCase array(1.2, array('1.2s', '00:00:01.20')), array(122.1, array('2 min 2.1s', '00:02:02.10')), array(-122.1, array('-2 min 2.1s', '-00:02:02.10')), - array(86400 * -365, array('-365 days 0 hours', '-8760:00:00')) + array(86400 * -365, array('-365 days 0 hours', '-365 days 00:00:00')) ); } @@ -222,4 +222,4 @@ class FormatterTest extends \PHPUnit_Framework_TestCase SitesManagerAPI::setSingletonInstance($mock); } -}
\ No newline at end of file +} diff --git a/tests/PHPUnit/Unit/Segment/SegmentExpressionTest.php b/tests/PHPUnit/Unit/Segment/SegmentExpressionTest.php index 6d9dcc67b1..71e64863c1 100644 --- a/tests/PHPUnit/Unit/Segment/SegmentExpressionTest.php +++ b/tests/PHPUnit/Unit/Segment/SegmentExpressionTest.php @@ -72,6 +72,7 @@ class SegmentExpressionTest extends \PHPUnit_Framework_TestCase array('A==B,C==D', array('where' => " (A = ? OR C = ? )", 'bind' => array('B', 'D'))), array('A!=B;C==D', array('where' => " ( A IS NULL OR A <> ? ) AND C = ? ", 'bind' => array('B', 'D'))), array('A!=B;C==D,E!=Hello World!=', array('where' => " ( A IS NULL OR A <> ? ) AND (C = ? OR ( E IS NULL OR E <> ? ) )", 'bind' => array('B', 'D', 'Hello World!='))), + array('A=@B;C=$D', array('where' => " A LIKE ? AND C LIKE ? ", 'bind' => array('%B%', '%D'))), array('A>B', array('where' => " A > ? ", 'bind' => array('B'))), array('A<B', array('where' => " A < ? ", 'bind' => array('B'))), @@ -83,6 +84,8 @@ class SegmentExpressionTest extends \PHPUnit_Framework_TestCase array('A=@B_', array('where' => " A LIKE ? ", 'bind' => array('%B\_%'))), array('A!@B%', array('where' => " ( A IS NULL OR A NOT LIKE ? ) ", 'bind' => array('%B\%%'))), + array('A=$B%', array('where' => " A LIKE ? ", 'bind' => array('%B\%'))), + array('A=^B%', array('where' => " A LIKE ? ", 'bind' => array('B\%%'))), ); } diff --git a/tests/PHPUnit/Unit/Tracker/RequestTest.php b/tests/PHPUnit/Unit/Tracker/RequestTest.php index 3db5c22a4a..7eb38059a2 100644 --- a/tests/PHPUnit/Unit/Tracker/RequestTest.php +++ b/tests/PHPUnit/Unit/Tracker/RequestTest.php @@ -470,6 +470,25 @@ class RequestTest extends UnitTestCase $this->assertContains($needle, $cookie . ''); } + public function test_getLocalTime() + { + $request = $this->buildRequest(array('h' => '12', 'm' => '34', 's' => '3')); + $this->assertSame('12:34:03', $request->getLocalTime()); + + + $request = $this->buildRequest(array('h' => '23', 'm' => '59', 's' => '59')); + $this->assertSame('23:59:59', $request->getLocalTime()); + } + + public function test_getLocalTime_shouldReturnValidTime_whenTimeWasInvalid() + { + $request = $this->buildRequest(array('h' => '26', 'm' => '60', 's' => '333')); + $this->assertSame('00:00:00', $request->getLocalTime()); + + $request = $this->buildRequest(array('h' => '-26', 'm' => '-60', 's' => '-333')); + $this->assertSame('00:00:00', $request->getLocalTime()); + } + public function test_getIdSite() { $request = $this->buildRequest(array('idsite' => '14')); diff --git a/tests/PHPUnit/Unit/UrlHelperTest.php b/tests/PHPUnit/Unit/UrlHelperTest.php index c3cd578a65..bfdf9959aa 100644 --- a/tests/PHPUnit/Unit/UrlHelperTest.php +++ b/tests/PHPUnit/Unit/UrlHelperTest.php @@ -150,32 +150,6 @@ class UrlHelperTest extends \PHPUnit_Framework_TestCase } /** - * Dataprovider for testExtractSearchEngineInformationFromUrl - */ - public function getSearchEngineUrls() - { - return Spyc::YAMLLoad(PIWIK_PATH_TEST_TO_ROOT .'/tests/resources/extractSearchEngineInformationFromUrlTests.yml'); - } - - /** - * @dataProvider getSearchEngineUrls - * @group Core - */ - public function testExtractSearchEngineInformationFromUrl($url, $engine, $keywords) - { - $this->includeDataFilesForSearchEngineTest(); - $returnedValue = UrlHelper::extractSearchEngineInformationFromUrl($url); - - $exptectedValue = false; - - if (!empty($engine)) { - $exptectedValue = array('name' => $engine, 'keywords' => $keywords); - } - - $this->assertEquals($exptectedValue, $returnedValue); - } - - /** * Dataprovider for testGetLossyUrl */ public function getLossyUrls() @@ -203,11 +177,6 @@ class UrlHelperTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, UrlHelper::getLossyUrl($input)); } - private function includeDataFilesForSearchEngineTest() - { - include "DataFiles/SearchEngines.php"; - } - /** * @group Core */ diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php index 5eed01f806..313f8f4b6a 100644 --- a/tests/PHPUnit/bootstrap.php +++ b/tests/PHPUnit/bootstrap.php @@ -82,6 +82,7 @@ function prepareServerVariables(Config $config) $_SERVER['HTTP_HOST'] = $testConfig['http_host']; $_SERVER['REQUEST_URI'] = $testConfig['request_uri']; $_SERVER['REMOTE_ADDR'] = $testConfig['remote_addr']; + $_SERVER['SERVER_PORT'] = $testConfig['port']; } function prepareTestDatabaseConfig(Config $config) diff --git a/tests/PHPUnit/config.ini.travis.php b/tests/PHPUnit/config.ini.travis.php index 093aeaca61..0261a631e2 100644 --- a/tests/PHPUnit/config.ini.travis.php +++ b/tests/PHPUnit/config.ini.travis.php @@ -14,6 +14,7 @@ tables_prefix = [tests] request_uri = "/" +port = 3000 [database_tests] host = 127.0.0.1 diff --git a/tests/README.md b/tests/README.md index aaa18d6d34..49a4882f01 100644 --- a/tests/README.md +++ b/tests/README.md @@ -31,7 +31,7 @@ To execute the tests: 1. To install PHPUnit, run `php composer.phar install --dev` in the Piwik root directory. -2. Ensure the `[database_tests]` section in `piwik/config/config.php.ini` is set up correctly, +2. Ensure the `[database_tests]` section in `piwik/config/config.ini.php` is set up correctly, i.e. with the correct password to prevent the following error: `SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: NO)` diff --git a/tests/UI/specs/CoreUpdaterDb_spec.js b/tests/UI/specs/CoreUpdaterDb_spec.js index 98586a7ca5..33236fda5b 100644 --- a/tests/UI/specs/CoreUpdaterDb_spec.js +++ b/tests/UI/specs/CoreUpdaterDb_spec.js @@ -18,7 +18,7 @@ describe("CoreUpdaterDb", function () { }); function apiUpgradeTest(format) { - it("should start the updater when an old version of Piwik is detected in the DB", function (done) { + it("should start the updater when an old version of Piwik is detected in the DB with format " + format, function (done) { expect.file('CoreUpdater.API.ErrorMessage' + format + '.txt').to.be.pageContents(function (page) { page.load(''); page.downloadUrl('?module=API&method=API.getPiwikVersion&format=' + format); diff --git a/tests/UI/specs/Dashboard_spec.js b/tests/UI/specs/Dashboard_spec.js index 3c6166c698..9607b126ac 100644 --- a/tests/UI/specs/Dashboard_spec.js +++ b/tests/UI/specs/Dashboard_spec.js @@ -38,11 +38,7 @@ describe("Dashboard", function () { var setup = function (done) { // make sure live widget doesn't refresh constantly for UI tests - testEnvironment.configOverride = { - General: { - 'live_widget_refresh_after_seconds': 1000000 - } - }; + testEnvironment.overrideConfig('General', 'live_widget_refresh_after_seconds', 1000000); testEnvironment.save(); // save empty layout for dashboard ID = 5 diff --git a/tests/UI/specs/Installation_spec.js b/tests/UI/specs/Installation_spec.js index 6830091da6..6aca4b0b78 100644 --- a/tests/UI/specs/Installation_spec.js +++ b/tests/UI/specs/Installation_spec.js @@ -121,6 +121,15 @@ describe("Installation", function () { }); page.click('.btn'); page.wait(3000); + + // manually remove port in tracking code, since ui-test.php won't be using the correct INI config file + page.evaluate(function () { + $('pre').each(function () { + var html = $(this).html(); + html = html.replace(/localhost\:[0-9]+/g, 'localhost'); + $(this).html(html); + }); + }); }, done); }); diff --git a/tests/UI/specs/Morpheus_spec.js b/tests/UI/specs/Morpheus_spec.js index e2dd4322e5..0a57d26540 100644 --- a/tests/UI/specs/Morpheus_spec.js +++ b/tests/UI/specs/Morpheus_spec.js @@ -12,11 +12,7 @@ describe("Morpheus", function () { before(function () { // Enable development mode - testEnvironment.configOverride = { - Development: { - enabled: true - } - }; + testEnvironment.overrideConfig('Development', 'enabled', true); testEnvironment.save(); }); diff --git a/tests/UI/specs/Overlay_spec.js b/tests/UI/specs/Overlay_spec.js index 5fb74621f5..c0adc30a43 100644 --- a/tests/UI/specs/Overlay_spec.js +++ b/tests/UI/specs/Overlay_spec.js @@ -12,6 +12,7 @@ describe("Overlay", function () { this.timeout(0); var url = null; + var urlWithSegment; function removeOptOutIframe(page) { page.evaluate(function () { @@ -20,8 +21,12 @@ describe("Overlay", function () { } before(function (done) { - url = "?module=Overlay&period=year&date=today&idSite=3#?l=" + encodeURIComponent(testEnvironment.overlayUrl).replace(/[%]/g, "$"); - + var baseUrl = '?module=Overlay&period=year&date=today&idSite=3'; + var hash = '#?l=' + encodeURIComponent(testEnvironment.overlayUrl).replace(/[%]/g, "$"); + + url = baseUrl + hash; + urlWithSegment = baseUrl + '&segment=' + encodeURIComponent('visitIp==20.56.34.67') + hash; + testEnvironment.callApi("SitesManager.addSiteAliasUrls", {idSite: 3, urls: [config.piwikUrl]}, done); }); @@ -49,6 +54,14 @@ describe("Overlay", function () { }); page.sendMouseEvent('mousemove', pos); + page.evaluate(function () { + $('div#PIS_StatusBar', $('iframe').contents()).each(function () { + var html = $(this).html(); + html = html.replace(/localhost\:[0-9]+/g, 'localhost'); + $(this).html(html); + }); + }); + removeOptOutIframe(page); }, done); }); @@ -114,4 +127,12 @@ describe("Overlay", function () { removeOptOutIframe(page); }, done); }); + + it("should load an overlay with segment", function (done) { + expect.screenshot("loaded_with_segment").to.be.capture(function (page) { + page.load(urlWithSegment); + + removeOptOutIframe(page); + }, done); + }); });
\ No newline at end of file diff --git a/tests/UI/specs/SegmentSelectorEditor_spec.js b/tests/UI/specs/SegmentSelectorEditor_spec.js index 0f53003c10..2bb5721762 100644 --- a/tests/UI/specs/SegmentSelectorEditor_spec.js +++ b/tests/UI/specs/SegmentSelectorEditor_spec.js @@ -74,7 +74,7 @@ describe("SegmentSelectorEditorTest", function () { it("should add new segment expression when segment dimension drag dropped", function (done) { expect.screenshot("dimension_drag_drop").to.be.captureSelector(selectorsToCapture, function (page) { page.click('.segmentEditorPanel .metric_category:contains(Actions)'); - page.dragDrop('.segmentEditorPanel li[data-metric=entryPageUrl]', '.segmentEditorPanel .ui-droppable'); + page.dragDrop('.segmentEditorPanel li[data-metric=actionUrl]', '.segmentEditorPanel .ui-droppable'); }, done); }); diff --git a/tests/UI/specs/Theme_spec.js b/tests/UI/specs/Theme_spec.js index 81c162b0c5..a7fffd2f30 100644 --- a/tests/UI/specs/Theme_spec.js +++ b/tests/UI/specs/Theme_spec.js @@ -20,18 +20,14 @@ describe("Theme", function () { testEnvironment.pluginsToLoad = ['ExampleTheme']; // Enable development mode to be able to see the UI demo page - testEnvironment.configOverride = { - Development: { - enabled: true - } - }; - + testEnvironment.overrideConfig('Development', 'enabled', true); testEnvironment.save(); clearAssets(); }); after(function () { + clearAssets(); }); diff --git a/tests/UI/specs/UIIntegration_spec.js b/tests/UI/specs/UIIntegration_spec.js index 972e15f23a..0849e5c8a2 100644 --- a/tests/UI/specs/UIIntegration_spec.js +++ b/tests/UI/specs/UIIntegration_spec.js @@ -30,7 +30,9 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? }); beforeEach(function () { - delete testEnvironment.configOverride; + if (testEnvironment.configOverride.database) { + delete testEnvironment.configOverride.database; + } testEnvironment.testUseMockAuth = 1; testEnvironment.save(); }); @@ -41,6 +43,7 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? testEnvironment.save(); }); + // dashboard tests it("should load dashboard1 correctly", function (done) { expect.screenshot("dashboard1").to.be.captureSelector('.pageWrap,.expandDataTableFooterDrawer', function (page) { @@ -149,6 +152,15 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? }, done); }); + // actions pages + it('should load the actions > pages help tooltip, including the "Report generated time"', function (done) { + expect.screenshot('actions_pages_tooltip_help').to.be.captureSelector('.pageWrap,.expandDataTableFooterDrawer', function (page) { + page.load("?" + urlBase + "#" + generalParams + "&module=Actions&action=menuGetPageUrls"); + page.mouseMove('h2[piwik-enriched-headline]'); + page.click(".helpIcon"); + }, done); + }); + it('should load the actions > entry pages page correctly', function (done) { expect.screenshot('actions_entry_pages').to.be.captureSelector('.pageWrap,.expandDataTableFooterDrawer', function (page) { page.load("?" + urlBase + "#?" + generalParams + "&category=General_Actions&subcategory=Actions_SubmenuPagesEntry"); @@ -512,15 +524,14 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? // DB error message it('should fail correctly when db information in config is incorrect', function (done) { - testEnvironment.configOverride = { - database: { - host: '127.50.50.50', - username: 'slkdfjsdlkfj', - password: 'slkdfjsldkfj', - dbname: 'abcdefg', - tables_prefix: 'gfedcba' - } - }; + + testEnvironment.overrideConfig('database', { + host: config.phpServer.REMOTE_ADDR, + username: 'slkdfjsdlkfj', + password: 'slkdfjsldkfj', + dbname: 'abcdefg', + tables_prefix: 'gfedcba' + }); testEnvironment.save(); expect.screenshot('db_connect_error').to.be.capture(function (page) { @@ -545,8 +556,16 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik? it('should load the widgets listing page correctly', function (done) { expect.screenshot('widgets_listing').to.be.captureSelector('.pageWrap', function (page) { page.load("?" + generalParams + "&module=Widgetize&action=index"); + page.mouseMove('.widgetpreview-categorylist>li:contains(Visitors)'); page.mouseMove('li[uniqueid="widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution"]'); + page.evaluate(function () { + $('.formEmbedCode').each(function () { + var val = $(this).val(); + val = val.replace(/localhost\:[0-9]+/g, 'localhost'); + $(this).val(val); + }); + }); }, done); }); diff --git a/tests/angularjs/scripts/install-ubuntu.sh b/tests/angularjs/scripts/install-ubuntu.sh index 60a564dae2..86953430d5 100755 --- a/tests/angularjs/scripts/install-ubuntu.sh +++ b/tests/angularjs/scripts/install-ubuntu.sh @@ -1,11 +1,7 @@ +#!/usr/bin/env bash DIR=`dirname $0` source $DIR/../../travis/travis-helper.sh -cd $DIR -travis_retry sudo apt-get -qq install python-software-properties -travis_retry sudo apt-add-repository -y ppa:chris-lea/node.js > /dev/null -travis_retry sudo apt-get -qq update - cd .. npm config set loglevel error travis_retry npm install .
\ No newline at end of file diff --git a/tests/angularjs/scripts/run-once.sh b/tests/angularjs/scripts/run-once.sh index 5f95af3fa5..4f796b4acb 100755 --- a/tests/angularjs/scripts/run-once.sh +++ b/tests/angularjs/scripts/run-once.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash DIR=`dirname $0` cd $DIR cd .. diff --git a/tests/angularjs/scripts/travis.sh b/tests/angularjs/scripts/travis.sh index f2002203ad..70e083a4f8 100755 --- a/tests/angularjs/scripts/travis.sh +++ b/tests/angularjs/scripts/travis.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash DIR=`dirname $0` cd $DIR ./install-ubuntu.sh diff --git a/tests/javascript/README.md b/tests/javascript/README.md index 5852407301..9e34fdff07 100644 --- a/tests/javascript/README.md +++ b/tests/javascript/README.md @@ -1,10 +1,9 @@ # JavaScript Tests ## Setup -Javascript integration tests require sqlite: - * ensure this PHP extension is enabled to make sure you run all tests apt-get install php5-sqlite - * Then please create an empty file `enable_sqlite` in `tests/javascript/enable_sqlite` - * Re-execute the tests and make sure the "missing sqlite" error message does not display +Javascript integration tests require an installed Piwik and ensure the `[database_tests]` section in `piwik/config/config.ini.php` is set up correctly, i.e. with th correct password to prevent the following error: `SQLSTATE[28000] [1045] Access denied for user 'root'@'localhost' (using password: NO)` + +The tests will create a database named `tracker_tests` and store several tracking requests in it. ## Execute diff --git a/tests/javascript/SQLite.php b/tests/javascript/SQLite.php deleted file mode 100644 index 5b4a3b3343..0000000000 --- a/tests/javascript/SQLite.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/*! - * Piwik - free/libre analytics platform - * - * SQLite shim - * - * @link http://piwik.org - * @license http://www.opensource.org/licenses/bsd-license.php Simplified BSD - */ -if (class_exists('SQLite3')) -{ - class SQLite extends SQLite3 - { - public function __construct($filename) - { - parent::__construct($filename); - - // for backward compatibility - if (version_compare(PHP_VERSION, '5.3.3') > 0) - { - $this->busyTimeout(60000); - } - } - - public function query_array($sql) - { - $result = parent::query($sql); - - $rows = array(); - while ($res = $result->fetchArray(SQLITE3_ASSOC)) - { - $rows[] = $res; - } - return $rows; - } - } -} -else if (extension_loaded('sqlite')) -{ - class SQLite - { - private $handle; - - public function __construct($filename) - { - $this->handle = sqlite_open($filename); - } - - public function query_array($sql) - { - return sqlite_array_query($this->handle, $sql); - } - - public function exec($sql) - { - return sqlite_exec($this->handle, $sql); - } - - public function changes() - { - return sqlite_changes($this->handle); - } - - public function close() - { - sqlite_close($this->handle); - unset($this->handle); - } - } -} diff --git a/tests/javascript/index.php b/tests/javascript/index.php index 702233d622..60d7f11558 100644 --- a/tests/javascript/index.php +++ b/tests/javascript/index.php @@ -5,7 +5,14 @@ <meta charset="utf-8"> <title>piwik.js: Unit Tests</title> <?php -require_once(dirname(__FILE__).'/SQLite.php'); +$root = dirname(__FILE__) . '/../..'; + +try { + $mysql = include_once $root . "/tests/PHPUnit/bootstrap.php"; +} catch (Exception $e) { + echo 'alert("' . $e->getMessage() . '")'; + $mysql = false; +} if(file_exists("stub.tpl")) { echo file_get_contents("stub.tpl"); @@ -22,17 +29,8 @@ function getHeartbeatToken() { return "<?php $token = md5(uniqid(mt_rand(), true)); echo $token; ?>"; } <?php -$sqlite = false; -if (file_exists("enable_sqlite")) { - if (class_exists('SQLite')) { - $sqlite = true; - } -} -if(!$sqlite) { - echo 'alert("WARNING: Javascript integration tests require sqlite, \n1) ensure this PHP extension is enabled to make sure you run all tests \napt-get install php5-sqlite \n2) Then please create an empty file enable_sqlite in tests/javascript/enable_sqlite \n3) Re-execute this page and make sure this popup does not display ");'; -} -if ($sqlite) { +if ($mysql) { echo ' var _paq = _paq || []; @@ -1930,7 +1928,7 @@ function PiwikTest() { }); test("API methods", function() { - expect(66); + expect(69); equal( typeof Piwik.addPlugin, 'function', 'addPlugin' ); equal( typeof Piwik.getTracker, 'function', 'getTracker' ); @@ -1959,6 +1957,9 @@ function PiwikTest() { equal( typeof tracker.setCustomData, 'function', 'setCustomData' ); equal( typeof tracker.getCustomData, 'function', 'getCustomData' ); equal( typeof tracker.setCustomRequestProcessing, 'function', 'setCustomRequestProcessing' ); + equal( typeof tracker.setCustomDimension, 'function', 'setCustomDimension' ); + equal( typeof tracker.getCustomDimension, 'function', 'getCustomDimension' ); + equal( typeof tracker.deleteCustomDimension, 'function', 'deleteCustomDimension' ); equal( typeof tracker.setCustomVariable, 'function', 'setCustomVariable' ); equal( typeof tracker.getCustomVariable, 'function', 'getCustomVariable' ); equal( typeof tracker.deleteCustomVariable, 'function', 'deleteCustomVariable' ); @@ -2241,12 +2242,48 @@ function PiwikTest() { } }); - test("Tracker setDomains() and isSiteHostName()", function() { - expect(13); + test("Tracker setDomains(), isSiteHostName(), isSiteHostPath(), findConfigCookiePathToUse() and getLinkIfShouldBeProcessed()", function() { + expect(117); var tracker = Piwik.getTracker(); + var initialDomains = tracker.getDomains(); + var domainAlias = initialDomains[0]; equal( typeof tracker.hook.test._isSiteHostName, 'function', "isSiteHostName" ); + equal( typeof tracker.hook.test._isSiteHostPath, 'function', "isSiteHostPath" ); + equal( typeof tracker.hook.test._getLinkIfShouldBeProcessed, 'function', "getLinkIfShouldBeProcessed" ); + equal( typeof tracker.hook.test._findConfigCookiePathToUse, 'function', "findConfigCookiePathToUse" ); + + var isSiteHostName = tracker.hook.test._isSiteHostName; + var isSiteHostPath = tracker.hook.test._isSiteHostPath; + var getLinkIfShouldBeProcessed = tracker.hook.test._getLinkIfShouldBeProcessed; + var findConfigCookiePathToUse = tracker.hook.test._findConfigCookiePathToUse; + + // tracker.setDomain() + + // test wildcards + tracker.setDomains( ['*.Example.com'] ); + propEqual(["*.Example.com", domainAlias], tracker.getDomains()), 'should add domainAlias'; + + tracker.setDomains( '*.Example.org' ); + propEqual(["*.Example.org", domainAlias], tracker.getDomains()), 'should handle a string'; + + tracker.setDomains( ['*.Example.com', '*.example.ORG'] ); + propEqual(["*.Example.com", '*.example.ORG', domainAlias], tracker.getDomains()), 'should be able to set many domains'; + + tracker.setDomains( [] ); + propEqual([domainAlias], tracker.getDomains()), 'setting an empty array should reset the list'; + + tracker.setDomains( ['*.Example.com', domainAlias + '/path', '*.example.ORG'] ); + propEqual(['*.Example.com', domainAlias + '/path', '*.example.ORG'], tracker.getDomains()), 'if domain alias is already given should not add domainAlias'; + + tracker.setDomains( ['.' + domainAlias + '/path'] ); + propEqual(['.' + domainAlias + '/path'], tracker.getDomains()), 'if domain alias with subdomain is already given should not add domainAlias'; + + + /** + * isSiteHostName () + */ // test wildcards tracker.setDomains( ['*.Example.com'] ); @@ -2254,21 +2291,171 @@ function PiwikTest() { // skip test if testing on localhost ok( window.location.hostname != 'localhost' ? !tracker.hook.test._isSiteHostName('localhost') : true, '!isSiteHostName("localhost")' ); - ok( !tracker.hook.test._isSiteHostName('google.com'), '!isSiteHostName("google.com")' ); - ok( tracker.hook.test._isSiteHostName('example.com'), 'isSiteHostName("example.com")' ); - ok( tracker.hook.test._isSiteHostName('www.example.com'), 'isSiteHostName("www.example.com")' ); - ok( tracker.hook.test._isSiteHostName('www.sub.example.com'), 'isSiteHostName("www.sub.example.com")' ); + ok( !isSiteHostName('google.com'), '!isSiteHostName("google.com")' ); + ok( isSiteHostName('example.com'), 'isSiteHostName("example.com")' ); + ok( isSiteHostName('www.example.com'), 'isSiteHostName("www.example.com")' ); + ok( isSiteHostName('www.sub.example.com'), 'isSiteHostName("www.sub.example.com")' ); tracker.setDomains( 'dev.piwik.org' ); - ok( !tracker.hook.test._isSiteHostName('piwik.org'), '!isSiteHostName("piwik.org")' ); - ok( tracker.hook.test._isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); - ok( !tracker.hook.test._isSiteHostName('piwik.example.org'), '!isSiteHostName("piwik.example.org")'); - ok( !tracker.hook.test._isSiteHostName('dev.piwik.org.com'), '!isSiteHostName("dev.piwik.org.com")'); + ok( !isSiteHostName('piwik.org'), '!isSiteHostName("piwik.org")' ); + ok( isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); + ok( !isSiteHostName('piwik.example.org'), '!isSiteHostName("piwik.example.org")'); + ok( !isSiteHostName('dev.piwik.org.com'), '!isSiteHostName("dev.piwik.org.com")'); tracker.setDomains( '.piwik.org' ); - ok( tracker.hook.test._isSiteHostName('piwik.org'), 'isSiteHostName("piwik.org")' ); - ok( tracker.hook.test._isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); - ok( !tracker.hook.test._isSiteHostName('piwik.org.com'), '!isSiteHostName("piwik.org.com")'); + ok( isSiteHostName('piwik.org'), 'isSiteHostName("piwik.org")' ); + ok( isSiteHostName('dev.piwik.org'), 'isSiteHostName("dev.piwik.org")' ); + ok( !isSiteHostName('piwik.org.com'), '!isSiteHostName("piwik.org.com")'); + + /** + * isSiteHostPath () + */ + + // with path + tracker.setDomains( '.piwik.org/path' ); + ok( isSiteHostPath('piwik.org', '/path'), 'isSiteHostPath("piwik.org", "/path")' ); + ok( isSiteHostPath('piwik.org', '/path/'), 'isSiteHostPath("piwik.org", "/path/")' ); + ok( isSiteHostPath('piwik.org', '/path/test'), 'isSiteHostPath("piwik.org", "/path/test)' ); + ok( isSiteHostPath('dev.piwik.org', '/path'), 'isSiteHostPath("dev.piwik.org", "/path")' ); + ok( !isSiteHostPath('piwik.org', '/pat'), '!isSiteHostPath("piwik.org", "/pat")'); + ok( !isSiteHostPath('piwik.org', '.com'), '!isSiteHostPath("piwik.org", ".com")'); + ok( !isSiteHostPath('piwik.com', '/path'), '!isSiteHostPath("piwik.com", "/path")'); + ok( !isSiteHostPath('piwik.com', '/path/test'), '!isSiteHostPath("piwik.com", "/path/test")'); + ok( !isSiteHostPath('piwik.com', ''), '!isSiteHostPath("piwik.com", "/path/test")'); + + // no path + var domains = ['.piwik.org', 'piwik.org', '*.piwik.org', '.piwik.org/']; + for (var i in domains) { + var domain = domains[i]; + tracker.setDomains( domain ); + ok( isSiteHostPath('piwik.org', '/path'), 'isSiteHostPath("piwik.org", "/path"), domain: ' + domain ); + ok( isSiteHostPath('piwik.org', '/path/'), 'isSiteHostPath("piwik.org", "/path/"), domain: ' + domain ); + ok( isSiteHostPath('piwik.org', '/path/test'), 'isSiteHostPath("piwik.org", "/path/test), domain: ' + domain ); + + if (domain === 'piwik.org') { + ok( !isSiteHostPath('dev.piwik.org', '/path'), 'isSiteHostPath("dev.piwik.org", "/path"), domain: ' + domain ); + } else { + ok( isSiteHostPath('dev.piwik.org', '/path'), 'isSiteHostPath("dev.piwik.org", "/path"), domain: ' + domain ); + } + ok( isSiteHostPath('piwik.org', '/pat'), '!isSiteHostPath("piwik.org", "/pat"), domain: ' + domain ); + ok( isSiteHostPath('piwik.org', '.com'), '!isSiteHostPath("piwik.org", ".com"), domain: ' + domain); + ok( isSiteHostPath('piwik.org', '/foo'), '!isSiteHostPath("piwik.com", "/foo"), domain: ' + domain); + ok( !isSiteHostPath('piwik.com', '/path'), '!isSiteHostPath("piwik.com", "/path"), domain: ' + domain); + ok( !isSiteHostPath('piwik.com', '/path/test'), '!isSiteHostPath("piwik.com", "/path/test"), domain: ' + domain); + ok( !isSiteHostPath('piwik.com', ''), '!isSiteHostPath("piwik.com", "/path/test"), domain: ' + domain); + } + + // multiple paths / domains + tracker.setDomains( ['piwik.org/path', 'piwik.org/foo', 'piwik.org/bar/baz', '.piwik.pro/test'] ); + ok( isSiteHostPath('piwik.pro', '/test/bar'), 'isSiteHostPath("piwik.pro", "/test/bar")' ); + ok( !isSiteHostPath('piwik.org', '/foobar/'), 'isSiteHostPath("piwik.org", "/foobar/")' ); + ok( isSiteHostPath('piwik.org', '/foo/bar'), 'isSiteHostPath("piwik.org", "/foo/bar")' ); + ok( isSiteHostPath('piwik.org', '/bar/baz/foo'), 'isSiteHostPath("piwik.org", "/bar/baz/foo/")' ); + ok( !isSiteHostPath('piwik.org', '/bar/ba'), 'isSiteHostPath("piwik.org", "/bar/ba")' ); + ok( isSiteHostPath('piwik.org', '/path/test'), 'isSiteHostPath("piwik.org", "/path/test)' ); + ok( isSiteHostPath('dev.piwik.pro', '/test'), 'isSiteHostPath("dev.piwik.pro", "/test")' ); + ok( !isSiteHostPath('dev.piwik.pro', '/'), 'isSiteHostPath("dev.piwik.pro", "/")' ); + ok( !isSiteHostPath('piwik.pro', '/'), 'isSiteHostPath("piwik.pro", "/")' ); + ok( !isSiteHostPath('piwik.org', '/'), 'isSiteHostPath("piwik.org", "/")' ); + ok( !isSiteHostPath('piwik.org', '/anythingelse'), 'isSiteHostPath("piwik.org", "/anythingelse")' ); + + // all is compared lower case + tracker.setDomains( '.piwik.oRg/PaTh' ); + ok( isSiteHostPath('piwiK.org', '/pAth'), 'isSiteHostPath("piwik.org", "/path")' ); + ok( isSiteHostPath('piwik.org', '/patH/'), 'isSiteHostPath("piwik.org", "/path/")' ); + ok( isSiteHostPath('Piwik.ORG', '/PATH/TEST'), 'isSiteHostPath("piwik.org", "/path/test)' ); + + /** + * getLinkIfShouldBeProcessed () + */ + var getLinkIfShouldBeProcessed = tracker.hook.test._getLinkIfShouldBeProcessed; + function createLink(url) { + var link = document.createElement('a'); + link.href = url; + return link; + } + + tracker.setDomains( ['.piwik.org/path', '.piwik.org/foo', '.piwik.org/bar/baz', '.piwik.pro/test'] ); + + // they should not be detected as outlink as they match one of the domains + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed http://www.piwik.org/foo/bar matches .piwik.org/foo') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed http://piwik.org/foo/bar matches .piwik.org/foo') + equal(undefined, getLinkIfShouldBeProcessed(createLink('piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed missing protocol only domain given') + equal(undefined, getLinkIfShouldBeProcessed(createLink('//piwik.org/foo/bar')), 'getLinkIfShouldBeProcessed no protcol but url starts with //') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo?x=1')), 'getLinkIfShouldBeProcessed url with query parameter should detect correct path') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo')), 'getLinkIfShouldBeProcessed path is same as allowed path') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo/')), 'getLinkIfShouldBeProcessed path is same as allowed path but with appended slash') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz/')), 'getLinkIfShouldBeProcessed multiple directories with appended slash') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz')), 'getLinkIfShouldBeProcessed multiple directories') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://WWW.PIWIK.ORG/BAR/BAZ')), 'getLinkIfShouldBeProcessed should test everything lowercase') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz/x/y/z')), 'getLinkIfShouldBeProcessed many appended paths') + equal(undefined, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar/baz?test=1&foo=bar')), 'getLinkIfShouldBeProcessed another test with query parameter and multiple directories') + propEqual({ + "href": "http://www.piwik.org/foo/download.apk", + "type": "download" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foo/download.apk')), 'getLinkIfShouldBeProcessed should detect download even if it is link to same domain') + propEqual({ + "href": "http://www.piwik.org/foobar/download.apk", + "type": "download" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/foobar/download.apk')), 'getLinkIfShouldBeProcessed should detect download even if it goes to different domain/path') + propEqual({ + "href": "http://www.piwik.com/foobar/download.apk", + "type": "download" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.com/foobar/download.apk')), 'getLinkIfShouldBeProcessed should detect download even if it goes to different domain') + propEqual({ + "href": "http://www.piwik.pro/foo/", + "type": "link" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.pro/foo/')), 'getLinkIfShouldBeProcessed path matches but domain not so outlink') + propEqual({ + "href": "http://www.piwik.org/bar", + "type": "link" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/bar')), 'getLinkIfShouldBeProcessed domain matches but path not so outlink') + propEqual({ + "href": "http://www.piwik.org/footer", + "type": "link" + }, getLinkIfShouldBeProcessed(createLink('http://www.piwik.org/footer')), 'getLinkIfShouldBeProcessed http://www.piwik.org/footer and there is domain piwik.org/foo but it should be outlink as path is different') + + + /** + * findConfigCookiePathToUse () + */ + + tracker.setDomains( ['.piwik.org', '.piwik.pro/foo/bar', '.piwik.pro/foo', '.piwik.com/test/foo', 'example.com/foo'] ); + + equal(null, findConfigCookiePathToUse('.piwik.org/test', 'http://piwik.org/test/two'), 'findConfigCookiePathToUse no cookiePath because there is a domain alias given allowing them all'); + equal('/foo', findConfigCookiePathToUse('.piwik.pro/foo', 'http://piwik.pro/foo/bar/test'), 'findConfigCookiePathToUse should find a match'); + equal('/foo', findConfigCookiePathToUse('.piwik.pro/foo/bar/test', 'http://piwik.pro/foo/bar/test'), 'findConfigCookiePathToUse should find a less restrictive path automatically'); + equal('/foo', findConfigCookiePathToUse('.piwik.pro/foo/bar/test', 'http://piwik.pro/foo'), 'findConfigCookiePathToUse should find a less restrictive path automatically, urlPath===domainPath'); + equal('/test/bar/test', findConfigCookiePathToUse('.piwik.com/test/bar/test', 'http://piwik.com/test/bar/test/'), 'findConfigCookiePathToUse should use exactly given path if no less restrictive version is available'); + equal('/test/foo', findConfigCookiePathToUse('.piwik.com/test/foo/test', 'http://piwik.com/test/foo/test'), 'findConfigCookiePathToUse should find a less restrictive path automatically, configAlias === currentUrl'); + equal('/test/foo', findConfigCookiePathToUse('.piwik.com/test/foo', 'http://piwik.com/test/foo/test'), 'findConfigCookiePathToUse should find a less restrictive path automatically'); + equal(null, findConfigCookiePathToUse('.piwik.pro/foo', 'http://piwik.pro/test'), 'findConfigCookiePathToUse should not return a path when user is actually not on that path'); + equal(null, findConfigCookiePathToUse('.piwik.pro/foo', 'http://piwik.pro'), 'findConfigCookiePathToUse when there is no path set we cannot use a configPath'); + + /** + * Test sets a good cookie path automatically + */ + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests'] ); + equal('/tests', tracker.getConfigCookiePath()), 'should set a cookie path automatically'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests/javascript'] ); + equal('/tests/javascript', tracker.getConfigCookiePath()), 'should set a cookie path automatically, multiple directories'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests/javascript', '.' + domainAlias + '/tests'] ); + equal('/tests', tracker.getConfigCookiePath()), 'should find shortest path for possible cookie path'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/tests/javascript', '.example.com/tests'] ); + equal('/tests/javascript', tracker.getConfigCookiePath()), 'should not find a shorter path when no other domain matches'; + + tracker.setCookiePath(null); + tracker.setDomains( ['.' + domainAlias + '/another/one', '.example.org/tests/javascript', '.example.com/tests'] ); + equal(null, tracker.getConfigCookiePath()), 'should not set a path when no domain and no path matches'; + + tracker.setCookiePath(null); }); test("Tracker getClassesRegExp()", function() { @@ -2710,7 +2897,7 @@ function PiwikTest() { }); <?php -if ($sqlite) { +if ($mysql) { ?> module("request", { @@ -2727,7 +2914,7 @@ if ($sqlite) { }); test("tracking", function() { - expect(102); + expect(114); // Prevent Opera and HtmlUnit from performing the default action (i.e., load the href URL) var stopEvent = function (evt) { @@ -2783,6 +2970,21 @@ if ($sqlite) { deepEqual( tracker.getCustomVariable(5), ["new name", ""], "getting a custom variable with no value" ); tracker.deleteCustomVariable(5); + equal(tracker.getCustomDimension(94), null, "if no custom dimension for this index is specified should return null"); + equal(tracker.getCustomDimension(-1), null, "if custom dimension index is invalid should return null"); + equal(tracker.getCustomDimension('not valid'), null, "if custom dimension index is invalid should return null"); + tracker.setCustomDimension(1, 5); + equal(tracker.getCustomDimension(1), "5", "set custom dimension should convert any value to a string" ); + tracker.setCustomDimension(1, "my custom value"); + equal(tracker.getCustomDimension(1), "my custom value", "should get stored custom dimension value" ); + tracker.setCustomDimension(2, undefined); + equal(tracker.getCustomDimension(2), "", "setCustomDimension should convert undefined to an empty string" ); + + tracker.setCustomDimension(3, 'my third value'); + equal(tracker.getCustomDimension(3), "my third value", "deleteCustomDimension verify a value is set for this dimension" ); + tracker.deleteCustomDimension(3); + equal(tracker.getCustomDimension(3), null, "deleteCustomDimension verify value was removed" ); + tracker.setDocumentTitle("PiwikTest"); var referrerUrl = "http://referrer.example.com/page/sub?query=test&test2=test3"; @@ -2791,7 +2993,10 @@ if ($sqlite) { referrerTimestamp = Math.round(new Date().getTime() / 1000); tracker.trackPageView(); - tracker.trackPageView("CustomTitleTest"); + equal(tracker.getCustomDimension(1), "my custom value", "custom dimensions should not be cleared after a tracked pageview"); + equal(tracker.getCustomDimension(2), "", "custom dimensions should not be cleared after a tracked pageview"); + + tracker.trackPageView("CustomTitleTest", {dimension2: 'my new value', dimension5: 'another dimension'}); var customUrlShouldNotChangeCampaign = "http://localhost.localdomain/?utm_campaign=NONONONONONONO&utm_term=PLEASE NO!"; tracker.setCustomUrl(customUrl); @@ -3056,6 +3261,12 @@ if ($sqlite) { // Test Custom variables ok( /SaveCustomVariableCookie.*&cvar=%7B%222%22%3A%5B%22cookiename2PAGE%22%2C%22cookievalue2PAGE%22%5D%7D.*&_cvar=%7B%221%22%3A%5B%22cookiename%22%2C%22cookievalue%22%5D%2C%222%22%3A%5B%22cookiename2%22%2C%22cookievalue2%22%5D%7D/.test(results), "test custom vars are set"); + // Test CustomDimension (persistent ones across requests) + ok( /dimension1=my%20custom%20value&dimension2=&/.test(results), "test custom dimensions are set"); + + // send along a page view and ony valid for this pageview (dimension 2 overwrites another one) + ok( /dimension2=my%20new%20value&dimension5=another%20dimension&dimension1=my%20custom%20value&data=%7B%22token/.test( results ), "trackPageView(customTitle, customData)" ); + // Test campaign parameters set ok( /&_rcn=YEAH&_rck=RIGHT!/.test( results), "Test campaign parameters found"); ok( /&_ref=http%3A%2F%2Freferrer.example.com%2Fpage%2Fsub%3Fquery%3Dtest%26test2%3Dtest3/.test( results), "Test cookie Ref URL found "); diff --git a/tests/javascript/piwik.php b/tests/javascript/piwik.php index 73fbedb232..0c38317175 100644 --- a/tests/javascript/piwik.php +++ b/tests/javascript/piwik.php @@ -1,7 +1,31 @@ <?php // piwik.php test harness -require_once(dirname(__FILE__).'/SQLite.php'); +if (!defined('PIWIK_DOCUMENT_ROOT')) { + define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__) . '/../..'); +} + +define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT); + + +require_once PIWIK_INCLUDE_PATH . '/core/bootstrap.php'; + +$environment = new \Piwik\Application\Environment(null); +$environment->init(); +$dbConfig = Piwik\Config::getInstance()->database_tests; +$dbConfig['dbname'] = 'tracker_tests'; + +try { + Piwik\Db::createDatabaseObject($dbConfig); +} catch (Exception $e) { + $dbInfosConnectOnly = $dbConfig; + $dbInfosConnectOnly['dbname'] = null; + Piwik\Db::createDatabaseObject($dbInfosConnectOnly); + Piwik\DbHelper::createDatabase($dbConfig['dbname']); + Piwik\Db::createDatabaseObject($dbConfig); +} + +$db = Piwik\Db::get(); function sendWebBug() { $trans_gif_64 = "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="; @@ -14,25 +38,14 @@ function isPost() return $_SERVER['REQUEST_METHOD'] == 'POST'; } -if (!file_exists("enable_sqlite")) { +if (!Piwik\Db::hasDatabaseObject()) { sendWebBug(); exit; } -if (!class_exists('SQLite')) { - sendWebBug(); - exit; -} - -$sqlite = new SQLite( 'unittest2.dbf' ); -if (!$sqlite) { - header("HTTP/1.0 500 Internal Server Error"); - exit; -} - -function getNextRequestId($sqlite, $token) +function getNextRequestId($db, $token) { - $requests = $sqlite->query_array("SELECT uri FROM requests WHERE token = \"$token\""); + $requests = $db->fetchAll("SELECT uri FROM requests WHERE token = \"$token\""); if (empty($requests)) { return 1; @@ -41,17 +54,14 @@ function getNextRequestId($sqlite, $token) return count($requests) + 1; } -if (filesize(dirname(__FILE__).'/unittest2.dbf') == 0) -{ - try { - $query = @$sqlite->exec( 'CREATE TABLE requests (requestid TEXT, token TEXT, ip TEXT, ts TEXT, uri TEXT, referer TEXT, ua TEXT)' ); - } catch (Exception $e) { - header("HTTP/1.0 500 Internal Server Error"); - exit; - } +try { + $db->query( 'CREATE TABLE IF NOT EXISTS `requests` (requestid TEXT, token TEXT, ip TEXT, ts TEXT, uri TEXT, referer TEXT, ua TEXT) DEFAULT CHARSET=utf8' ); +} catch (Exception $e) { + header("HTTP/1.0 500 Internal Server Error"); + exit; } -function logRequest($sqlite, $uri, $data) { +function logRequest($db, $uri, $data) { $ip = $_SERVER['REMOTE_ADDR']; $ts = $_SERVER['REQUEST_TIME']; @@ -62,9 +72,9 @@ function logRequest($sqlite, $uri, $data) { $token = isset($data['token']) ? $data['token'] : ''; - $id = getNextRequestId($sqlite, $token); + $id = getNextRequestId($db, $token); - $query = $sqlite->exec("INSERT INTO requests (requestid, token, ip, ts, uri, referer, ua) VALUES (\"$id\", \"$token\", \"$ip\", \"$ts\", \"$uri\", \"$referrer\", \"$ua\")"); + $query = $db->query("INSERT INTO requests (requestid, token, ip, ts, uri, referer, ua) VALUES (\"$id\", \"$token\", \"$ip\", \"$ts\", \"$uri\", \"$referrer\", \"$ua\")"); return $query; } @@ -75,8 +85,7 @@ if (isset($_GET['requests'])) { echo "<html><head><title>$token</title></head><body>\n"; -// $result = $sqlite->query_array("SELECT uri FROM requests"); - $result = @$sqlite->query_array("SELECT uri FROM requests WHERE token = \"$token\" AND ua = \"$ua\" ORDER BY ts ASC, requestid ASC"); + $result = @$db->fetchAll("SELECT uri FROM requests WHERE token = \"$token\" AND ua = \"$ua\" ORDER BY ts ASC, requestid ASC"); if ($result !== false) { $nofRows = count($result); echo "<span>$nofRows</span>\n"; @@ -107,7 +116,7 @@ if (isset($_GET['requests'])) { $data = array('token' => $matches[1]); } - $query = $query && logRequest($sqlite, $uri . $request, $data); + $query = $query && logRequest($db, $uri . $request, $data); } } else { @@ -115,16 +124,13 @@ if (isset($_GET['requests'])) { $uri .= '?' . file_get_contents('php://input'); } - $query = logRequest($sqlite, $uri, $data); + $query = logRequest($db, $uri, $data); } if (!$query) { header("HTTP/1.0 500 Internal Server Error"); } else { -// echo 'Number of rows modified: ', $sqlite->changes(); sendWebBug(); } } } - -$sqlite->close(); diff --git a/tests/javascript/piwiktest.js b/tests/javascript/piwiktest.js index 415bd57d8a..67157c6d96 100644 --- a/tests/javascript/piwiktest.js +++ b/tests/javascript/piwiktest.js @@ -20,11 +20,14 @@ Piwik.addPlugin('testPlugin', { '_isObject : isObject,' + '_isString : isString,' + '_isSiteHostName : isSiteHostName,' + + '_isSiteHostPath : isSiteHostPath,' + '_getClassesRegExp : getClassesRegExp,' + '_hasCookies : hasCookies,' + '_getCookie : getCookie,' + '_getCookieName : getCookieName,' + '_setCookie : setCookie,' + + '_getLinkIfShouldBeProcessed : getLinkIfShouldBeProcessed,' + + '_findConfigCookiePathToUse : findConfigCookiePathToUse,' + '_encode : encodeWrapper,' + '_decode : decodeWrapper,' + '_urldecode : urldecode,' + diff --git a/tests/javascript/testrunner.js b/tests/javascript/testrunner.js index 7ac70b4a31..7194540ec8 100644 --- a/tests/javascript/testrunner.js +++ b/tests/javascript/testrunner.js @@ -22,7 +22,8 @@ // IN THE SOFTWARE var fs = require("fs"); -var url = 'http://localhost/tests/javascript/'; +var system = require("system"); +var url = system.args[1] || 'http://localhost/tests/javascript/'; function printError(message) { console.error(message + "\n"); diff --git a/tests/lib/screenshot-testing/support/app.js b/tests/lib/screenshot-testing/support/app.js index 95995797c2..51ca56ad55 100644 --- a/tests/lib/screenshot-testing/support/app.js +++ b/tests/lib/screenshot-testing/support/app.js @@ -229,7 +229,7 @@ Application.prototype.doRunTests = function () { this.runner = mocha.run(function () { // remove symlinks if (!options['keep-symlinks']) { - var symlinks = ['libs', 'plugins', 'tests', 'piwik.js']; + var symlinks = ['libs', 'plugins', 'tests', 'misc', 'piwik.js']; symlinks.forEach(function (item) { var file = path.join(uiTestsDir, '..', 'PHPUnit', 'proxy', item); diff --git a/tests/lib/screenshot-testing/support/chai-extras.js b/tests/lib/screenshot-testing/support/chai-extras.js index aeb2d86b1b..576229946e 100644 --- a/tests/lib/screenshot-testing/support/chai-extras.js +++ b/tests/lib/screenshot-testing/support/chai-extras.js @@ -181,6 +181,7 @@ function capture(screenName, compareAgainst, selector, pageSetupFn, comparisonTh child.on("exit", function (code) { if (testFailure) { testFailure = 'Processed screenshot does not match expected for ' + screenshotFileName + ' ' + testFailure; + testFailure += 'TestEnvironment was ' + JSON.stringify(testEnvironment); } if (code == 0 && !testFailure) { diff --git a/tests/lib/screenshot-testing/support/page-renderer.js b/tests/lib/screenshot-testing/support/page-renderer.js index 2bb909f721..5c92abb276 100644 --- a/tests/lib/screenshot-testing/support/page-renderer.js +++ b/tests/lib/screenshot-testing/support/page-renderer.js @@ -338,7 +338,7 @@ PageRenderer.prototype.capture = function (outputPath, callback, selector) { self.abort(); callback(new Error("Screenshot load timeout. Details:\n" + timeoutDetails)); - }, 120 * 1000); + }, 180 * 1000); if (this.webpage === null) { this._recreateWebPage(); @@ -654,6 +654,7 @@ PageRenderer.prototype._removeUrlFromQueue = function (url) { } }; +var linkObject = document.createElement('a'); PageRenderer.prototype._setupWebpageEvents = function () { var self = this; this.webpage.onError = function (message, trace) { @@ -669,11 +670,31 @@ PageRenderer.prototype._setupWebpageEvents = function () { self._logMessage(msgStack.join('\n')); }; + linkObject.setAttribute('href', config.piwikUrl); + var piwikHost = linkObject.hostname, + piwikPort = linkObject.port; + this.webpage.onResourceRequested = function (requestData, networkRequest) { - self._addUrlToQueue(requestData.url); + var url = requestData.url; + + // replaces the requested URL to the piwik URL w/ a port, if it does not have one. This allows us to run UI + // tests when Piwik is on a port, w/o having to have different UI screenshots. (This is one half of the + // solution, the other half is in config/environment/ui-test.php, where we remove all ports from Piwik URLs.) + if (piwikPort && piwikPort != 0) { + linkObject.setAttribute('href', url); + + if (linkObject.hostname == piwikHost && (!linkObject.port || linkObject.port == 0 || linkObject.port == 80)) { + linkObject.port = piwikPort; + url = linkObject.href; + + networkRequest.changeUrl(url); + } + } + + self._addUrlToQueue(url); if (VERBOSE) { - self._logMessage('Requesting resource (#' + requestData.id + 'URL:' + requestData.url + ')'); + self._logMessage('Requesting resource (#' + requestData.id + 'URL:' + url + ')'); } }; diff --git a/tests/lib/screenshot-testing/support/test-environment.js b/tests/lib/screenshot-testing/support/test-environment.js index 700493f933..01914c8c24 100644 --- a/tests/lib/screenshot-testing/support/test-environment.js +++ b/tests/lib/screenshot-testing/support/test-environment.js @@ -25,6 +25,7 @@ TestingEnvironment.prototype.reload = function () { this['useOverrideJs'] = true; this['loadRealTranslations'] = true; // UI tests should test w/ real translations, not translation keys this['testUseMockAuth'] = true; + this['configOverride'] = {}; if (fs.exists(testingEnvironmentOverridePath)) { var data = JSON.parse(fs.read(testingEnvironmentOverridePath)); @@ -34,6 +35,33 @@ TestingEnvironment.prototype.reload = function () { } }; +/** + * Overrides a config entry. + * + * You can use this method either to set one specific config value `overrideConfig(group, name, value)` + * or you can set a whole group of values `overrideConfig(group, valueObject)`. + */ +TestingEnvironment.prototype.overrideConfig = function (group, name, value) { + if (!name) { + return; + } + + if (!this['configOverride']) { + this['configOverride'] = {}; + } + + if ((typeof value) === 'undefined') { + this['configOverride'][group] = name; + return; + } + + if (!this['configOverride'][group]) { + this['configOverride'][group] = {}; + } + + this['configOverride'][group][name] = value; +}; + TestingEnvironment.prototype.save = function () { var copy = {}; for (var key in this) { diff --git a/tests/travis b/tests/travis -Subproject 2a41a93a776f5f053c8dd34c029061e1cd4d6a6 +Subproject 73a4b16ebb9a5db23bca53115195e8317ce6917 |