Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <tsteur@users.noreply.github.com>2019-06-19 01:05:52 +0300
committerMatthieu Aubry <mattab@users.noreply.github.com>2019-06-19 01:05:52 +0300
commitb3555e1f1efcbb63158e6c116e1fb5c8805851f8 (patch)
tree5a3cd39cc9051cda2b949669530c5cde753ce92d /plugins/Tour
parent57f7184a0dc49df4eacbbd27ca0aef909668b410 (diff)
Add widget to help users getting started and complete important steps with Matomo (#12468)
* added Tour plugin * enable tour plugin by default * better logic to handle steps * refactor plugin, add more steps * lots more code improvements, ui improvements, translations, challenges * lots of more improvements * adding more challenges * improve detection * more tweaks and tests * a few more tweaks * update some wordings * switch two levels talent and professional * apply review feedback * Update ChallengeCustomLogo.php * Expected files for system tests
Diffstat (limited to 'plugins/Tour')
-rw-r--r--plugins/Tour/.gitignore1
-rw-r--r--plugins/Tour/API.php106
-rw-r--r--plugins/Tour/CHANGELOG.md4
-rw-r--r--plugins/Tour/Dao/DataFinder.php61
-rw-r--r--plugins/Tour/Engagement/Challenge.php151
-rw-r--r--plugins/Tour/Engagement/ChallengeAddedAnnotation.php36
-rw-r--r--plugins/Tour/Engagement/ChallengeAddedSegment.php61
-rw-r--r--plugins/Tour/Engagement/ChallengeAddedUser.php37
-rw-r--r--plugins/Tour/Engagement/ChallengeAddedWebsite.php61
-rw-r--r--plugins/Tour/Engagement/ChallengeBrowseMarketplace.php39
-rw-r--r--plugins/Tour/Engagement/ChallengeChangeVisualisation.php36
-rw-r--r--plugins/Tour/Engagement/ChallengeConfigureGeolocation.php55
-rw-r--r--plugins/Tour/Engagement/ChallengeCreatedGoal.php38
-rw-r--r--plugins/Tour/Engagement/ChallengeCustomLogo.php62
-rw-r--r--plugins/Tour/Engagement/ChallengeCustomiseDashboard.php61
-rw-r--r--plugins/Tour/Engagement/ChallengeDisableBrowserArchiving.php37
-rw-r--r--plugins/Tour/Engagement/ChallengeFlattenActions.php36
-rw-r--r--plugins/Tour/Engagement/ChallengeScheduledReport.php61
-rw-r--r--plugins/Tour/Engagement/ChallengeSelectDateRange.php36
-rw-r--r--plugins/Tour/Engagement/ChallengeSetupTwoFa.php44
-rw-r--r--plugins/Tour/Engagement/ChallengeTrackingCode.php61
-rw-r--r--plugins/Tour/Engagement/ChallengeViewRowEvolution.php36
-rw-r--r--plugins/Tour/Engagement/ChallengeViewVisitorProfile.php36
-rw-r--r--plugins/Tour/Engagement/ChallengeViewVisitsLog.php36
-rw-r--r--plugins/Tour/Engagement/Challenges.php116
-rw-r--r--plugins/Tour/Engagement/Levels.php149
-rw-r--r--plugins/Tour/Tour.php144
-rw-r--r--plugins/Tour/Widgets/GetEngagement.php86
-rw-r--r--plugins/Tour/config/test.php22
-rw-r--r--plugins/Tour/javascripts/engagement.js30
-rw-r--r--plugins/Tour/lang/en.json56
-rw-r--r--plugins/Tour/stylesheets/engagement.less32
-rw-r--r--plugins/Tour/templates/engagement.twig58
-rw-r--r--plugins/Tour/tests/Fixtures/SimpleFixtureTrackFewVisits.php60
-rw-r--r--plugins/Tour/tests/Integration/ChallengeTest.php97
-rw-r--r--plugins/Tour/tests/System/APITest.php107
-rw-r--r--plugins/Tour/tests/System/DataFinderTest.php97
-rw-r--r--plugins/Tour/tests/System/TourTest.php83
-rw-r--r--plugins/Tour/tests/System/expected/test___Tour.getChallenges.xml155
-rw-r--r--plugins/Tour/tests/System/expected/test___Tour.getLevel.xml11
-rw-r--r--plugins/Tour/tests/System/expected/test_nothingCompleted__Tour.getLevel.xml11
-rw-r--r--plugins/Tour/tests/UI/.gitignore2
-rw-r--r--plugins/Tour/tests/UI/Tour_spec.js88
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/.gitkeep0
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_all_completed.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_2.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_3.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_completed.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_initial.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_none_completed.png3
-rw-r--r--plugins/Tour/tests/UI/expected-screenshots/Tour_widget_skipped_goal.png3
52 files changed, 2620 insertions, 0 deletions
diff --git a/plugins/Tour/.gitignore b/plugins/Tour/.gitignore
new file mode 100644
index 0000000000..c8c9480010
--- /dev/null
+++ b/plugins/Tour/.gitignore
@@ -0,0 +1 @@
+tests/System/processed/*xml \ No newline at end of file
diff --git a/plugins/Tour/API.php b/plugins/Tour/API.php
new file mode 100644
index 0000000000..72c61e1a6e
--- /dev/null
+++ b/plugins/Tour/API.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\Tour;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Engagement\Levels;
+use Piwik\Plugins\Tour\Engagement\Challenges;
+
+/**
+ * API for Tour plugin which helps you getting familiar with Matomo.
+ *
+ * @method static \Piwik\Plugins\Tour\API getInstance()
+ */
+class API extends \Piwik\Plugin\API
+{
+
+ /**
+ * @var Challenges
+ */
+ private $challenges;
+
+ /**
+ * Levels
+ */
+ private $levels;
+
+ public function __construct(Challenges $challenges, Levels $levels)
+ {
+ $this->challenges = $challenges;
+ $this->levels = $levels;
+ }
+
+ /**
+ * Get all challenges that can be completed by a super user.
+ *
+ * @return array[]
+ */
+ public function getChallenges()
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ $challenges = array();
+
+ foreach ($this->challenges->getChallenges() as $challenge) {
+ $challenges[] = array(
+ 'id' => $challenge->getId(),
+ 'name' => $challenge->getName(),
+ 'description' => $challenge->getDescription(),
+ 'isCompleted' => $challenge->isCompleted(),
+ 'isSkipped' => $challenge->isSkipped(),
+ 'url' => $challenge->getUrl()
+ );
+ }
+
+ return $challenges;
+ }
+
+ /**
+ * Skip a specific challenge.
+ *
+ * @param string $id
+ * @return bool
+ * @throws \Exception
+ */
+ public function skipChallenge($id)
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ foreach ($this->challenges->getChallenges() as $challenge) {
+ if ($challenge->getId() === $id) {
+ if (!$challenge->isCompleted()) {
+ $challenge->skipChallenge();
+ return true;
+ }
+
+ throw new \Exception('Challenge already completed');
+ }
+ }
+
+ throw new \Exception('Challenge not found');
+ }
+
+ /**
+ * Get details about the current level this user has progressed to.
+ * @return array
+ */
+ public function getLevel()
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ return array(
+ 'description' => $this->levels->getCurrentDescription(),
+ 'currentLevel' => $this->levels->getCurrentLevel(),
+ 'currentLevelName' => $this->levels->getCurrentLevelName(),
+ 'nextLevelName' => $this->levels->getNextLevelName(),
+ 'numLevelsTotal' => $this->levels->getNumLevels(),
+ 'challengesNeededForNextLevel' => $this->levels->getNumChallengesNeededToNextLevel(),
+ );
+ }
+}
diff --git a/plugins/Tour/CHANGELOG.md b/plugins/Tour/CHANGELOG.md
new file mode 100644
index 0000000000..2b5e6cefc9
--- /dev/null
+++ b/plugins/Tour/CHANGELOG.md
@@ -0,0 +1,4 @@
+## Changelog
+
+3.0.0
+ - Initial version \ No newline at end of file
diff --git a/plugins/Tour/Dao/DataFinder.php b/plugins/Tour/Dao/DataFinder.php
new file mode 100644
index 0000000000..587f062222
--- /dev/null
+++ b/plugins/Tour/Dao/DataFinder.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Dao;
+
+use Piwik\Common;
+
+class DataFinder
+{
+
+ public function hasTrackedData()
+ {
+ $sql = sprintf('SELECT idsite FROM %s LIMIT 1', Common::prefixTable('log_visit'));
+
+ $result = \Piwik\Db::fetchOne($sql, array());
+
+ return !empty($result);
+ }
+
+ public function hasAddedWebsite($login)
+ {
+ $sql = sprintf("SELECT count(*) as num_websites FROM %s WHERE idsite != 1 and creator_login = ? LIMIT 1", Common::prefixTable('site'));
+
+ $result = \Piwik\Db::fetchOne($sql, array($login));
+
+ return $result > 0;
+ }
+
+ public function hasAddedNewEmailReport($login)
+ {
+ $sql = sprintf("SELECT count(*) as num_reports FROM %s WHERE login = ? LIMIT 1", Common::prefixTable('report'));
+
+ $result = \Piwik\Db::fetchOne($sql, array($login));
+
+ return $result > 0;
+ }
+
+ public function hasAddedOrCustomisedDashboard($login)
+ {
+ $sql = sprintf("SELECT count(*) as num_dashboards FROM %s WHERE login = ? LIMIT 1", Common::prefixTable('user_dashboard'));
+
+ $result = \Piwik\Db::fetchOne($sql, array($login));
+
+ return $result > 0;
+ }
+
+ public function hasAddedSegment($login)
+ {
+ $sql = sprintf("SELECT count(*) as num_segments FROM %s WHERE login = ? LIMIT 1", Common::prefixTable('segment'));
+
+ $result = \Piwik\Db::fetchOne($sql, array($login));
+
+ return $result > 0;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/Challenge.php b/plugins/Tour/Engagement/Challenge.php
new file mode 100644
index 0000000000..164561c8ce
--- /dev/null
+++ b/plugins/Tour/Engagement/Challenge.php
@@ -0,0 +1,151 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Settings\Storage\Backend\PluginSettingsTable;
+
+
+/**
+ * Defines a new challenge which a super user needs to complete in order to become a "Matomo expert".
+ * Plugins can add new challenges by listening to the {@hook Tour.filterChallenges} event.
+ *
+ * @since 3.10.0
+ * @api
+ */
+abstract class Challenge
+{
+ const APPENDIX_SKIPPED = '_skipped';
+ const APPENDIX_COMPLETED = '_completed';
+
+ private static $settings = null;
+
+ /**
+ * The human readable name that will be shown in the onboarding widget. Should be max 3 or 4 words and represent an
+ * action, like "Add a report"
+ * @return string
+ */
+ abstract public function getName();
+
+ /**
+ * A short unique ID that represents this challenge, for example "add_report".
+ * @return string
+ */
+ abstract public function getId();
+
+ /**
+ * By default, we attribute a challenge as soon as it was completed manually by calling `$challenge->setCompleted()`.
+ *
+ * If we can detect whether a particular user has already completed a challenge in the past then we mark it automatically
+ * as completed. We can detect this automatically eg by querying the DB and check if a particular login has for example
+ * created a segment etc. We do this only if the query is supposed to be fast. Otherwise we would fallback to the manual
+ * way.
+ *
+ * @return bool
+ */
+ public function isCompleted()
+ {
+ return $this->hasAttribute(self::APPENDIX_COMPLETED);
+ }
+
+ /**
+ * A detailed description that describes the value of the action the user needs to complete, or some tips on how
+ * to complete this challenge. Will be shown when hovering a challenge name.
+ * @return string
+ */
+ public function getDescription()
+ {
+ return '';
+ }
+
+ /**
+ * A URL that has more information about how to complete the given event or a URL within the Matomo app to directly
+ * complete a challenge. For example "add_user" challenge could directly link to the user management.
+ * @return string
+ */
+ public function getUrl()
+ {
+ return '';
+ }
+
+ private function getPluginSettingsInstance()
+ {
+ return new PluginSettingsTable('Tour', Piwik::getCurrentUserLogin());
+ }
+
+ private function getSettings()
+ {
+ if (!isset(self::$settings)) {
+ $pluginSettings = $this->getPluginSettingsInstance();
+ self::$settings = $pluginSettings->load();
+ }
+
+ return self::$settings;
+ }
+
+ public static function clearCache()
+ {
+ self::$settings = null;
+ }
+
+ /**
+ * Detect if the challenge was skipped.
+ * @ignore
+ * @return bool
+ */
+ public function isSkipped()
+ {
+ return $this->hasAttribute(self::APPENDIX_SKIPPED);
+ }
+
+ /**
+ * Skip this challenge.
+ * @ignore
+ * @return bool
+ */
+ public function skipChallenge()
+ {
+ $this->storeAttribute(self::APPENDIX_SKIPPED);
+ }
+
+ /**
+ * Set this challenge was completed successfully by the current user. Only works for a super user.
+ * @return bool
+ */
+ public function setCompleted()
+ {
+ $this->storeAttribute(self::APPENDIX_COMPLETED);
+ }
+
+ private function hasAttribute($appendix)
+ {
+ $settings = $this->getSettings();
+
+ if (!empty($settings[$this->getId() . $appendix])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function storeAttribute($appendix)
+ {
+ if (!Piwik::hasUserSuperUserAccess()) {
+ return;
+ }
+ $pluginSettings = $this->getPluginSettingsInstance();
+ $settings = $pluginSettings->load();
+
+ if (empty($settings[$this->getId() . $appendix])) {
+ $settings[$this->getId() . $appendix] = '1';
+ $pluginSettings->save($settings);
+ self::clearCache();
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeAddedAnnotation.php b/plugins/Tour/Engagement/ChallengeAddedAnnotation.php
new file mode 100644
index 0000000000..09be083f79
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeAddedAnnotation.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeAddedAnnotation extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_AddAnnotation');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Annotations_PluginDescription');
+ }
+
+ public function getId()
+ {
+ return 'add_annotation';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/annotations/';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeAddedSegment.php b/plugins/Tour/Engagement/ChallengeAddedSegment.php
new file mode 100644
index 0000000000..c5ad13518f
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeAddedSegment.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+
+class ChallengeAddedSegment extends Challenge
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_AddSegment');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('SegmentEditor_PluginDescription');
+ }
+
+ public function getId()
+ {
+ return 'add_segment';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $login = Piwik::getCurrentUserLogin();
+ $this->completed = $this->finder->hasAddedSegment($login);
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/segmentation/';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeAddedUser.php b/plugins/Tour/Engagement/ChallengeAddedUser.php
new file mode 100644
index 0000000000..30176d2b1f
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeAddedUser.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Url;
+
+class ChallengeAddedUser extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_AddUser');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('UsersManager_PluginDescription');
+ }
+
+ public function getId()
+ {
+ return 'add_user';
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'UsersManager', 'action' => 'index', 'widget' => false));
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeAddedWebsite.php b/plugins/Tour/Engagement/ChallengeAddedWebsite.php
new file mode 100644
index 0000000000..b709a0bae8
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeAddedWebsite.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+use Piwik\Url;
+
+class ChallengeAddedWebsite extends Challenge
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_AddWebsite');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('SitesManager_PluginDescription');
+ }
+
+ public function getId()
+ {
+ return 'add_website';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $this->completed = $this->finder->hasAddedWebsite(Piwik::getCurrentUserLogin());
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'SitesManager', 'action' => 'index', 'widget' => false));
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeBrowseMarketplace.php b/plugins/Tour/Engagement/ChallengeBrowseMarketplace.php
new file mode 100644
index 0000000000..b7207f5318
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeBrowseMarketplace.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Url;
+
+class ChallengeBrowseMarketplace extends Challenge
+{
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_BrowseMarketplace');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Marketplace_PluginDescription');
+ }
+
+ public function getId()
+ {
+ return 'browse_marketplace';
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'Marketplace', 'action' => 'overview', 'widget' => false));
+ }
+
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeChangeVisualisation.php b/plugins/Tour/Engagement/ChallengeChangeVisualisation.php
new file mode 100644
index 0000000000..d0b760b541
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeChangeVisualisation.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeChangeVisualisation extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_ChangeVisualisation');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_ChangeVisualisationDescription');
+ }
+
+ public function getId()
+ {
+ return 'change_visualisations';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/piwik-tour/#a-look-at-a-piwik-report';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeConfigureGeolocation.php b/plugins/Tour/Engagement/ChallengeConfigureGeolocation.php
new file mode 100644
index 0000000000..b77f7f85ee
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeConfigureGeolocation.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Container\StaticContainer;
+use Piwik\Piwik;
+use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult;
+use Piwik\Plugins\UserCountry\Diagnostic\GeolocationDiagnostic;
+use Piwik\Url;
+
+class ChallengeConfigureGeolocation extends Challenge
+{
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_ConfigureGeolocation');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_ConfigureGeolocationDescription');
+ }
+
+ public function getId()
+ {
+ return 'configure_geolocation';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $locationDiagnostic = StaticContainer::get(GeolocationDiagnostic::class);
+ $result = $locationDiagnostic->execute();
+ $this->completed = !empty($result[0]) && $result[0]->getStatus() === DiagnosticResult::STATUS_OK;
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'UserCountry', 'action' => 'adminIndex', 'widget' => false));
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeCreatedGoal.php b/plugins/Tour/Engagement/ChallengeCreatedGoal.php
new file mode 100644
index 0000000000..2c619d6c0e
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeCreatedGoal.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Url;
+
+class ChallengeCreatedGoal extends Challenge
+{
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_DefineGoal');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_DefineGoalDescription');
+ }
+
+ public function getId()
+ {
+ return 'define_goal';
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'Goals', 'action' => 'manage', 'widget' => false));
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeCustomLogo.php b/plugins/Tour/Engagement/ChallengeCustomLogo.php
new file mode 100644
index 0000000000..07a01551b8
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeCustomLogo.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Plugins\CoreAdminHome\CustomLogo;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+use Piwik\Url;
+
+class ChallengeCustomLogo extends Challenge
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_UploadLogo');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('CoreAdminHome_CustomLogoHelpText');
+ }
+
+ public function getId()
+ {
+ return 'custom_logo';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $logo = new CustomLogo();
+ $this->completed = $logo->isEnabled();
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'generalSettings', 'widget' => false)) . '#/#useCustomLogo';
+ }
+
+}
diff --git a/plugins/Tour/Engagement/ChallengeCustomiseDashboard.php b/plugins/Tour/Engagement/ChallengeCustomiseDashboard.php
new file mode 100644
index 0000000000..d422566ebd
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeCustomiseDashboard.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+
+class ChallengeCustomiseDashboard extends Challenge
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_CustomiseDashboard');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_CustomiseDashboardDescription');
+ }
+
+ public function getId()
+ {
+ return 'customise_dashboard';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $login = Piwik::getCurrentUserLogin();
+ $this->completed = $this->finder->hasAddedOrCustomisedDashboard($login);
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/piwik-tour/#dashboard-widgets';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeDisableBrowserArchiving.php b/plugins/Tour/Engagement/ChallengeDisableBrowserArchiving.php
new file mode 100644
index 0000000000..8fa481ab10
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeDisableBrowserArchiving.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\ArchiveProcessor\Rules;
+use Piwik\Piwik;
+
+class ChallengeDisableBrowserArchiving extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_DisableBrowserArchiving');
+ }
+
+ public function getId()
+ {
+ return 'disable_browser_archiving';
+ }
+
+ public function isCompleted()
+ {
+ return !Rules::isBrowserTriggerEnabled();
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/setup-auto-archiving/';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeFlattenActions.php b/plugins/Tour/Engagement/ChallengeFlattenActions.php
new file mode 100644
index 0000000000..4cba6fd90b
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeFlattenActions.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeFlattenActions extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_FlattenActions');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_FlattenActionsDescription');
+ }
+
+ public function getId()
+ {
+ return 'flatten_actions';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/piwik-tour/#flattening-reports';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeScheduledReport.php b/plugins/Tour/Engagement/ChallengeScheduledReport.php
new file mode 100644
index 0000000000..ba694ac046
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeScheduledReport.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+use Piwik\Url;
+
+class ChallengeScheduledReport extends Challenge
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_AddReport');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('ScheduledReports_PluginDescription');
+ }
+
+ public function getId()
+ {
+ return 'add_scheduled_report';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $this->completed = $this->finder->hasAddedNewEmailReport(Piwik::getCurrentUserLogin());
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'ScheduledReports', 'action' => 'index', 'widget' => false));
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeSelectDateRange.php b/plugins/Tour/Engagement/ChallengeSelectDateRange.php
new file mode 100644
index 0000000000..fc9284ee36
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeSelectDateRange.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeSelectDateRange extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_SelectDateRange');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_SelectDateRangeDescription');
+ }
+
+ public function getId()
+ {
+ return 'select_date_range';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/piwik-tour/#select-a-date-range';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeSetupTwoFa.php b/plugins/Tour/Engagement/ChallengeSetupTwoFa.php
new file mode 100644
index 0000000000..dc5fa1c65c
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeSetupTwoFa.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Container\StaticContainer;
+use Piwik\Piwik;
+use Piwik\Plugins\TwoFactorAuth\TwoFactorAuthentication;
+
+class ChallengeSetupTwoFa extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_SetupX', Piwik::translate('TwoFactorAuth_TwoFactorAuthentication'));
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('TwoFactorAuth_TwoFactorAuthenticationIntro', array('', ''));
+ }
+
+ public function getId()
+ {
+ return 'setup_twofa';
+ }
+
+ public function isCompleted()
+ {
+ $twoFa = StaticContainer::get(TwoFactorAuthentication::class);
+ return $twoFa->isUserUsingTwoFactorAuthentication(Piwik::getCurrentUserLogin());
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/faq/general/faq_27245';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeTrackingCode.php b/plugins/Tour/Engagement/ChallengeTrackingCode.php
new file mode 100644
index 0000000000..dba870e0b2
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeTrackingCode.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+use Piwik\Url;
+
+class ChallengeTrackingCode extends Challenge
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ /**
+ * @var null|bool
+ */
+ private $completed = null;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ public function getName()
+ {
+ return Piwik::translate('Tour_EmbedTrackingCode');
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('CoreAdminHome_TrackingCodeIntro');
+ }
+
+ public function getId()
+ {
+ return 'track_data';
+ }
+
+ public function isCompleted()
+ {
+ if (!isset($this->completed)) {
+ $this->completed = $this->finder->hasTrackedData();
+ }
+ return $this->completed;
+ }
+
+ public function getUrl()
+ {
+ return 'index.php' . Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'trackingCodeGenerator', 'widget' => false));
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeViewRowEvolution.php b/plugins/Tour/Engagement/ChallengeViewRowEvolution.php
new file mode 100644
index 0000000000..915334929b
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeViewRowEvolution.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeViewRowEvolution extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_ViewX', Piwik::translate('Tour_RowEvolution'));
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_ViewRowEvolutionDescription');
+ }
+
+ public function getId()
+ {
+ return 'view_row_evolution';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/row-evolution/';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeViewVisitorProfile.php b/plugins/Tour/Engagement/ChallengeViewVisitorProfile.php
new file mode 100644
index 0000000000..18b6275da8
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeViewVisitorProfile.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeViewVisitorProfile extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_ViewX', Piwik::translate('Live_VisitorProfile'));
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_ViewVisitorProfileDescription');
+ }
+
+ public function getId()
+ {
+ return 'view_visitor_profile';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/user-profile/';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/ChallengeViewVisitsLog.php b/plugins/Tour/Engagement/ChallengeViewVisitsLog.php
new file mode 100644
index 0000000000..56fe4bc875
--- /dev/null
+++ b/plugins/Tour/Engagement/ChallengeViewVisitsLog.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Piwik;
+
+class ChallengeViewVisitsLog extends Challenge
+{
+ public function getName()
+ {
+ return Piwik::translate('Tour_ViewX', Piwik::translate('Live_VisitsLog'));
+ }
+
+ public function getDescription()
+ {
+ return Piwik::translate('Tour_ViewVisitsLogDescription');
+ }
+
+ public function getId()
+ {
+ return 'view_visits_log';
+ }
+
+ public function getUrl()
+ {
+ return 'https://matomo.org/docs/real-time/#visits-log';
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/Challenges.php b/plugins/Tour/Engagement/Challenges.php
new file mode 100644
index 0000000000..ffc570930b
--- /dev/null
+++ b/plugins/Tour/Engagement/Challenges.php
@@ -0,0 +1,116 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\Container\StaticContainer;
+use Piwik\Piwik;
+use Piwik\Plugin;
+use Piwik\Plugins\CoreAdminHome\Controller;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+use Piwik\Plugins\UserCountry\UserCountry;
+
+class Challenges
+{
+ /**
+ * @var DataFinder
+ */
+ private $finder;
+
+ public function __construct(DataFinder $dataFinder)
+ {
+ $this->finder = $dataFinder;
+ }
+
+ private function isActivePlugin($pluginName)
+ {
+ return Plugin\Manager::getInstance()->isPluginActivated($pluginName);
+ }
+
+ public function getChallenges()
+ {
+ /** @var Challenge[] $challenges */
+ $challenges = array(
+ StaticContainer::get(ChallengeTrackingCode::class),
+ );
+
+ if ($this->isActivePlugin('Goals')) {
+ $challenges[] = StaticContainer::get(ChallengeCreatedGoal::class);
+ }
+
+ $challenges[] = StaticContainer::get(ChallengeCustomLogo::class);
+
+ if ($this->isActivePlugin('UsersManager')) {
+ $challenges[] = StaticContainer::get(ChallengeAddedUser::class);
+ }
+ if ($this->isActivePlugin('SitesManager')) {
+ $challenges[] = StaticContainer::get(ChallengeAddedWebsite::class);
+ }
+
+ $challenges[] = StaticContainer::get(ChallengeFlattenActions::class);
+ $challenges[] = StaticContainer::get(ChallengeChangeVisualisation::class);
+
+ if ($this->isActivePlugin('ScheduledReports')) {
+ $challenges[] = StaticContainer::get(ChallengeScheduledReport::class);
+ }
+ if ($this->isActivePlugin('Dashboard')) {
+ $challenges[] = StaticContainer::get(ChallengeCustomiseDashboard::class);
+ }
+ if ($this->isActivePlugin('SegmentEditor')) {
+ $challenges[] = StaticContainer::get(ChallengeAddedSegment::class);
+ }
+ if ($this->isActivePlugin('Annotations')) {
+ $challenges[] = StaticContainer::get(ChallengeAddedAnnotation::class);
+ }
+
+ if ($this->isActivePlugin('TwoFactorAuth')) {
+ $challenges[] = StaticContainer::get(ChallengeSetupTwoFa::class);
+ }
+
+ if (Controller::isGeneralSettingsAdminEnabled()) {
+ $challenges[] = StaticContainer::get(ChallengeDisableBrowserArchiving::class);
+ }
+
+ if (UserCountry::isGeoLocationAdminEnabled()) {
+ $challenges[] = StaticContainer::get(ChallengeConfigureGeolocation::class);
+ }
+
+ // we're adding this simple challenge only later in the process since there might not be enough data yet in
+ // the beginning to actually get much value from it
+ $challenges[] = StaticContainer::get(ChallengeSelectDateRange::class);
+
+ if ($this->isActivePlugin('Live')) {
+ $challenges[] = StaticContainer::get(ChallengeViewVisitsLog::class);
+ $challenges[] = StaticContainer::get(ChallengeViewVisitorProfile::class);
+ }
+
+ $challenges[] = StaticContainer::get(ChallengeViewRowEvolution::class);
+
+ if ($this->isActivePlugin('Marketplace')) {
+ $challenges[] = StaticContainer::get(ChallengeBrowseMarketplace::class);
+ }
+
+ /**
+ * Triggered to add new challenges to the "welcome to Matomo tour".
+ *
+ * **Example**
+ *
+ * public function addChallenge(&$challenges)
+ * {
+ * $challenges[] = new MyChallenge();
+ * }
+ *
+ * @param Challenge[] $challenges An array of challenges
+ */
+ Piwik::postEvent('Tour.filterChallenges', array(&$challenges));
+
+ return $challenges;
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Engagement/Levels.php b/plugins/Tour/Engagement/Levels.php
new file mode 100644
index 0000000000..19a985604d
--- /dev/null
+++ b/plugins/Tour/Engagement/Levels.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Engagement;
+
+use Piwik\API\Request;
+use Piwik\Piwik;
+
+class Levels
+{
+ /**
+ * @var array
+ */
+ private $challenges = array();
+
+ private function getChallenges()
+ {
+ if (empty($this->challenges)) {
+ $this->challenges = Request::processRequest('Tour.getChallenges', [], []);
+ }
+
+ return $this->challenges;
+ }
+
+ public function getNumChallengesCompleted()
+ {
+ $challenges = $this->getChallenges();
+
+ $completed = 0;
+ foreach ($challenges as $challenge) {
+ if ($challenge['isSkipped'] || $challenge['isCompleted']) {
+ $completed++;
+ }
+ }
+ return $completed;
+ }
+
+ public function getCurrentLevel()
+ {
+ $completed = $this->getNumChallengesCompleted();
+
+ $current = 0;
+ foreach ($this->getLevels() as $threshold => $level) {
+ if ($completed >= $threshold) {
+ $current++;
+ }
+ }
+ return $current;
+ }
+
+ public function getCurrentLevelName()
+ {
+ $completed = $this->getNumChallengesCompleted();
+
+ $current = '';
+ foreach ($this->getLevels() as $threshold => $level) {
+ if ($completed >= $threshold) {
+ $current = $level;
+ }
+ }
+ return $current;
+ }
+
+ public function getNextLevelName()
+ {
+ $completed = $this->getNumChallengesCompleted();
+
+ foreach ($this->getLevels() as $threshold => $level) {
+ if ($completed < $threshold) {
+ return $level;
+ }
+ }
+ }
+
+ public function getNumLevels()
+ {
+ $levels = $this->getLevels();
+ return count($levels);
+ }
+
+ public function getNumChallengesNeededToNextLevel()
+ {
+ $completed = $this->getNumChallengesCompleted();
+
+ foreach ($this->getLevels() as $threshold => $level) {
+ if ($completed < $threshold) {
+ return $threshold - $completed;
+ }
+ }
+ }
+
+ public function getCurrentDescription()
+ {
+ $login = Piwik::getCurrentUserLogin();
+ $numChallengesCompleted = $this->getNumChallengesCompleted();
+ $numChallengesTotal = $this->getNumChallengesTotal();
+
+ if ($numChallengesCompleted <= ($numChallengesTotal / 4)) {
+ return Piwik::translate('Tour_Part1Title', $login);
+ }
+
+ if ($numChallengesCompleted <= ($numChallengesTotal / 2)) {
+ return Piwik::translate('Tour_Part2Title', $login);
+ }
+
+ if ($numChallengesCompleted <= ($numChallengesTotal / 1.333)) {
+ return Piwik::translate('Tour_Part3Title', $login);
+ }
+
+ return Piwik::translate('Tour_Part4Title', $login);
+ }
+
+ private function getNumChallengesTotal()
+ {
+ $challenges = $this->getChallenges();
+ return count($challenges);
+ }
+
+ public function getLevels()
+ {
+ $numChallengesTotal = $this->getNumChallengesTotal();
+
+ $levels = array(
+ 0 => Piwik::translate('Tour_MatomoBeginner'),
+ 5 => Piwik::translate('Tour_MatomoIntermediate'),
+ );
+
+ if ($numChallengesTotal > 10) {
+ // the number of challenges varies from Matomo to Matomo depending on activated plugins and activated
+ // features. Therefore we may remove some levels if there aren't too many challenges available.
+ $levels[10] = Piwik::translate('Tour_MatomoTalent');
+ }
+
+ if ($numChallengesTotal > 15) {
+ $levels[15] = Piwik::translate('Tour_MatomoProfessional');
+ }
+
+ $levels[$numChallengesTotal] = Piwik::translate('Tour_MatomoExpert');
+
+ return $levels;
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/Tour/Tour.php b/plugins/Tour/Tour.php
new file mode 100644
index 0000000000..15eee69ee7
--- /dev/null
+++ b/plugins/Tour/Tour.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour;
+
+use Piwik\Common;
+use Piwik\Container\StaticContainer;
+use Piwik\Piwik;
+use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
+use Piwik\Plugins\CoreVisualizations\Visualizations\Sparkline;
+use Piwik\Plugins\Tour\Engagement\Challenge;
+use Piwik\Plugins\Tour\Engagement\ChallengeAddedAnnotation;
+use Piwik\Plugins\Tour\Engagement\ChallengeAddedUser;
+use Piwik\Plugins\Tour\Engagement\ChallengeBrowseMarketplace;
+use Piwik\Plugins\Tour\Engagement\ChallengeChangeVisualisation;
+use Piwik\Plugins\Tour\Engagement\ChallengeCreatedGoal;
+use Piwik\Plugins\Tour\Engagement\ChallengeFlattenActions;
+use Piwik\Plugins\Tour\Engagement\ChallengeSelectDateRange;
+use Piwik\Plugins\Tour\Engagement\ChallengeViewRowEvolution;
+use Piwik\Plugins\Tour\Engagement\ChallengeViewVisitorProfile;
+use Piwik\Plugins\Tour\Engagement\ChallengeViewVisitsLog;
+
+class Tour extends \Piwik\Plugin
+{
+
+ public function registerEvents()
+ {
+ return array(
+ 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
+ 'AssetManager.getJavaScriptFiles' => 'getJsFiles',
+ 'Dashboard.changeDefaultDashboardLayout' => 'changeDefaultDashboardLayout',
+ 'API.Annotations.add.end' => 'onAnnotationAdded',
+ 'API.Goals.addGoal.end' => 'onGoalAdded',
+ 'API.UsersManager.addUser' => 'onUserAdded',
+ 'Controller.CoreHome.getRowEvolutionPopover' => 'onViewRowEvolution',
+ 'Controller.Live.getLastVisitsDetails' => 'onViewVisitorLog',
+ 'Controller.Live.getVisitorProfilePopup' => 'onViewVisitorProfile',
+ 'Controller.Marketplace.overview' => 'onBrowseMarketplace',
+ 'ViewDataTable.configure' => array('function' => 'onConfigureView', 'after' => true),
+ );
+ }
+
+ public function onBrowseMarketplace()
+ {
+ $this->setSimpleChallengeCompleted(ChallengeBrowseMarketplace::class);
+ }
+
+ public function onConfigureView()
+ {
+ if (Common::getRequestVar('period', '', 'string') === 'range') {
+ $this->setSimpleChallengeCompleted(ChallengeSelectDateRange::class);
+ }
+
+ if (Common::getRequestVar('flat', '0', 'string') === '1') {
+ $module = Piwik::getModule();
+ if ($module === 'Actions' || $module === 'Contents' || $module === 'UsersFlow') {
+ $this->setSimpleChallengeCompleted(ChallengeFlattenActions::class);
+ }
+ }
+
+ $viewDataTable = Common::getRequestVar('viewDataTable', '', 'string');
+ if ($viewDataTable && !Common::getRequestVar('forceView', '', 'string')) {
+ if ($viewDataTable !== Sparkline::ID && $viewDataTable !== Evolution::ID) {
+ // sparkline and graphEvolution may be used without forceView
+ $this->setSimpleChallengeCompleted(ChallengeChangeVisualisation::class);
+ }
+ }
+ }
+
+ private function setSimpleChallengeCompleted($className)
+ {
+ if (Piwik::hasUserSuperUserAccess()) {
+ /** @var Challenge $challenge */
+ $challenge = StaticContainer::get($className);
+ $challenge->setCompleted();
+ }
+ }
+
+ public function onViewRowEvolution()
+ {
+ $this->setSimpleChallengeCompleted(ChallengeViewRowEvolution::class);
+ }
+
+ public function onViewVisitorLog()
+ {
+ $this->setSimpleChallengeCompleted(ChallengeViewVisitsLog::class);
+ }
+
+ public function onViewVisitorProfile()
+ {
+ $this->setSimpleChallengeCompleted(ChallengeViewVisitorProfile::class);
+ }
+
+ public function onAnnotationAdded($response)
+ {
+ if (Piwik::hasUserSuperUserAccess() && !empty($response)) {
+ $annotation = new ChallengeAddedAnnotation();
+ $annotation->setCompleted();
+ }
+ }
+
+ public function onGoalAdded($response)
+ {
+ if (Piwik::hasUserSuperUserAccess() && !empty($response)) {
+ $annotation = new ChallengeCreatedGoal();
+ $annotation->setCompleted();
+ }
+ }
+
+ public function onUserAdded($response)
+ {
+ if (Piwik::hasUserSuperUserAccess()) {
+ $annotation = new ChallengeAddedUser();
+ $annotation->setCompleted();
+ }
+ }
+
+ public function changeDefaultDashboardLayout(&$defaultLayout)
+ {
+ if (Piwik::hasUserSuperUserAccess()) {
+ $defaultLayout = json_decode($defaultLayout);
+ $engagementWidget = array('uniqueId' => 'widgetTourgetEngagement', 'parameters' => array('module' => 'Tour', 'action' => 'getEngagement'));
+ if (isset($defaultLayout[2])) {
+ array_unshift($defaultLayout[2], $engagementWidget);
+ }
+ $defaultLayout = json_encode($defaultLayout);
+ }
+ }
+
+ public function getStylesheetFiles(&$stylesheets)
+ {
+ $stylesheets[] = "plugins/Tour/stylesheets/engagement.less";
+ }
+
+ public function getJsFiles(&$jsFiles)
+ {
+ $jsFiles[] = "plugins/Tour/javascripts/engagement.js";
+ }
+}
diff --git a/plugins/Tour/Widgets/GetEngagement.php b/plugins/Tour/Widgets/GetEngagement.php
new file mode 100644
index 0000000000..b4053d2237
--- /dev/null
+++ b/plugins/Tour/Widgets/GetEngagement.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\Widgets;
+
+use Piwik\API\Request;
+use Piwik\Common;
+use Piwik\Plugins\Tour\Engagement\Levels;
+use Piwik\Plugins\Tour\Engagement\Challenges;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\Piwik;
+
+class GetEngagement extends Widget
+{
+ const NUM_CHALLENGES_PER_PAGE = 5;
+
+ /**
+ * @var Challenges
+ */
+ private $challenges;
+
+ /**
+ * Levels
+ */
+ private $levels;
+
+ public function __construct(Challenges $challenges, Levels $levels)
+ {
+ $this->challenges = $challenges;
+ $this->levels = $levels;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('About Matomo');
+ $config->setName(Piwik::translate('Tour_BecomeMatomoExpert'));
+ $config->setOrder(99);
+
+ if (!Piwik::hasUserSuperUserAccess()) {
+ $config->disable();
+ }
+ }
+
+ public function render()
+ {
+ Piwik::checkUserHasSuperUserAccess();
+
+ $challenges = Request::processRequest('Tour.getChallenges', [], []);
+ $level = Request::processRequest('Tour.getLevel', [], []);
+
+ $numCompletedWithoutInterruption = 0;
+
+ $done = true;
+ foreach ($challenges as $challenge) {
+ if (!$challenge['isCompleted'] && !$challenge['isSkipped']) {
+ $done = false;
+ } else if ($done) {
+ // as soon as some challenge was not completed, we need to make sure to show that page.
+ $numCompletedWithoutInterruption++;
+ }
+ }
+
+ $page = floor($numCompletedWithoutInterruption / self::NUM_CHALLENGES_PER_PAGE);
+ $page = Common::getRequestVar('page', $page, 'int');
+ $numPagesTotal = ceil(count($challenges) / self::NUM_CHALLENGES_PER_PAGE) - 1;
+
+ $startPosition = self::NUM_CHALLENGES_PER_PAGE * $page;
+ $challenges = array_slice($challenges, $startPosition, self::NUM_CHALLENGES_PER_PAGE);
+
+ return $this->renderTemplate('engagement', array(
+ 'isCompleted' => $done,
+ 'challenges' => $challenges,
+ 'currentPage' => $page,
+ 'previousPage' => $page >= 1 ? $page - 1 : null,
+ 'nextPage' => $page < ($numPagesTotal) ? $page + 1 : null,
+ 'level' => $level
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Tour/config/test.php b/plugins/Tour/config/test.php
new file mode 100644
index 0000000000..1d43a00f1e
--- /dev/null
+++ b/plugins/Tour/config/test.php
@@ -0,0 +1,22 @@
+<?php
+
+return array(
+ 'observers.global' => DI\add(array(
+ array('API.Tour.getChallenges.end', function (&$challenges) {
+ $completeAllChanges = \Piwik\Container\StaticContainer::get('test.vars.completeAllChallenges');
+ if ($completeAllChanges) {
+ foreach ($challenges as &$challenge) {
+ $challenge['isSkipped'] = true;
+ $challenge['isCompleted'] = true;
+ }
+ }
+ $completeNoChallenge = \Piwik\Container\StaticContainer::get('test.vars.completeNoChallenge');
+ if ($completeNoChallenge) {
+ foreach ($challenges as &$challenge) {
+ $challenge['isSkipped'] = false;
+ $challenge['isCompleted'] = false;
+ }
+ }
+ }),
+ ))
+);
diff --git a/plugins/Tour/javascripts/engagement.js b/plugins/Tour/javascripts/engagement.js
new file mode 100644
index 0000000000..ad36b96e97
--- /dev/null
+++ b/plugins/Tour/javascripts/engagement.js
@@ -0,0 +1,30 @@
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+var tourEngagement = {
+ skipChallenge: function (key) {
+ var $challenge = $('.tourEngagement .' + key + ' ');
+ $challenge.find('.icon-hide').removeClass('icon-hide').addClass('icon-ok');
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams({module: 'API', method: 'Tour.skipChallenge', id: key}, 'get');
+ ajaxRequest.withTokenInUrl();
+ ajaxRequest.setFormat('json');
+ ajaxRequest.send();
+ },
+ goToPage: function (page) {
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams({module: 'Tour', action: 'getEngagement', page: page}, 'get');
+ ajaxRequest.withTokenInUrl();
+ ajaxRequest.setFormat('html');
+ ajaxRequest.setCallback(function (callback) {
+ $('.widgetBody.tourEngagement').parent().html(callback);
+ })
+ ajaxRequest.send();
+ }
+}; \ No newline at end of file
diff --git a/plugins/Tour/lang/en.json b/plugins/Tour/lang/en.json
new file mode 100644
index 0000000000..64157cecfd
--- /dev/null
+++ b/plugins/Tour/lang/en.json
@@ -0,0 +1,56 @@
+{
+ "Tour": {
+ "PluginDescription": "Become a Matomo Expert in no time by completing challenges that get you familiar with Matomo.",
+ "Tour": "Tour",
+ "Engagement": "Engagement",
+ "UploadLogo": "Upload your logo",
+ "AddUser": "Add another user",
+ "AddWebsite": "Add another website",
+ "AddReport": "Add a scheduled report",
+ "AddSegment": "Add a segment",
+ "AddAnnotation": "Add an annotation",
+ "BrowseMarketplace": "Browse Marketplace",
+ "PreviousChallenges": "Previous challenges",
+ "NextChallenges": "Next challenges",
+ "RowEvolution": "Row evolution",
+ "ViewX": "View %s",
+ "SetupX": "Setup %s",
+ "SelectDateRange": "Select a date range",
+ "SelectDateRangeDescription": "Select a date range in the calendar.",
+ "ChangeVisualisation": "Change a visualisation",
+ "ChangeVisualisationDescription": "View a report and select the visualisation icon at the bottom of a report to view the same report in a different visualisation.",
+ "FlattenActions": "Flatten a page report",
+ "FlattenActionsDescription": "Go to Behaviour => Pages and click in the bottom of the report on the cog icon to flatten the report. A flattened report changes the hierarchy from a grouped report to a list.",
+ "ViewRowEvolutionDescription": "Row evolution shows you the current & past metric data for any row in any report.",
+ "ViewVisitsLogDescription": "The visits Log shows you all the individual visits and actions each visitor took on your site.",
+ "ViewVisitorProfileDescription": "The visitor log helps you to understand your visitors’ individual behavior by summarizing and listing your visitors’ visits.",
+ "ShareAllChallengesCompleted": "Just unlocked the %1$s achievement by completing all Matomo challenges.",
+ "ChallengeCompleted": "Congratulations, you have completed this challenge.",
+ "BecomeMatomoExpert": "Become a Matomo Expert",
+ "YouCanCallYourselfExpert": "You can now call yourself a true %1$sMatomo Expert%2$s.",
+ "StatusLevel": "You are currently a %1$s. Complete %2$s more challenges and you become a %3$s.",
+ "ConfigureGeolocationDescription": "Ensure the location of your visitors is correctly detected.",
+ "DisableBrowserArchiving": "Disable browser archiving for better performance",
+ "SkipThisChallenge": "Skip this challenge",
+ "ShareYourAchievementOn": "Share your achievement on %1$s.",
+ "MatomoSenior": "Matomo Talent",
+ "MatomoBeginner": "Matomo Beginner",
+ "MatomoIntermediate": "Matomo Intermediate",
+ "MatomoTalent": "Matomo Talent",
+ "MatomoProfessional": "Matomo Professional",
+ "MatomoExpert": "Matomo Expert",
+ "ConfigureGeolocation": "Configure geolocation",
+ "CustomiseDashboard": "Customise dashboard",
+ "CustomiseDashboardDescription": "You can add new widgets to your Dashboard, to ensure it reports on all of your most important metrics.",
+ "DefineGoal": "Add a goal",
+ "DefineGoalDescription": "Conversion tracking is a great way to identify whether you are meeting your current business objectives, identify new objectives, view and analyse your performance, and learn how to increase your conversions, conversion rates, and revenue per visit.",
+ "EmbedTrackingCode": "Embed tracking code",
+ "CompletionTitle": "Well done!",
+ "CompletionMessage": "You have completed all the challenges. Give yourself a pat on the back.",
+ "Part1Title": "Welcome to Matomo %1$s. This widget will help you become a Matomo expert in no time.",
+ "Part2Title": "Keep it up %1$s! You’re well on your way to becoming a Matomo expert.",
+ "Part3Title": "Great progress %1$s! If you continue like this, you’ll be an expert in no time!",
+ "Part4Title": "Great progress %1$s! Only a few more challenges to complete.",
+ "OnlyVisibleToSuperUser": "Only you as a %1$sSuper User%2$s can see this widget."
+ }
+} \ No newline at end of file
diff --git a/plugins/Tour/stylesheets/engagement.less b/plugins/Tour/stylesheets/engagement.less
new file mode 100644
index 0000000000..4b882bf866
--- /dev/null
+++ b/plugins/Tour/stylesheets/engagement.less
@@ -0,0 +1,32 @@
+.tourEngagement {
+
+ .icon-star {
+ font-size:32px;
+ }
+
+ .successStar {
+ color:#43a047;
+ }
+
+ .upgradeStar {
+ color:@color-silver;
+ }
+
+ .completed,
+ .icon-ok {
+ color:#43a047;
+ }
+ .icon-hide {
+ color: #666;
+ }
+ .icon-ok, .icon-hide {
+ margin-right: 5px;
+ }
+
+ li {
+ padding: 4px 0;
+ }
+ .tourSuperUserNote {
+ color: @color-silver;
+ }
+} \ No newline at end of file
diff --git a/plugins/Tour/templates/engagement.twig b/plugins/Tour/templates/engagement.twig
new file mode 100644
index 0000000000..10ca82296e
--- /dev/null
+++ b/plugins/Tour/templates/engagement.twig
@@ -0,0 +1,58 @@
+<div class="widgetBody tourEngagement">
+ <p title="{{ 'Tour_StatusLevel'|translate(level.currentLevelName, level.challengesNeededForNextLevel, level.nextLevelName)|e('html_attr') }}">
+ {% for i in 1..level.numLevelsTotal %}
+ <span class="icon-star {% if level.currentLevel >= i %}successStar{% else %}upgradeStar{% endif %}"></span>
+ {% endfor %}
+ </p>
+
+ {% if isCompleted %}
+ <p><strong class="completed">{{ 'Tour_CompletionTitle'|translate }}</strong>
+ {{ 'Tour_CompletionMessage'|translate }}
+ <br />
+ <br />
+ {{ 'Tour_YouCanCallYourselfExpert'|translate('<strong class="successStar">', '</strong>')|raw }}<br /><br />
+ {{ 'Tour_ShareYourAchievementOn'|translate('<a target="_blank" rel="noreferrer noopener" href="http://twitter.com/share?text='~ "Tour_ShareAllChallengesCompleted"|translate(level.currentLevelName)|e('url') ~ '&url=' ~ "https://matomo.org"|e('url') ~ '">Twitter</a>')|raw }}
+ </p>
+ {% else %}
+ {% if level.description %}<p>{{ level.description }}</p>{% endif %}
+
+ <p>
+ {{ 'Tour_StatusLevel'|translate('<strong>'~ level.currentLevelName ~'</strong>', level.challengesNeededForNextLevel, '<strong>'~ level.nextLevelName ~'</strong>')|raw }}
+ </p>
+
+ <ul>
+ {% for challenge in challenges %}
+ <li class="tourChallenge {{ challenge.id|e('html_attr') }}" title="{{ challenge.description|e('html_attr') }}">
+ {%- if challenge.isCompleted or challenge.isSkipped %}
+ <span class="icon-ok" title="{{ 'Tour_ChallengeCompleted'|translate|e('html_attr') }}"></span>
+ {% else %}
+ <a href="javascript:void 0;" onclick="tourEngagement.skipChallenge('{{ challenge.id|e('js') }}')" title="{{ 'Tour_SkipThisChallenge'|translate|e('html_attr') }}"><span class="icon-hide"></span></a>
+ {% endif %}
+ {% if challenge.url is not empty %}
+ <a href="{{ challenge.url|safelink|e('html_attr') }}" target="_blank" rel="noreferrer noopener">{{ challenge.name }}</a>
+ {% else %}
+ {{ challenge.name }}
+ {% endif %}
+ </li>
+ {% endfor %}
+ </ul>
+ <hr />
+ <p style="text-align: center;padding-bottom: 0;">
+ {% if previousPage is not null %}
+ <a class="previousChallenges" onclick="tourEngagement.goToPage({{ previousPage|e('js') }})">&lsaquo; {% if nextPage %}{{ 'General_Previous'|translate }}{% else %}{{ 'Tour_PreviousChallenges'|translate }}{% endif %}</a>
+ {% endif %}
+ {% if nextPage %}
+ {% if previousPage is not null %} | {% endif %}
+ <a class="nextChallenges" onclick="tourEngagement.goToPage({{ nextPage|e('js') }})">{% if previousPage is not null %}{{ 'General_Next'|translate }}{% else %}{{ 'Tour_NextChallenges'|translate }}{% endif %} &rsaquo;</a>
+ {% endif %}
+ </p>
+ <hr />
+ <p class="tourSuperUserNote">{{ 'Tour_OnlyVisibleToSuperUser'|translate('<a href="https://matomo.org/faq/general/faq_35/" target="_blank" rel="noreferrer noopener">', '</a>')|raw }}</p>
+ <script>
+ jQuery(window).off('focus.tourEngagement').on('focus.tourEngagement', function () {
+ tourEngagement.goToPage({{ currentPage|e('js') }});
+ });
+ </script>
+ {% endif %}
+
+</div> \ No newline at end of file
diff --git a/plugins/Tour/tests/Fixtures/SimpleFixtureTrackFewVisits.php b/plugins/Tour/tests/Fixtures/SimpleFixtureTrackFewVisits.php
new file mode 100644
index 0000000000..7bbdf8cb4c
--- /dev/null
+++ b/plugins/Tour/tests/Fixtures/SimpleFixtureTrackFewVisits.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\tests\Fixtures;
+
+use Piwik\Date;
+use Piwik\Tests\Framework\Fixture;
+
+/**
+ * Generates tracker testing data for our DataFinderTest
+ *
+ * This Simple fixture adds one website and tracks one visit with couple pageviews and an ecommerce conversion
+ */
+class SimpleFixtureTrackFewVisits extends Fixture
+{
+ public $dateTime = '2013-01-23 01:23:45';
+ public $idSite = 1;
+
+ public function setUp()
+ {
+ $this->setUpWebsite();
+ $this->trackFirstVisit();
+ }
+
+ public function tearDown()
+ {
+ // empty
+ }
+
+ private function setUpWebsite()
+ {
+ if (!self::siteCreated($this->idSite)) {
+ $idSite = self::createWebsite($this->dateTime, $ecommerce = 1);
+ $this->assertSame($this->idSite, $idSite);
+ }
+ }
+
+ protected function trackFirstVisit()
+ {
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrl('http://example.com/');
+ self::checkResponse($t->doTrackPageView('Viewing homepage'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime());
+ $t->setUrl('http://example.com/sub/page');
+ self::checkResponse($t->doTrackPageView('Second page view'));
+
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.25)->getDatetime());
+ $t->addEcommerceItem($sku = 'SKU_ID', $name = 'Test item!', $category = 'Test & Category', $price = 777, $quantity = 33);
+ self::checkResponse($t->doTrackEcommerceOrder('TestingOrder', $grandTotal = 33 * 77));
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Tour/tests/Integration/ChallengeTest.php b/plugins/Tour/tests/Integration/ChallengeTest.php
new file mode 100644
index 0000000000..58d49dbd0d
--- /dev/null
+++ b/plugins/Tour/tests/Integration/ChallengeTest.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\Tour\tests\Integration;
+
+use Piwik\Plugins\Tour\Engagement\Challenge;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+
+class CustomTestChallenge extends Challenge {
+ public function getId()
+ {
+ return 'test_challenge';
+ }
+ public function getName()
+ {
+ return 'Test Challenge';
+ }
+}
+
+class CustomTest2Challenge extends Challenge {
+ public function getId()
+ {
+ return 'test_challenge2';
+ }
+ public function getName()
+ {
+ return 'Test Challenge2';
+ }
+}
+
+/**
+ * @group Tour
+ * @group ChallengeTest
+ * @group Plugins
+ */
+class ChallengeTest extends IntegrationTestCase
+{
+ /**
+ * @var Challenge
+ */
+ private $challenge;
+
+ /**
+ * @var Challenge
+ */
+ private $challenge2;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->challenge = new CustomTestChallenge();
+ $this->challenge2 = new CustomTest2Challenge();
+ }
+
+ public function tearDown()
+ {
+ Challenge::clearCache();
+ parent::tearDown();
+ }
+
+ public function test_skip()
+ {
+ $this->assertFalse($this->challenge->isSkipped());
+ $this->assertFalse($this->challenge2->isSkipped());
+ $this->assertFalse($this->challenge->isCompleted());
+ $this->assertFalse($this->challenge2->isCompleted());
+
+ $this->challenge2->skipChallenge();
+
+ $this->assertFalse($this->challenge->isSkipped());
+ $this->assertTrue($this->challenge2->isSkipped());
+ $this->assertFalse($this->challenge->isCompleted());
+ $this->assertFalse($this->challenge2->isCompleted());
+ }
+
+ public function test_complete()
+ {
+ $this->assertFalse($this->challenge->isSkipped());
+ $this->assertFalse($this->challenge2->isSkipped());
+ $this->assertFalse($this->challenge->isCompleted());
+ $this->assertFalse($this->challenge2->isCompleted());
+
+ $this->challenge->setCompleted();
+
+ $this->assertFalse($this->challenge->isSkipped());
+ $this->assertFalse($this->challenge2->isSkipped());
+ $this->assertTrue($this->challenge->isCompleted());
+ $this->assertFalse($this->challenge2->isCompleted());
+ }
+
+}
diff --git a/plugins/Tour/tests/System/APITest.php b/plugins/Tour/tests/System/APITest.php
new file mode 100644
index 0000000000..0287f417b3
--- /dev/null
+++ b/plugins/Tour/tests/System/APITest.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\tests\System;
+
+use Piwik\API\Request;
+use Piwik\Piwik;
+use Piwik\Plugins\Tour\Engagement\Challenge;
+use Piwik\Plugins\Tour\tests\Fixtures\SimpleFixtureTrackFewVisits;
+use Piwik\Tests\Framework\TestCase\SystemTestCase;
+
+/**
+ * @group Tour
+ * @group APITest
+ * @group Plugins
+ */
+class APITest extends SystemTestCase
+{
+ /**
+ * @var SimpleFixtureTrackFewVisits
+ */
+ public static $fixture = null; // initialized below class definition
+
+ /**
+ * @dataProvider getApiForTesting
+ */
+ public function testApi($api, $params)
+ {
+ $this->runApiTests($api, $params);
+ }
+
+ public function getApiForTesting()
+ {
+ $apiToTest = array();
+
+ $apiToTest[] = array(array('Tour.getLevel', 'Tour.getChallenges'),
+ array(
+ 'idSite' => 1,
+ )
+ );
+
+ return $apiToTest;
+ }
+
+ public function test_skipStep()
+ {
+ $steps = Request::processRequest('Tour.getChallenges');
+
+ $this->assertFalse($steps[1]['isSkipped']);
+
+ Request::processRequest('Tour.skipChallenge', array('id' => $steps[1]['id']));
+
+ Challenge::clearCache();
+
+ $steps = Request::processRequest('Tour.getChallenges');
+ $this->assertTrue($steps[1]['isSkipped']);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Challenge already completed
+ */
+ public function test_skipStep_alreadyCompleted()
+ {
+ Request::processRequest('Tour.skipChallenge', array('id' => 'track_data'));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Challenge not found
+ */
+ public function test_skipStep_invalid()
+ {
+ Request::processRequest('Tour.skipChallenge', array('id' => 'foobarbaz'));
+ }
+
+ public function test_getLevel_WhenNothingCompleted()
+ {
+ Piwik::addAction('API.Tour.getChallenges.end', function (&$challenges) {
+ foreach ($challenges as &$challenge) {
+ $challenge['isSkipped'] = false;
+ $challenge['isCompleted'] = false;
+ }
+ });
+ $this->runApiTests(array('Tour.getLevel'), array(
+ 'testSuffix' => 'nothingCompleted'
+ ));
+ }
+
+ public static function getOutputPrefix()
+ {
+ return '';
+ }
+
+ public static function getPathToTestDirectory()
+ {
+ return dirname(__FILE__);
+ }
+
+}
+
+APITest::$fixture = new SimpleFixtureTrackFewVisits(); \ No newline at end of file
diff --git a/plugins/Tour/tests/System/DataFinderTest.php b/plugins/Tour/tests/System/DataFinderTest.php
new file mode 100644
index 0000000000..4345ada15f
--- /dev/null
+++ b/plugins/Tour/tests/System/DataFinderTest.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\tests\System;
+
+use Piwik\API\Request;
+use Piwik\Plugins\ScheduledReports\ScheduledReports;
+use Piwik\Plugins\Tour\Dao\DataFinder;
+use Piwik\Plugins\Tour\tests\Fixtures\SimpleFixtureTrackFewVisits;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\SystemTestCase;
+
+/**
+ * @group Tour
+ * @group DataFinderTest
+ * @group Plugins
+ */
+class DataFinderTest extends SystemTestCase
+{
+ /**
+ * @var SimpleFixtureTrackFewVisits
+ */
+ public static $fixture = null; // initialized below class definition
+
+ /**
+ * @var DataFinder
+ */
+ private $dataFinder;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->dataFinder = new DataFinder();
+ }
+
+ public function test_hasTracked()
+ {
+ $this->assertTrue($this->dataFinder->hasTrackedData());
+ }
+
+ public function test_hasAddedWebsite()
+ {
+ Fixture::createWebsite('2014-03-04 00:00:00');
+
+ $this->assertFalse($this->dataFinder->hasAddedWebsite('foobar'));
+ $this->assertTrue($this->dataFinder->hasAddedWebsite(Fixture::ADMIN_USER_LOGIN));
+ }
+
+ public function test_hasAddedSegment()
+ {
+ $this->assertFalse($this->dataFinder->hasAddedSegment(Fixture::ADMIN_USER_LOGIN));
+
+ Request::processRequest('SegmentEditor.add', array(
+ 'name' => 'foo', 'definition' => 'visitServerHour==5'
+ ));
+ $this->assertTrue($this->dataFinder->hasAddedSegment(Fixture::ADMIN_USER_LOGIN));
+ }
+
+ public function test_hasAddedOrCustomisedDashboard()
+ {
+ $this->assertFalse($this->dataFinder->hasAddedOrCustomisedDashboard(Fixture::ADMIN_USER_LOGIN));
+
+ Request::processRequest('Dashboard.createNewDashboardForUser', array(
+ 'login' => Fixture::ADMIN_USER_LOGIN, 'dashboardName' => 'foo'
+ ));
+ $this->assertTrue($this->dataFinder->hasAddedOrCustomisedDashboard(Fixture::ADMIN_USER_LOGIN));
+ }
+
+ public function test_hasAddedNewEmailReport()
+ {
+ $this->assertFalse($this->dataFinder->hasAddedNewEmailReport(Fixture::ADMIN_USER_LOGIN));
+
+ Request::processRequest('ScheduledReports.addReport', array(
+ 'idSite' => self::$fixture->idSite, 'description' => 'foo', 'period' => 'week', 'hour' => 5,
+ 'reportType' => 'email', 'reportFormat' => 'html', 'reports' => array('MultiSites_getAll'), 'parameters' => array('emailMe' => true, 'evolutionGraph' => false, 'displayFormat' => ScheduledReports::DISPLAY_FORMAT_GRAPHS_ONLY_FOR_KEY_METRICS)
+ ));
+ $this->assertTrue($this->dataFinder->hasAddedNewEmailReport(Fixture::ADMIN_USER_LOGIN));
+ }
+
+ public static function getOutputPrefix()
+ {
+ return '';
+ }
+
+ public static function getPathToTestDirectory()
+ {
+ return dirname(__FILE__);
+ }
+
+}
+
+DataFinderTest::$fixture = new SimpleFixtureTrackFewVisits(); \ No newline at end of file
diff --git a/plugins/Tour/tests/System/TourTest.php b/plugins/Tour/tests/System/TourTest.php
new file mode 100644
index 0000000000..4163192670
--- /dev/null
+++ b/plugins/Tour/tests/System/TourTest.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Tour\tests\System;
+
+use Piwik\API\Request;
+use Piwik\Container\StaticContainer;
+use Piwik\Date;
+use Piwik\Plugins\Tour\Engagement\ChallengeAddedAnnotation;
+use Piwik\Plugins\Tour\Engagement\ChallengeAddedUser;
+use Piwik\Plugins\Tour\Engagement\ChallengeCreatedGoal;
+use Piwik\Plugins\Tour\tests\Fixtures\SimpleFixtureTrackFewVisits;
+use Piwik\Tests\Framework\TestCase\SystemTestCase;
+
+/**
+ * @group Tour
+ * @group TourTest
+ * @group Plugins
+ */
+class TourTest extends SystemTestCase
+{
+ /**
+ * @var SimpleFixtureTrackFewVisits
+ */
+ public static $fixture = null; // initialized below class definition
+
+ public function setUp()
+ {
+ parent::setUp();
+ }
+
+ public function test_hasCreatedGoal()
+ {
+ $goal = StaticContainer::get(ChallengeCreatedGoal::class);
+
+ $this->assertFalse($goal->isCompleted());
+
+ Request::processRequest('Goals.addGoal', array(
+ 'idSite' => self::$fixture->idSite, 'name' => 'MyGoal', 'matchAttribute' => 'url', 'pattern' => 'foobar', 'patternType' => 'contains'
+ ));
+
+ $this->assertTrue($goal->isCompleted());
+ }
+
+ public function test_hasAddedUser()
+ {
+ $user = StaticContainer::get(ChallengeAddedUser::class);
+ $this->assertFalse($user->isCompleted());
+
+ Request::processRequest('UsersManager.addUser', array('userLogin' => 'myerwerwer', 'password' => '2342k4234234', 'email' => 'tesr@matomo.org'));
+
+ $this->assertTrue($user->isCompleted());
+ }
+
+ public function test_hasAddedAnnotation()
+ {
+ $annotation = StaticContainer::get(ChallengeAddedAnnotation::class);
+ $this->assertFalse($annotation->isCompleted());
+
+ Request::processRequest('Annotations.add', array(
+ 'idSite' => self::$fixture->idSite, 'date' => Date::now()->getDatetime(), 'note' => 'foo bar'));
+
+ $this->assertTrue($annotation->isCompleted());
+ }
+
+ public static function getOutputPrefix()
+ {
+ return '';
+ }
+
+ public static function getPathToTestDirectory()
+ {
+ return dirname(__FILE__);
+ }
+
+}
+
+TourTest::$fixture = new SimpleFixtureTrackFewVisits(); \ No newline at end of file
diff --git a/plugins/Tour/tests/System/expected/test___Tour.getChallenges.xml b/plugins/Tour/tests/System/expected/test___Tour.getChallenges.xml
new file mode 100644
index 0000000000..6c32fe483e
--- /dev/null
+++ b/plugins/Tour/tests/System/expected/test___Tour.getChallenges.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <id>track_data</id>
+ <name>Embed tracking code</name>
+ <description>Matomo offers you various ways to embed the tracking code for your website, mobile app, and even for any device or application.</description>
+ <isCompleted>1</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=CoreAdminHome&amp;action=trackingCodeGenerator</url>
+ </row>
+ <row>
+ <id>define_goal</id>
+ <name>Add a goal</name>
+ <description>Conversion tracking is a great way to identify whether you are meeting your current business objectives, identify new objectives, view and analyse your performance, and learn how to increase your conversions, conversion rates, and revenue per visit.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=Goals&amp;action=manage</url>
+ </row>
+ <row>
+ <id>custom_logo</id>
+ <name>Upload your logo</name>
+ <description>You can customize the Matomo logo which will be displayed in the user interface and email reports.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=CoreAdminHome&amp;action=generalSettings#useCustomLogo</url>
+ </row>
+ <row>
+ <id>add_user</id>
+ <name>Add another user</name>
+ <description>Users Management lets you add new users, edit existing users and give them access to view or administrate websites. </description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=UsersManager&amp;action=index</url>
+ </row>
+ <row>
+ <id>add_website</id>
+ <name>Add another website</name>
+ <description>Websites management lets you add a new website and edit existing websites. </description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=SitesManager&amp;action=index</url>
+ </row>
+ <row>
+ <id>flatten_actions</id>
+ <name>Flatten a page report</name>
+ <description>Go to Behaviour =&gt; Pages and click in the bottom of the report on the cog icon to flatten the report. A flattened report changes the hierarchy from a grouped report to a list.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/piwik-tour/#flattening-reports</url>
+ </row>
+ <row>
+ <id>change_visualisations</id>
+ <name>Change a visualisation</name>
+ <description>View a report and select the visualisation icon at the bottom of a report to view the same report in a different visualisation.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/piwik-tour/#a-look-at-a-piwik-report</url>
+ </row>
+ <row>
+ <id>add_scheduled_report</id>
+ <name>Add a scheduled report</name>
+ <description>Create custom reports and schedule them to be emailed daily, weekly or monthly to one or several people. Several report formats are supported (html, pdf, csv, images).</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=ScheduledReports&amp;action=index</url>
+ </row>
+ <row>
+ <id>customise_dashboard</id>
+ <name>Customise dashboard</name>
+ <description>You can add new widgets to your Dashboard, to ensure it reports on all of your most important metrics.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/piwik-tour/#dashboard-widgets</url>
+ </row>
+ <row>
+ <id>add_segment</id>
+ <name>Add a segment</name>
+ <description>A segment is a set of criteria used to select only a part of the entire set of visits. Using segments you can inject arbitrary context back into your reports.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/segmentation/</url>
+ </row>
+ <row>
+ <id>add_annotation</id>
+ <name>Add an annotation</name>
+ <description>Allows you to attach notes to different days to mark changes made to your website, save analyses you make regarding your data and share your thoughts with your colleagues. By annotating your data, you will make sure you remember why your data looks the way it does.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/annotations/</url>
+ </row>
+ <row>
+ <id>setup_twofa</id>
+ <name>Setup Two-factor authentication</name>
+ <description>Two-factor authentication increase your account security by adding an additional layer of verification when you log in. Each time you log in you will not only be asked to provide your login and password, but also an additional authentication token which changes periodically and is generated for example on your mobile device. This means that even when someone knows your username and password, they still won't be able to log in unless they have access to your mobile device for example.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/faq/general/faq_27245</url>
+ </row>
+ <row>
+ <id>disable_browser_archiving</id>
+ <name>Disable browser archiving for better performance</name>
+ <description />
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/setup-auto-archiving/</url>
+ </row>
+ <row>
+ <id>configure_geolocation</id>
+ <name>Configure geolocation</name>
+ <description>Ensure the location of your visitors is correctly detected.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=UserCountry&amp;action=adminIndex</url>
+ </row>
+ <row>
+ <id>select_date_range</id>
+ <name>Select a date range</name>
+ <description>Select a date range in the calendar.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/piwik-tour/#select-a-date-range</url>
+ </row>
+ <row>
+ <id>view_visits_log</id>
+ <name>View Visits Log</name>
+ <description>The visits Log shows you all the individual visits and actions each visitor took on your site.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/real-time/#visits-log</url>
+ </row>
+ <row>
+ <id>view_visitor_profile</id>
+ <name>View Visitor profile</name>
+ <description>The visitor log helps you to understand your visitors’ individual behavior by summarizing and listing your visitors’ visits.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/user-profile/</url>
+ </row>
+ <row>
+ <id>view_row_evolution</id>
+ <name>View Row evolution</name>
+ <description>Row evolution shows you the current &amp; past metric data for any row in any report.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>https://matomo.org/docs/row-evolution/</url>
+ </row>
+ <row>
+ <id>browse_marketplace</id>
+ <name>Browse Marketplace</name>
+ <description>Extend and expand the functionality of Matomo via the Marketplace by downloading plugins and themes.</description>
+ <isCompleted>0</isCompleted>
+ <isSkipped>0</isSkipped>
+ <url>index.php?module=Marketplace&amp;action=overview</url>
+ </row>
+</result> \ No newline at end of file
diff --git a/plugins/Tour/tests/System/expected/test___Tour.getLevel.xml b/plugins/Tour/tests/System/expected/test___Tour.getLevel.xml
new file mode 100644
index 0000000000..ef46d821fa
--- /dev/null
+++ b/plugins/Tour/tests/System/expected/test___Tour.getLevel.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <description>Welcome to Matomo superUserLogin. This widget will help you become a Matomo expert in no time.</description>
+ <currentLevel>1</currentLevel>
+ <currentLevelName>Matomo Beginner</currentLevelName>
+ <nextLevelName>Matomo Intermediate</nextLevelName>
+ <numLevelsTotal>5</numLevelsTotal>
+ <challengesNeededForNextLevel>4</challengesNeededForNextLevel>
+ </row>
+</result> \ No newline at end of file
diff --git a/plugins/Tour/tests/System/expected/test_nothingCompleted__Tour.getLevel.xml b/plugins/Tour/tests/System/expected/test_nothingCompleted__Tour.getLevel.xml
new file mode 100644
index 0000000000..ef46d821fa
--- /dev/null
+++ b/plugins/Tour/tests/System/expected/test_nothingCompleted__Tour.getLevel.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <description>Welcome to Matomo superUserLogin. This widget will help you become a Matomo expert in no time.</description>
+ <currentLevel>1</currentLevel>
+ <currentLevelName>Matomo Beginner</currentLevelName>
+ <nextLevelName>Matomo Intermediate</nextLevelName>
+ <numLevelsTotal>5</numLevelsTotal>
+ <challengesNeededForNextLevel>4</challengesNeededForNextLevel>
+ </row>
+</result> \ No newline at end of file
diff --git a/plugins/Tour/tests/UI/.gitignore b/plugins/Tour/tests/UI/.gitignore
new file mode 100644
index 0000000000..f39be478e7
--- /dev/null
+++ b/plugins/Tour/tests/UI/.gitignore
@@ -0,0 +1,2 @@
+/processed-ui-screenshots
+/screenshot-diffs \ No newline at end of file
diff --git a/plugins/Tour/tests/UI/Tour_spec.js b/plugins/Tour/tests/UI/Tour_spec.js
new file mode 100644
index 0000000000..c279cb8f6e
--- /dev/null
+++ b/plugins/Tour/tests/UI/Tour_spec.js
@@ -0,0 +1,88 @@
+/**!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+describe("Tour", function () {
+ this.timeout(0);
+
+ this.fixture = "Piwik\\Plugins\\Tour\\tests\\Fixtures\\SimpleFixtureTrackFewVisits";
+
+ var generalParams = 'idSite=1&period=day&date=2010-01-03',
+ widgetizeParams = "module=Widgetize&action=iframe";
+
+ var widgetUrl = "?" + widgetizeParams + "&" + generalParams + "&moduleToWidgetize=Tour&actionToWidgetize=getEngagement";
+
+
+ function setCompleteAllChallenges()
+ {
+ testEnvironment.completeAllChallenges = 1;
+ testEnvironment.save();
+ }
+
+ function setCompleteNoChallenges()
+ {
+ testEnvironment.completeNoChallenge = 1;
+ testEnvironment.save();
+ }
+
+ before(function () {
+ testEnvironment.pluginsToLoad = ['Tour'];
+ testEnvironment.save();
+ });
+
+ afterEach(function () {
+ delete testEnvironment.completeAllChallenges;
+ delete testEnvironment.completeNoChallenge;
+ testEnvironment.save();
+ });
+
+ it('should load widget', function (done) {
+ expect.screenshot('widget_initial').to.be.capture(function (page) {
+ page.load(widgetUrl);
+ }, done);
+ });
+
+ it('should skip goal step', function (done) {
+ expect.screenshot('widget_skipped_goal').to.be.capture(function (page) {
+ page.click('.tourChallenge.define_goal .icon-hide');
+ }, done);
+ });
+
+ it('should mark some challanges as completed', function (done) {
+ expect.screenshot('widget_complete_some_challenges').to.be.capture(function (page) {
+ page.load('?module=Widgetize&action=iframe&disableLink=0&widget=1&moduleToWidgetize=Actions&viewDataTable=table&flat=1&actionToWidgetize=getPageUrls&idSite=1&period=range&date=2018-01-02,2018-01-03&disableLink=1&widget=1&');
+ page.load('?module=Widgetize&action=iframe&forceView=1&viewDataTable=VisitorLog&small=1&disableLink=0&widget=1&moduleToWidgetize=Live&actionToWidgetize=getLastVisitsDetails&idSite=1&period=day&date=yesterday&disableLink=1&widget=1');
+ page.load('?module=Widgetize&action=iframe&disableLink=0&widget=1&moduleToWidgetize=Live&actionToWidgetize=getVisitorProfilePopup&idSite=1&period=day&date=yesterday&disableLink=1&widget=1');
+ page.load(widgetUrl);
+ }, done);
+ });
+
+ it('go to page 2', function (done) {
+ expect.screenshot('widget_complete_some_challenges_page_2').to.be.capture(function (page) {
+ page.click('.nextChallenges');
+ }, done);
+ });
+
+ it('go to page 3', function (done) {
+ expect.screenshot('widget_complete_some_challenges_page_3').to.be.capture(function (page) {
+ page.click('.nextChallenges');
+ }, done);
+ });
+
+ it('should load widget when all completed', function (done) {
+ expect.screenshot('widget_all_completed').to.be.capture(function (page) {
+ setCompleteAllChallenges();
+ page.load(widgetUrl);
+ }, done);
+ });
+
+ it('should load widget when none completed', function (done) {
+ expect.screenshot('widget_none_completed').to.be.capture(function (page) {
+ setCompleteNoChallenges();
+ page.load(widgetUrl);
+ }, done);
+ });
+}); \ No newline at end of file
diff --git a/plugins/Tour/tests/UI/expected-screenshots/.gitkeep b/plugins/Tour/tests/UI/expected-screenshots/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/.gitkeep
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_all_completed.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_all_completed.png
new file mode 100644
index 0000000000..5e890b9d95
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_all_completed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f5c31f92ec13b6fe54fffae548f5c68cb58481e0b08992731d900411bd6028b0
+size 24218
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges.png
new file mode 100644
index 0000000000..f40040dc7a
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f315a984ce4d7df8b838badf1336e13c10de411faa89b80c47fada8585c421c6
+size 42324
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_2.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_2.png
new file mode 100644
index 0000000000..bb7781a37c
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_2.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:412c5a03700014a1d8b67fa9a2b36497ce0c6805579f100cf0cd575a1a0185d1
+size 43612
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_3.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_3.png
new file mode 100644
index 0000000000..9ef1d0f7c0
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_complete_some_challenges_page_3.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:109b2c705dfdda471b31c15251e91ae939a6ec4799e2f8f7c90b0f3791232f77
+size 46790
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_completed.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_completed.png
new file mode 100644
index 0000000000..e40b6fc4a6
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_completed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8fac087844261e4b758cec51dca9f6dd60715bc3fdf3c6a52047b445b6de7630
+size 13143
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_initial.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_initial.png
new file mode 100644
index 0000000000..bbceffa327
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_initial.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b711be6cbe2ff5a6fc100e176088cd8bfe7c8548d432d1dc48fb0793089411ed
+size 43767
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_none_completed.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_none_completed.png
new file mode 100644
index 0000000000..007c7d1d45
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_none_completed.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b1a9f7f97f6ee3b8ca9c5ec8183f77b984cbe3687f2a7a77950ac1c5721914a0
+size 43870
diff --git a/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_skipped_goal.png b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_skipped_goal.png
new file mode 100644
index 0000000000..71034b28e7
--- /dev/null
+++ b/plugins/Tour/tests/UI/expected-screenshots/Tour_widget_skipped_goal.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:134971197018ea22394e692624f12b7666bb93e5d203cca9491e9273c4fa3263
+size 43692