From a555bec5f6b157db385d423cad9cd1ef910e6730 Mon Sep 17 00:00:00 2001 From: diosmosis <benaka@piwik.pro> Date: Wed, 1 Oct 2014 19:05:48 -0700 Subject: [PATCH] Create angularjs directive for Piwik notifications and use in old Notificaction class. --- plugins/CoreHome/CoreHome.php | 4 + .../notification/notification.controller.js | 34 +++++ .../notification/notification.directive.html | 8 ++ .../notification/notification.directive.js | 95 ++++++++++++ .../notification/notification.directive.less | 84 +++++++++++ plugins/CoreHome/javascripts/notification.js | 135 ++++-------------- .../CoreHome/stylesheets/notification.less | 87 +---------- .../ExampleUI/templates/notifications.twig | 7 +- 8 files changed, 256 insertions(+), 198 deletions(-) create mode 100644 plugins/CoreHome/angularjs/notification/notification.controller.js create mode 100644 plugins/CoreHome/angularjs/notification/notification.directive.html create mode 100644 plugins/CoreHome/angularjs/notification/notification.directive.js create mode 100644 plugins/CoreHome/angularjs/notification/notification.directive.less diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index 187f356c5e..f833b1e5cc 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -69,6 +69,7 @@ class CoreHome extends \Piwik\Plugin $stylesheets[] = "plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less"; $stylesheets[] = "plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less"; $stylesheets[] = "plugins/CoreHome/angularjs/dialogtoggler/ngdialog.less"; + $stylesheets[] = "plugins/CoreHome/angularjs/notification/notification.directive.less"; } public function getJsFiles(&$jsFiles) @@ -149,6 +150,9 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.directive.js"; $jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.controller.js"; $jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler-urllistener.service.js"; + + $jsFiles[] = "plugins/CoreHome/angularjs/notification/notification.controller.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/notification/notification.directive.js"; } public function getClientSideTranslationKeys(&$translationKeys) diff --git a/plugins/CoreHome/angularjs/notification/notification.controller.js b/plugins/CoreHome/angularjs/notification/notification.controller.js new file mode 100644 index 0000000000..645cc13e59 --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.controller.js @@ -0,0 +1,34 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').controller('NotificationController', NotificationController); + + NotificationController.$inject = ['piwikApi']; + + function NotificationController(piwikApi) { + /** + * Marks a persistent notification as read so it will not reappear on the next page + * load. + */ + this.markNotificationAsRead = function () { + var notificationId = this.notificationId; + if (!notificationId) { + return; + } + + piwikApi.post( + { // GET params + module: 'CoreHome', + action: 'markNotificationAsRead' + }, + { // POST params + notificationId: notificationId + } + ); + }; + } +})(); \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.html b/plugins/CoreHome/angularjs/notification/notification.directive.html new file mode 100644 index 0000000000..a643be8a3b --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.directive.html @@ -0,0 +1,8 @@ +<div class="notification system"> + <button type="button" class="close" data-dismiss="alert" ng-if="!noclear" ng-click="notification.markNotificationAsRead()">×</button> + <strong ng-if="title">{{ title }}</strong> + + <!-- ng-transclude causes directive child elements to be added here --> + <div ng-transclude style="display:inline-block"></div> + +</div> \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.js b/plugins/CoreHome/angularjs/notification/notification.directive.js new file mode 100644 index 0000000000..167c61575b --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.directive.js @@ -0,0 +1,95 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Directive to show a notification. + * + * Note: using this directive is preferred over the Notification class (which uses jquery + * exclusively). + * + * Supports the following attributes: + * + * * **context**: Either 'success', 'error', 'info', 'warning' + * * **type**: Either 'toast', 'persistent', 'transient' + * * **noclear**: If truthy, no clear button is displayed. For persistent notifications, has no effect. + * + * Usage: + * + * <div piwik-notification context="success" type="persistent" noclear="true"> + * <strong>Info: </strong>My notification message. + * </div> + */ +(function () { + angular.module('piwikApp').directive('piwikNotification', piwikNotification); + + piwikNotification.$inject = ['piwik', '$timeout']; + + function piwikNotification(piwik, $timeout) { + return { + restrict: 'A', + scope: { + notificationId: '@?', + title: '@?notificationTitle', // TODO: shouldn't need this since the title can be specified within + // HTML of the node that uses the directive. + context: '@?', + type: '@?', + noclear: '=?' + }, + transclude: true, + templateUrl: 'plugins/CoreHome/angularjs/notification/notification.directive.html?cb=' + piwik.cacheBuster, + controller: 'NotificationController', + controllerAs: 'notification', + link: function (scope, element, attrs) { + if (scope.context) { + element.children('.notification').addClass('notification-' + scope.context); + } + + if (scope.type == 'persistent') { + // otherwise it is never possible to dismiss the notification + scope.noclear = false; + } + + if (scope.notificationId) { + closeExistingNotificationHavingSameIdIfNeeded(); + } + + if ('toast' == scope.type) { + addToastEvent(); + } + + if (!scope.noclear) { + addCloseEvent(); + } + + function closeExistingNotificationHavingSameIdIfNeeded() { + // TODO: instead of doing a global query for notification, there should be a notification-container + // directive that manages notifications. + var existingNode = angular.element('.system.notification[notification-id=' + attrs.id + ']'); + if (existingNode && existingNode.length) { + existingNode.remove(); + } + } + + function addToastEvent() { + $timeout(function () { + element.fadeOut('slow', function() { + element.remove(); + }); + }, 12 * 1000); + } + + function addCloseEvent() { + element.on('click', '.close', function (event) { + if (event && event.delegateTarget) { + angular.element(event.delegateTarget).remove(); + } + }); + } + } + }; + } +})(); \ No newline at end of file diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.less b/plugins/CoreHome/angularjs/notification/notification.directive.less new file mode 100644 index 0000000000..bc3c5e3f5f --- /dev/null +++ b/plugins/CoreHome/angularjs/notification/notification.directive.less @@ -0,0 +1,84 @@ +.system.notification { + color: #9b7a44; + float: none; + + padding: 15px 35px 15px 15px; + text-shadow: 0 1px 0 rgba(255,255,255,.5); + background-color: #ffffe0; + border: 1px solid #e6db55; + border-radius: 3px; + font-size: 14px; + margin: 0px; + margin-bottom: 16px; + + a { + color: #9b7a44; + text-decoration: underline; + } + + .close { + position: relative; + top: -5px; + right: -28px; + line-height: 20px; + } + + button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + } + .close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); + } + + &.notification-success { + background-color: #dff0d8; + border-color: #c3d6b7; + color: #468847; + + a { + color: #468847 + } + } + &.notification-danger, + &.notification-error { + background-color: #f2dede; + border-color: #d5bfc4; + color: #b94a48; + + a { + color: #b94a48 + } + } + &.notification-info { + background-color: #d9edf7; + border-color: #a7d3e3; + color: #3a87ad; + + a { + color: #3a87ad + } + } + + &.notification-block { + padding-top: 14px; + padding-bottom: 14px; + } + &.notification-block > p, + &.notification-block > ul { + margin-bottom: 0; + } + &.notification-block p + p { + margin-top: 5px; + } +} \ No newline at end of file diff --git a/plugins/CoreHome/javascripts/notification.js b/plugins/CoreHome/javascripts/notification.js index 7bd92042a5..266477446e 100644 --- a/plugins/CoreHome/javascripts/notification.js +++ b/plugins/CoreHome/javascripts/notification.js @@ -50,26 +50,8 @@ options = {}; } - if ('persistent' == options.type) { - // otherwise it is never possible to dismiss the notification - options.noclear = false; - } - - closeExistingNotificationHavingSameIdIfNeeded(options); - - var template = generateNotificationHtmlMarkup(options, message); - var $notificationNode = placeNotification(template, options); - this.$node = $notificationNode; - - if ('persistent' == options.type) { - addPersistentEvent($notificationNode); - } else if ('toast' == options.type) { - addToastEvent($notificationNode); - } - - if (!options.noclear) { - addCloseEvent($notificationNode); - } + var template = generateNotificationHtmlMarkup(options, message); + this.$node = placeNotification(template, options); }; Notification.prototype.scrollToNotification = function () { @@ -80,69 +62,37 @@ exports.Notification = Notification; - function closeExistingNotificationHavingSameIdIfNeeded(options) - { - if (!options.id) { - return; - } - - var $existingNode = $('.system.notification[data-id=' + options.id + ']'); - if ($existingNode && $existingNode.length) { - $existingNode.remove(); - } - } - function generateNotificationHtmlMarkup(options, message) { - var template = buildNotificationStart(options); - - if (!options.noclear) { - template += buildClearButton(); - } - - if (options.title) { - template += buildTitle(options); - } - - template += message; - template += buildNotificationEnd(); - - return template; - } - - function buildNotificationStart(options) { - var template = '<div class="notification system'; - - if (options.context) { - template += ' notification-' + options.context; - } - - template += '"'; - - if (options.id) { - template += ' data-id="' + options.id + '"'; + var attributeMapping = { + id: 'notification-id', + title: 'notification-title', + context: 'context', + type: 'type', + noclear: 'noclear' + }, + html = '<div piwik-notification'; + + for (var key in attributeMapping) { + if (attributeMapping.hasOwnProperty(key) + && options[key] + ) { + html += ' ' + attributeMapping[key] + '="' + options[key].toString().replace(/"/g, """) + '"'; + } } - template += '>'; - - return template; - } - - function buildNotificationEnd() { - return '</div>'; - } - - function buildClearButton() { - return '<button type="button" class="close" data-dismiss="alert">×</button>'; - } + html += '>' + message + '</div>'; - function buildTitle(options) { - return '<strong>' + options.title + '</strong> '; + return html; } function placeNotification(template, options) { - var $notificationNode = $(template); + // compile the template in angular + angular.element(document).injector().invoke(function ($compile, $rootScope) { + $compile($notificationNode)($rootScope.$new(true)); + }); + if (options.style) { $notificationNode.css(options.style); } @@ -158,41 +108,4 @@ return $notificationNode; } - - function addToastEvent($notificationNode) - { - setTimeout(function () { - $notificationNode.fadeOut( 'slow', function() { - $notificationNode.remove(); - $notificationNode = null; - }); - }, 12 * 1000); - } - - function addCloseEvent($notificationNode) { - $notificationNode.on('click', '.close', function (event) { - if (event && event.delegateTarget) { - $(event.delegateTarget).remove(); - } - }); - }; - - function addPersistentEvent($notificationNode) { - var notificationId = $notificationNode.data('id'); - - if (!notificationId) { - return; - } - - $notificationNode.on('click', '.close', function (event) { - var ajaxHandler = new ajaxHelper(); - ajaxHandler.addParams({ - module: 'CoreHome', - action: 'markNotificationAsRead' - }, 'GET'); - ajaxHandler.addParams({notificationId: notificationId}, 'POST'); - ajaxHandler.send(true); - }); - }; - })(jQuery, require); \ No newline at end of file diff --git a/plugins/CoreHome/stylesheets/notification.less b/plugins/CoreHome/stylesheets/notification.less index 23b29e1fe4..36f7085f6f 100644 --- a/plugins/CoreHome/stylesheets/notification.less +++ b/plugins/CoreHome/stylesheets/notification.less @@ -5,89 +5,4 @@ .notification { margin: 10px; } -} - -.system.notification { - color: #9b7a44; - float: none; - - padding: 15px 35px 15px 15px; - text-shadow: 0 1px 0 rgba(255,255,255,.5); - background-color: #ffffe0; - border: 1px solid #e6db55; - border-radius: 3px; - font-size: 14px; - margin: 0px; - margin-bottom: 16px; - - a { - color: #9b7a44; - text-decoration: underline; - } - - .close { - position: relative; - top: -5px; - right: -28px; - line-height: 20px; - } - - button.close { - padding: 0; - cursor: pointer; - background: transparent; - border: 0; - -webkit-appearance: none; - } - .close { - float: right; - font-size: 20px; - font-weight: bold; - line-height: 20px; - color: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); - } - - &.notification-success { - background-color: #dff0d8; - border-color: #c3d6b7; - color: #468847; - - a { - color: #468847 - } - } - &.notification-danger, - &.notification-error { - background-color: #f2dede; - border-color: #d5bfc4; - color: #b94a48; - - a { - color: #b94a48 - } - } - &.notification-info { - background-color: #d9edf7; - border-color: #a7d3e3; - color: #3a87ad; - - a { - color: #3a87ad - } - } - - &.notification-block { - padding-top: 14px; - padding-bottom: 14px; - } - &.notification-block > p, - &.notification-block > ul { - margin-bottom: 0; - } - &.notification-block p + p { - margin-top: 5px; - } -} +} \ No newline at end of file diff --git a/plugins/ExampleUI/templates/notifications.twig b/plugins/ExampleUI/templates/notifications.twig index 2212ead3d9..1058a50a61 100644 --- a/plugins/ExampleUI/templates/notifications.twig +++ b/plugins/ExampleUI/templates/notifications.twig @@ -4,6 +4,11 @@ <h2>Inline notification example:</h2> <div style="display:inline-block;margin-top:10px;" id="exampleUI_notifications"> - {{ 'This is an example for an inline notification. Have you noticed the success message disappeared after a few seconds?'|notification({'placeAt': '#exampleUI_notifications', 'title': 'Info: ', 'noclear': true, 'context': 'info'}) }} + <div piwik-notification + notification-title="Info:" + noclear="true" + context="info"> + This is an example for an inline notification. Have you noticed the success message disappeared after a few seconds? + </div> </div> {% endblock %} \ No newline at end of file -- GitLab