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:
authorZoltan Flamis <flamisz@gmail.com>2021-03-17 23:52:07 +0300
committerGitHub <noreply@github.com>2021-03-17 23:52:07 +0300
commitdabc1caf64b16f40298bd6b7c43cf93cd0071b07 (patch)
tree9d1e845f697633a095f2aa76b86b2ac4c02a3424
parent7527bce11d1b3f9a5ace08eb26e5379f7702cf7c (diff)
Ask users to refer us to other friends (#17234)
* skeleton for the refer banner * refer banner wip * share buttons * show refer banner logic * fix linkedin share, hide mastodon * fix double call for remind me later and error on response * add tests for refer banner * fix travis tests * fix travis tests * add thanks feedback and language files * hide fb share * turn on fb share * update refer banner copies * add changes according to pr comments * normalize lfs Co-authored-by: diosmosis <diosmosis@users.noreply.github.com>
-rw-r--r--plugins/Feedback/Controller.php39
-rw-r--r--plugins/Feedback/Feedback.php94
-rw-r--r--plugins/Feedback/FeedbackReminder.php34
-rw-r--r--plugins/Feedback/ReferReminder.php34
-rw-r--r--plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html8
-rw-r--r--plugins/Feedback/angularjs/refer-banner/refer-banner.controller.js99
-rw-r--r--plugins/Feedback/angularjs/refer-banner/refer-banner.directive.html45
-rw-r--r--plugins/Feedback/angularjs/refer-banner/refer-banner.directive.js28
-rw-r--r--plugins/Feedback/angularjs/refer-banner/refer-banner.directive.less69
-rw-r--r--plugins/Feedback/images/facebook_logo.svg1
-rw-r--r--plugins/Feedback/images/linkedin_logo.svg1
-rw-r--r--plugins/Feedback/images/mastodon_logo.svg1
-rw-r--r--plugins/Feedback/images/twitter_logo.svg1
-rw-r--r--plugins/Feedback/lang/en.json9
-rw-r--r--plugins/Feedback/templates/referBanner.twig1
-rw-r--r--plugins/Feedback/tests/Fixtures/ReferBannerFixture.php25
-rw-r--r--plugins/Feedback/tests/Integration/ControllerTest.php29
-rw-r--r--plugins/Feedback/tests/Integration/ReferBannerTest.php116
-rw-r--r--plugins/Feedback/tests/UI/ReferBanner_spec.js30
-rw-r--r--plugins/Feedback/tests/UI/expected-screenshots/ReferBannerTest_feedback_popup.png3
20 files changed, 639 insertions, 28 deletions
diff --git a/plugins/Feedback/Controller.php b/plugins/Feedback/Controller.php
index 73fa5e346a..5fa8a94023 100644
--- a/plugins/Feedback/Controller.php
+++ b/plugins/Feedback/Controller.php
@@ -8,17 +8,16 @@
*/
namespace Piwik\Plugins\Feedback;
-use Piwik\Common;
-use Piwik\Container\StaticContainer;
use Piwik\Date;
-use Piwik\Option;
+use Piwik\View;
use Piwik\Piwik;
+use Piwik\Common;
use Piwik\Version;
-use Piwik\View;
+use Piwik\Container\StaticContainer;
+use Piwik\Plugins\Feedback\ReferReminder;
+use Piwik\Plugins\Feedback\FeedbackReminder;
+use Piwik\DataTable\Renderer\Json;
-/**
- *
- */
class Controller extends \Piwik\Plugin\Controller
{
function index()
@@ -37,12 +36,34 @@ class Controller extends \Piwik\Plugin\Controller
public function updateFeedbackReminderDate()
{
Piwik::checkUserIsNotAnonymous();
+
$nextReminder = Common::getRequestVar('nextReminder');
+
if ($nextReminder !== Feedback::NEVER_REMIND_ME_AGAIN) {
$nextReminder = Date::now()->getStartOfDay()->addDay($nextReminder)->toString('Y-m-d');
}
- $optionKey = 'Feedback.nextFeedbackReminder.' . Piwik::getCurrentUserLogin();
- Option::set($optionKey, $nextReminder);
+ $feedbackReminder = new FeedbackReminder();
+ $feedbackReminder->setUserOption($nextReminder);
+
+ Json::sendHeaderJSON();
+ return json_encode(['Next reminder date: ' . $nextReminder]);
+ }
+
+ public function updateReferReminderDate()
+ {
+ Piwik::checkUserIsNotAnonymous();
+
+ $nextReminder = Common::getRequestVar('nextReminder');
+
+ if ($nextReminder !== Feedback::NEVER_REMIND_ME_AGAIN) {
+ $nextReminder = Date::now()->getStartOfDay()->addDay($nextReminder)->toString('Y-m-d');
+ }
+
+ $referReminder = new ReferReminder();
+ $referReminder->setUserOption($nextReminder);
+
+ Json::sendHeaderJSON();
+ return json_encode(['Next reminder date: ' . $nextReminder]);
}
}
diff --git a/plugins/Feedback/Feedback.php b/plugins/Feedback/Feedback.php
index 2cad4d77de..dbbd2dd7ad 100644
--- a/plugins/Feedback/Feedback.php
+++ b/plugins/Feedback/Feedback.php
@@ -8,12 +8,12 @@
*/
namespace Piwik\Plugins\Feedback;
-use Piwik\Common;
use Piwik\Date;
-use Piwik\Option;
+use Piwik\View;
use Piwik\Piwik;
+use Piwik\Common;
use Piwik\Plugins\UsersManager\Model;
-use Piwik\View;
+use Piwik\Plugins\Feedback\FeedbackReminder;
/**
*
@@ -31,7 +31,7 @@ class Feedback extends \Piwik\Plugin
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
- 'Controller.CoreHome.index.end' => 'renderFeedbackPopup'
+ 'Controller.CoreHome.index.end' => 'renderViewsAndAddToPage'
);
}
@@ -40,6 +40,7 @@ class Feedback extends \Piwik\Plugin
$stylesheets[] = "plugins/Feedback/stylesheets/feedback.less";
$stylesheets[] = "plugins/Feedback/angularjs/ratefeature/ratefeature.directive.less";
$stylesheets[] = "plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.less";
+ $stylesheets[] = "plugins/Feedback/angularjs/refer-banner/refer-banner.directive.less";
}
public function getJsFiles(&$jsFiles)
@@ -49,11 +50,14 @@ class Feedback extends \Piwik\Plugin
$jsFiles[] = "plugins/Feedback/angularjs/ratefeature/ratefeature.directive.js";
$jsFiles[] = "plugins/Feedback/angularjs/feedback-popup/feedback-popup.controller.js";
$jsFiles[] = "plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.js";
+ $jsFiles[] = "plugins/Feedback/angularjs/refer-banner/refer-banner.directive.js";
+ $jsFiles[] = "plugins/Feedback/angularjs/refer-banner/refer-banner.controller.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
{
$translationKeys[] = 'Feedback_ThankYou';
+ $translationKeys[] = 'Feedback_ThankYouForSpreading';
$translationKeys[] = 'Feedback_RateFeatureTitle';
$translationKeys[] = 'Feedback_RateFeatureThankYouTitle';
$translationKeys[] = 'Feedback_RateFeatureLeaveMessageLike';
@@ -64,17 +68,84 @@ class Feedback extends \Piwik\Plugin
$translationKeys[] = 'Feedback_PleaseLeaveExternalReviewForMatomo';
$translationKeys[] = 'Feedback_RemindMeLater';
$translationKeys[] = 'Feedback_NeverAskMeAgain';
+ $translationKeys[] = 'Feedback_ReferMatomo';
+ $translationKeys[] = 'Feedback_ReferBannerTitle';
+ $translationKeys[] = 'Feedback_ReferBannerLonger';
+ $translationKeys[] = 'Feedback_ReferBannerSocialShareText';
+ $translationKeys[] = 'Feedback_ReferBannerEmailShareSubject';
+ $translationKeys[] = 'Feedback_ReferBannerEmailShareBody';
$translationKeys[] = 'General_Ok';
$translationKeys[] = 'General_Cancel';
}
- public function renderFeedbackPopup(&$pageHtml)
+ public function renderViewsAndAddToPage(&$pageHtml)
+ {
+ $feedbackPopopView = $this->renderFeedbackPopup();
+ $referBannerView = $this->renderReferBanner();
+
+ $views = [$feedbackPopopView, $referBannerView];
+ $implodedViews = implode('', $views);
+
+ $endOfBody = strpos($pageHtml, '</body>');
+ $pageHtml = substr_replace($pageHtml, $implodedViews, $endOfBody, 0);
+ }
+
+ public function renderFeedbackPopup()
{
$popupView = new View('@Feedback/feedbackPopup');
$popupView->promptForFeedback = (int)$this->getShouldPromptForFeedback();
- $popupHtml = $popupView->render();
- $endOfBody = strpos($pageHtml, "</body>");
- $pageHtml = substr_replace($pageHtml, $popupHtml, $endOfBody, 0);
+
+ return $popupView->render();
+ }
+
+ public function renderReferBanner()
+ {
+ $referBannerView = new View('@Feedback/referBanner');
+ $referBannerView->showReferBanner = (int) $this->showReferBanner();
+
+ return $referBannerView->render();
+ }
+
+ public function showReferBanner()
+ {
+ if (Piwik::isUserIsAnonymous()) {
+ return false;
+ }
+
+ if (!Piwik::hasUserSuperUserAccess()) {
+ return false;
+ }
+
+ if ($this->isDisabledInTestMode()) {
+ return false;
+ }
+
+ $shouldShowReferBanner = true;
+
+ /**
+ * @internal
+ */
+ Piwik::postEvent('Feedback.showReferBanner', [&$shouldShowReferBanner]);
+
+ if (!$shouldShowReferBanner) {
+ return false;
+ }
+
+ $referReminder = new ReferReminder();
+ $nextReminderDate = $referReminder->getUserOption();
+
+ if ($nextReminderDate === false) {
+ return true;
+ }
+
+ if ($nextReminderDate === self::NEVER_REMIND_ME_AGAIN) {
+ return false;
+ }
+
+ $now = Date::now()->getTimestamp();
+ $nextReminderDate = Date::factory($nextReminderDate);
+
+ return $nextReminderDate->getTimestamp() <= $now;
}
public function getShouldPromptForFeedback()
@@ -88,9 +159,8 @@ class Feedback extends \Piwik\Plugin
return false;
}
- $login = Piwik::getCurrentUserLogin();
- $feedbackReminderKey = 'Feedback.nextFeedbackReminder.' . Piwik::getCurrentUserLogin();
- $nextReminderDate = Option::get($feedbackReminderKey);
+ $feedbackReminder = new FeedbackReminder();
+ $nextReminderDate = $feedbackReminder->getUserOption();
if ($nextReminderDate === self::NEVER_REMIND_ME_AGAIN) {
return false;
@@ -98,7 +168,7 @@ class Feedback extends \Piwik\Plugin
if ($nextReminderDate === false) {
$model = new Model();
- $user = $model->getUser($login);
+ $user = $model->getUser(Piwik::getCurrentUserLogin());
if (empty($user['date_registered'])) {
return false;
}
diff --git a/plugins/Feedback/FeedbackReminder.php b/plugins/Feedback/FeedbackReminder.php
new file mode 100644
index 0000000000..64b8827496
--- /dev/null
+++ b/plugins/Feedback/FeedbackReminder.php
@@ -0,0 +1,34 @@
+<?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\Feedback;
+
+use Piwik\Piwik;
+use Piwik\Option;
+
+class FeedbackReminder
+{
+ public $userLogin;
+ public $option;
+
+ public function __construct()
+ {
+ $this->userLogin = Piwik::getCurrentUserLogin();
+ $this->option = 'Feedback.nextFeedbackReminder';
+ }
+
+ public function getUserOption()
+ {
+ return Option::get("{$this->option}.{$this->userLogin}");
+ }
+
+ public function setUserOption($value)
+ {
+ Option::set("{$this->option}.{$this->userLogin}", $value);
+ }
+}
diff --git a/plugins/Feedback/ReferReminder.php b/plugins/Feedback/ReferReminder.php
new file mode 100644
index 0000000000..b6cfa46c3b
--- /dev/null
+++ b/plugins/Feedback/ReferReminder.php
@@ -0,0 +1,34 @@
+<?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\Feedback;
+
+use Piwik\Piwik;
+use Piwik\Option;
+
+class ReferReminder
+{
+ public $userLogin;
+ public $option;
+
+ public function __construct()
+ {
+ $this->userLogin = Piwik::getCurrentUserLogin();
+ $this->option = 'Feedback.nextReferReminder';
+ }
+
+ public function getUserOption()
+ {
+ return Option::get("{$this->option}.{$this->userLogin}");
+ }
+
+ public function setUserOption($value)
+ {
+ Option::set("{$this->option}.{$this->userLogin}", $value);
+ }
+}
diff --git a/plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html b/plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html
index 28be4b3f3b..ef48a83efe 100644
--- a/plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html
+++ b/plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html
@@ -1,9 +1,9 @@
<div ng-controller="FeedbackPopupController">
- <div class="feedbackPopup ui-confirm"
- piwik-dialog="feedbackPopup.dialog.show"
- yes="feedbackPopup.remindMeLater()"
+ <div class="feedbackPopup ui-confirm"
+ piwik-dialog="feedbackPopup.dialog.show"
+ yes="feedbackPopup.remindMeLater()"
no="feedbackPopup.dontShowAgain()"
- close="feedbackPopup.remindMeLater()"
+ close=""
>
<div class="intro">
diff --git a/plugins/Feedback/angularjs/refer-banner/refer-banner.controller.js b/plugins/Feedback/angularjs/refer-banner/refer-banner.controller.js
new file mode 100644
index 0000000000..fb18a6b7ff
--- /dev/null
+++ b/plugins/Feedback/angularjs/refer-banner/refer-banner.controller.js
@@ -0,0 +1,99 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link http://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ReferBannerController', ReferBannerController);
+
+ ReferBannerController.$inject = ['$scope'];
+
+ function ReferBannerController($scope) {
+ var setNextReminder = function(nextReminder) {
+ var ajaxHandler = new ajaxHelper();
+ ajaxHandler.addParams({'module': 'Feedback', 'action': 'updateReferReminderDate'}, 'GET');
+ ajaxHandler.addParams({'nextReminder': nextReminder}, 'POST');
+ ajaxHandler.send();
+ };
+
+ var closeBanner = function() {
+ $scope.referBanner.show = false;
+
+ if ($scope.referBanner.shared === false) {
+ setNextReminder(6 * 30);
+ }
+ };
+
+ var share = function() {
+ $scope.referBanner.showThanks = true;
+ $scope.referBanner.shared = true;
+
+ setNextReminder(-1);
+ }
+
+ $scope.socialUrl = function (type) {
+ var text = _pk_translate('Feedback_ReferBannerSocialShareText');
+ var url = 'https://matomo.org/google-owns-your-data/?pk_campaign=share&pk_kwd=onpremise';
+
+ if (type === 'twitter') {
+ var base = 'https://twitter.com/intent/tweet?';
+
+ var params = { 'text': text, 'url': url};
+ var paramString = '';
+ for (const param in params) {
+ paramString += param + '=' + encodeURIComponent(params[param]) + '&';
+ }
+
+ return base + paramString.slice(0, -1);
+ }
+
+ if (type === 'facebook') {
+ var base = 'https://www.facebook.com/sharer.php?';
+
+ var params = { 't': text, 'u': url};
+ var paramString = '';
+ for (const param in params) {
+ paramString += param + '=' + encodeURIComponent(params[param]) + '&';
+ }
+
+ return base + paramString.slice(0, -1);
+ }
+
+ if (type === 'linkedin') {
+ var base = 'https://www.linkedin.com/sharing/share-offsite/?';
+
+ var params = { 'url': url };
+ var paramString = '';
+ for (const param in params) {
+ paramString += param + '=' + encodeURIComponent(params[param]) + '&';
+ }
+
+ return base + paramString.slice(0, -1);
+ }
+
+ return '#';
+ };
+
+ $scope.referEmail = function () {
+ var subject = _pk_translate('Feedback_ReferBannerEmailShareSubject');
+ var body = _pk_translate('Feedback_ReferBannerEmailShareBody');
+
+ return encodeURI('mailto:YOUR_FRIEND@EMAIL.ADDRESS?subject=' + subject + '&body=' + body);
+ }
+
+ var init = function() {
+ $scope.referBanner.show = false;
+ $scope.referBanner.showThanks = false;
+ $scope.referBanner.closeBanner = closeBanner;
+ $scope.referBanner.share = share;
+ $scope.referBanner.shared = false;
+
+ if ($scope.showReferBanner === 1) {
+ $scope.referBanner.show = true;
+ };
+ };
+
+ init();
+ }
+})();
diff --git a/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.html b/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.html
new file mode 100644
index 0000000000..d3ce1e862a
--- /dev/null
+++ b/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.html
@@ -0,0 +1,45 @@
+<div ng-controller="ReferBannerController">
+ <div class="refer-banner" ng-show="referBanner.show">
+ <div class="flex items-center justify-center h-full" id="default-text">
+ <div class="flex-none"></div>
+ <div class="flex items-center justify-center flex-grow">
+ <div>
+ {{ 'Feedback_ReferBannerTitle'|translate }}
+ <span class="hide-on-med-and-down">{{ 'Feedback_ReferBannerLonger'|translate }}</span>
+ </div>
+
+ <div>
+ <a ng-href="{{ referEmail() }}" ng-click="referBanner.share()" target="_blank" class="btn premiumDownloadPiwik" rel="noreferrer noopener" id="send-email" style="width: max-content;">
+ {{ 'Feedback_ReferMatomo'|translate }}
+ </a>
+ </div>
+
+ <div class="hide-on-med-and-down flex items-center">
+ <div>or share us on</div>
+ <div class="flex items-center">
+ <a ng-href="{{ socialUrl('twitter') }}" ng-click="referBanner.share()" class="share-button" target="_blank" rel="noreferrer noopener">
+ <ng-include src="'plugins/Feedback/images/twitter_logo.svg'" />
+ </a>
+ <a ng-href="{{ socialUrl('facebook') }}" ng-click="referBanner.share()" class="share-button" target="_blank" rel="noreferrer noopener">
+ <ng-include src="'plugins/Feedback/images/facebook_logo.svg'" />
+ </a>
+ <a ng-href="{{ socialUrl('linkedin') }}" ng-click="referBanner.share()" class="share-button" target="_blank" rel="noreferrer noopener">
+ <ng-include src="'plugins/Feedback/images/linkedin_logo.svg'" />
+ </a>
+ <a ng-show="{{ false }}" href="{{ socialUrl('mastodon') }}" ng-click="referBanner.share()" class="share-button" target="_blank" rel="noreferrer noopener">
+ <ng-include src="'plugins/Feedback/images/mastodon_logo.svg'" />
+ </a>
+ </div>
+ </div>
+ </div>
+
+ <div title="Close" class="icon-close flex-none" ng-click="referBanner.closeBanner()"></div>
+ </div>
+ </div>
+
+ <div class="ui-confirm ratefeatureDialog" piwik-dialog="referBanner.showThanks" yes="">
+ <h2>{{ 'Feedback_ThankYouForSpreading'|translate }}</h2>
+
+ <input type="button" value="{{ 'General_Ok'|translate }}" role="yes"/>
+ </div>
+</div>
diff --git a/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.js b/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.js
new file mode 100644
index 0000000000..4c14a49e3f
--- /dev/null
+++ b/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.js
@@ -0,0 +1,28 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link http://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-refer-banner>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikReferBanner', piwikReferBanner);
+
+ piwikReferBanner.$inject = ['piwik'];
+
+ function piwikReferBanner(piwik){
+ return {
+ restrict: 'A',
+ scope: {
+ showReferBanner: '<'
+ },
+ templateUrl: 'plugins/Feedback/angularjs/refer-banner/refer-banner.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'ReferBannerController',
+ controllerAs: 'referBanner',
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.less b/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.less
new file mode 100644
index 0000000000..829348231d
--- /dev/null
+++ b/plugins/Feedback/angularjs/refer-banner/refer-banner.directive.less
@@ -0,0 +1,69 @@
+@media only screen and (max-width: 600px) {
+ .refer-banner {
+ min-height: 48px;
+ height: auto !important;
+ font-size: 13px;
+ }
+}
+
+.flex {
+ display: flex;
+}
+
+.flex-grow {
+ flex-grow: 1;
+}
+
+.flex-none {
+ flex: none;
+}
+
+.items-center {
+ align-items: center;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.h-full {
+ height: 100%;
+}
+
+.refer-banner {
+ height: 48px;
+ position: fixed;
+ width: 100%;
+ text-align: center;
+ color: #fff;
+ bottom: 0;
+ background: #263238;
+ clear: both;
+ z-index: 9999;
+}
+
+.refer-banner .icon-close {
+ background: #263238;
+ font-size: 12px;
+ color: #fff;
+ cursor: pointer;
+ width: 18px;
+ margin-right: 4px;
+}
+
+.refer-banner .piwikOrgLink {
+ color: #fff;
+ text-decoration: underline;
+}
+
+.refer-banner .share-button {
+ color:#fff;
+ display:inline-block;
+ width:20px;
+ margin-left: 4px;
+}
+
+.refer-banner .signUpPiwik,
+.refer-banner .premiumDownloadPiwik {
+ margin: 5px;
+}
diff --git a/plugins/Feedback/images/facebook_logo.svg b/plugins/Feedback/images/facebook_logo.svg
new file mode 100644
index 0000000000..54a49626e0
--- /dev/null
+++ b/plugins/Feedback/images/facebook_logo.svg
@@ -0,0 +1 @@
+<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg> \ No newline at end of file
diff --git a/plugins/Feedback/images/linkedin_logo.svg b/plugins/Feedback/images/linkedin_logo.svg
new file mode 100644
index 0000000000..6a5826451d
--- /dev/null
+++ b/plugins/Feedback/images/linkedin_logo.svg
@@ -0,0 +1 @@
+<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg> \ No newline at end of file
diff --git a/plugins/Feedback/images/mastodon_logo.svg b/plugins/Feedback/images/mastodon_logo.svg
new file mode 100644
index 0000000000..e19e492758
--- /dev/null
+++ b/plugins/Feedback/images/mastodon_logo.svg
@@ -0,0 +1 @@
+<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M23.193 7.88c0-5.207-3.411-6.733-3.411-6.733C18.062.357 15.108.025 12.041 0h-.076c-3.069.025-6.02.357-7.74 1.147 0 0-3.412 1.526-3.412 6.732 0 1.193-.023 2.619.015 4.13.124 5.092.934 10.11 5.641 11.355 2.17.574 4.034.695 5.536.612 2.722-.15 4.25-.972 4.25-.972l-.09-1.975s-1.945.613-4.13.54c-2.165-.075-4.449-.234-4.799-2.892a5.5 5.5 0 0 1-.048-.745s2.125.52 4.818.643c1.646.075 3.19-.097 4.758-.283 3.007-.359 5.625-2.212 5.954-3.905.517-2.665.475-6.508.475-6.508zm-4.024 6.709h-2.497v-6.12c0-1.29-.543-1.944-1.628-1.944-1.2 0-1.802.776-1.802 2.313v3.349h-2.484v-3.35c0-1.537-.602-2.313-1.802-2.313-1.085 0-1.628.655-1.628 1.945v6.119H4.831V8.285c0-1.29.328-2.314.987-3.07.68-.759 1.57-1.147 2.674-1.147 1.278 0 2.246.491 2.886 1.474L12 6.585l.622-1.043c.64-.983 1.608-1.474 2.886-1.474 1.104 0 1.994.388 2.674 1.146.658.757.986 1.781.986 3.07v6.305z"/></svg> \ No newline at end of file
diff --git a/plugins/Feedback/images/twitter_logo.svg b/plugins/Feedback/images/twitter_logo.svg
new file mode 100644
index 0000000000..49a98867fa
--- /dev/null
+++ b/plugins/Feedback/images/twitter_logo.svg
@@ -0,0 +1 @@
+<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg> \ No newline at end of file
diff --git a/plugins/Feedback/lang/en.json b/plugins/Feedback/lang/en.json
index 9c7182a8c4..de60599b62 100644
--- a/plugins/Feedback/lang/en.json
+++ b/plugins/Feedback/lang/en.json
@@ -14,6 +14,7 @@
"RateFeatureTitle": "Do you like the '%s' feature? Please rate and leave a comment",
"SendFeedback": "Send Feedback",
"ThankYou": "Thank you for helping us to make Matomo better!",
+ "ThankYouForSpreading": "Thank you for spreading the word and creating a safer web",
"TopLinkTooltip": "Tell us what you think, or request Professional Support.",
"ViewAnswersToFAQ": "View answers to %1$sFrequently Asked Questions%2$s",
"FAQs": "FAQs",
@@ -41,6 +42,12 @@
"RemoveOtherLabel": "I want to see my 'Others' data",
"PurgeOldData": "I want to remove some old data",
"SearchHelpResources": "Search matomo.org help resources",
- "PopularHelpTopics": "Popular help topics"
+ "PopularHelpTopics": "Popular help topics",
+ "ReferMatomo": "Refer Matomo",
+ "ReferBannerTitle": "Don't let your friend's data end up in the wrong hands!",
+ "ReferBannerLonger": "Refer them to Matomo Analytics now to take back control!",
+ "ReferBannerEmailShareSubject": "Refer them to Matomo Analytics now to take back control!",
+ "ReferBannerEmailShareBody": "I choose Matomo, an ethical alternative to Google Analytics that gives me 100% data ownership and protects the data of my website visitors.\r\nI’m sharing this message in the hope that you too will take back the power from Google and get complete ownership of your own data.\r\n\r\nCheck out Matomo at https://matomo.org",
+ "ReferBannerSocialShareText": "If you’re using Google Analytics, stay in control by switching to an ethical alternative like Matomo now!"
}
}
diff --git a/plugins/Feedback/templates/referBanner.twig b/plugins/Feedback/templates/referBanner.twig
new file mode 100644
index 0000000000..628d56c45c
--- /dev/null
+++ b/plugins/Feedback/templates/referBanner.twig
@@ -0,0 +1 @@
+<div piwik-refer-banner show-refer-banner="{{ showReferBanner }}"></div>
diff --git a/plugins/Feedback/tests/Fixtures/ReferBannerFixture.php b/plugins/Feedback/tests/Fixtures/ReferBannerFixture.php
new file mode 100644
index 0000000000..7e461bdb48
--- /dev/null
+++ b/plugins/Feedback/tests/Fixtures/ReferBannerFixture.php
@@ -0,0 +1,25 @@
+<?php
+
+
+namespace Piwik\Plugins\Feedback\tests\Fixtures;
+
+use Piwik\Date;
+use Piwik\Option;
+use Piwik\Tests\Fixtures\UITestFixture;
+
+class ReferBannerFixture extends UITestFixture
+{
+ public function setUp(): void
+ {
+ parent::setUp();
+ $yesterday = Date::yesterday();
+ Option::set('Feedback.nextReferReminder.superUserLogin', $yesterday->toString('Y-m-d'));
+ }
+
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ Option::delete('Feedback.nextReferReminder.superUserLogin');
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Feedback/tests/Integration/ControllerTest.php b/plugins/Feedback/tests/Integration/ControllerTest.php
index 533dc533f5..a1699cf2bd 100644
--- a/plugins/Feedback/tests/Integration/ControllerTest.php
+++ b/plugins/Feedback/tests/Integration/ControllerTest.php
@@ -62,7 +62,6 @@ class ControllerTest extends IntegrationTestCase
);
}
-
public function test_updateFeedbackReminder_addNinetyDays()
{
$_POST['nextReminder'] = '90';
@@ -88,4 +87,30 @@ class ControllerTest extends IntegrationTestCase
FakeAccess::$superUser = false;
$this->controller->updateFeedbackReminderDate();
}
-} \ No newline at end of file
+
+ public function test_updateReferReminder_add180Days()
+ {
+ $_POST['nextReminder'] = '180';
+ $this->controller->updateReferReminderDate();
+
+ $option = Option::get('Feedback.nextReferReminder.user1');
+ $this->assertEquals($option, '2019-11-27');
+ }
+
+ public function test_updateReferReminder_neverAgain()
+ {
+ $_POST['nextReminder'] = '-1';
+ $this->controller->updateReferReminderDate();
+
+ $option = Option::get('Feedback.nextReferReminder.user1');
+ $this->assertEquals($option, '-1');
+ }
+
+ public function test_updateReferReminder_notLoggedIn()
+ {
+ $this->expectException(NoAccessException::class);
+ FakeAccess::$identity = null;
+ FakeAccess::$superUser = false;
+ $this->controller->updateReferReminderDate();
+ }
+}
diff --git a/plugins/Feedback/tests/Integration/ReferBannerTest.php b/plugins/Feedback/tests/Integration/ReferBannerTest.php
new file mode 100644
index 0000000000..b7b86aeae3
--- /dev/null
+++ b/plugins/Feedback/tests/Integration/ReferBannerTest.php
@@ -0,0 +1,116 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link http://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\Feedback\tests\Unit;
+
+
+use Piwik\Date;
+use Piwik\Option;
+use Piwik\Plugins\Feedback\Feedback;
+use Piwik\Plugins\UsersManager\Model;
+use Piwik\Tests\Framework\Mock\FakeAccess;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+
+class ReferBannerTest extends IntegrationTestCase
+{
+ /** @var Feedback */
+ private $feedback;
+
+ /** @var Model */
+ private $userModel;
+
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->feedback = $this->createPartialMock(Feedback::class, ['isDisabledInTestMode']);
+ $this->feedback->method('isDisabledInTestMode')->willReturn(false);
+
+ $this->userModel = new Model();
+ $this->userModel->addUser(
+ 'user1',
+ 'a98732d98732',
+ 'user1@example.com',
+ '2019-03-03',
+ 'super'
+ );
+ FakeAccess::$identity = 'user1';
+ FakeAccess::$superUser = false;
+ }
+
+ public function tearDown(): void
+ {
+ Option::deleteLike('Feedback.nextReferReminder.%');
+ $this->userModel->deleteUserOnly('user1');
+
+ parent::tearDown();
+ }
+
+ public function provideContainerConfig()
+ {
+ return array(
+ 'Piwik\Access' => new FakeAccess()
+ );
+ }
+
+
+ public function test_shouldNotShowReferBannerTo_AnonymousUser()
+ {
+ FakeAccess::$identity = '';
+
+ $this->assertFalse($this->feedback->showReferBanner());
+ }
+
+ public function test_shouldNotShowReferBannerTo_NotSuperUser()
+ {
+ FakeAccess::$identity = 'user1';
+
+ $this->assertFalse($this->feedback->showReferBanner());
+ }
+
+ public function test_shouldShowReferBannerTo_SuperUser()
+ {
+ FakeAccess::$identity = 'super';
+ FakeAccess::$superUser = true;
+
+ $this->assertTrue($this->feedback->showReferBanner());
+ }
+
+ public function test_shouldNotShowReferBanner_ifNeverRemindOn()
+ {
+ FakeAccess::$identity = 'super';
+ FakeAccess::$superUser = true;
+ Option::set('Feedback.nextReferReminder.super', '-1');
+
+ $this->assertFalse($this->feedback->showReferBanner());
+ }
+
+ public function test_shouldNotShowReferBanner_ifNextReminderDateInTheFuture()
+ {
+ FakeAccess::$identity = 'super';
+ FakeAccess::$superUser = true;
+
+ Date::$now = strtotime('2021-01-01');
+ $futureDate = Date::factory('2021-02-01')->toString('Y-m-d');
+ Option::set('Feedback.nextReferReminder.super', $futureDate);
+
+ $this->assertFalse($this->feedback->showReferBanner());
+ }
+
+ public function test_shouldShowReferBanner_ifNextReminderDateInThePast()
+ {
+ FakeAccess::$identity = 'super';
+ FakeAccess::$superUser = true;
+
+ Date::$now = strtotime('2021-01-01');
+ $pastDate = Date::factory('2020-01-01')->toString('Y-m-d');
+ Option::set('Feedback.nextReferReminder.super', $pastDate);
+
+ $this->assertTrue($this->feedback->showReferBanner());
+ }
+}
diff --git a/plugins/Feedback/tests/UI/ReferBanner_spec.js b/plugins/Feedback/tests/UI/ReferBanner_spec.js
new file mode 100644
index 0000000000..7b0364f111
--- /dev/null
+++ b/plugins/Feedback/tests/UI/ReferBanner_spec.js
@@ -0,0 +1,30 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * UsersManager screenshot tests.
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+describe("ReferBannerTest", function () {
+ this.timeout(0);
+ this.fixture = "Piwik\\Plugins\\Feedback\\tests\\Fixtures\\ReferBannerFixture";
+
+ var url = "?module=CoreHome&action=index&idSite=1&period=day&date=2019-07-11&forceFeedbackTest=1";
+
+ before(async function() {
+ await page.webpage.setViewport({
+ width: 1250,
+ height: 768
+ });
+ });
+
+ it('should display popup when next reminder date is in past', async function () {
+ await page.goto(url);
+ await page.waitForNetworkIdle();
+
+ var banner = await page.waitFor('.refer-banner', { visible: true });
+ expect(await banner.screenshot()).to.matchImage('feedback_popup');
+ });
+});
diff --git a/plugins/Feedback/tests/UI/expected-screenshots/ReferBannerTest_feedback_popup.png b/plugins/Feedback/tests/UI/expected-screenshots/ReferBannerTest_feedback_popup.png
new file mode 100644
index 0000000000..b33ed5d4c8
--- /dev/null
+++ b/plugins/Feedback/tests/UI/expected-screenshots/ReferBannerTest_feedback_popup.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de4ce7f20f82c2c70a2ff0587ec3e14f5d93869e12f9cd4a6ac057d9747c0bc2
+size 14151