├── .gitignore ├── .stylelintignore ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── README.md ├── amd ├── build │ ├── custom.min.js │ ├── custom.min.js.map │ ├── notif.min.js │ └── notif.min.js.map └── src │ ├── custom.js │ └── notif.js ├── block_advnotifications.php ├── classes ├── base_table.php ├── event │ ├── notification_created.php │ ├── notification_deleted.php │ └── notification_updated.php ├── notifications_table.php ├── privacy │ └── provider.php ├── restore_table.php └── task │ └── advnotifications.php ├── db ├── access.php ├── install.xml ├── tasks.php └── upgrade.php ├── docs ├── AdvNotifications.gif ├── AdvNotifications.webm ├── AdvancedNotifications.pdf └── AlertTypes.png ├── edit_form.php ├── lang └── en │ └── block_advnotifications.php ├── locallib.php ├── package.json ├── pages ├── notifications.php ├── process.php └── restore.php ├── pix ├── danger.png ├── danger.svg ├── info.png ├── info.svg ├── success.png ├── success.svg ├── warning.png └── warning.svg ├── renderer.php ├── scss ├── base │ ├── _utils.scss │ └── _variables.scss ├── block │ └── _block.scss ├── page │ ├── _navigation.scss │ └── _notifications.scss └── styles.scss ├── settings.php ├── styles.css └── version.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories that git should ignore. 2 | .DS_Store 3 | node_modules 4 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | **/src/** 2 | scss/* 3 | **/*.scss 4 | **/**/*.scss -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: php 4 | 5 | dist: xenial 6 | 7 | services: 8 | - mysql 9 | 10 | php: 11 | - 7.4 12 | - 7.3 13 | - 7.2 14 | 15 | addons: 16 | postgresql: "9.6" 17 | 18 | env: 19 | global: 20 | - MOODLE_BRANCH=MOODLE_310_STABLE 21 | - MOODLE_BRANCH=MOODLE_39_STABLE 22 | - MOODLE_BRANCH=MOODLE_38_STABLE 23 | - MOODLE_BRANCH=MOODLE_35_STABLE 24 | 25 | matrix: 26 | - DB=pgsql 27 | - DB=mysqli 28 | 29 | cache: 30 | directories: 31 | - $HOME/.composer/cache 32 | - $HOME/.npm 33 | 34 | before_install: 35 | - phpenv config-rm xdebug.ini 36 | - nvm install 14.0.0 37 | - nvm use 14.0.0 38 | - cd ../.. 39 | - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 40 | - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH" 41 | 42 | install: 43 | - moodle-plugin-ci install 44 | - > 45 | if [ "$DB" = 'mysqli' ]; 46 | then 47 | sudo mkdir /mnt/ramdisk 48 | sudo mount -t tmpfs -o size=1024m tmpfs /mnt/ramdisk 49 | sudo service mysql stop 50 | sudo mv /var/lib/mysql /mnt/ramdisk 51 | sudo ln -s /mnt/ramdisk/mysql /var/lib/mysql 52 | sudo service mysql restart 53 | fi 54 | - > 55 | if [ "$DB" = 'pgsql' ]; 56 | then 57 | sudo mkdir /mnt/ramdisk 58 | sudo mount -t tmpfs -o size=1024m tmpfs /mnt/ramdisk 59 | sudo service postgresql stop 60 | sudo mv /var/lib/postgresql /mnt/ramdisk 61 | sudo ln -s /mnt/ramdisk/postgresql /var/lib/postgresql 62 | sudo service postgresql start 9.6 63 | fi 64 | 65 | script: 66 | - moodle-plugin-ci phplint 67 | - moodle-plugin-ci phpcpd 68 | - moodle-plugin-ci phpmd 69 | - moodle-plugin-ci codechecker 70 | - moodle-plugin-ci phpdoc 71 | - moodle-plugin-ci validate 72 | - moodle-plugin-ci savepoints 73 | - moodle-plugin-ci grunt -t stylecheck -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.4.2 - 10/02/2021 4 | 5 | ### Changed 6 | * Bumped version number. 7 | 8 | ### Fixed 9 | * Privacy API provider cleanup - removed mtrace (prevent Unit Test warnings) & whitespaces. 10 | 11 | ## v1.4.1 - 06/01/2021 12 | 13 | ### Added 14 | * JS source maps. 15 | 16 | ### Changed 17 | * Increased 'precision'/length of `seen` field in database (from 4 to 10). 18 | * Minor Moodle code-check fixes. 19 | * Bumped version number. 20 | 21 | ## v1.4.0 - 18/12/2020 22 | 23 | ### Added 24 | * Users can now create/manage their own (instance-based/non-global) notifications 25 | * Enable via capability/permissions - `manageownnotifications`. 26 | * A package.json file for easier (local) testing, checks & generation of code. 27 | 28 | ### Changed 29 | * General code, UI & language strings cleanup. 30 | * Updated README.md with new feature listed. 31 | * Bumped version number. 32 | 33 | ### Fixed 34 | * TravisCI file - updated moodle-plugin-ci to use Moodle HQ's. 35 | 36 | ## v1.3.12 - 13/10/2020 37 | 38 | ### Changed 39 | * Bumped version number. 40 | 41 | ### Fixed 42 | * Improper default settings value. Now sets first date format as default. 43 | 44 | ## v1.3.11 - 06/10/2020 45 | 46 | ### Added 47 | * Gruntfile - scss & js processing. 48 | * More date formats available. 49 | 50 | ### Changed 51 | * UI/UX Improvements. 52 | * Cleaned up unused SCSS variables & functions. 53 | * Bumped version number. 54 | 55 | ### Fixed 56 | * 'Date-to' would not be shown if 'Date-from' was not set (0). It now correctly checks if date-to is set instead. 57 | * Various travis & moodle plugin db-recommended code style fixes. 58 | 59 | ## v1.3.10 - 02/10/2020 60 | 61 | ### Added 62 | * Setting to customise 'date format' shown by plugin. 63 | 64 | ### Changed 65 | * Utilised bootstrap more for form styling. 66 | * Bumped version number. 67 | 68 | ## v1.3.9 - 22/06/2020 69 | 70 | ### Changed 71 | * Travis file updated to Moodle 3.9. 72 | * Updated buttons classes to be more consistent - primary, secondary, light. 73 | * Bumped version number. 74 | 75 | ## v1.3.8 - 29/04/2020 76 | 77 | ### Changed 78 | * Update imagery - video and gif. 79 | * Simplify README. 80 | * Bumped version number. 81 | 82 | ### Fixed 83 | * Announcement alert type now works properly for preview alert - allowing live preview if custom CSS exists for announcement type alert. 84 | 85 | ## v1.3.7 - 19/12/2019 86 | 87 | ### Changed 88 | * Travis file updated to Moodle 3.8. 89 | * Bumped version number. 90 | 91 | ## v1.3.6 - 19/09/2019 92 | 93 | ### Changed 94 | * Code style fixes. 95 | * Bumped version number. 96 | 97 | ## v1.3.5 - 17/09/2019 98 | 99 | ### Changed 100 | * Moved JS to AMD format. 101 | * Updated TravisCI file to Ubuntu Xenial - MDL-65992. 102 | * Bumped version number. 103 | 104 | ## v1.3.4 - 11/07/2019 105 | 106 | ### Changed 107 | * Removed redundant global variable. 108 | * Bumped version number. 109 | 110 | ### Fixed 111 | * Use lang string for notification type in table(s). 112 | 113 | ## v1.3.3 - 11/07/2019 114 | 115 | ### Added 116 | * Support for theme to override images - thanks @amandadoughty. 117 | 118 | ### Changed 119 | * 'Message' field width enlarged. 120 | * Minor CSS updates. 121 | * Bumped version number. 122 | 123 | ### Fixed 124 | * Placeholder for date fields updated to expected format - for browsers that don't support 'date' input types. 125 | 126 | ## v1.3.2 - 23/01/2019 127 | 128 | ### Changed 129 | * TravisCI file update - ci tool version/source. 130 | * README tweak. 131 | * Bumped version number. 132 | 133 | ### Fixed 134 | * Squashed regression bug - filter removed data attributes required for AJAX calls to dismiss notifications. 135 | * Seen count SQL - when user exports data "Yes" or "No" was shown instead of the seen count. 136 | * Only show manage-related links if the user has permission. 137 | 138 | ## v1.3.1 - 21/01/2019 139 | 140 | ### Changed 141 | * TravisCI file update. 142 | * Bumped version number. 143 | 144 | ### Fixed 145 | * Minor code style fixes. 146 | * EOL fixes. 147 | 148 | ## v1.3.0 - 21/01/2019 149 | 150 | ### Added 151 | * Support for Privacy API (GDPR Compliance). 152 | * Language strings for Privacy API. 153 | 154 | ### Changed 155 | * Bumped version number. 156 | 157 | ## v1.2.4 - 18/07/2018 158 | 159 | ### Added 160 | * Support added for multilang (and other) filters. 161 | 162 | ### Changed 163 | * Bumped version number. 164 | 165 | ## v1.2.3 - 13/07/2018 166 | 167 | ### Changed 168 | * If the from/to dates are not set, a `-` is now displayed instead of 01/01/1970. 169 | * If the from/to dates match the 'to date' is displayed as a `-`. 170 | * Updated README - HTML allowed since previous update. 171 | * Bumped version number. 172 | 173 | ## v1.2.2 - 05/07/2018 174 | 175 | ### Added 176 | * 'Allow HTML' setting - toggles if basic HTML is allowed in the Title and Message (filtered for scripts, etc). 177 | 178 | ### Changed 179 | * Preview & Form-related JavaScript improved - more robust and responsive UI/UX. 180 | * Minor styling update for status indicator (Saving/Done). 181 | * Bumped version number. 182 | 183 | ## v1.2.1 - 27/04/2018 184 | 185 | ### Added 186 | * Added .stylelintignore file to exclude scss files (only check final css file). 187 | 188 | ### Changed 189 | * Bumped version number. 190 | 191 | ## v1.2.0 - 24/04/2018 192 | 193 | ### Added 194 | * Added breadcrumbs to manage & restore pages. 195 | * Added language strings for breadcrumbs. 196 | 197 | ### Changed 198 | * Updated dates in Changelog (typos). 199 | * Bumped version number. 200 | 201 | ### Fixed 202 | * Fixed minor illogical JS error. 203 | 204 | ## v1.1.1 - 12/03/2018 205 | 206 | ### Changed 207 | * Small UX Improvement - better padding for dismiss button. 208 | * Bumped version number. 209 | 210 | ### Fixed 211 | * Fixed bug that caused illogical redirect(s). 212 | 213 | ## v1.1.0 - 05/03/2018 214 | 215 | ### Added 216 | * Added locallib file to manage preparation of notifications to be rendered. 217 | * Added CHANGELOG.md file to start keeping track of changes. 218 | 219 | ### Changed 220 | * Moved database calls out of renderer function 'render_notification' - it now purely renders the notifications. 221 | * PHPDocs corrections for class 'advnotifications'. 222 | * Updated 'Message' field to be texarea - allowing for resizing of field. 223 | * Updated SCSS (and therefore CSS) to support resizing of textarea & improved UX for dismiss/close button. 224 | * Bumped version number. -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* eslint no-undef: "error" */ 2 | /* eslint camelcase: 2 */ 3 | /* eslint-env node */ 4 | 5 | "use strict"; 6 | 7 | /** 8 | * Gruntfile for the plugin. 9 | * 10 | * This file configures tasks to be run by Grunt 11 | * https://gruntjs.com/ for the current theme. 12 | * 13 | * 14 | * Requirements: 15 | * ------------- 16 | * nodejs, npm, grunt-cli. 17 | * 18 | * Installation: 19 | * ------------- 20 | * node and npm: instructions at https://nodejs.org/ 21 | * 22 | * run for each task (and grunt-cli): `[sudo] npm install -g grunt-cli --save-dev` 23 | * run for each task to install locally:: `[sudo] npm install grunt-cli --save-dev` 24 | * 25 | * node dependencies: run `npm install` in the root directory. 26 | * 27 | * 28 | * Usage: 29 | * ------ 30 | * Call tasks from the plugin root directory. Default behaviour 31 | * (calling only `grunt`) is to run the watch task detailed below. 32 | * 33 | * 34 | * Porcelain tasks: 35 | * ---------------- 36 | * The nice user interface intended for everyday use. Provide a 37 | * high level of automation and convenience for specific use-cases. 38 | * 39 | * grunt css Create CSS from the SCSS. 40 | * 41 | * 42 | * Based on https://github.com/willianmano/moodle-theme_moove/ Gruntfile.js - thank you! 43 | * Based on https://gitlab.com/jezhops/moodle-theme_adaptable/ Gruntfile.js - thank you! 44 | * 45 | * @package block_advnotifications 46 | * @author Based on code originally written by: 47 | * Willian Mano - {@link https://conecti.me} & G J Barnard - {@link https://moodle.org/user/profile.php?id=442195} 48 | * @copyright 2020 LearningWorks 49 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 | */ 51 | 52 | module.exports = function(grunt) { 53 | let decachephp = "../../admin/cli/purge_caches.php"; 54 | const sass = require('node-sass'); 55 | 56 | grunt.initConfig({ 57 | watch: { 58 | options: { 59 | nospawn: true, 60 | livereload: true 61 | }, 62 | css: { 63 | files: ["scss/**/*.scss"], 64 | tasks: ["css", "decache"] 65 | }, 66 | js: { 67 | files: ["amd/src/*.js"], 68 | tasks: ["uglify", "decache"] 69 | } 70 | }, 71 | sass: { 72 | dist: { 73 | files: { 74 | "styles.css": "scss/styles.scss" 75 | } 76 | }, 77 | options: { 78 | includePaths: ["scss"], 79 | implementation: sass, 80 | indentWidth: 4, 81 | outputStyle: "expanded" 82 | } 83 | }, 84 | stylelint: { 85 | scss: { 86 | options: {syntax: "scss"}, 87 | src: ["scss/**/*.scss"] 88 | }, 89 | css: { 90 | src: ["styles.css"], 91 | options: { 92 | configOverrides: { 93 | rules: { 94 | "at-rule-no-unknown": true, 95 | } 96 | } 97 | } 98 | } 99 | }, 100 | uglify: { 101 | dist: { 102 | files: { 103 | 'amd/build/custom.min.js': 'amd/src/custom.js', 104 | 'amd/build/notif.min.js': 'amd/src/notif.js', 105 | } 106 | } 107 | }, 108 | exec: { 109 | decache: { 110 | cmd: "php " + decachephp, 111 | callback: function(error) { 112 | if (!error) { 113 | grunt.log.writeln("Moodle caches purged."); 114 | } else { 115 | grunt.log.writeln("Some error occurred:"); 116 | grunt.log.writeln(error); 117 | } 118 | } 119 | } 120 | } 121 | }); 122 | 123 | // Load tasks. 124 | grunt.loadNpmTasks("grunt-exec"); 125 | grunt.loadNpmTasks("grunt-contrib-watch"); 126 | grunt.loadNpmTasks('grunt-sass'); 127 | grunt.loadNpmTasks("grunt-stylelint"); 128 | grunt.loadNpmTasks('grunt-contrib-uglify'); 129 | 130 | // Register tasks. 131 | grunt.registerTask("default", ["watch"]); 132 | grunt.registerTask("css", ["stylelint:scss", "sass", "stylelint:css"]); 133 | grunt.registerTask("stylecheck", ["stylelint:scss", "stylelint:css"]); 134 | grunt.registerTask("js", ["uglify"]); 135 | grunt.registerTask("decache", ["exec:decache"]); 136 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/learningworks/moodle-block_advnotifications.svg?branch=master)](https://travis-ci.org/learningworks/moodle-block_advnotifications) 2 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](README.md) 3 | 4 | ## Easily create, manage and display notifications/alerts to users 5 | 6 | [![Functionality](docs/AdvNotifications.gif)](docs/AdvNotifications.gif) 7 | 8 | This block allows users to display DEFCON-like alerts, which are Bootstrap-based, allowing for various configurations. 9 | This could be useful in cases such as alerting users of scheduled outages, welcoming them to your site, teachers can use it to notify users of changes/due dates, etc. 10 | 11 | 12 | ### Features: 13 | 14 | * Customisable title & message 15 | * Basic HTML tags allowed for advanced users 16 | * Multi-lingual/filter support 17 | * Multiple types of notifications (Bootstrap-based styles) 18 | * Type-based icons (optional setting) 19 | * Dismissible/Non-Dismissible 20 | * Customisable date range to show notifications 21 | * Display a notification to the user a set amount of times 22 | * Instance-based or global/site-wide notifications 23 | * Users (e.g. teachers) can create and manage their own instance-based notifications - **disabled by default** 24 | * Enable/Disable a/all notifications (Site-wide and instance-based) 25 | * Edit/Delete/Restore notifications 26 | * Option to auto-delete notification after end date 27 | * Option to permanently delete notifications that's had the deleted flag for more than 30 days 28 | * Option to automatically remove user (dismissed/seen) records that relates to notifications that don't exist anymore 29 | * AJAX used to improve user-experience and simplify processes 30 | * Live-preview when adding/editing a notification 31 | * Easy to use, but fully documented with all the nitty-gritty information 32 | * Implements Privacy API (GDPR Compliance) 33 | 34 | For full documentation, please check [here](docs/AdvancedNotifications.pdf) - or check the plugin's `/docs` directory. 35 | 36 | 37 | #### Notification Anatomy 38 | 39 | [![Alert Types](docs/AlertTypes.png)](docs/AlertTypes.png) 40 | 41 | 42 | #### Installation Notice 43 | 44 | All the plugin's settings are disabled by default. Enable it upon installation if you wish to start using it immediately or enable it later by navigating to Site Administration > Plugins > Blocks > Advanced Notifications. 45 | 46 | 47 | #### Backwards Compatibility/Progressive Enhancement 48 | 49 | Although the plugin works and is usable without JavaScript, it is highly recommended to use the plugin with JavaScript enabled. 50 | Using the plugin with JavaScript disabled does not allow for some features to be used to their full potential ranging from dismissing a notification to dynamically editing existing notifications and the live-preview feature - all of which relies on JavaScript in some form to make the user's experience more enjoyable. 51 | 52 | 53 | #### Pull Requests 54 | 55 | Pull requests are welcome - submit pull requests to address issues, add features, fix typos, anything! 56 | 57 | 58 | #### TODO: 59 | 60 | * Add time (hh:mm) to 'From/To' setting. 61 | -------------------------------------------------------------------------------- /amd/build/custom.min.js: -------------------------------------------------------------------------------- 1 | define(["jquery"],function(l){return{initialise:function(){l(document).ready(function(){var e=l("#region-main"),t=l("#add_notification_wrapper_id"),s={save:"Save",update:"Update",req:"Required...",preview:"Preview",title:"Title",message:"Message"};e.on("click",".notifications_table tr > td > form > button[type=submit]",function(e){e.preventDefault();var n={call:"ajax",purpose:"",tableaction:"",blockid:""},i=l(this).closest("form").attr("data-edit"),e=l(this).closest("form").attr("data-delete");n.blockid=l(this).closest("form").find("[name=blockid]")[0].value,d(),void 0!==i&&!1!==i?(n.purpose="edit",n.tableaction=i,(i=l("#add_notification_save")).addClass("update"),i.val(s.update)):void 0!==e&&!1!==e&&(n.purpose="delete",n.tableaction=e);e=M.cfg.wwwroot+"/blocks/advnotifications/pages/process.php?sesskey="+M.cfg.sesskey;l.post(e,n).fail(function(){console.error("No 'manage' response received.")}).done(function(e){if(e=JSON.parse(e),0'),l("#add_notification_purpose").val("update")),t=l("#add_notification_wrapper_id").find("#add_notification_"+i),"enabled"!==i&&"global"!==i&&"dismissible"!==i&&"aicon"!==i||1!=e[i]?"enabled"!==i&&"global"!==i&&"dismissible"!==i&&"aicon"!==i||0!=e[i]?t.val(e[i]):t.prop("checked",!1):t.prop("checked",!0))}o()}})}),e.on("click",".notifications_restore_table tr > td > form > button[type=submit]",function(e){e.preventDefault();var i={call:"ajax",purpose:"",tableaction:"",blockid:""},t=l(this).closest("form").attr("data-restore"),e=l(this).closest("form").attr("data-permdelete");i.blockid=l(this).closest("form").find("[name=blockid]")[0].value,void 0!==t&&!1!==t?(i.purpose="restore",i.tableaction=t):void 0!==e&&!1!==e&&(i.purpose="permdelete",i.tableaction=e);e=M.cfg.wwwroot+"/blocks/advnotifications/pages/process.php?sesskey="+M.cfg.sesskey;l.post(e,i).fail(function(){console.error("No 'restore/permdelete' response received.")}).done(function(e){e=JSON.parse(e),0'+s.req+"").insertAfter(t[0].nextSibling));o.hide()}).done(function(){o.find(".saving").hide(),o.find(".done").show(),c(),setTimeout(function(){o.fadeOut(function(){o.find(".done").hide(),o.find(".saving").show()})},1500),l("#advnotifications_table_wrapper").load("# #advnotifications_table_wrapper > *")}))}),t.on("input propertychange paste","#add_notification_title, #add_notification_message",function(){o()}),l("#add_notification_type").on("change",function(){o()}),l("#add_notification_dismissible").on("change",function(){o()}),l("#add_notification_aicon").on("change",function(){o()});var i,n,o=function(){var e=t.find("#add_notification_title");0 img").attr("src",M.util.image_url(e,"block_advnotifications")),l("#add_notification_dismissible")[0].checked?(l(".preview-alert-dismissible").show(),i.addClass("dismissible")):(l(".preview-alert-dismissible").hide(),i.removeClass("dismissible")),l("#add_notification_aicon")[0].checked?(l(".preview-aicon").show(),i.addClass("aicon")):(l(".preview-aicon").hide(),i.removeClass("aicon"))},a=function(){var e=l("#notification_preview_wrapper"),i='
'+s.preview+'
'+s.title+'
'+s.message+'
';0'+s.req+"").insertAfter(l(i[e]).closest("select")[0].nextSibling),!1;return!0},d=function(){l("select.requiredfield").removeClass("requiredfield"),l("strong.requiredfield").remove()},c=function(){l("#add_notification_form")[0].reset(),d(),a();var e=l("#add_notification_save");e.removeClass("update"),l("#add_notification_id").remove(),l("#add_notification_purpose").val("add"),e.val(s.save)};i={call:"ajax",purpose:"strings"},n=M.cfg.wwwroot+"/blocks/advnotifications/pages/process.php?sesskey="+M.cfg.sesskey,l.post(n,i).fail(function(){console.error("No 'strings' response received.")}).done(function(e){s=e}).always(function(){a()}),l("#add_notification_form").append('')})}}}); 2 | //# sourceMappingURL=custom.min.js.map -------------------------------------------------------------------------------- /amd/build/notif.min.js: -------------------------------------------------------------------------------- 1 | define(["jquery"],function(n){return{initialise:function(){n(document).ready(function(){n(".block_advnotifications").on("click",".dismissible",function(){var i=n(this).attr("data-dismiss");n(this).slideUp("150",function(){n(this).remove()});var s={call:"ajax"};s.dismiss=i;i=M.cfg.wwwroot+"/blocks/advnotifications/pages/process.php?sesskey="+M.cfg.sesskey;n.post(i,s).fail(function(){console.error("No 'dismiss' response received.")}).done(function(){})})})}}}); 2 | //# sourceMappingURL=notif.min.js.map -------------------------------------------------------------------------------- /amd/build/notif.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/notif.js"],"names":["define","$","initialise","document","ready","on","dismiss","attr","slideUp","remove","callpath","M","cfg","wwwroot","sesskey","post","call","fail","console","error","done"],"mappings":"AAWAA,OAAM,gCAAC,CAAC,QAAD,CAAD,CAAa,SAASC,CAAT,CAAY,CAG3B,MAAO,CACHC,UAAU,CAAE,qBAAW,CAEnBD,CAAC,CAACE,QAAD,CAAD,CAAYC,KAAZ,CAAkB,UAAW,CAEzBH,CAAC,CAAC,yBAAD,CAAD,CAA6BI,EAA7B,CAAgC,OAAhC,CAAyC,cAAzC,CAAyD,UAAW,CAEhE,GAAIC,CAAAA,CAAO,CAAGL,CAAC,CAAC,IAAD,CAAD,CAAQM,IAAR,CAAa,cAAb,CAAd,CAEAN,CAAC,CAAC,IAAD,CAAD,CAAQO,OAAR,CAAgB,KAAhB,CAAuB,UAAW,CAC9BP,CAAC,CAAC,IAAD,CAAD,CAAQQ,MAAR,EACH,CAFD,EAJgE,GAa5DC,CAAAA,CAAQ,CAAGC,CAAC,CAACC,GAAF,CAAMC,OAAN,CAAgB,qDAAhB,CAAwEF,CAAC,CAACC,GAAF,CAAME,OAb7B,CAgBhEb,CAAC,CAACc,IAAF,CAAOL,CAAP,CAPe,CACNM,IADM,CACC,MADD,CAENV,OAFM,CAEIA,CAFJ,CAOf,EAA2BW,IAA3B,CAAgC,UAAW,CACvCC,OAAO,CAACC,KAAR,CAAc,iCAAd,CACH,CAFD,EAEGC,IAFH,CAEQ,UAAW,CAElB,CAJD,CAKH,CArBD,CAsBH,CAxBD,CAyBH,CA5BE,CA8BV,CAjCK,CAAN","sourcesContent":["/* eslint no-console: [\"error\", { allow: [\"error\"] }], max-nested-callbacks: [\"error\", 7] */\n/**\n * @package block_advnotifications\n * @copyright 2019 onwards LearningWorks Ltd {@link https://learningworks.co.nz/}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @author Zander Potgieter \n */\n\n/**\n * @module block_advnotifications/notif\n */\ndefine(['jquery'], function($) {\n // JQuery is available via $.\n\n return {\n initialise: function() {\n // Module initialised.\n $(document).ready(function() {\n // USER DISMISSING/CLICKING ON A NOTIFICATION.\n $('.block_advnotifications').on('click', '.dismissible', function() {\n\n var dismiss = $(this).attr('data-dismiss');\n\n $(this).slideUp('150', function() {\n $(this).remove();\n });\n\n // TODO - Move ajax call to Moodle's ajax/webservice call.\n var senddata = {}; // Data Object.\n senddata.call = 'ajax';\n senddata.dismiss = dismiss;\n\n var callpath = M.cfg.wwwroot + \"/blocks/advnotifications/pages/process.php?sesskey=\" + M.cfg.sesskey;\n\n // Update user preferences.\n $.post(callpath, senddata).fail(function() {\n console.error(\"No 'dismiss' response received.\");\n }).done(function() {\n // User dismissed notification. Do something maybe...\n });\n });\n });\n }\n };\n});"],"file":"notif.min.js"} -------------------------------------------------------------------------------- /amd/src/custom.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: ["error", { allow: ["error"] }], max-nested-callbacks: ["error", 7] */ 2 | /** 3 | * @package block_advnotifications 4 | * @copyright 2019 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 5 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6 | * @author Zander Potgieter 7 | */ 8 | 9 | /** 10 | * @module block_advnotifications/custom 11 | */ 12 | define(['jquery'], function($) { 13 | // JQuery is available via $. 14 | 15 | return { 16 | initialise: function() { 17 | // Module initialised. 18 | $(document).ready(function() { 19 | // Commonly (multiple times) used elements. 20 | var mainregion = $('#region-main'); 21 | var addregion = $('#add_notification_wrapper_id'); 22 | var strings = { 23 | save: 'Save', 24 | update: 'Update', 25 | req: 'Required...', 26 | preview: 'Preview', 27 | title: 'Title', 28 | message: 'Message' 29 | }; 30 | 31 | // MANAGING NOTIFICATIONS. 32 | mainregion.on('click', '.notifications_table tr > td > form > button[type=submit]', function(e) { 33 | e.preventDefault(); 34 | var senddata = {}; // Data Object. 35 | senddata.call = 'ajax'; 36 | senddata.purpose = ''; 37 | senddata.tableaction = ''; 38 | senddata.blockid = ''; 39 | 40 | // Check if user wants to edit/delete. 41 | var eattr = $(this).closest('form').attr('data-edit'); 42 | var dattr = $(this).closest('form').attr('data-delete'); 43 | senddata.blockid = $(this).closest('form').find('[name=blockid]')[0].value; 44 | refreshRequired(); 45 | 46 | // Check if anchor element has attribute, retrieved from above. 47 | if (typeof eattr !== typeof undefined && eattr !== false) { 48 | senddata.purpose = 'edit'; 49 | senddata.tableaction = eattr; 50 | 51 | var savebutton = $('#add_notification_save'); 52 | savebutton.addClass('update'); 53 | savebutton.val(strings.update); 54 | } else if (typeof dattr !== typeof undefined && dattr !== false) { 55 | senddata.purpose = 'delete'; 56 | senddata.tableaction = dattr; 57 | } 58 | 59 | var callpath = M.cfg.wwwroot + "/blocks/advnotifications/pages/process.php?sesskey=" + M.cfg.sesskey; 60 | 61 | // Perform tableaction. 62 | $.post(callpath, senddata).fail(function() { 63 | console.error("No 'manage' response received."); 64 | }).done(function(data) { 65 | data = JSON.parse(data); 66 | 67 | // User deleted/edited notification. 68 | if (parseInt(data.done, 10) > 0) { 69 | $('#tr' + senddata.purpose + data.done).closest("tr").fadeOut(250, function() { 70 | $(this).remove(); 71 | clearForm(); 72 | refreshPreview(); 73 | }); 74 | } else if (senddata.purpose === "edit") { 75 | for (var i in data) { 76 | if (data.hasOwnProperty(i)) { 77 | 78 | // Need this for updating. 79 | if (i === "id") { 80 | var form = $('#add_notification_form'); 81 | 82 | // Because we're doing a standard submit, we need extra inputs to pass params. 83 | // But first, remove old hidden inputs. 84 | $('#add_notification_id').remove(); 85 | form.prepend( 86 | '' 87 | ); 88 | 89 | $('#add_notification_purpose').val('update'); 90 | } 91 | 92 | var affectelement = $('#add_notification_wrapper_id').find('#add_notification_' + i); 93 | 94 | // Check whether checkboxes should be checked or not. 95 | // We also don't assign a value to checkbox input fields. 96 | if ( 97 | ( 98 | i === 'enabled' || 99 | i === 'global' || 100 | i === 'dismissible' || 101 | i === 'aicon' 102 | ) && data[i] == 1) { 103 | affectelement.prop('checked', true); 104 | } else if ( 105 | (i === 'enabled' || 106 | i === 'global' || 107 | i === 'dismissible' || 108 | i === 'aicon') && data[i] == 0) { 109 | affectelement.prop('checked', false); 110 | } else { 111 | affectelement.val(data[i]); 112 | } 113 | } 114 | } 115 | reloadPreview(); 116 | } 117 | }); 118 | }); 119 | 120 | // Restore & Permanently delete notifications. 121 | mainregion.on('click', '.notifications_restore_table tr > td > form > button[type=submit]', function(e) { 122 | 123 | e.preventDefault(); 124 | var senddata = {}; // Data Object. 125 | senddata.call = 'ajax'; 126 | senddata.purpose = ''; 127 | senddata.tableaction = ''; 128 | senddata.blockid = ''; 129 | 130 | // Check if user wants to restore/delete. 131 | var rattr = $(this).closest('form').attr('data-restore'); 132 | var pdattr = $(this).closest('form').attr('data-permdelete'); 133 | senddata.blockid = $(this).closest('form').find('[name=blockid]')[0].value; 134 | 135 | // Check if anchor element has attribute, retrieved from above. 136 | if (typeof rattr !== typeof undefined && rattr !== false) { 137 | senddata.purpose = 'restore'; 138 | senddata.tableaction = rattr; 139 | } else if (typeof pdattr !== typeof undefined && pdattr !== false) { 140 | senddata.purpose = 'permdelete'; 141 | senddata.tableaction = pdattr; 142 | } 143 | 144 | var callpath = M.cfg.wwwroot + "/blocks/advnotifications/pages/process.php?sesskey=" + M.cfg.sesskey; 145 | 146 | // Perform tableaction. 147 | $.post(callpath, senddata).fail(function() { 148 | console.error("No 'restore/permdelete' response received."); 149 | }).done(function(data) { 150 | data = JSON.parse(data); 151 | 152 | // User deleted/restored notification. 153 | // Object 'done' is returned for both restore & delete. 154 | if (parseInt(data.done, 10) > 0) { 155 | $('#tr' + senddata.purpose + data.done).closest("tr").fadeOut(250, function() { 156 | $(this).remove(); 157 | }); 158 | } 159 | }); 160 | }); 161 | 162 | // Clear form. 163 | addregion.on('click', '#add_notification_cancel', function(e) { 164 | e.preventDefault(); 165 | clearForm(); 166 | }); 167 | 168 | // Managing more notifications. 169 | mainregion.on('submit', '#add_notification_form', function(e) { 170 | e.preventDefault(); 171 | var status = $('#add_notification_status'); 172 | var form = $('#add_notification_form'); 173 | 174 | refreshRequired(); 175 | if (!checkRequired()) { 176 | // Stop if required fields are not supplied. 177 | return; 178 | } 179 | 180 | status.show(); 181 | 182 | var senddata = $(this).serialize(); // Data Object. 183 | 184 | var callpath = M.cfg.wwwroot + "/blocks/advnotifications/pages/process.php"; 185 | 186 | // Perform tableaction. 187 | $.post(callpath, senddata).fail(function(data) { 188 | console.error("No 'add' response received."); 189 | 190 | var error = data.responseJSON.error; 191 | 192 | for (var i in error) { 193 | if (error.hasOwnProperty(i)) { 194 | var sfield = form.find('select[name=' + error[i] + ']'); 195 | sfield.addClass('requiredfield'); 196 | $( 197 | '' + strings.req + '' 198 | ).insertAfter(sfield[0].nextSibling); 199 | } 200 | } 201 | 202 | status.hide(); 203 | }).done(function() { 204 | // User saved notification. 205 | status.find('.saving').hide(); 206 | status.find('.done').show(); 207 | 208 | // Clear Form. 209 | clearForm(); 210 | 211 | setTimeout(function() { 212 | status.fadeOut(function() { 213 | status.find('.done').hide(); 214 | status.find('.saving').show(); 215 | }); 216 | }, 1500); 217 | 218 | $('#advnotifications_table_wrapper').load('# #advnotifications_table_wrapper > *'); 219 | }); 220 | }); 221 | 222 | // LIVE PREVIEW. 223 | // Dynamically update preview alert as user changes textbox content. 224 | addregion.on('input propertychange paste', '#add_notification_title, #add_notification_message', function() { 225 | reloadPreview(); 226 | }); 227 | 228 | // Dynamically update preview alert type. 229 | $('#add_notification_type').on('change', function() { 230 | reloadPreview(); 231 | }); 232 | 233 | $('#add_notification_dismissible').on('change', function() { 234 | // Checking specifically whether ticked/checked or not to ensure it's displayed correctly (not toggling). 235 | reloadPreview(); 236 | }); 237 | 238 | $('#add_notification_aicon').on('change', function() { 239 | // Checking specifically whether ticked/checked or not to ensure it's displayed correctly (not toggling). 240 | reloadPreview(); 241 | }); 242 | 243 | // Check if preview is displaying correct (Update it). 244 | var reloadPreview = function() { 245 | // Update title. 246 | var title = addregion.find('#add_notification_title'); 247 | if (title.val().length > 0) { 248 | addregion.find('.preview-title')[0].innerHTML = title.val(); 249 | } else { 250 | addregion.find('.preview-title')[0].innerHTML = strings.title; 251 | } 252 | 253 | // Update message. 254 | var message = addregion.find('#add_notification_message'); 255 | if (message.val().length > 0) { 256 | addregion.find('.preview-message')[0].innerHTML = message.val(); 257 | } else { 258 | addregion.find('.preview-message')[0].innerHTML = strings.message; 259 | } 260 | 261 | // Check notification type. 262 | var alerttype = $('#add_notification_type').val(); 263 | var previewalert = $('#add_notification_wrapper_id .preview-alert'); 264 | 265 | // Clear existing classes. 266 | previewalert.removeClass('alert-info alert-success alert-danger alert-warning announcement'); 267 | 268 | // Special check for announcement type. 269 | if (alerttype === 'announcement') { 270 | previewalert.addClass(alerttype); 271 | alerttype = 'info'; 272 | } 273 | 274 | // If anything unexpected, set to info type. 275 | if (alerttype !== 'info' && alerttype !== 'success' && alerttype !== 'warning' && alerttype !== 'danger') { 276 | alerttype = 'info'; 277 | } 278 | 279 | // Add type of alert class. 280 | previewalert.addClass('alert-' + alerttype); 281 | 282 | $('.preview-aicon').find('> img').attr('src', M.util.image_url(alerttype, 'block_advnotifications')); 283 | 284 | // Check if dismissable. 285 | if (!$('#add_notification_dismissible')[0].checked) { 286 | $('.preview-alert-dismissible').hide(); 287 | previewalert.removeClass('dismissible'); 288 | } else { 289 | $('.preview-alert-dismissible').show(); 290 | previewalert.addClass('dismissible'); 291 | } 292 | 293 | // Check if icon should be shown. 294 | if (!$('#add_notification_aicon')[0].checked) { 295 | $('.preview-aicon').hide(); 296 | previewalert.removeClass('aicon'); 297 | } else { 298 | $('.preview-aicon').show(); 299 | previewalert.addClass('aicon'); 300 | } 301 | }; 302 | 303 | var init = function() { 304 | // Get strings. 305 | var senddata = {}; // Data Object. 306 | senddata.call = 'ajax'; 307 | senddata.purpose = 'strings'; 308 | 309 | var callpath = M.cfg.wwwroot + "/blocks/advnotifications/pages/process.php?sesskey=" + M.cfg.sesskey; 310 | 311 | $.post(callpath, senddata).fail(function() { 312 | console.error("No 'strings' response received."); 313 | }).done(function(data) { 314 | // TODO: ONLY DO THIS IF AJAX SUCCESSFUL - don't render with English first?). 315 | // Store strings and update preview. 316 | strings = data; 317 | }).always(function() { 318 | // Always prepend live preview. Use langstrings if AJAX successful, otherwise use strings declared at top. 319 | refreshPreview(); 320 | }); 321 | 322 | // JS is enabled, so we can use AJAX in the new notification form. 323 | $('#add_notification_form').append( 324 | '' 325 | ); 326 | }; 327 | 328 | // Shiny new and fresh preview. 329 | var refreshPreview = function() { 330 | var previewelem = $('#notification_preview_wrapper'); 331 | var previewdom = 332 | '
' + 333 | '' + strings.preview + '
' + 334 | '
' + 335 | '' + 338 | '' + strings.title + ' ' + 339 | '
' + strings.message + '
' + 340 | '' + 341 | '
' + 342 | '
'; 343 | 344 | // If it exists already, remove before adding again. 345 | if (previewelem.length > 0) { 346 | previewelem.remove(); 347 | // Don't slide in. 348 | $(previewdom).prependTo($(addregion)); 349 | } else { 350 | // Slide in. 351 | $(previewdom).prependTo($(addregion)).hide().slideDown(); 352 | } 353 | }; 354 | 355 | var checkRequired = function() { 356 | var disselopt = $('#add_notification_form select option:selected:disabled'); 357 | 358 | for (var opt in disselopt) { 359 | if (disselopt.hasOwnProperty(opt)) { 360 | if ($(disselopt[opt]).prop('disabled')) { 361 | $(disselopt[opt]).closest('select').addClass('requiredfield'); 362 | $('' + strings.req + '') 363 | .insertAfter($(disselopt[opt]).closest('select')[0].nextSibling); 364 | 365 | return false; 366 | } 367 | } 368 | } 369 | return true; 370 | }; 371 | 372 | var refreshRequired = function() { 373 | $('select.requiredfield').removeClass('requiredfield'); 374 | $('strong.requiredfield').remove(); 375 | }; 376 | 377 | var clearForm = function() { 378 | $('#add_notification_form')[0].reset(); 379 | refreshRequired(); 380 | refreshPreview(); 381 | 382 | // Change save button back to normal. 383 | var savebutton = $('#add_notification_save'); 384 | savebutton.removeClass('update'); 385 | $('#add_notification_id').remove(); 386 | $('#add_notification_purpose').val('add'); 387 | 388 | savebutton.val(strings.save); 389 | }; 390 | 391 | init(); 392 | }); 393 | } 394 | }; 395 | }); -------------------------------------------------------------------------------- /amd/src/notif.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: ["error", { allow: ["error"] }], max-nested-callbacks: ["error", 7] */ 2 | /** 3 | * @package block_advnotifications 4 | * @copyright 2019 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 5 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6 | * @author Zander Potgieter 7 | */ 8 | 9 | /** 10 | * @module block_advnotifications/notif 11 | */ 12 | define(['jquery'], function($) { 13 | // JQuery is available via $. 14 | 15 | return { 16 | initialise: function() { 17 | // Module initialised. 18 | $(document).ready(function() { 19 | // USER DISMISSING/CLICKING ON A NOTIFICATION. 20 | $('.block_advnotifications').on('click', '.dismissible', function() { 21 | 22 | var dismiss = $(this).attr('data-dismiss'); 23 | 24 | $(this).slideUp('150', function() { 25 | $(this).remove(); 26 | }); 27 | 28 | // TODO - Move ajax call to Moodle's ajax/webservice call. 29 | var senddata = {}; // Data Object. 30 | senddata.call = 'ajax'; 31 | senddata.dismiss = dismiss; 32 | 33 | var callpath = M.cfg.wwwroot + "/blocks/advnotifications/pages/process.php?sesskey=" + M.cfg.sesskey; 34 | 35 | // Update user preferences. 36 | $.post(callpath, senddata).fail(function() { 37 | console.error("No 'dismiss' response received."); 38 | }).done(function() { 39 | // User dismissed notification. Do something maybe... 40 | }); 41 | }); 42 | }); 43 | } 44 | }; 45 | }); -------------------------------------------------------------------------------- /block_advnotifications.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Block for displaying notifications to users. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | 29 | /** 30 | * Class block_advnotifications extends base blocks class. Initialise and render notifications. 31 | * 32 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 33 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 | */ 35 | class block_advnotifications extends block_base 36 | { 37 | /** 38 | * Initialise block, set title. 39 | */ 40 | public function init() { 41 | $this->title = get_string('advnotifications', 'block_advnotifications'); 42 | } 43 | 44 | /** 45 | * Get and render content of block. 46 | * 47 | * @return bool|stdClass|stdObject 48 | * @throws dml_exception 49 | */ 50 | public function get_content() { 51 | global $CFG; 52 | 53 | if ($this->content !== null) { 54 | return $this->content; 55 | } 56 | 57 | if (get_config('block_advnotifications', 'enable')) { 58 | require_once($CFG->dirroot . '/blocks/advnotifications/locallib.php'); 59 | 60 | $this->content = new stdClass(); 61 | 62 | // Get the renderer for this page. 63 | $renderer = $this->page->get_renderer('block_advnotifications'); 64 | 65 | // Get & prepare notifications to render. 66 | $notifications = prep_notifications($this->instance->id); 67 | 68 | // Render notifications. 69 | $html = $renderer->render_notification($notifications); 70 | 71 | $this->content->text = $html; 72 | 73 | return $this->content; 74 | } 75 | 76 | return false; 77 | } 78 | 79 | /** 80 | * FROM ::parent DOCS. 81 | * Return a block_contents object representing the full contents of this block. 82 | * 83 | * This internally calls ->get_content(), and then adds the editing controls etc. 84 | * 85 | * @param renderer_base $output The core_renderer to use when generating the output. 86 | * @return block_contents $bc A representation of the block, for rendering. 87 | * @since Moodle 2.0. 88 | * @throws moodle_exception 89 | */ 90 | public function get_content_for_output($output) { 91 | $bc = parent::get_content_for_output($output); 92 | 93 | // Only do this if bc has been set (block has content, editing mode on, etc). 94 | if (isset($bc)) { 95 | $context = context_system::instance(); 96 | $bcontext = context_block::instance($bc->blockinstanceid); 97 | if ($this->page->user_can_edit_blocks() && 98 | (has_capability('block/advnotifications:managenotifications', $context) || 99 | has_capability('block/advnotifications:manageownnotifications', $bcontext))) { 100 | // Edit config icon - always show - needed for positioning UI. 101 | $str = new lang_string('advnotifications_table_title', 'block_advnotifications'); 102 | $controls = new action_menu_link_secondary( 103 | new moodle_url('/blocks/advnotifications/pages/notifications.php', array('blockid' => $bc->blockinstanceid)), 104 | new pix_icon('a/view_list_active', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')), 105 | $str, 106 | array('class' => 'editing_manage') 107 | ); 108 | 109 | array_unshift($bc->controls, $controls); 110 | } 111 | } 112 | 113 | return $bc; 114 | } 115 | 116 | /** 117 | * Gets Javascript that's required by the plugin. 118 | */ 119 | public function get_required_javascript() { 120 | parent::get_required_javascript(); 121 | 122 | $this->page->requires->js_call_amd('block_advnotifications/notif', 'initialise'); 123 | } 124 | 125 | /** 126 | * Allow multiple instances of the block throughout the site. 127 | * 128 | * @return bool 129 | */ 130 | public function instance_allow_multiple() { 131 | // Are you going to allow multiple instances of each block? 132 | // If yes, then it is assumed that the block WILL use per-instance configuration. 133 | return true; 134 | } 135 | 136 | /** 137 | * HTML attributes such as 'class' or 'title' can be injected into the block. 138 | * 139 | * @return array 140 | */ 141 | public function html_attributes() { 142 | $attributes = parent::html_attributes(); 143 | 144 | if (!empty($this->config->class)) { 145 | $attributes['class'] .= ' ' . $this->config->class; 146 | } 147 | 148 | return $attributes; 149 | } 150 | 151 | /** 152 | * Specifies that block has global configurations/admin settings 153 | * 154 | * @return bool 155 | */ 156 | public function has_config() { 157 | return true; 158 | } 159 | 160 | /** 161 | * Default return is false - header will be shown. Added check to show heading only if editing. 162 | * 163 | * @return boolean 164 | */ 165 | public function hide_header() { 166 | // If editing, show header. 167 | if ($this->page->user_is_editing()) { 168 | return false; 169 | } 170 | return true; 171 | } 172 | } -------------------------------------------------------------------------------- /classes/base_table.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Base table that lists notifications to allow visibility and certain actions. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | // Load tablelib lib. 29 | require_once($CFG->dirroot .'/lib/tablelib.php'); 30 | 31 | // The word 'notifications' is used twice, as I'm using the 'pluginname_filename' convention. 32 | 33 | /** 34 | * Sets up the table which lists notifications and allows for management of listed items. 35 | * 36 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 37 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 | */ 39 | class advnotifications_base_table extends table_sql { 40 | 41 | // Lang strings that get re-used below is stored in variables to improve efficiency (Don't have to get strings many times). 42 | /** 43 | * @var null|string 44 | */ 45 | private $yes = null; 46 | /** 47 | * @var null|string 48 | */ 49 | private $no = null; 50 | 51 | /** 52 | * Constructor 53 | * @param int $uniqueid all tables have to have a unique id, this is used 54 | * as a key when storing table properties like sort order in the session. 55 | */ 56 | public function __construct($uniqueid) { 57 | parent::__construct($uniqueid); 58 | // Define the list of columns to show. 59 | $columns = array( 60 | 'id', 61 | 'title', 62 | 'type', 63 | 'enabled', 64 | 'global', 65 | 'aicon', 66 | 'dismissible', 67 | 'times', 68 | 'date_from', 69 | 'date_to', 70 | 'actions' 71 | ); 72 | $this->define_columns($columns); 73 | 74 | // Define the titles of columns to show in header from lang file. // Examples. 75 | $headers = array( 76 | get_string('advnotifications_field_id', 'block_advnotifications'), // Id: 1. 77 | get_string('advnotifications_field_title', 'block_advnotifications'), // Title: Site Maintenance. 78 | get_string('advnotifications_field_type', 'block_advnotifications'), // Type: info. 79 | get_string('advnotifications_field_enabled', 'block_advnotifications'), // Enabled: Yes. 80 | get_string('advnotifications_field_global', 'block_advnotifications'), // Global: Yes. 81 | get_string('advnotifications_field_aicon', 'block_advnotifications'), // AIcon: Yes. 82 | get_string('advnotifications_field_dismissible', 'block_advnotifications'), // Dismissible: Yes. 83 | get_string('advnotifications_field_times', 'block_advnotifications'), // Times: 10. 84 | get_string('advnotifications_field_date_from', 'block_advnotifications'), // Date From: dd/mm/yyyy. 85 | get_string('advnotifications_field_date_to', 'block_advnotifications'), // Date To: dd/mm/yyyy. 86 | get_string('advnotifications_field_actions', 'block_advnotifications'), // Actions: Edit | Delete. 87 | ); 88 | $this->define_headers($headers); 89 | 90 | $this->sortable(true, 'id', SORT_DESC); 91 | $this->no_sorting('actions'); 92 | 93 | // Lang string initialisation. 94 | $this->yes = get_string('advnotifications_cell_yes', 'block_advnotifications'); // Yes. 95 | $this->no = get_string('advnotifications_cell_no', 'block_advnotifications'); // No. 96 | } 97 | 98 | /** 99 | * This function is called for each data row to allow processing of the 100 | * id value. 101 | * 102 | * @param object $values Contains object with all the values of record. 103 | * @return integer Returns notification ids - easier sorting 104 | */ 105 | public function col_id($values) { 106 | return $values->id; 107 | } 108 | 109 | /** 110 | * This function is called for each data row to allow processing of the 111 | * title value. 112 | * 113 | * @param object $values Contains object with all the values of record. 114 | * @return $string Returns notification's title - easier sorting 115 | */ 116 | public function col_title($values) { 117 | return shorten_text($values->title, 22, true); 118 | } 119 | 120 | /** 121 | * This function is called for each data row to allow processing of the 122 | * type value. 123 | * 124 | * @param object $values Contains object with all the values of record. 125 | * @return $string Return notification type (for styling purposes) 126 | */ 127 | public function col_type($values) { 128 | return get_string('advnotifications_add_option_' . $values->type, 'block_advnotifications'); 129 | } 130 | 131 | /** 132 | * This function is called for each data row to allow processing of the 133 | * enabled value. 134 | * 135 | * @param object $values Contains object with all the values of record. 136 | * @return $string Return whether notification is enabled or not 137 | */ 138 | public function col_enabled($values) { 139 | return ($values->enabled == 1 ? $this->yes : $this->no); 140 | } 141 | 142 | /** 143 | * This function is called for each data row to allow processing of the 144 | * global value. 145 | * 146 | * @param object $values Contains object with all the values of record. 147 | * @return $string Return whether notification will be propogated globally/site-wide or not 148 | */ 149 | public function col_global($values) { 150 | return ($values->global == 1 ? $this->yes : $this->no); 151 | } 152 | 153 | /** 154 | * This function is called for each data row to allow processing of the 155 | * aicon value. 156 | * 157 | * @param object $values Contains object with all the values of record. 158 | * @return $string Return whether notification is to display an icon or not 159 | */ 160 | public function col_aicon($values) { 161 | return ($values->aicon == 1 ? $this->yes : $this->no); 162 | } 163 | 164 | /** 165 | * This function is called for each data row to allow processing of the 166 | * dismissible value. 167 | * 168 | * @param object $values Contains object with all the values of record. 169 | * @return $string Return whether notification is dismissible or not 170 | */ 171 | public function col_dismissible($values) { 172 | return ($values->dismissible == 1 ? $this->yes : $this->no); 173 | } 174 | 175 | /** 176 | * This function is called for each data row to allow processing of the 177 | * times value. 178 | * 179 | * @param object $values Contains object with all the values of record. 180 | * @return $string Return number of times the user should view the notification 181 | */ 182 | public function col_times($values) { 183 | return $values->times; 184 | } 185 | 186 | /** 187 | * This function is called for each data row to allow processing of the 188 | * date_from value. 189 | * 190 | * @param object $values Contains object with all the values of record. 191 | * @return integer Return value from when the notification should be displayed 192 | * @throws dml_exception 193 | */ 194 | public function col_date_from($values) { 195 | if ($values->date_from <= 0) { 196 | return '-'; 197 | } 198 | 199 | return date(get_config('block_advnotifications', 'dateformat'), $values->date_from); 200 | } 201 | 202 | /** 203 | * This function is called for each data row to allow processing of the 204 | * date_to value. 205 | * 206 | * @param object $values Contains object with all the values of record. 207 | * @return integer Return value until when the notification should be displayed 208 | * @throws dml_exception 209 | */ 210 | public function col_date_to($values) { 211 | if ($values->date_to <= 0 || $values->date_from === $values->date_to) { 212 | return '-'; 213 | } 214 | 215 | return date(get_config('block_advnotifications', 'dateformat'), $values->date_to); 216 | } 217 | 218 | /** 219 | * This function is not part of the public api. 220 | */ 221 | public function print_nothing_to_display() { 222 | $this->print_initials_bar(); 223 | 224 | echo '

' . get_string('advnotifications_table_empty', 'block_advnotifications') . '

'; 225 | } 226 | } -------------------------------------------------------------------------------- /classes/event/notification_created.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * The block_advnotifications notification created event. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2021 Daniel Neis Araujo 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace block_advnotifications\event; 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | /** 29 | * The block_advnotifications notification created event class. 30 | * 31 | * @package block_advnotifications 32 | * @copyright 2021 Daniel Neis Araujo 33 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 | */ 35 | class notification_created extends \core\event\base { 36 | 37 | /** 38 | * Init method. 39 | */ 40 | protected function init() { 41 | $this->data['objecttable'] = 'block_advnotifications'; 42 | $this->data['crud'] = 'c'; 43 | $this->data['edulevel'] = self::LEVEL_PARTICIPATING; 44 | } 45 | 46 | /** 47 | * Get event name. 48 | * 49 | * @return string 50 | */ 51 | public static function get_name() { 52 | return get_string('event_notification_created', 'block_advnotifications'); 53 | } 54 | 55 | /** 56 | * Get URL related to the action. 57 | * 58 | * @return \moodle_url 59 | */ 60 | public function get_url() { 61 | return new \moodle_url('/block/advnotifications/notifications.php', array('blockid' => $this->contextinstanceid)); 62 | } 63 | 64 | /** 65 | * Returns description of what happened. 66 | * 67 | * @return string 68 | */ 69 | public function get_description() { 70 | return "The user with id '$this->userid' created the notification with id '$this->objectid' 71 | for the block with id '$this->contextinstanceid'."; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /classes/event/notification_deleted.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * The block_advnotifications notification deleted event. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2021 Daniel Neis Araujo 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace block_advnotifications\event; 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | /** 29 | * The block_advnotifications notification deleted event class. 30 | * 31 | * @package block_advnotifications 32 | * @copyright 2021 Daniel Neis Araujo 33 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 | */ 35 | class notification_deleted extends \core\event\base { 36 | 37 | /** 38 | * Init method. 39 | */ 40 | protected function init() { 41 | $this->data['objecttable'] = 'block_advnotifications'; 42 | $this->data['crud'] = 'd'; 43 | $this->data['edulevel'] = self::LEVEL_PARTICIPATING; 44 | } 45 | 46 | /** 47 | * Get event name. 48 | * 49 | * @return string 50 | */ 51 | public static function get_name() { 52 | return get_string('event_notification_deleted', 'block_advnotifications'); 53 | } 54 | 55 | /** 56 | * Get URL related to the action. 57 | * 58 | * @return \moodle_url 59 | */ 60 | public function get_url() { 61 | return new \moodle_url('/block/advnotifications/notifications.php', array('blockid' => $this->contextinstanceid)); 62 | } 63 | 64 | /** 65 | * Returns description of what happened. 66 | * 67 | * @return string 68 | */ 69 | public function get_description() { 70 | return "The user with id '$this->userid' deleted the notification with id '$this->objectid' 71 | for the block with id '$this->contextinstanceid'. 72 | The title was '" . $this->other['old_title'] . "'. 73 | The message was '" . $this->other['old_message'] . "'. 74 | The date from was '" . userdate($this->other['old_date_from']) . "'. 75 | The date to was '" . userdate($this->other['old_date_to']) . "'."; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /classes/event/notification_updated.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * The block_advnotifications notification updated event. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2021 Daniel Neis Araujo 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace block_advnotifications\event; 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | /** 29 | * The block_advnotifications notification updated event class. 30 | * 31 | * @package block_advnotifications 32 | * @copyright 2021 Daniel Neis Araujo 33 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 | */ 35 | class notification_updated extends \core\event\base { 36 | 37 | /** 38 | * Init method. 39 | */ 40 | protected function init() { 41 | $this->data['objecttable'] = 'block_advnotifications'; 42 | $this->data['crud'] = 'u'; 43 | $this->data['edulevel'] = self::LEVEL_OTHER; 44 | } 45 | 46 | /** 47 | * Get event name. 48 | * 49 | * @return string 50 | */ 51 | public static function get_name() { 52 | return get_string('event_notification_updated', 'block_advnotifications'); 53 | } 54 | 55 | /** 56 | * Get URL related to the action. 57 | * 58 | * @return \moodle_url 59 | */ 60 | public function get_url() { 61 | return new \moodle_url('/block/advnotifications/notifications.php', array('blockid' => $this->contextinstanceid)); 62 | } 63 | 64 | /** 65 | * Returns description of what happened. 66 | * 67 | * @return string 68 | */ 69 | public function get_description() { 70 | return "The user with id '$this->userid' updated the notification with id '$this->objectid' 71 | for the block with id '$this->contextinstanceid'. 72 | The title was changed from '" . $this->other['old_title'] . "' to '" . $this->other['new_title'] . "'. 73 | The message was changed from '" . $this->other['old_message'] . "' to '" . $this->other['new_message'] . "'. 74 | The date from was changed from '" . userdate($this->other['old_date_from']) . "' to '" . userdate($this->other['new_date_from']) . "'. 75 | The date to was changed from '" . userdate($this->other['old_date_to']) . "' to '" . userdate($this->other['new_date_to']) . "'."; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /classes/notifications_table.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Table that lists notifications. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | // Load parent (& tablelib lib). 29 | require_once(dirname(__FILE__) . '/base_table.php'); 30 | 31 | // The word 'notifications' is used twice, as I'm using the 'pluginname_filename' convention. 32 | 33 | /** 34 | * Sets up the table which lists notifications and allows for management of listed items. 35 | * 36 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 37 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 | */ 39 | class advnotifications_notifications_table extends advnotifications_base_table { 40 | 41 | /** 42 | * This function is called for each data row to allow processing of the 43 | * actions value. 44 | * 45 | * @param object $values Contains object with all the values of record. 46 | * @return string Return url to view the individual transaction 47 | * @throws coding_exception 48 | */ 49 | public function col_actions($values) { 50 | global $CFG; 51 | 52 | if ($this->is_downloading()) { 53 | return get_string('advnotifications_edit_label', 'block_advnotifications') . ' | ' . 54 | get_string('advnotifications_delete_label', 'block_advnotifications'); 55 | } else { 56 | return '
58 | 59 | 60 | 61 | 62 | 64 |
65 |
67 | 68 | 69 | 70 | 71 | 73 |
'; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Privacy API provider. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2018 LearningWorks Ltd - learningworks.co.nz 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace block_advnotifications\privacy; 26 | 27 | use context_block; 28 | use context_system; 29 | use core_privacy\local\metadata\collection; 30 | use \core_privacy\local\metadata\provider as metadata_provider; 31 | use core_privacy\local\request\approved_contextlist; 32 | use core_privacy\local\request\approved_userlist; 33 | use core_privacy\local\request\contextlist; 34 | use core_privacy\local\request\core_userlist_provider; 35 | use core_privacy\local\request\plugin\provider as plugin_provider; 36 | use core_privacy\local\request\transform; 37 | use core_privacy\local\request\userlist; 38 | use core_privacy\local\request\writer; 39 | 40 | defined('MOODLE_INTERNAL') || die(); 41 | 42 | /** @var string Flag used to determine if notification is block-based or global */ 43 | const SITE_NOTIFICATION = "-1"; 44 | 45 | /** 46 | * Class provider - extends core to leverage the Privacy API. 47 | * 48 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 49 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 | */ 51 | class provider implements 52 | // This plugin stores personal user data. 53 | metadata_provider, 54 | 55 | // Data is provided directly to core. 56 | plugin_provider, 57 | 58 | // This plugin is can determine which users' data it's captured. 59 | core_userlist_provider { 60 | 61 | /** 62 | * Get metadata about a user used by the plugin. 63 | * 64 | * @param collection $collection The collection of metadata. 65 | * @return collection $collection The collection returned as a whole. 66 | */ 67 | public static function get_metadata(collection $collection) : collection { 68 | // Add items to collection. 69 | $collection->add_database_table( 70 | 'block_advnotifications', 71 | [ 72 | 'title' => 'privacy:metadata:block_advnotifications:title', 73 | 'message' => 'privacy:metadata:block_advnotifications:message', 74 | 'blockid' => 'privacy:metadata:block_advnotifications:blockid', 75 | 'deleted' => 'privacy:metadata:block_advnotifications:deleted', 76 | 'deleted_by' => 'privacy:metadata:block_advnotifications:deleted_by', 77 | 'created_by' => 'privacy:metadata:block_advnotifications:created_by' 78 | ], 79 | 'privacy:metadata:block_advnotifications' 80 | ); 81 | 82 | $collection->add_database_table( 83 | 'block_advnotificationsdissed', 84 | [ 85 | 'user_id' => 'privacy:metadata:block_advnotificationsdissed:user_id', 86 | 'not_id' => 'privacy:metadata:block_advnotificationsdissed:not_id', 87 | 'dismissed' => 'privacy:metadata:block_advnotificationsdissed:dismissed', 88 | 'seen' => 'privacy:metadata:block_advnotificationsdissed:seen' 89 | ], 90 | 'privacy:metadata:block_advnotificationsdissed' 91 | ); 92 | 93 | return $collection; 94 | } 95 | 96 | /** 97 | * Get list of contexts containing user info for the given user. 98 | * 99 | * @param int $userid User ID to find contexts for. 100 | * @return contextlist $contextlist List of contexts used by the plugin. 101 | */ 102 | public static function get_contexts_for_userid(int $userid) : contextlist { 103 | // User data only on system/block context. 104 | global $DB; 105 | $contextlist = new \core_privacy\local\request\contextlist(); 106 | 107 | // Get context IDs from block instance IDs of notifications user has seen. 108 | $contextlist->add_from_sql( 109 | "SELECT c.id 110 | FROM {context} c 111 | JOIN {block_advnotifications} adv ON adv.blockid = c.instanceid 112 | JOIN {block_advnotificationsdissed} advdis ON advdis.user_id = :userid 113 | WHERE c.contextlevel = :contextblock", 114 | array('userid' => $userid, 'contextblock' => CONTEXT_BLOCK) 115 | ); 116 | 117 | // TODO: Check if needed... 118 | // Check if system context should be added for user. 119 | $blockids = $DB->get_records_sql( 120 | "SELECT DISTINCT adv.blockid 121 | FROM {block_advnotifications} adv 122 | JOIN {block_advnotificationsdissed} advdis ON advdis.not_id = adv.id 123 | WHERE advdis.user_id = :userid", 124 | array('userid' => $userid) 125 | ); 126 | 127 | // Check if block ID is not empy/null. 128 | if (isset($blockids) && !empty($blockids)) { 129 | 130 | // If notification set to display globally, system context is added. 131 | if (array_key_exists(SITE_NOTIFICATION, $blockids)) { 132 | $contextlist->add_system_context(); 133 | } 134 | } 135 | 136 | return $contextlist; 137 | } 138 | 139 | /** 140 | * Get the list of users who have data within a context. 141 | * 142 | * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 143 | */ 144 | public static function get_users_in_context(userlist $userlist) { 145 | $context = $userlist->get_context(); 146 | 147 | // Check if contexts are one block or system-level (others not allowed). 148 | if (!($context instanceof context_block || $context instanceof context_system)) { 149 | return; 150 | } 151 | 152 | // Different sql for system/block contexts. 153 | if ($context->contextlevel == CONTEXT_SYSTEM) { 154 | // Get and add user IDs in system context. 155 | $userlist->add_from_sql('user_id', 156 | "SELECT advdis.user_id 157 | FROM {block_advnotificationsdissed} advdis 158 | JOIN {block_advnotifications} adv ON adv.id = advdis.not_id 159 | WHERE adv.blockid = :blockid", 160 | array('blockid' => SITE_NOTIFICATION) 161 | ); 162 | } else if ($context->contextlevel == CONTEXT_BLOCK) { 163 | // Get and add user IDs in block context. 164 | $userlist->add_from_sql('user_id', 165 | "SELECT advdis.user_id 166 | FROM {block_advnotificationsdissed} advdis 167 | JOIN {block_advnotifications} adv ON adv.id = advdis.not_id 168 | JOIN {context} c ON c.instanceid = adv.blockid 169 | WHERE c.contextlevel = :contextlevel", 170 | array('contextlevel' => CONTEXT_BLOCK) 171 | ); 172 | } 173 | } 174 | 175 | /** 176 | * Export all of user's data in the given context(s). 177 | * 178 | * @param approved_contextlist $contextlist The approved contexts to export information for. 179 | */ 180 | public static function export_user_data(approved_contextlist $contextlist) { 181 | global $DB; 182 | 183 | $blockdata = []; 184 | $sitedata = []; 185 | 186 | $userid = $contextlist->get_user()->id; 187 | 188 | /* TODO: Edge case if user has not seen the notification... What about deleted_by or created_by? 189 | MAYBE ADD: OR adv.deleted_by = :userid 190 | OR adv.created_by = :userid */ 191 | $alluserdata = $DB->get_records_sql( 192 | "SELECT adv.title, adv.message, adv.blockid, adv.deleted, adv.deleted_by, adv.created_by, 193 | advdis.user_id, advdis.dismissed, advdis.seen 194 | FROM {block_advnotifications} adv 195 | JOIN {block_advnotificationsdissed} advdis ON advdis.not_id = adv.id 196 | WHERE advdis.user_id = :userid", 197 | array('userid' => $userid) 198 | ); 199 | 200 | // Get and export user data. 201 | foreach ($alluserdata as $userdata) { 202 | if ($userdata->blockid !== SITE_NOTIFICATION) { 203 | $blockdata[] = (object)[ 204 | 'title' => $userdata->title, 205 | 'message' => $userdata->message, 206 | 'blockid' => $userdata->blockid, 207 | 'deleted' => transform::yesno($userdata->deleted), 208 | 'deleted_by' => $userdata->deleted_by, 209 | 'created_by' => $userdata->created_by, 210 | 'user_id' => $userdata->user_id, 211 | 'dismissed' => transform::yesno($userdata->dismissed), 212 | 'seen' => $userdata->seen 213 | ]; 214 | } else if ($userdata->blockid === SITE_NOTIFICATION) { 215 | $sitedata[] = (object)[ 216 | 'title' => $userdata->title, 217 | 'message' => $userdata->message, 218 | 'blockid' => $userdata->blockid, 219 | 'deleted' => transform::yesno($userdata->deleted), 220 | 'deleted_by' => $userdata->deleted_by, 221 | 'created_by' => $userdata->created_by, 222 | 'user_id' => $userdata->user_id, 223 | 'dismissed' => transform::yesno($userdata->dismissed), 224 | 'seen' => $userdata->seen 225 | ]; 226 | } 227 | } 228 | 229 | // Export depending on context. 230 | if (isset($blockdata) && !empty($blockdata)) { 231 | $data = (object)[ 232 | 'advnotifications' => $blockdata, 233 | ]; 234 | 235 | // Using user context for better export presentation. 236 | writer::with_context(\context_user::instance($userid))->export_data([ 237 | get_string('pluginname', 'block_advnotifications') 238 | ], $data); 239 | } 240 | if (isset($sitedata) && !empty($sitedata)) { 241 | $data = (object)[ 242 | 'advnotifications' => $sitedata, 243 | ]; 244 | writer::with_context(\context_system::instance())->export_data([ 245 | get_string('pluginname', 'block_advnotifications') 246 | ], $data); 247 | } 248 | } 249 | 250 | /** 251 | * Delete all data for all users in the specified context. 252 | * 253 | * @param \context $context The specific context to delete data for. 254 | */ 255 | public static function delete_data_for_all_users_in_context(\context $context) { 256 | global $DB; 257 | 258 | // Check if approved context. 259 | if (!($context instanceof context_block || $context instanceof context_system)) { 260 | return; 261 | } 262 | 263 | // Handle system context first. 264 | if ($context->contextlevel == CONTEXT_SYSTEM) { 265 | $delrecords = $DB->get_records('block_advnotifications', 266 | array('blockid' => SITE_NOTIFICATION), 267 | null, 268 | 'id'); 269 | 270 | foreach ($delrecords as $delrecord) { 271 | static::adv_delete_record_data($delrecord->id); 272 | } 273 | } else if ($context->contextlevel == CONTEXT_BLOCK) { // Handle block context next. 274 | // Get id of notification data to delete based on provided context instance id. 275 | // And block instance id is saved in DB already, so just check against that. 276 | $delrecords = $DB->get_records('block_advnotifications', 277 | array('blockid' => $context->instanceid), 278 | null, 279 | 'id'); 280 | 281 | foreach ($delrecords as $delrecord) { 282 | static::adv_delete_record_data($delrecord->id); 283 | } 284 | } 285 | } 286 | 287 | /** 288 | * Delete all user data for the specified user, in the specified contexts. 289 | * 290 | * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 291 | */ 292 | public static function delete_data_for_user(approved_contextlist $contextlist) { 293 | // Get user id. 294 | $userid = $contextlist->get_user()->id; 295 | 296 | // Delete their data. 297 | static::adv_delete_user_data($userid); 298 | } 299 | 300 | /** 301 | * Delete multiple users within a single context. 302 | * 303 | * @param approved_userlist $userlist The approved context and user information to delete information for. 304 | */ 305 | public static function delete_data_for_users(approved_userlist $userlist) { 306 | // For each user, delete the user data. 307 | foreach ($userlist->get_userids() as $userid) { 308 | static::adv_delete_user_data($userid); 309 | } 310 | } 311 | 312 | /** 313 | * Delete user data generated/managed by the block - based on user passed. 314 | * 315 | * @param int $userid The userid of the user which data need to be deleted. 316 | */ 317 | public static function adv_delete_user_data($userid) { 318 | // We won't delete notification due to user data being deleted - just 'clear' user id. 319 | global $DB; 320 | 321 | // If user created notification. 322 | $DB->set_field('block_advnotifications', 'created_by', -1, array('created_by' => $userid)); 323 | 324 | // If user deleted notification. 325 | $DB->set_field('block_advnotifications', 'deleted_by', -1, array('deleted_by' => $userid)); 326 | 327 | // If user viewed/dismsissed notification. 328 | $DB->delete_records('block_advnotificationsdissed', array('user_id' => $userid)); 329 | } 330 | 331 | /** 332 | * Delete user data generated/managed by the block - based on record passed. 333 | * 334 | * @param int $recordid The recordid of the notification which contains user data to be deleted. 335 | */ 336 | public static function adv_delete_record_data($recordid) { 337 | global $DB; 338 | // We won't delete notification due to user data being deleted - just 'clear' user id. 339 | 340 | // If user created notification. 341 | $DB->set_field('block_advnotifications', 'created_by', -1, array('id' => $recordid)); 342 | 343 | // If user deleted notification. 344 | $DB->set_field('block_advnotifications', 'deleted_by', -1, array('id' => $recordid)); 345 | 346 | // If user viewed/dismsissed notification. 347 | $DB->delete_records('block_advnotificationsdissed', array('not_id' => $recordid)); 348 | } 349 | } 350 | 351 | -------------------------------------------------------------------------------- /classes/restore_table.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Table to list and manage deleted notifications (restore). 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | // Load parent (& tablelib lib). 29 | require_once(dirname(__FILE__) . '/base_table.php'); 30 | 31 | /** 32 | * Sets up the table which lists deleted notification - that can be restored again or deleted permanently. 33 | * 34 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 35 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 | */ 37 | class advnotifications_restore_table extends advnotifications_base_table { 38 | 39 | /** 40 | * This function is called for each data row to allow processing of the 41 | * actions value. 42 | * 43 | * @param object $values Contains object with all the values of record. 44 | * @return $string Return url to view the individual transaction 45 | */ 46 | public function col_actions($values) { 47 | global $CFG; 48 | 49 | if ($this->is_downloading()) { 50 | return get_string('advnotifications_restore_label', 'block_advnotifications') . ' | ' . 51 | get_string('advnotifications_delete_label', 'block_advnotifications'); 52 | } else { 53 | return '
55 | 56 | 57 | 58 | 59 | 61 |
62 |
64 | 65 | 66 | 67 | 68 | 70 |
'; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /classes/task/advnotifications.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Setup scheduled tasks for performing routine maintenance. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2017 LearningWorks Ltd - learningworks.co.nz 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace block_advnotifications\task; 26 | 27 | defined('MOODLE_INTERNAL') || die(); 28 | 29 | /** 30 | * Class advnotifications - extends core to leverage the Tasks API. 31 | * 32 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 33 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 | */ 35 | class advnotifications extends \core\task\scheduled_task { 36 | /** 37 | * Get the name of the task. 38 | * 39 | * @return string. 40 | */ 41 | public function get_name() { 42 | // Shown in admin screens. 43 | return get_string('advnotifications_task_name', 'block_advnotifications'); 44 | } 45 | 46 | /** 47 | * Remove old (or deleted) notifications from table block_advnotifications & cleanup table block_advnotificationsdissed. 48 | */ 49 | public function execute() { 50 | global $DB; 51 | 52 | // TODO - echo "\n\t" . get_string('advnotifications_cron_heading', 'block_advnotifications') . "\n";. 53 | 54 | // Auto-Permanent Delete Feature. 55 | if (get_config('block_advnotifications', 'auto_perma_delete')) { 56 | // TODO - echo "\n\t\t- " . get_string('advnotifications_cron_auto_perma_delete', 'block_advnotifications') . "\n";. 57 | 58 | // Permanently delete notifications that's had the deleted flag for more than 30 days. 59 | $DB->delete_records_select('block_advnotifications', 60 | 'deleted_at < :limit AND deleted_at <> 0 AND deleted = 1', 61 | array('limit' => strtotime('-30 days')) 62 | ); 63 | } 64 | 65 | // Auto Delete Flagging Feature. 66 | if (get_config('block_advnotifications', 'auto_delete')) { 67 | // TODO - echo "\t\t- " . get_string('advnotifications_cron_auto_delete', 'block_advnotifications') . "\n";. 68 | 69 | // Add deleted flag to notifications that's passed their end-date. 70 | $DB->set_field_select('block_advnotifications', 71 | 'deleted', 72 | '1', 73 | 'date_to < :now AND date_from <> date_to', 74 | array('now' => time()) 75 | ); 76 | 77 | // Record time of setting 'deleted' flag. 78 | $DB->set_field_select('block_advnotifications', 79 | 'deleted_at', 80 | time(), 81 | 'deleted = 1' 82 | ); 83 | } 84 | 85 | // Auto User Data Deletion Feature. 86 | if (get_config('block_advnotifications', 'auto_delete_user_data')) { 87 | // TODO - echo "\t\t- " . get_string('advnotifications_cron_auto_delete_udata', 'block_advnotifications') . "\n\n";. 88 | 89 | // Remove user records that relates to notifications that don't exist anymore. 90 | $todelete = $DB->get_records_sql('SELECT band.id 91 | FROM {block_advnotificationsdissed} band 92 | LEFT JOIN {block_advnotifications} ban ON band.not_id = ban.id 93 | WHERE ban.id IS NULL'); 94 | 95 | $DB->delete_records_list('block_advnotificationsdissed', 96 | 'id', 97 | array_keys((array)$todelete) 98 | ); 99 | } 100 | } 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Manage user capabilities specific to Advanced Notifications plugin. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | $capabilities = array( 29 | 'block/advnotifications:myaddinstance' => array( 30 | 'captype' => 'write', 31 | 'contextlevel' => CONTEXT_SYSTEM, 32 | 'archetypes' => array( 33 | 'user' => CAP_ALLOW 34 | ), 35 | 'clonepermissionsfrom' => 'moodle/my:manageblocks' 36 | ), 37 | 'block/advnotifications:addinstance' => array( 38 | 'riskbitmask' => RISK_SPAM | RISK_XSS, 39 | 'captype' => 'write', 40 | 'contextlevel' => CONTEXT_BLOCK, 41 | 'archetypes' => array( 42 | 'editingteacher' => CAP_ALLOW, 43 | 'manager' => CAP_ALLOW 44 | ), 45 | 'clonepermissionsfrom' => 'moodle/site:manageblocks' 46 | ), 47 | 'block/advnotifications:managenotifications' => array( 48 | 'riskbitmask' => RISK_SPAM, 49 | 'captype' => 'write', 50 | 'contextlevel' => CONTEXT_SYSTEM, 51 | 'archetypes' => array( 52 | 'frontpage' => CAP_PREVENT, 53 | 'guest' => CAP_PREVENT, 54 | 'user' => CAP_PREVENT, 55 | 'student' => CAP_PREVENT, 56 | 'teacher' => CAP_PREVENT, 57 | 'editingteacher' => CAP_PREVENT, 58 | 'coursecreator' => CAP_PREVENT, 59 | 'manager' => CAP_ALLOW, 60 | ) 61 | ), 62 | 'block/advnotifications:manageownnotifications' => array( 63 | 'riskbitmask' => RISK_SPAM, 64 | 'captype' => 'write', 65 | 'contextlevel' => CONTEXT_BLOCK, 66 | 'archetypes' => array( 67 | 'frontpage' => CAP_PREVENT, 68 | 'guest' => CAP_PREVENT, 69 | 'user' => CAP_PREVENT, 70 | 'student' => CAP_PREVENT, 71 | 'teacher' => CAP_PREVENT, 72 | 'editingteacher' => CAP_PREVENT, 73 | 'coursecreator' => CAP_PREVENT, 74 | 'manager' => CAP_ALLOW, 75 | ) 76 | ) 77 | ); -------------------------------------------------------------------------------- /db/install.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 |
40 |
-------------------------------------------------------------------------------- /db/tasks.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * When to run the task(s). 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2017 LearningWorks Ltd - learningworks.co.nz 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $tasks = array( 28 | array( 29 | 'classname' => 'block_advnotifications\task\advnotifications', 30 | 'blocking' => 0, 31 | 'minute' => '0', 32 | 'hour' => '0', 33 | 'day' => '*', 34 | 'dayofweek' => '*', 35 | 'month' => '*' 36 | ) 37 | ); 38 | -------------------------------------------------------------------------------- /db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Executes on plugin upgrade. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | /** 29 | * When upgrading plugin, execute the following code. 30 | * 31 | * @param int $oldversion Previous version of plugin (from DB). 32 | * @return bool Successful upgrade or not. 33 | */ 34 | function xmldb_block_advnotifications_upgrade($oldversion) { 35 | global $DB; 36 | $dbman = $DB->get_manager(); 37 | 38 | // This was needed to add tables during development - probably not even need anymore. Leaving just-in-case (for now). 39 | if ($oldversion < 2017100217) { 40 | // Define table block_advnotifications to be created. 41 | $table = new xmldb_table('block_advnotifications'); 42 | 43 | // Adding fields to table block_advnotifications. 44 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 45 | $table->add_field('title', XMLDB_TYPE_TEXT, null, null, null, null, null); 46 | $table->add_field('message', XMLDB_TYPE_TEXT, null, null, null, null, null); 47 | $table->add_field('type', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'info'); 48 | $table->add_field('aicon', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null); 49 | $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null); 50 | $table->add_field('global', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1'); 51 | $table->add_field('blockid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '1'); 52 | $table->add_field('dismissible', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null); 53 | $table->add_field('date_from', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, '0000000000'); 54 | $table->add_field('date_to', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, '0000000000'); 55 | $table->add_field('times', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0'); 56 | $table->add_field('deleted', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null); 57 | $table->add_field('deleted_at', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, '0000000000'); 58 | $table->add_field('deleted_by', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1'); 59 | $table->add_field('created_by', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1'); 60 | 61 | // Adding keys to table block_advnotifications. 62 | $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 63 | 64 | // Conditionally launch create table for block_advnotifications. 65 | if (!$dbman->table_exists($table)) { 66 | $dbman->create_table($table); 67 | } 68 | 69 | // Define table block_advnotificationsdissed to be created. 70 | $table = new xmldb_table('block_advnotificationsdissed'); 71 | 72 | // Adding fields to table block_advnotificationsdissed. 73 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 74 | $table->add_field('user_id', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null); 75 | $table->add_field('not_id', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null); 76 | $table->add_field('dismissed', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, null); 77 | $table->add_field('seen', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0'); 78 | 79 | // Adding keys to table block_advnotificationsdissed. 80 | $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 81 | 82 | // Conditionally launch create table for block_advnotificationsdissed. 83 | if (!$dbman->table_exists($table)) { 84 | $dbman->create_table($table); 85 | } 86 | 87 | // Advnotifications savepoint reached. 88 | upgrade_block_savepoint(true, 2017100217, 'advnotifications'); 89 | } 90 | 91 | // If upgrading from version earlier than v1.4.1 - max for 'seen' needs to be increased. 92 | if ($oldversion < 2021010616) { 93 | 94 | // Increase length/precision of field 'seen' in table 'block_advnotificationsdissed' to '10'. 95 | $table = new xmldb_table('block_advnotificationsdissed'); 96 | $field = new xmldb_field('seen', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 97 | 98 | // Update DB. 99 | $dbman->change_field_precision($table, $field); 100 | 101 | // Upgrade savepoint reached. 102 | upgrade_block_savepoint(true, 2021010616, 'advnotifications'); 103 | } 104 | 105 | // Add future upgrade points here. 106 | 107 | return true; 108 | } -------------------------------------------------------------------------------- /docs/AdvNotifications.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/docs/AdvNotifications.gif -------------------------------------------------------------------------------- /docs/AdvNotifications.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/docs/AdvNotifications.webm -------------------------------------------------------------------------------- /docs/AdvancedNotifications.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/docs/AdvancedNotifications.pdf -------------------------------------------------------------------------------- /docs/AlertTypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/docs/AlertTypes.png -------------------------------------------------------------------------------- /edit_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Edit form to manipulate block configuration. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | /** 29 | * Form displayed when configuring block. 30 | * 31 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | class block_advnotifications_edit_form extends block_edit_form { 35 | 36 | /** 37 | * Build form. 38 | * 39 | * @param object $mform 40 | */ 41 | protected function specific_definition($mform) { 42 | global $CFG; 43 | 44 | // Used for navigation links to keep track of blockid (if any). 45 | $blockid = optional_param('bui_editid', '', PARAM_INT); 46 | $param = ''; 47 | $xparam = ''; 48 | 49 | if (isset($blockid) && $blockid !== '') { 50 | $param = '?blockid=' . $blockid; 51 | $xparam = '&blockid=' . $blockid; 52 | } 53 | 54 | // Section header title according to language file. 55 | $mform->addElement('header', 'configheader', get_string('blocksettings', 'block')); 56 | 57 | $context = context_system::instance(); 58 | 59 | // Only render links to users that are allowed to manage notifications. 60 | if (has_capability('block/advnotifications:managenotifications', $context)) { 61 | $mform->addElement('html', 62 | '' 79 | ); 80 | } 81 | 82 | // Allows a custom class to be added to the block for styling purposes. 83 | $mform->addElement('text', 'config_class', get_string('advnotifications_class', 'block_advnotifications')); 84 | $mform->setDefault('config_class', ''); 85 | $mform->setType('config_class', PARAM_TEXT); 86 | } 87 | } -------------------------------------------------------------------------------- /lang/en/block_advnotifications.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * All the configurable strings used throughout the plugin. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | $string['pluginname'] = 'Advanced notifications'; 27 | 28 | // Capabilities. 29 | $string['advnotifications:addinstance'] = 'Add a new Advanced notifications block'; 30 | $string['advnotifications:myaddinstance'] = 'Add a new Advanced notifications block to the my Moodle page'; 31 | $string['advnotifications:managenotifications'] = 'Manage notifications and the relative settings'; 32 | $string['advnotifications:manageownnotifications'] = 'Manage own notifications and the relative settings'; 33 | 34 | // Block Configuration. 35 | $string['advnotifications'] = 'Advanced notifications'; 36 | $string['advnotifications_class'] = 'Block class:'; 37 | 38 | // Notifications Table Column Names & Table Related Lang Strings. 39 | $string['advnotifications_field_id'] = 'ID'; 40 | $string['advnotifications_field_title'] = 'Title'; 41 | $string['advnotifications_field_type'] = 'Type'; 42 | $string['advnotifications_field_enabled'] = 'Enabled'; 43 | $string['advnotifications_field_global'] = 'Global'; 44 | $string['advnotifications_field_aicon'] = 'Icon'; 45 | $string['advnotifications_field_dismissible'] = 'Dismissible'; 46 | $string['advnotifications_field_times'] = 'View Times'; 47 | $string['advnotifications_field_date_from'] = 'From'; 48 | $string['advnotifications_field_date_to'] = 'To'; 49 | $string['advnotifications_field_actions'] = 'Actions'; 50 | $string['advnotifications_edit_label'] = 'Edit'; 51 | $string['advnotifications_delete_label'] = 'Delete'; 52 | $string['advnotifications_restore_label'] = 'Restore'; 53 | $string['advnotifications_table_empty'] = 'No notifications to show!'; 54 | $string['advnotifications_cell_yes'] = 'Yes'; 55 | $string['advnotifications_cell_no'] = 'No'; 56 | 57 | $string['advnotifications_restore_table_warning'] = 'Warning! Deleting notifications from this table will permanently delete it from the database. It is recommended to use the auto-delete features of the plugin...'; 58 | 59 | // Manage Advanced Notification Lang Strings. 60 | $string['advnotifications_table_title'] = 'Manage notifications'; 61 | $string['advnotifications_table_title_short'] = 'Manage'; 62 | $string['advnotifications_table_heading'] = 'Advanced notifications'; 63 | 64 | $string['advnotifications_restore_table_title'] = 'Restore notifications'; 65 | $string['advnotifications_restore_table_title_short'] = 'Restore notifications'; 66 | $string['advnotifications_restore_table_heading'] = 'Advanced notifications restore'; 67 | 68 | // New Notification Lang Strings. 69 | $string['advnotifications_enabled'] = 'Enabled?'; 70 | $string['advnotifications_global'] = 'Global notification?'; 71 | $string['add_notification_global_notice'] = 'This notification will be displayed globally/site-wide!'; 72 | $string['add_notif_local_notice'] = 'This notification will only be displayed on the page you\'re managing this block from!'; 73 | $string['advnotifications_title'] = 'Title'; 74 | $string['advnotifications_message'] = 'Message'; 75 | $string['advnotifications_type'] = 'Type'; 76 | $string['advnotifications_times'] = '# of times'; 77 | $string['advnotifications_times_label'] = 'Number of times to display the notification to a user (0 = forever)'; 78 | $string['advnotifications_aicon'] = 'Icon?'; 79 | $string['advnotifications_dismissible'] = 'Dismissible?'; 80 | $string['advnotifications_date_from'] = 'From:'; 81 | $string['advnotifications_date_to'] = 'To:'; 82 | $string['advnotifications_date_info'] = 'Same date = forever'; 83 | $string['advnotifications_save'] = 'Save'; 84 | $string['advnotifications_update'] = 'Update'; 85 | $string['advnotifications_cancel'] = 'Cancel'; 86 | $string['advnotifications_req'] = 'Required...'; 87 | $string['advnotifications_preview'] = 'Preview'; 88 | 89 | // Renderer. 90 | $string['advnotifications_add_heading'] = 'New notification'; 91 | $string['advnotifications_add_option_info'] = 'Information'; 92 | $string['advnotifications_add_option_success'] = 'Success'; 93 | $string['advnotifications_add_option_warning'] = 'Warning'; 94 | $string['advnotifications_add_option_danger'] = 'Danger'; 95 | $string['advnotifications_add_option_announcement'] = 'Announcement'; 96 | $string['advnotifications_add_saving'] = 'Saving...'; 97 | $string['advnotifications_add_done'] = 'Done!'; 98 | 99 | // Admin Settings. 100 | $string['setting/navigation'] = 'Navigation:'; 101 | $string['setting/navigation_desc'] = '{$a->left}{$a->right}'; 102 | 103 | $string['setting/settings'] = 'Settings:'; 104 | 105 | $string['setting/enable'] = 'Enable:'; 106 | $string['setting/enable_desc'] = 'Toggles whether all notifications are enabled/disabled
'; 107 | $string['setting/enable_default'] = ''; 108 | 109 | $string['setting/html'] = 'Allow HTML:'; 110 | $string['setting/html_desc'] = 'Toggles whether basic HTML is allowed in notifications\' titles/messages'; 111 | $string['setting/html_default'] = ''; 112 | 113 | $string['setting/multilang'] = 'Multi-lang/Filter support:'; 114 | $string['setting/multilang_desc'] = 'Toggles whether the multilang filter (and others) are supported in notifications\' titles/messages.
Note - This is for more advanced users & HTML needs to be enabled (above).
'; 115 | $string['setting/multilang_default'] = ''; 116 | 117 | $string['setting/dateformat'] = 'Date format:'; 118 | $string['setting/dateformat_desc'] = 'Dates will be shown in the chosen format.
'; 119 | 120 | $string['setting/auto_delete'] = 'Auto delete:'; 121 | $string['setting/auto_delete_desc'] = 'Toggles whether a notification that go past the set end-date is automatically deleted - but can be restored again.
(Helps with housekeeping/management)'; 122 | $string['setting/auto_delete_default'] = ''; 123 | 124 | $string['setting/auto_perma_delete'] = 'Auto permanent delete:'; 125 | $string['setting/auto_perma_delete_desc'] = 'Toggles whether notifications that have been deleted for more than 30 days are automatically permanently deleted from the database.
(Helps with housekeeping/management)'; 126 | $string['setting/auto_perma_delete_default'] = ''; 127 | 128 | $string['setting/auto_delete_user_data'] = 'Auto delete user data:'; 129 | $string['setting/auto_delete_user_data_desc'] = 'Toggles whether user data (such as whether the user has seen/dismissed notifications that don\'t exist anymore, etc) related to advanced notifications is automatically deleted.
(Helps with housekeeping/management)
'; 130 | $string['setting/auto_delete_user_data_default'] = ''; 131 | 132 | // Navigation Links. 133 | $string['advnotifications_nav_heading'] = 'Notifications:'; 134 | $string['advnotifications_nav_manage'] = 'Manage'; 135 | $string['advnotifications_nav_restore'] = 'Restore'; 136 | $string['advnotifications_nav_settings'] = 'Settings'; 137 | 138 | // Error Messages. 139 | $string['advnotifications_err_forbidden'] = 'Forbidden, please login again...'; 140 | $string['advnotifications_err_nojsedit'] = 'Editing only supported with JavaScript enabled. Re-create the desired notification or enable JavaScript and try again.'; 141 | $string['advnotifications_err_req'] = 'The following fields are required: {$a}'; 142 | $string['advnotifications_err_nocapability'] = 'You don\'t have permission to do that...'; 143 | 144 | // Cron Messages. 145 | $string['advnotifications_task_name'] = 'Advanced notifications'; 146 | $string['advnotifications_cron_heading'] = 'Cleaning advanced notifications'; 147 | $string['advnotifications_cron_auto_perma_delete'] = 'Permanently delete notifications that\'s had the deleted flag for more than 30 days...'; 148 | $string['advnotifications_cron_auto_delete'] = 'Add deleted flag to notifications that\'s passed their end-date...'; 149 | $string['advnotifications_cron_auto_delete_udata'] = 'Remove user records that relates to notifications that don\'t exist anymore...'; 150 | 151 | // Misc. 152 | $string['advnotifications_join'] = ' & '; 153 | $string['event_notification_created'] = 'Advanced notification created'; 154 | $string['event_notification_deleted'] = 'Advanced notification deleted'; 155 | $string['event_notification_updated'] = 'Advanced notification updated'; 156 | 157 | // Privacy API. 158 | $string['privacy:metadata:block_advnotifications'] = 'Information about notifications the user has been exposed to and recorded interactions.'; 159 | $string['privacy:metadata:block_advnotifications:title'] = 'The title of the notification.'; 160 | $string['privacy:metadata:block_advnotifications:message'] = 'The body/message of the notification.'; 161 | $string['privacy:metadata:block_advnotifications:blockid'] = 'The ID of the block from which the notification was created (if any).'; 162 | $string['privacy:metadata:block_advnotifications:deleted'] = 'Whether the notification has been deleted from the site (1 = deleted).'; 163 | $string['privacy:metadata:block_advnotifications:deleted_by'] = 'The ID of the user that deleted the notification (if any).'; 164 | $string['privacy:metadata:block_advnotifications:created_by'] = 'The ID of the user that created the notifications (if any).'; 165 | $string['privacy:metadata:block_advnotificationsdissed'] = 'Information about the user (as consumer)/notification relationship.'; 166 | $string['privacy:metadata:block_advnotificationsdissed:user_id'] = 'The ID of the user that has seen/dismissed the notification.'; 167 | $string['privacy:metadata:block_advnotificationsdissed:not_id'] = 'The associated notification ID.'; 168 | $string['privacy:metadata:block_advnotificationsdissed:dismissed'] = 'Flag of whether the notification has been dismissed by the user or not (1 = dismissed).'; 169 | $string['privacy:metadata:block_advnotificationsdissed:seen'] = 'A count of how many times the user has seen/been shown the notification.'; 170 | -------------------------------------------------------------------------------- /locallib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Library of functions for the plugin to leverage. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2018 LearningWorks Ltd - learningworks.co.nz 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | /** 28 | * This functions determines which notifications to render and what their attributes should be. 29 | * 30 | * @param mixed $instanceid Block instance id. 31 | * @return array Array of notifications' attributes needed for rendering. 32 | * @throws dml_exception 33 | */ 34 | function prep_notifications($instanceid) { 35 | global $DB, $USER; 36 | 37 | $filternotif = false; 38 | // Check if we should apply filters to title/message or not. 39 | if (get_config('block_advnotifications', 'multilang')) { 40 | $filternotif = true; 41 | } 42 | 43 | // Notifications to render. 44 | $rendernotif = []; 45 | 46 | // CONDITIONS - add any future conditions here. 47 | $conditions = array(); 48 | // No deleted notifications. 49 | $conditions['deleted'] = 0; 50 | // No disabled notifications. 51 | $conditions['enabled'] = 1; 52 | 53 | // Get notifications with conditions from above. 54 | $allnotifs = $DB->get_records('block_advnotifications', $conditions); 55 | 56 | foreach ($allnotifs as $notif) { 57 | // Keep track of number of times the user has seen the notification. 58 | // Check if a record of the user exists in the dismissed/seen table. 59 | // TODO: Move DB queries out of loop. 60 | $userseen = $DB->get_record('block_advnotificationsdissed', 61 | array('user_id' => $USER->id, 'not_id' => $notif->id) 62 | ); 63 | 64 | // Get notification settings to determine whether to render it or not. 65 | $render = false; 66 | 67 | // Check if forever or in date-range. 68 | if (($notif->date_from === $notif->date_to) || ($notif->date_from < time() && $notif->date_to > time())) { 69 | $render = true; 70 | } 71 | 72 | // Don't render if user has seen it more (or equal) to the times specified or if they've dismissed it. 73 | if ($userseen !== false) { 74 | if (($userseen->seen >= $notif->times && $notif->times != 0) || ($userseen->dismissed > 0)) { 75 | $render = false; 76 | } 77 | } 78 | 79 | // Don't render if notification isn't a global notification and the instanceid's/blockid's don't match. 80 | if ($notif->blockid != $instanceid && $notif->global == 0) { 81 | $render = false; 82 | } 83 | 84 | if ($render) { 85 | // Update how many times the user has seen the notification. 86 | if ($userseen === false) { 87 | $seenrecord = new stdClass(); 88 | $seenrecord->user_id = $USER->id; 89 | $seenrecord->not_id = $notif->id; 90 | $seenrecord->dismissed = 0; 91 | $seenrecord->seen = 1; 92 | 93 | $DB->insert_record('block_advnotificationsdissed', $seenrecord); 94 | } else { 95 | $upseenrecord = new stdClass(); 96 | $upseenrecord->id = $userseen->id; 97 | $upseenrecord->seen = $userseen->seen + 1; 98 | 99 | $DB->update_record('block_advnotificationsdissed', $upseenrecord); 100 | } 101 | 102 | // Get type to know which (bootstrap) class to apply. 103 | $aicon = ''; 104 | 105 | // Allows for custom styling and serves as a basic filter if anything unwanted was somehow submitted. 106 | if ($notif->type == "info" || $notif->type == "success" || $notif->type == "warning" || $notif->type == "danger") { 107 | $aicon = $notif->type; 108 | } else { 109 | $notif->type = ($notif->type == "announcement") ? 'info announcement' : 'info'; 110 | $aicon = 'info'; 111 | } 112 | 113 | // Extra classes to add to the notification wrapper - at least having the 'type' of alert. 114 | $extraclasses = ' ' . $notif->type; 115 | if ($notif->dismissible == 1) { 116 | $extraclasses .= ' dismissible'; 117 | } 118 | if ($notif->times > 0) { 119 | $extraclasses .= ' limitedtimes'; 120 | } 121 | if ($notif->aicon == 1) { 122 | $extraclasses .= ' aicon'; 123 | } 124 | 125 | // Construct notification - also format title/text to support multilang (filtered) strings. 126 | $rendernotif[] = array('extraclasses' => $extraclasses, // Additional classes. 127 | 'notifid' => $notif->id, // Notification id. 128 | 'alerttype' => $notif->type, // Alert type (styling). 129 | 'aiconflag' => $notif->aicon, // Render icon flag. 130 | 'aicon' => $aicon, // Which icon to render. 131 | 'title' => $filternotif ? format_text($notif->title, FORMAT_HTML) : $notif->title, // Title. 132 | 'message' => $filternotif ? format_text($notif->message, FORMAT_HTML) : $notif->message, // Notification text. 133 | 'dismissible' => $notif->dismissible); // Dismissible flag. 134 | } 135 | } 136 | 137 | return $rendernotif; 138 | } 139 | 140 | /** 141 | * Get date formats supported byt the plugin. 142 | * 143 | * @return array Array of formats as key and today's date in that format as value. 144 | */ 145 | function get_date_formats() { 146 | $formats = []; 147 | 148 | // Add supported formats to array. 149 | $formats['d/m/Y'] = date('d/m/Y'); 150 | $formats['j/n/y'] = date('j/n/y'); 151 | $formats['m-d-Y'] = date('m-d-Y'); 152 | $formats['n-j-y'] = date('n-j-y'); 153 | $formats['j M y'] = date('j M y'); 154 | $formats['j F Y'] = date('j F Y'); 155 | 156 | return $formats; 157 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "block-advnotifications-tasks", 3 | "version": "0.1.0", 4 | "description": "Grunt tasks for Advanced Notifications block plugin.", 5 | "main": "Gruntfile.js", 6 | "author": { 7 | "name": "LearningWorks Ltd", 8 | "url": "https://learningworks.co.nz/" 9 | }, 10 | "devDependencies": { 11 | "grunt": "^1.3.0", 12 | "grunt-contrib-uglify": "^5.0.0", 13 | "grunt-contrib-watch": "^1.1.0", 14 | "grunt-exec": "^0.4.7", 15 | "grunt-sass": "^2.1.0", 16 | "grunt-stylelint": "^0.6.0" 17 | }, 18 | "engines": { 19 | "node": ">=10" 20 | }, 21 | "license": "GPL-3.0", 22 | "dependencies": { 23 | "grunt-cli": "^1.3.2", 24 | "stylelint": "^8.0.0", 25 | "stylelint-csstree-validator": "^1.9.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pages/notifications.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Notification page where notfications are created and managed. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | // Load in Moodle config. 27 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 28 | 29 | // Load in Moodle's Tablelib lib. 30 | require_once($CFG->dirroot . '/lib/tablelib.php'); 31 | // Call in block's table file. 32 | require_once($CFG->dirroot . '/blocks/advnotifications/classes/notifications_table.php'); 33 | 34 | // PARAMS. 35 | $params = array(); 36 | 37 | // Determines whether or not to download the table. 38 | $download = optional_param('download', '', PARAM_ALPHA); 39 | 40 | // Determines whether notification is global or instance-based. 41 | $blockinstance = optional_param('blockid', '', PARAM_INT); 42 | 43 | // Used for navigation links to keep track of blockid (if any). 44 | $param = ''; 45 | $xparam = ''; 46 | 47 | // Build params array (used to build url later). 48 | if (isset($download) && $download !== '') { 49 | $params['download'] = 1; 50 | } 51 | 52 | // TODO: Use 'new moodle_url()' instead? 53 | if (isset($blockinstance) && $blockinstance !== '') { 54 | $param = '?blockid=' . $blockinstance; 55 | $xparam = '&blockid=' . $blockinstance; 56 | $params['blockid'] = $blockinstance; 57 | $bcontext = context_block::instance($blockinstance); 58 | } 59 | 60 | // Force the user to login/create an account to access this page. 61 | require_login(); 62 | 63 | $context = context_system::instance(); 64 | $allnotifs = has_capability('block/advnotifications:managenotifications', $context); 65 | $ownnotifs = false; 66 | 67 | if (!$allnotifs) { 68 | if (empty($blockinstance) || !isset($blockinstance) || $blockinstance === -1) { 69 | throw new moodle_exception('advnotifications_err_nocapability', 'block_advnotifications'); 70 | } 71 | $ownnotifs = has_capability('block/advnotifications:manageownnotifications', $bcontext); 72 | } 73 | 74 | if (!$allnotifs && !$ownnotifs) { 75 | throw new moodle_exception('advnotifications_err_nocapability', 'block_advnotifications'); 76 | } 77 | 78 | // Set PAGE variables. 79 | $url = new moodle_url($CFG->wwwroot . '/blocks/advnotifications/pages/notifications.php'); 80 | $PAGE->set_context($context); 81 | $PAGE->set_url($url, $params); 82 | 83 | // Get the renderer for this page. 84 | $renderer = $PAGE->get_renderer('block_advnotifications'); 85 | 86 | $table = new advnotifications_notifications_table('advnotifications-list'); 87 | $table->is_downloading($download, 'advnotifications-list', 'Advanced Notifications List'); 88 | 89 | if (!$table->is_downloading()) { 90 | // Only print headers if not asked to download data. 91 | // Print the page header. 92 | $PAGE->set_title(get_string('advnotifications_table_title', 'block_advnotifications')); 93 | $PAGE->set_heading(get_string('advnotifications_table_heading', 'block_advnotifications')); 94 | $PAGE->requires->jquery(); 95 | $PAGE->requires->js_call_amd('block_advnotifications/custom', 'initialise'); 96 | if (isset($bcontext) && $ccontext = $bcontext->get_course_context(false)) { 97 | $course = $DB->get_field('course', 'fullname', ['id' => $ccontext->instanceid]); 98 | $PAGE->navbar->add(format_string($course), new moodle_url('/course/view.php', ['id' => $ccontext->instanceid])); 99 | } 100 | $PAGE->navbar->add(get_string('blocks')); 101 | $PAGE->navbar->add(get_string('pluginname', 'block_advnotifications')); 102 | $PAGE->navbar->add(get_string('advnotifications_table_title_short', 'block_advnotifications')); 103 | 104 | echo $OUTPUT->header(); 105 | 106 | echo '

' . get_string('advnotifications_table_title', 'block_advnotifications') . '

'; 107 | } 108 | 109 | // Configure the table. 110 | $table->define_baseurl($url, $params); 111 | 112 | $table->set_attribute('class', 'admin_table general_table notifications_table'); 113 | $table->collapsible(false); 114 | 115 | $table->is_downloadable(true); 116 | $table->show_download_buttons_at(array(TABLE_P_BOTTOM)); 117 | 118 | // Set SQL params for table. 119 | $sqlwhere = 'deleted = :deleted'; 120 | $sqlparams = array('deleted' => 0); 121 | if ($ownnotifs && !$allnotifs) { 122 | $sqlwhere .= ' AND created_by = :created_by'; 123 | $sqlparams['created_by'] = $USER->id; 124 | } 125 | 126 | $table->set_sql('*', "{block_advnotifications}", $sqlwhere, $sqlparams); 127 | 128 | $navbuttons['left'] = '' . 130 | get_string('advnotifications_nav_restore', 'block_advnotifications') . ''; 131 | $navbuttons['right'] = ''; 132 | if ($allnotifs) { 133 | $navbuttons['right'] = '' . 135 | get_string('advnotifications_nav_settings', 'block_advnotifications') . 136 | ''; 137 | 138 | $params['global'] = true; 139 | } 140 | 141 | // Add navigation controls before the table. 142 | echo '
' . 143 | get_string('setting/navigation_desc', 'block_advnotifications', $navbuttons) . 144 | '


'; 145 | 146 | // Add a wrapper with an id, which makes reloading the table easier (when using ajax). 147 | echo '
'; 148 | $table->out(20, true); 149 | echo '

'; 150 | 151 | echo $renderer->add_notification($params); 152 | 153 | if (!$table->is_downloading()) { 154 | echo $OUTPUT->footer(); 155 | } 156 | -------------------------------------------------------------------------------- /pages/process.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Used to process user actions. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | // Load in Moodle config. 27 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 28 | 29 | try { 30 | require_sesskey(); 31 | } catch (EXCEPTION $e) { 32 | header('HTTP/1.0 403 Forbidden'); 33 | echo json_encode(array("result" => "Failed", 34 | "Notification" => get_string('advnotifications_err_forbidden', 'block_advnotifications'))); 35 | exit(); 36 | } 37 | 38 | require_login(); 39 | 40 | global $USER; 41 | 42 | header('HTTP/1.0 200 OK'); 43 | 44 | // TODO - Check if insertions/updates/deletions were successful, and return appropriate message. 45 | 46 | // GET PARAMETERS. 47 | // Check if ajax or other type of call. 48 | $calltype = optional_param('call', null, PARAM_TEXT); 49 | 50 | // Notification details. 51 | $enabled = optional_param('enabled', null, PARAM_TEXT); 52 | $global = optional_param('global', null, PARAM_TEXT); 53 | $blockinstance = optional_param('blockid', -1, PARAM_INT); 54 | if (get_config('block_advnotifications', 'html')) { 55 | $title = optional_param('title', null, PARAM_CLEANHTML); 56 | $message = optional_param('message', null, PARAM_CLEANHTML); 57 | } else { 58 | $title = optional_param('title', null, PARAM_TEXT); 59 | $message = optional_param('message', null, PARAM_TEXT); 60 | } 61 | $type = optional_param('type', null, PARAM_TEXT); 62 | $times = optional_param('times', null, PARAM_INT); 63 | $aicon = optional_param('aicon', null, PARAM_TEXT); 64 | $dismissible = optional_param('dismissible', null, PARAM_TEXT); 65 | $datefrom = optional_param('date_from', null, PARAM_TEXT); 66 | $dateto = optional_param('date_to', null, PARAM_TEXT); 67 | 68 | $dismiss = optional_param('dismiss', null, PARAM_TEXT); // User dismissed notification. 69 | $purpose = optional_param('purpose', null, PARAM_TEXT); // Purpose of request. 70 | $tableaction = optional_param('tableaction', null, PARAM_TEXT); // ID of item to action. 71 | 72 | // Check if ajax call or not (Progressive Enhancement - yay!). 73 | $ajax = false; 74 | 75 | if ($calltype === 'ajax') { 76 | $ajax = true; 77 | } 78 | 79 | // GLOBAL. 80 | // Sort out whether global or instance-based - if the global variable contains anything it is assumed to be global. 81 | if (isset($global) && $global != "") { 82 | $global = 1; 83 | } else { 84 | $global = 0; 85 | } 86 | 87 | // NEW NOTIFICATION. 88 | // Change the checkbox values to integers for DB - another level of security. 89 | if ($enabled == 'on' || $enabled == '1') { 90 | $enabled = 1; 91 | } else { 92 | $enabled = 0; 93 | } 94 | if ($aicon == 'on' || $aicon == '1') { 95 | $aicon = 1; 96 | } else { 97 | $aicon = 0; 98 | } 99 | if ($dismissible == 'on' || $dismissible == '1') { 100 | $dismissible = 1; 101 | } else { 102 | $dismissible = 0; 103 | } 104 | 105 | // TODO: Check if successful? 106 | // Convert dates to epoch for DB. If empty, set to 0 (forever) by default. 107 | $datefrom == "" ? $datefrom = 0 : $datefrom = strtotime($datefrom); 108 | $dateto == "" ? $dateto = 0 : $dateto = strtotime($dateto); 109 | 110 | if (isset($dismiss) && $dismiss != '') { 111 | $notification = $DB->get_record('block_advnotifications', 112 | array('id' => $dismiss) 113 | ); 114 | $userdissed = $DB->get_record('block_advnotificationsdissed', 115 | array('user_id' => $USER->id, 'not_id' => $dismiss) 116 | ); 117 | 118 | // Update if the user has dismissed the notification. 119 | if ($userdissed) { 120 | $DB->set_field('block_advnotificationsdissed', 'dismissed', 1, array('id' => $userdissed->id)); 121 | } 122 | 123 | if ($ajax) { 124 | echo json_encode("Di: Successful"); 125 | exit(); 126 | } else { 127 | exit(); 128 | } 129 | } 130 | 131 | $context = context_system::instance(); 132 | $allnotifs = has_capability('block/advnotifications:managenotifications', $context); 133 | $ownnotifs = false; 134 | 135 | // TODO - move to using Moodle's core ajax string retrieval method. 136 | // The 'strings' purpose/action is just getting strings. 137 | if (isset($purpose) && $purpose !== 'strings') { 138 | if (!$allnotifs) { 139 | $bcontext = context_block::instance($blockinstance); 140 | $ownnotifs = has_capability('block/advnotifications:manageownnotifications', $bcontext); 141 | } 142 | 143 | if (!$allnotifs && !$ownnotifs) { 144 | throw new moodle_exception('advnotifications_err_nocapability', 'block_advnotifications'); 145 | } 146 | } 147 | 148 | // Build redirect url params. 149 | $params = []; 150 | if (isset($blockinstance) && $blockinstance > -1) { 151 | $params['blockid'] = $blockinstance; 152 | } 153 | 154 | // Handle Delete/Edit early as it requires few resources, and then we can quickly exit(), 155 | // this is the new AJAX/JS deletion/editing method. 156 | if (isset($tableaction) && $tableaction != '') { 157 | if ($purpose == 'edit') { 158 | $enotification = $DB->get_record('block_advnotifications', array('id' => $tableaction)); 159 | 160 | $enotification->date_from = date('Y-m-d', $enotification->date_from); 161 | $enotification->date_to = date('Y-m-d', $enotification->date_to); 162 | 163 | if ($ajax) { 164 | echo json_encode($enotification); 165 | exit(); 166 | } else { 167 | redirect(new moodle_url('/blocks/advnotifications/pages/notifications.php', $params), 168 | get_string('advnotifications_err_nojsedit', 'block_advnotifications')); 169 | } 170 | } else if ($purpose == 'delete') { 171 | $dnotification = new stdClass(); 172 | $dnotification->id = $tableaction; 173 | $dnotification->deleted = 1; 174 | $dnotification->deleted_at = time(); 175 | $dnotification->deleted_by = $USER->id; 176 | 177 | $old = $DB->get_record('block_advnotifications', ['id' => $tableaction]); 178 | $DB->update_record('block_advnotifications', $dnotification); 179 | 180 | $params = [ 181 | 'objectid' => $dnotification->id, 182 | 'other' => [ 183 | 'old_title' => $old->title, 184 | 'old_message' => $old->message, 185 | 'old_date_from' => $old->date_from, 186 | 'old_date_to' => $old->date_to, 187 | ] 188 | ]; 189 | if ($blockinstance > 0) { 190 | $params['context'] = context_block::instance($blockinstance); 191 | } else { 192 | $params['context'] = context_system::instance(); 193 | } 194 | $event = \block_advnotifications\event\notification_deleted::create($params); 195 | $event->trigger(); 196 | 197 | if ($ajax) { 198 | echo json_encode(array("done" => $tableaction)); 199 | exit(); 200 | } else { 201 | redirect(new moodle_url('/blocks/advnotifications/pages/notifications.php', $params)); 202 | } 203 | } else if ($purpose == 'restore') { 204 | $rnotification = new stdClass(); 205 | $rnotification->id = $tableaction; 206 | $rnotification->deleted = 0; 207 | $rnotification->deleted_at = 0; 208 | $rnotification->deleted_by = -1; 209 | 210 | $DB->update_record('block_advnotifications', $rnotification); 211 | 212 | if ($ajax) { 213 | echo json_encode(array("done" => $tableaction)); 214 | exit(); 215 | } else { 216 | redirect(new moodle_url('/blocks/advnotifications/pages/restore.php', $params)); 217 | } 218 | } else if ($purpose == 'permdelete') { 219 | $DB->delete_records('block_advnotifications', array('id' => $tableaction)); 220 | 221 | if ($ajax) { 222 | echo json_encode(array('done' => $tableaction)); 223 | exit(); 224 | } else { 225 | redirect(new moodle_url('/blocks/advnotifications/pages/restore.php', $params)); 226 | } 227 | } 228 | } 229 | 230 | // Get plugin strings so JS can use appropriate locale strings. 231 | if ($purpose == 'strings') { 232 | if ($ajax) { 233 | $strings = new stdClass(); 234 | 235 | $strings->save = get_string('advnotifications_save', 'block_advnotifications'); 236 | $strings->update = get_string('advnotifications_update', 'block_advnotifications'); 237 | $strings->req = get_string('advnotifications_req', 'block_advnotifications'); 238 | $strings->preview = get_string('advnotifications_preview', 'block_advnotifications'); 239 | $strings->title = get_string('advnotifications_title', 'block_advnotifications'); 240 | $strings->message = get_string('advnotifications_message', 'block_advnotifications'); 241 | 242 | header('Content-Type: application/json; charset=UTF-8'); 243 | echo json_encode($strings); 244 | exit(); 245 | } 246 | // Else do nothing... No JS, no JS strings needed... 247 | } 248 | 249 | // Update existing notification, instead of inserting a new one. 250 | if ($purpose == 'update') { 251 | // Only check for id parameter when updating. 252 | $id = optional_param('id', null, PARAM_INT); 253 | 254 | // Prevent users from making notifications global if they aren't allowed to. 255 | if (!$allnotifs) { 256 | $global = 0; 257 | } 258 | 259 | $old = $DB->get_record('block_advnotifications', ['id' => $id]); 260 | // Update an existing notification. 261 | $urow = new stdClass(); 262 | 263 | $urow->id = $id; 264 | $urow->title = $title; 265 | $urow->message = $message; 266 | $urow->type = $type; 267 | $urow->aicon = $aicon; 268 | $urow->enabled = $enabled; 269 | $urow->global = $global; 270 | $urow->blockid = $blockinstance; 271 | $urow->dismissible = $dismissible; 272 | $urow->date_from = $datefrom; 273 | $urow->date_to = $dateto; 274 | $urow->times = $times; 275 | 276 | $DB->update_record('block_advnotifications', $urow); 277 | 278 | $params = [ 279 | 'context' => context_block::instance($blockinstance), 280 | 'objectid' => $urow->id, 281 | 'other' => [ 282 | 'old_title' => $old->title, 283 | 'old_message' => $old->message, 284 | 'old_date_from' => $old->date_from, 285 | 'old_date_to' => $old->date_to, 286 | 'new_title' => $urow->title, 287 | 'new_message' => $urow->message, 288 | 'new_date_from' => $urow->date_from, 289 | 'new_date_to' => $urow->date_to 290 | ] 291 | ]; 292 | $event = \block_advnotifications\event\notification_updated::create($params); 293 | $event->trigger(); 294 | 295 | if ($ajax) { 296 | echo json_encode(array("updated" => $title)); 297 | exit(); 298 | } else { 299 | redirect(new moodle_url('/blocks/advnotifications/pages/notifications.php', $params), 300 | get_string('advnotifications_err_nojsedit', 'block_advnotifications')); 301 | } 302 | } 303 | 304 | if ($purpose == "add") { 305 | // Check for required fields. 306 | $error = ''; 307 | $fields = []; 308 | 309 | if (!isset($type)) { 310 | $fields[] = 'type'; 311 | $error .= '"' . get_string('advnotifications_type', 'block_advnotifications') . '"'; 312 | } 313 | if (!isset($times)) { 314 | $fields[] = 'times'; 315 | 316 | if ($error !== '') { 317 | $error .= get_string('advnotifications_join', 'block_advnotifications'); 318 | } 319 | 320 | $error .= '"' . get_string('advnotifications_times', 'block_advnotifications') . '"'; 321 | } 322 | if ($error !== '') { 323 | if ($ajax) { 324 | // Return Error. 325 | // Technically we should never reach this if JS is enabled client-side, 326 | // but leaving it in case validation slipped past JS. 327 | header('HTTP/1.1 400 Bad Request Invalid Input'); 328 | header('Content-Type: application/json; charset=UTF-8'); 329 | echo json_encode(array('error' => $fields)); 330 | exit(); 331 | } else { 332 | // Redirect with Error. 333 | redirect(new moodle_url('/blocks/advnotifications/pages/notifications.php', $params), 334 | get_string('advnotifications_err_req', 'block_advnotifications', $error)); 335 | } 336 | } 337 | 338 | // Prevent users from making notifications global if they aren't allowed to. 339 | if (!$allnotifs) { 340 | $global = 0; 341 | } 342 | 343 | // Create a new notification - Used for both Ajax Calls & NON-JS calls. 344 | $row = new stdClass(); 345 | 346 | $row->title = $title; 347 | $row->message = $message; 348 | $row->type = $type; 349 | $row->aicon = $aicon; 350 | $row->enabled = $enabled; 351 | $row->global = $global; 352 | $row->blockid = $blockinstance; 353 | $row->dismissible = $dismissible; 354 | $row->date_from = $datefrom; 355 | $row->date_to = $dateto; 356 | $row->times = $times; 357 | $row->deleted = 0; 358 | $row->deleted_at = 0; 359 | $row->deleted_by = -1; 360 | $row->created_by = $USER->id; 361 | 362 | $id = $DB->insert_record('block_advnotifications', $row); 363 | 364 | $params = ['objectid' => $id]; 365 | if ($blockinstance > 0) { 366 | $params['context'] = context_block::instance($blockinstance); 367 | } else { 368 | $params['context'] = context_system::instance(); 369 | } 370 | $event = \block_advnotifications\event\notification_created::create($params); 371 | $event->trigger(); 372 | 373 | // Send JSON response if AJAX call was made, otherwise simply redirect to origin page. 374 | if ($ajax) { 375 | // Return Successful. 376 | echo json_encode("I: Successful"); 377 | exit(); 378 | } else { 379 | redirect(new moodle_url('/blocks/advnotifications/pages/notifications.php', $params)); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /pages/restore.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Restore page where deleted notifications can be... restored. 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | // Load in Moodle config. 27 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 28 | // Load in Moodle's Tablelib lib. 29 | require_once($CFG->dirroot . '/lib/tablelib.php'); 30 | // Call in block's table file. 31 | require_once($CFG->dirroot . '/blocks/advnotifications/classes/restore_table.php'); 32 | 33 | // PARAMS. 34 | $params = array(); 35 | 36 | // Determines which notification the user wishes to restore. 37 | $restore = optional_param('restore', null, PARAM_INT); 38 | 39 | // Determines which notification the user wishes to delete. 40 | $delete = optional_param('delete', null, PARAM_INT); 41 | 42 | // Determines whether or not to download the table. 43 | $download = optional_param('download', null, PARAM_ALPHA); 44 | 45 | // Used for navigation links to keep track of blockid (if any). 46 | $blockid = optional_param('blockid', '', PARAM_INT); 47 | 48 | $param = ''; 49 | $xparam = ''; 50 | 51 | if (isset($blockid) && $blockid !== '') { 52 | $param = '?blockid=' . $blockid; 53 | $xparam = '&blockid=' . $blockid; 54 | } 55 | 56 | if ( !!$download ) { 57 | $params['download'] = 1; 58 | } 59 | 60 | if ( !!$delete ) { 61 | // If wanting to delete a notification, delete from DB immediately before the table is rendered. 62 | $DB->delete_records('block_advnotifications', array('id' => $delete)); 63 | } 64 | 65 | // Force the user to login/create an account to access this page. 66 | require_login(); 67 | 68 | $context = context_system::instance(); 69 | $allnotifs = has_capability('block/advnotifications:managenotifications', $context); 70 | $ownnotifs = false; 71 | 72 | if (!$allnotifs) { 73 | if (empty($blockid) || !isset($blockid) || $blockid === -1) { 74 | throw new moodle_exception('advnotifications_err_nocapability', 'block_advnotifications'); 75 | } 76 | $bcontext = context_block::instance($blockid); 77 | $ownnotifs = has_capability('block/advnotifications:manageownnotifications', $bcontext); 78 | } 79 | 80 | if (!$allnotifs && !$ownnotifs) { 81 | throw new moodle_exception('advnotifications_err_nocapability', 'block_advnotifications'); 82 | } 83 | 84 | // Set PAGE variables. 85 | $url = new moodle_url($CFG->wwwroot . '/blocks/advnotifications/pages/restore.php'); 86 | $PAGE->set_context($context); 87 | $PAGE->set_url($url, $params); 88 | 89 | 90 | $table = new advnotifications_restore_table('advnotifications-list-restore'); 91 | $table->is_downloading($download, 'advnotifications-list-restore', 'Advanced Notifications List Restore'); 92 | 93 | if (!$table->is_downloading()) { 94 | // Only print headers if not asked to download data. 95 | // Print the page header. 96 | $PAGE->set_title(get_string('advnotifications_restore_table_title', 'block_advnotifications')); 97 | $PAGE->set_heading(get_string('advnotifications_restore_table_heading', 'block_advnotifications')); 98 | $PAGE->requires->jquery(); 99 | $PAGE->requires->js_call_amd('block_advnotifications/custom', 'initialise'); 100 | $PAGE->navbar->add(get_string('blocks')); 101 | $PAGE->navbar->add(get_string('pluginname', 'block_advnotifications')); 102 | $PAGE->navbar->add(get_string('advnotifications_restore_table_title_short', 'block_advnotifications')); 103 | 104 | echo $OUTPUT->header(); 105 | 106 | echo '

' . get_string('advnotifications_restore_table_title', 'block_advnotifications') . '

'; 107 | } 108 | 109 | // Configure the table. 110 | $table->define_baseurl($url, $params); 111 | 112 | $table->set_attribute('class', 'admin_table general_table notifications_restore_table'); 113 | $table->collapsible(false); 114 | 115 | $table->is_downloadable(true); 116 | $table->show_download_buttons_at(array(TABLE_P_BOTTOM)); 117 | 118 | // Set SQL params for table. 119 | $sqlwhere = 'deleted = :deleted'; 120 | $sqlparams = array('deleted' => 1); 121 | if ($ownnotifs && !$allnotifs) { 122 | $sqlwhere .= ' AND created_by = :created_by'; 123 | $sqlparams['created_by'] = $USER->id; 124 | } 125 | 126 | $table->set_sql('*', "{block_advnotifications}", $sqlwhere, $sqlparams); 127 | 128 | // Print warning about permanently deleting notifications. 129 | echo '
130 |
131 | ' . get_string('advnotifications_restore_table_warning', 'block_advnotifications') . ' 132 |
133 |
'; 134 | 135 | $navbuttons['left'] = '' . 137 | get_string('advnotifications_nav_manage', 'block_advnotifications') . ''; 138 | $navbuttons['right'] = ''; 139 | if ($allnotifs) { 140 | $navbuttons['right'] = '' . 142 | get_string('advnotifications_nav_settings', 'block_advnotifications') . ''; 143 | } 144 | 145 | // Add navigation controls before the table. 146 | echo '
' . 147 | get_string('setting/navigation_desc', 'block_advnotifications', $navbuttons) . 148 | '


'; 149 | 150 | // Add a wrapper with an id, which makes reloading the table easier (when using ajax). 151 | echo '
'; 152 | $table->out(20, true); 153 | echo '
'; 154 | 155 | if (!$table->is_downloading()) { 156 | echo $OUTPUT->footer(); 157 | } 158 | -------------------------------------------------------------------------------- /pix/danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/pix/danger.png -------------------------------------------------------------------------------- /pix/danger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /pix/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/pix/info.png -------------------------------------------------------------------------------- /pix/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pix/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/pix/success.png -------------------------------------------------------------------------------- /pix/success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /pix/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learningworks/moodle-block_advnotifications/b253b07f260aeb49f3f636087fe3f99a23eaab62/pix/warning.png -------------------------------------------------------------------------------- /pix/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /renderer.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Advanced Notifications renderer - what gets displayed 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | /** 29 | * Renders notifications. 30 | * 31 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | class block_advnotifications_renderer extends plugin_renderer_base 35 | { 36 | /** 37 | * Renders notification on page. 38 | * 39 | * @param array $notifications Attributes about notifications to render. 40 | * @return string Returns HTML to render notification. 41 | */ 42 | public function render_notification($notifications) { 43 | $html = ''; 44 | 45 | // Render all the appropriate notifications. 46 | foreach ($notifications as $notification) { 47 | // Open notification block. 48 | $html .= '
'; 51 | 52 | if (!empty($notification['aiconflag']) && $notification['aiconflag'] == 1) { 53 | $html .= ''; 55 | } 56 | if (!empty($notification['title'])) { 57 | $html .= '' . $notification['title'] . ' '; 58 | } 59 | if (!empty($notification['message'])) { 60 | $html .= $notification['message']; 61 | } 62 | 63 | // If dismissible, add close button. 64 | if ($notification['dismissible'] == 1) { 65 | $html .= '
×
'; 66 | } 67 | 68 | // Close notification block. 69 | $html .= '
'; 70 | } 71 | 72 | return $html; 73 | } 74 | 75 | /** 76 | * Render interface to add a notification. 77 | * 78 | * @param array $params - passes information such whether notification is new or the block's instance id. 79 | * @return string - returns HTML to render (add notification form HTML). 80 | * @throws coding_exception 81 | */ 82 | public function add_notification($params) { 83 | global $CFG; 84 | 85 | $html = ''; 86 | 87 | // New Notification Form. 88 | $html .= '
89 |

' . 90 | get_string('advnotifications_add_heading', 'block_advnotifications') . 91 | '

92 |
93 |
94 |
96 |
97 | 98 | 101 |
' . 102 | ((array_key_exists('blockid', $params) && 103 | array_key_exists('global', $params) && 104 | $params['global'] === true) ? 105 | '
106 | 107 | 110 | 112 |
' : 113 | ((array_key_exists('global', $params) && 114 | $params['global'] === true) ? 115 | '
116 | 117 | ' . get_string('add_notification_global_notice', 'block_advnotifications') . ' 118 | 119 | 120 |
' : 121 | '
122 | ' . get_string('add_notif_local_notice', 'block_advnotifications') . ' 123 | 124 |
')) . 125 | '
126 | 128 | 130 |
131 |
132 | 152 | 155 |
156 |
157 | 172 | 175 | ' . 176 | get_string('advnotifications_times_label', 'block_advnotifications') . ' 177 |
178 |
179 |
180 | 181 | 183 |
184 |
185 | 189 | 191 |
192 |
193 |
194 | 196 | 201 | 203 | 208 | ' . 209 | get_string('advnotifications_date_info', 'block_advnotifications') . ' 210 |
211 | 212 | 213 | 217 | 228 |
229 |
230 |
' . 231 | get_string('advnotifications_add_saving', 'block_advnotifications') . 232 | '
233 | 236 |
237 |
238 |
239 |
'; 240 | 241 | return $html; 242 | } 243 | } -------------------------------------------------------------------------------- /scss/base/_utils.scss: -------------------------------------------------------------------------------- 1 | // 2 | // FILE CONTAINING HANDY DANDY SASS FUNCTIONS/MIXINS FOR USE 3 | // 4 | 5 | // 6 | // Returns the matching value from the colors SASS map 7 | // 8 | @function color($key) { 9 | @return map-get($colors, $key); 10 | } -------------------------------------------------------------------------------- /scss/base/_variables.scss: -------------------------------------------------------------------------------- 1 | // 2 | // VARIABLES FILE 3 | // 4 | 5 | // SASS Map containing plugin's colours 6 | $colors: ( 7 | black: #000, 8 | red: #f00, 9 | red--dark: #730000, 10 | green--dark: #060, 11 | 12 | // Utility 13 | required: rgba(175, 30, 30, 1), 14 | required_background: rgba(255, 0, 0, 0.075) 15 | ); -------------------------------------------------------------------------------- /scss/block/_block.scss: -------------------------------------------------------------------------------- 1 | // Displaying the actual block/notification. 2 | body { 3 | .block.block_advnotifications { 4 | .content { 5 | margin-top: 0 !important; /* stylelint-disable-line declaration-no-important */ // Override core !important CSS :( . 6 | .notification-block-wrapper { 7 | margin-bottom: 1em; 8 | .alert { 9 | margin-bottom: 0; 10 | position: relative; 11 | padding: 0.75rem 1.25rem; 12 | .notification-block-close { 13 | position: absolute; 14 | right: 1em; 15 | top: calc(50% - 0.5em); 16 | cursor: pointer; 17 | font-size: 1em; 18 | line-height: 1em; 19 | transition: 250ms; 20 | &:hover { 21 | color: color(black); /* stylelint-disable-line csstree/validator */ 22 | transition: 250ms; 23 | } 24 | } 25 | } 26 | 27 | // Moodle automatically bolds class 'warning', so this negates that (for the notifications specifically). 28 | &.warning { 29 | font-weight: 400; 30 | } 31 | 32 | &.aicon { 33 | .alert { 34 | padding-left: 3em; 35 | > .notification_aicon { 36 | height: 1.5em; 37 | position: absolute; 38 | top: calc(50% - 0.75em); 39 | margin-left: -2em; 40 | opacity: 0.5; 41 | } 42 | } 43 | } 44 | 45 | &.dismissible { 46 | .alert { 47 | padding-right: 2em; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | &:not(.editing) { 54 | .block.block_advnotifications { 55 | box-shadow: none; 56 | border: none; 57 | background-color: transparent; 58 | margin-bottom: 0 !important; /* stylelint-disable-line declaration-no-important */ // Override core !important CSS :( . 59 | padding: 0; 60 | min-height: 0; 61 | 62 | .card-body { 63 | padding: 0 !important; /* stylelint-disable-line declaration-no-important */ // Override core !important CSS :( . 64 | } 65 | 66 | .header { 67 | display: none; 68 | } 69 | 70 | .content { 71 | padding: 0; 72 | background-color: transparent; 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /scss/page/_navigation.scss: -------------------------------------------------------------------------------- 1 | #advnotifications_manage { 2 | .btn.instance { 3 | margin-right: 0.5rem; 4 | } 5 | } -------------------------------------------------------------------------------- /scss/page/_notifications.scss: -------------------------------------------------------------------------------- 1 | // Managing the notifications. 2 | body { 3 | #advnotifications_table_wrapper, 4 | #advnotifications_restore_table_wrapper { 5 | td { 6 | > form { 7 | display: inline; 8 | 9 | button { 10 | border: none; 11 | background: none; 12 | transform: scale(1.1); 13 | transition: 100ms; 14 | 15 | &[class*="restore"], 16 | &[class*="edit"] { 17 | color: color(green--dark); /* stylelint-disable-line csstree/validator */ 18 | } 19 | 20 | &[class*="delete"] { 21 | color: color(red--dark); /* stylelint-disable-line csstree/validator */ 22 | } 23 | 24 | &:hover, 25 | &:focus { 26 | outline: none; 27 | transform: scale(1.25); 28 | transition: 100ms; 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | 36 | #add_notification_wrapper_id { 37 | margin-top: 1.5em; 38 | .alert.preview-alert { 39 | position: relative; 40 | padding-right: 14px; 41 | &.aicon { 42 | padding-left: 3em; 43 | } 44 | &.dismissible { 45 | padding-right: 2em; 46 | } 47 | 48 | > * { 49 | display: inline; 50 | } 51 | > .preview-aicon { 52 | display: block; 53 | img { 54 | height: 1.5em; 55 | position: absolute; 56 | top: calc(50% - 0.75em); 57 | margin-left: -2em; 58 | opacity: 0.5; 59 | } 60 | } 61 | > .preview-alert-dismissible { 62 | position: absolute; 63 | font-size: 1em; 64 | line-height: 1em; 65 | top: calc(50% - 0.5em); 66 | right: 1em; 67 | 68 | > * { 69 | cursor: pointer; 70 | } 71 | } 72 | } 73 | .add_notification_form_wrapper { 74 | #add_notification_form { 75 | .row { 76 | margin-left: 0; 77 | margin-right: 0; 78 | } 79 | 80 | .text-muted { 81 | width: 100%; 82 | } 83 | 84 | #add_notification_title { 85 | margin-top: 1.5em; 86 | } 87 | #add_notification_message { 88 | width: 100%; 89 | margin-bottom: 1.5em; 90 | resize: both; 91 | min-width: 50px; 92 | min-height: 1.5em; 93 | } 94 | .requiredfield { 95 | background-color: color(required_background); /* stylelint-disable-line csstree/validator */ 96 | border: 1px solid color(red); /* stylelint-disable-line csstree/validator */ 97 | } 98 | strong.requiredfield { 99 | padding: 0.25em; 100 | border-color: transparent; 101 | border-radius: 5px; 102 | color: color(required); /* stylelint-disable-line csstree/validator */ 103 | } 104 | 105 | strong.required { 106 | color: color(required); /* stylelint-disable-line csstree/validator */ 107 | font-size: 1.25em; 108 | padding: 0 5px; 109 | vertical-align: top; 110 | } 111 | 112 | input[type="date"] { 113 | width: calc(100% / 12 * 7); // Fix Bootstrap. 114 | } 115 | 116 | .btn { 117 | margin-top: 0.5em; 118 | margin-bottom: 0; 119 | } 120 | 121 | #add_notification_status { 122 | display: none; 123 | position: relative; 124 | margin-top: 10px; 125 | .signal { 126 | border: 5px solid #333; 127 | border-radius: 30px; 128 | height: 30px; 129 | left: 10px; 130 | margin: -15px 0 0 -15px; 131 | opacity: 0; 132 | position: absolute; 133 | top: 15px; 134 | width: 30px; 135 | animation: pulsate 1s ease-out; 136 | animation-iteration-count: infinite; 137 | animation-direction: reverse; 138 | } 139 | .saving { 140 | padding-top: 5px; 141 | margin-left: 40px; 142 | } 143 | .done { 144 | display: none; 145 | padding-top: 5px; 146 | margin-left: 40px; 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | 154 | // Animations. 155 | @keyframes pulsate { 156 | 0% { 157 | transform: scale(.1); 158 | opacity: 0.0; 159 | } 160 | 50% { 161 | opacity: 1; 162 | } 163 | 100% { 164 | transform: scale(1.2); 165 | opacity: 0; 166 | } 167 | } -------------------------------------------------------------------------------- /scss/styles.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Advanced Notifications SCSS 3 | * 4 | * @package block_advnotifications 5 | * @copyright 2016 LearningWorks Ltd 6 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7 | */ 8 | 9 | // 10 | // BASE STYLES 11 | // 12 | @import "base/variables"; 13 | @import "base/utils"; 14 | 15 | 16 | // 17 | // PAGE STYLES 18 | // 19 | @import "page/notifications"; 20 | @import "page/navigation"; 21 | 22 | 23 | // 24 | // BLOCK PLUGIN STYLES 25 | // 26 | @import "block/block"; -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Advanced Notifications block settings 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | require_once('locallib.php'); 29 | 30 | global $CFG; 31 | 32 | if ($ADMIN->fulltree) { 33 | 34 | // Used for navigation links to keep track of blockid (if any). 35 | $blockid = optional_param('blockid', '', PARAM_INT); 36 | $param = ''; 37 | 38 | if (isset($blockid) && $blockid !== '') { 39 | $param = '?blockid=' . $blockid; 40 | } 41 | 42 | $navbuttons['left'] = '' . 44 | get_string('advnotifications_nav_manage', 'block_advnotifications') . ''; 45 | $navbuttons['right'] = '' . 47 | get_string('advnotifications_nav_restore', 'block_advnotifications') . ''; 48 | 49 | // SETTINGS' NAVIGATIONAL LINKS HEADING & LINKS. 50 | $settings->add( 51 | new admin_setting_heading( 52 | 'block_advnotifications/navigation', // NAME. 53 | get_string('setting/navigation', 'block_advnotifications'), // TITLE. 54 | '
' . 55 | get_string('setting/navigation_desc', 'block_advnotifications', $navbuttons) . 56 | '

' // DESCRIPTION. 57 | ) 58 | ); 59 | 60 | // SETTINGS HEADING. 61 | $settings->add( 62 | new admin_setting_heading( 63 | 'block_advnotifications/settings', // NAME. 64 | get_string('setting/settings', 'block_advnotifications'), // TITLE. 65 | null 66 | ) 67 | ); 68 | 69 | // ENABLE TOGGLE. 70 | $settings->add( 71 | new admin_setting_configcheckbox( 72 | 'block_advnotifications/enable', // NAME. 73 | get_string('setting/enable', 'block_advnotifications'), // TITLE. 74 | get_string('setting/enable_desc', 'block_advnotifications'), // DESCRIPTION. 75 | get_string('setting/enable_default', 'block_advnotifications') // DEFAULT. 76 | ) 77 | ); 78 | 79 | // ALLOW HTML TOGGLE. 80 | $settings->add( 81 | new admin_setting_configcheckbox( 82 | 'block_advnotifications/html', // NAME. 83 | get_string('setting/html', 'block_advnotifications'), // TITLE. 84 | get_string('setting/html_desc', 'block_advnotifications'), // DESCRIPTION. 85 | get_string('setting/html_default', 'block_advnotifications') // DEFAULT. 86 | ) 87 | ); 88 | 89 | // MULTILANG FILTER(S) SUPPORT TOGGLE. 90 | $settings->add( 91 | new admin_setting_configcheckbox( 92 | 'block_advnotifications/multilang', // NAME. 93 | get_string('setting/multilang', 'block_advnotifications'), // TITLE. 94 | get_string('setting/multilang_desc', 'block_advnotifications'), // DESCRIPTION. 95 | get_string('setting/multilang_default', 'block_advnotifications') // DEFAULT. 96 | ) 97 | ); 98 | 99 | // DATE FORMAT. 100 | $options = get_date_formats(); 101 | $settings->add( 102 | new admin_setting_configselect( 103 | 'block_advnotifications/dateformat', // NAME. 104 | get_string('setting/dateformat', 'block_advnotifications'), // TITLE. 105 | get_string('setting/dateformat_desc', 'block_advnotifications'), // DESCRIPTION. 106 | array_keys($options)[0], // DEFAULT. 107 | $options // OPTIONS. 108 | ) 109 | ); 110 | 111 | // AUTO-DELETE TOGGLE. 112 | $settings->add( 113 | new admin_setting_configcheckbox( 114 | 'block_advnotifications/auto_delete', // NAME. 115 | get_string('setting/auto_delete', 'block_advnotifications'), // TITLE. 116 | get_string('setting/auto_delete_desc', 'block_advnotifications'), // DESCRIPTION. 117 | get_string('setting/auto_delete_default', 'block_advnotifications') // DEFAULT. 118 | ) 119 | ); 120 | 121 | // AUTO-PERMADELETE OLD DELETED NOTIFICATIONS. 122 | $settings->add( 123 | new admin_setting_configcheckbox( 124 | 'block_advnotifications/auto_perma_delete', // NAME. 125 | get_string('setting/auto_perma_delete', 'block_advnotifications'), // TITLE. 126 | get_string('setting/auto_perma_delete_desc', 'block_advnotifications'), // DESCRIPTION. 127 | get_string('setting/auto_perma_delete_default', 'block_advnotifications') // DEFAULT. 128 | ) 129 | ); 130 | 131 | // AUTO-DELETE USER DATA TOGGLE. 132 | $settings->add( 133 | new admin_setting_configcheckbox( 134 | 'block_advnotifications/auto_delete_user_data', // NAME. 135 | get_string('setting/auto_delete_user_data', 'block_advnotifications'), // TITLE. 136 | get_string('setting/auto_delete_user_data_desc', 'block_advnotifications'), // DESCRIPTION. 137 | get_string('setting/auto_delete_user_data_default', 'block_advnotifications') // DEFAULT. 138 | ) 139 | ); 140 | } -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Advanced Notifications SCSS 3 | * 4 | * @package block_advnotifications 5 | * @copyright 2016 LearningWorks Ltd 6 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 7 | */ 8 | body #advnotifications_table_wrapper td > form, 9 | body #advnotifications_restore_table_wrapper td > form { 10 | display: inline; 11 | } 12 | 13 | body #advnotifications_table_wrapper td > form button, 14 | body #advnotifications_restore_table_wrapper td > form button { 15 | border: none; 16 | background: none; 17 | transform: scale(1.1); 18 | transition: 100ms; 19 | } 20 | 21 | body #advnotifications_table_wrapper td > form button[class*="restore"], 22 | body #advnotifications_table_wrapper td > form button[class*="edit"], 23 | body #advnotifications_restore_table_wrapper td > form button[class*="restore"], 24 | body #advnotifications_restore_table_wrapper td > form button[class*="edit"] { 25 | color: #060; 26 | } 27 | 28 | body #advnotifications_table_wrapper td > form button[class*="delete"], 29 | body #advnotifications_restore_table_wrapper td > form button[class*="delete"] { 30 | color: #730000; 31 | } 32 | 33 | body #advnotifications_table_wrapper td > form button:hover, 34 | body #advnotifications_table_wrapper td > form button:focus, 35 | body #advnotifications_restore_table_wrapper td > form button:hover, 36 | body #advnotifications_restore_table_wrapper td > form button:focus { 37 | outline: none; 38 | transform: scale(1.25); 39 | transition: 100ms; 40 | } 41 | 42 | body #add_notification_wrapper_id { 43 | margin-top: 1.5em; 44 | } 45 | 46 | body #add_notification_wrapper_id .alert.preview-alert { 47 | position: relative; 48 | padding-right: 14px; 49 | } 50 | 51 | body #add_notification_wrapper_id .alert.preview-alert.aicon { 52 | padding-left: 3em; 53 | } 54 | 55 | body #add_notification_wrapper_id .alert.preview-alert.dismissible { 56 | padding-right: 2em; 57 | } 58 | 59 | body #add_notification_wrapper_id .alert.preview-alert > * { 60 | display: inline; 61 | } 62 | 63 | body #add_notification_wrapper_id .alert.preview-alert > .preview-aicon { 64 | display: block; 65 | } 66 | 67 | body #add_notification_wrapper_id .alert.preview-alert > .preview-aicon img { 68 | height: 1.5em; 69 | position: absolute; 70 | top: calc(50% - 0.75em); 71 | margin-left: -2em; 72 | opacity: 0.5; 73 | } 74 | 75 | body #add_notification_wrapper_id .alert.preview-alert > .preview-alert-dismissible { 76 | position: absolute; 77 | font-size: 1em; 78 | line-height: 1em; 79 | top: calc(50% - 0.5em); 80 | right: 1em; 81 | } 82 | 83 | body #add_notification_wrapper_id .alert.preview-alert > .preview-alert-dismissible > * { 84 | cursor: pointer; 85 | } 86 | 87 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form .row { 88 | margin-left: 0; 89 | margin-right: 0; 90 | } 91 | 92 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form .text-muted { 93 | width: 100%; 94 | } 95 | 96 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form #add_notification_title { 97 | margin-top: 1.5em; 98 | } 99 | 100 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form #add_notification_message { 101 | width: 100%; 102 | margin-bottom: 1.5em; 103 | resize: both; 104 | min-width: 50px; 105 | min-height: 1.5em; 106 | } 107 | 108 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form .requiredfield { 109 | background-color: rgba(255, 0, 0, 0.075); 110 | border: 1px solid #f00; 111 | } 112 | 113 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form strong.requiredfield { 114 | padding: 0.25em; 115 | border-color: transparent; 116 | border-radius: 5px; 117 | color: #af1e1e; 118 | } 119 | 120 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form strong.required { 121 | color: #af1e1e; 122 | font-size: 1.25em; 123 | padding: 0 5px; 124 | vertical-align: top; 125 | } 126 | 127 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form input[type="date"] { 128 | width: calc(100% / 12 * 7); 129 | } 130 | 131 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form .btn { 132 | margin-top: 0.5em; 133 | margin-bottom: 0; 134 | } 135 | 136 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form #add_notification_status { 137 | display: none; 138 | position: relative; 139 | margin-top: 10px; 140 | } 141 | 142 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form #add_notification_status .signal { 143 | border: 5px solid #333; 144 | border-radius: 30px; 145 | height: 30px; 146 | left: 10px; 147 | margin: -15px 0 0 -15px; 148 | opacity: 0; 149 | position: absolute; 150 | top: 15px; 151 | width: 30px; 152 | animation: pulsate 1s ease-out; 153 | animation-iteration-count: infinite; 154 | animation-direction: reverse; 155 | } 156 | 157 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form #add_notification_status .saving { 158 | padding-top: 5px; 159 | margin-left: 40px; 160 | } 161 | 162 | body #add_notification_wrapper_id .add_notification_form_wrapper #add_notification_form #add_notification_status .done { 163 | display: none; 164 | padding-top: 5px; 165 | margin-left: 40px; 166 | } 167 | 168 | @keyframes pulsate { 169 | 0% { 170 | transform: scale(0.1); 171 | opacity: 0.0; 172 | } 173 | 50% { 174 | opacity: 1; 175 | } 176 | 100% { 177 | transform: scale(1.2); 178 | opacity: 0; 179 | } 180 | } 181 | 182 | #advnotifications_manage .btn.instance { 183 | margin-right: 0.5rem; 184 | } 185 | 186 | body .block.block_advnotifications .content { 187 | margin-top: 0 !important; /* stylelint-disable-line declaration-no-important */ 188 | } 189 | 190 | body .block.block_advnotifications .content .notification-block-wrapper { 191 | margin-bottom: 1em; 192 | } 193 | 194 | body .block.block_advnotifications .content .notification-block-wrapper .alert { 195 | margin-bottom: 0; 196 | position: relative; 197 | padding: 0.75rem 1.25rem; 198 | } 199 | 200 | body .block.block_advnotifications .content .notification-block-wrapper .alert .notification-block-close { 201 | position: absolute; 202 | right: 1em; 203 | top: calc(50% - 0.5em); 204 | cursor: pointer; 205 | font-size: 1em; 206 | line-height: 1em; 207 | transition: 250ms; 208 | } 209 | 210 | body .block.block_advnotifications .content .notification-block-wrapper .alert .notification-block-close:hover { 211 | color: #000; 212 | transition: 250ms; 213 | } 214 | 215 | body .block.block_advnotifications .content .notification-block-wrapper.warning { 216 | font-weight: 400; 217 | } 218 | 219 | body .block.block_advnotifications .content .notification-block-wrapper.aicon .alert { 220 | padding-left: 3em; 221 | } 222 | 223 | body .block.block_advnotifications .content .notification-block-wrapper.aicon .alert > .notification_aicon { 224 | height: 1.5em; 225 | position: absolute; 226 | top: calc(50% - 0.75em); 227 | margin-left: -2em; 228 | opacity: 0.5; 229 | } 230 | 231 | body .block.block_advnotifications .content .notification-block-wrapper.dismissible .alert { 232 | padding-right: 2em; 233 | } 234 | 235 | body:not(.editing) .block.block_advnotifications { 236 | box-shadow: none; 237 | border: none; 238 | background-color: transparent; 239 | margin-bottom: 0 !important; /* stylelint-disable-line declaration-no-important */ 240 | padding: 0; 241 | min-height: 0; 242 | } 243 | 244 | body:not(.editing) .block.block_advnotifications .card-body { 245 | padding: 0 !important; /* stylelint-disable-line declaration-no-important */ 246 | } 247 | 248 | body:not(.editing) .block.block_advnotifications .header { 249 | display: none; 250 | } 251 | 252 | body:not(.editing) .block.block_advnotifications .content { 253 | padding: 0; 254 | background-color: transparent; 255 | } 256 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Block version details 19 | * 20 | * @package block_advnotifications 21 | * @copyright 2016 onwards LearningWorks Ltd {@link https://learningworks.co.nz/} 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @author Zander Potgieter 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | $plugin->component = 'block_advnotifications'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494). 29 | $plugin->version = 2021092301; // YYYYMMDDHH (year, month, day, 24-hr format hour). 30 | $plugin->requires = 2018051703; // YYYYMMDDHH (Version number for Moodle v3.5.3 as at 21/01/2019). 31 | $plugin->maturity = MATURITY_STABLE; // Code maturity/stability. 32 | $plugin->release = 'v1.4.2'; // Human-readable release version. 33 | --------------------------------------------------------------------------------