├── .github └── workflows │ └── stable.yml ├── .gitignore ├── CHANGES.md ├── README.md ├── amd ├── build │ ├── copy_to_clipboard.min.js │ ├── copy_to_clipboard.min.js.map │ ├── modal_iframe.min.js │ └── modal_iframe.min.js.map └── src │ ├── copy_to_clipboard.js │ └── modal_iframe.js ├── backup └── moodle2 │ ├── backup_etherpadlite_activity_task.class.php │ ├── backup_etherpadlite_stepslib.php │ ├── restore_etherpadlite_activity_task.class.php │ └── restore_etherpadlite_stepslib.php ├── classes ├── api │ ├── api_exception.php │ ├── client.php │ └── dummy_client.php ├── dates.php ├── event │ └── course_module_viewed.php ├── observer │ └── group.php ├── output │ ├── component │ │ └── urlsettingsnote.php │ └── renderer.php ├── privacy │ └── provider.php ├── task │ └── delete_moodle_group_pad.php └── util.php ├── db ├── access.php ├── events.php ├── install.xml ├── log.php └── upgrade.php ├── empty.html ├── index.php ├── lang └── en │ └── etherpadlite.php ├── lib.php ├── locallib.php ├── mod_form.php ├── pix └── monologo.svg ├── settings.php ├── styles.css ├── templates ├── closebutton.mustache ├── content.mustache ├── iframe.mustache ├── instance_link.mustache ├── modal.mustache └── urlsettingsnote.mustache ├── tests ├── behat │ ├── behat_mod_etherpadlite.php │ ├── etherpadlite_create.feature │ └── etherpadlite_show_fullscreen.feature ├── fixtures │ └── dummyoutput.html ├── generator │ ├── behat_mod_etherpadlite_generator.php │ └── lib.php ├── generator_test.php └── lib_test.php ├── version.php └── view.php /.github/workflows/stable.yml: -------------------------------------------------------------------------------- 1 | name: release branch test 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | REPO_DIRECTORY: ${{ github.event.repository.name }} 7 | 8 | jobs: 9 | citest: 10 | name: CI test 11 | env: 12 | IGNORE_PATHS: tests/fixtures 13 | runs-on: 'ubuntu-latest' 14 | 15 | services: 16 | postgres: 17 | image: postgres:14 18 | env: 19 | POSTGRES_USER: 'postgres' 20 | POSTGRES_HOST_AUTH_METHOD: 'trust' 21 | options: >- 22 | --health-cmd pg_isready 23 | --health-interval 10s 24 | --health-timeout 5s 25 | --health-retries 3 26 | ports: 27 | - 5432:5432 28 | 29 | mariadb: 30 | image: mariadb:10.11 31 | env: 32 | MYSQL_USER: 'root' 33 | MYSQL_ALLOW_EMPTY_PASSWORD: "true" 34 | ports: 35 | - 3306:3306 36 | options: >- 37 | --health-cmd="mysqladmin ping" 38 | --health-interval 10s 39 | --health-timeout 5s 40 | --health-retries 3 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | php: ['8.2', '8.3'] 45 | database: ['mariadb', 'pgsql'] 46 | moodlebranch: ['MOODLE_500_STABLE'] 47 | 48 | steps: 49 | - name: Check out repository code 50 | uses: actions/checkout@v4 51 | with: 52 | path: plugin 53 | 54 | - name: Install node ${{ matrix.node }} 55 | uses: actions/setup-node@v3 56 | with: 57 | node-version: ${{ matrix.node }} 58 | 59 | - name: Setup PHP ${{ matrix.php }} 60 | uses: shivammathur/setup-php@v2 61 | with: 62 | php-version: ${{ matrix.php }} 63 | extensions: pgsql, mysqli, zip, gd, xmlrpc, soap 64 | ini-values: max_input_vars=5000 65 | coverage: none 66 | 67 | - name: Initialise moodle-plugin-ci 68 | run: | 69 | composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 70 | # Add dirs to $PATH 71 | echo $(cd ci/bin; pwd) >> $GITHUB_PATH 72 | echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH 73 | # PHPUnit depends on en_AU.UTF-8 locale 74 | sudo locale-gen en_AU.UTF-8 75 | 76 | - name: Install Moodle 77 | run: moodle-plugin-ci install -vvv --plugin ./plugin --db-host=127.0.0.1 78 | env: 79 | DB: ${{ matrix.database }} 80 | MOODLE_BRANCH: ${{ matrix.moodlebranch }} 81 | 82 | - name: Run phplint 83 | if: ${{ always() }} 84 | run: moodle-plugin-ci phplint 85 | 86 | - name: Run codechecker 87 | if: ${{ always() }} 88 | run: moodle-plugin-ci codechecker --max-warnings=0 89 | 90 | - name: Run validate 91 | if: ${{ always() }} 92 | run: moodle-plugin-ci validate 93 | 94 | - name: Run savepoints 95 | if: ${{ always() }} 96 | run: moodle-plugin-ci savepoints 97 | 98 | - name: Run mustache 99 | continue-on-error: true # This step will show errors but will not fail 100 | if: ${{ always() }} 101 | run: moodle-plugin-ci mustache 102 | 103 | - name: Run grunt 104 | continue-on-error: true # This step will show errors but will not fail 105 | if: ${{ always() }} 106 | run: moodle-plugin-ci grunt --max-lint-warnings=0 107 | 108 | - name: Run phpdoc 109 | continue-on-error: true # This step will show errors but will not fail 110 | if: ${{ always() }} 111 | run: moodle-plugin-ci phpdoc 112 | 113 | - name: Run phpunit 114 | if: ${{ always() }} 115 | run: moodle-plugin-ci phpunit 116 | 117 | - name: Run behat with scss deprecations check 118 | if: ${{ always() }} 119 | run: | 120 | php /home/runner/work/$REPO_DIRECTORY/$REPO_DIRECTORY/moodle/admin/tool/behat/cli/init.php --scss-deprecations 121 | moodle-plugin-ci behat --profile chrome 122 | 123 | - name: PHP Copy/Paste Detector 124 | continue-on-error: true # This step will show errors but will not fail 125 | if: ${{ always() }} 126 | run: moodle-plugin-ci phpcpd 127 | 128 | - name: PHP Mess Detector 129 | continue-on-error: true # This step will show errors but will not fail 130 | if: ${{ always() }} 131 | run: moodle-plugin-ci phpmd 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | .DS_Store -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | moodle-mod_etherpadlite 2 | ======================= 3 | 4 | Changes 5 | ------- 6 | 7 | ### v5.0 8 | * Tint monologo icon with dark grey as standard, resolves (#77) 9 | * adjust code for Moodle 5.0 using Bootstrap 5.3 10 | 11 | ### v4.4 12 | 13 | * 2024-07-10 - Requires now Moodle 4.4 14 | * 2024-07-10 - Optimize github actions 15 | * 2024-07-10 - Fix some smaller coding style issues 16 | * 2024-07-10 - All recent Moodle and PHP versions (Luca Bösch) #75 17 | * 2024-05-26 - Smaller monologo icon for Moodle ≥ 4.4 (Luca Bösch) #74 18 | 19 | ### v4.3.1 20 | 21 | * 2023-11-09 - Fix client construtor #73 (Thank to Daniil Fajnberg) 22 | 23 | ### v4.3 24 | 25 | * 2023-10-15 - Add close button to the etherpad page 26 | * 2023-10-15 - Optimize error handling 27 | * 2023-10-15 - Adjust coding style to new rules 28 | 29 | ### v4.2-r5 30 | 31 | * 2023-05-10 - Added provider.php to pass test (Thanks to Karl Michael Reyes from Catalyst IT Canada LTD) 32 | 33 | ### v4.2-r4 34 | 35 | * 2023-06-12 - add new settings to adjust CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT (Thanks to Vincent Schneider) 36 | 37 | ### v4.2-r3 38 | 39 | * 2023-05-10 - add missing slash 40 | 41 | ### v4.2-r2 42 | 43 | * 2023-05-10 - Fix require_once into stable path 44 | 45 | ### v4.2-r1 46 | 47 | * 2023-04-27 - Switch to testing mode if etherpadlite|url is empty 48 | 49 | ### v4.0-r7 50 | 51 | * 2022-12-15 - Add support for course reset 52 | * 2022-12-15 - Add backup/restore for group pads 53 | 54 | ### v4.0-r6 55 | 56 | * 2022-11-16 - Add testing support for behat and phpunit test 57 | * 2022-11-16 - Add CHANGES.md 58 | 59 | ### v4.0-r5 60 | 61 | * 2022-11-14 - fix upgrade.php. 62 | * 2022-11-07 - Fix event classes abstraction. 63 | 64 | ### v4.0-r5 65 | 66 | * 2022-11-07 - Fix coding style and add phpdoc comments. 67 | 68 | ### v4.0-r4 69 | 70 | * 2022-11-03 - Add new monologo. 71 | 72 | ### v4.0-r1 73 | 74 | * 2022-05-14 - First stable version for Moodle 4.0. 75 | 76 | ### v4.0-beta2 77 | 78 | * 2022-04-14 - Seperate Groups Feature (#34). 79 | 80 | ### v4.0-beta1 81 | 82 | * 2022-03-14 - Start optimizing for Moodle 4.0. 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This is a module, which integrates etherpad-lite in Moodle 3.5 or higher 4 | 5 | Features: 6 | 7 | - Add / View / Delete Pads 8 | - Users have the same name & writing color in all pads 9 | - Moodle Import / Export support 10 | - optional guest allowance 11 | - It supports etherpad-lite servers, which can only be accessed through the API (access only through Moodle) 12 | - It can check the HTTPS certificate of the ep-lite server for full security (man in the middle attacks) 13 | 14 | 15 | 16 | ## Prerequirement 17 | You need an etherpad-lite server, which is running on at least the same 2nd-level-domain as your moodle server. 18 | 19 | On the github page you'll find all information you need, to install the server: https://github.com/ether/etherpad-lite 20 | 21 | We recommend to use the etherpad-lite version 1.8.0 or higher 22 | 23 | It's also recommended to use the latest stable release of nodejs 24 | (http://nodejs.org/) 25 | 26 | *we are using nodejs 0.8.26. But we test new ep-lite versions always with this node version, before updating productive* 27 | 28 | When you want, that the server is only accessible via Moodle, then I recommend to install ep_remove_embed over the ep-lite admin interface. This removes the embed link.
29 | *To access the admin area, uncomment the user section in settings.json* 30 | 31 | ### Working ep-lite installation 32 | - Ubuntu 12.04 33 | - apt-get git, nginx, abiword, make, g++ 34 | - donwload nodejs from nodejs.org and compile it (you can use 'n' to switch between versions ) 35 | - etherpad-lite from git 36 | - ep-lite settings.json: 37 | - "requireSession":true 38 | - "editOnly":true 39 | - "abiword": "/usr/bin/abiword" 40 | - ep-lite plugin: *ep_remove_embed* via admin interface 41 | - upstart script 42 | - logrotate 43 | - nginx as reverse proxy with https 44 | 45 | # INSTALLATION 46 | 47 | 1. Copy this repository to the moodle subfolder: **mod/etherpadlite** 48 | 49 | 2. open your admin/index.php page and follow the instructions 50 | 51 | # Configuration 52 | 1. Server Url from your etherpadlite server *Make sure, that your moodle server can access this URL (don't forget to include a trailing slash!)* 53 | 2. ApiKey: this is stored in the file: `APIKEY.txt` on your etherpadlite server 54 | 3. Padname: this is optional and maybe just for debugging the databse 55 | 4. Cookie Domain: Enter the domain as described 56 | 5. Session elapse time: How long should one session be valid? 57 | 6. Https Redirect: This redirects moodle to https, so that the user feels secure
(later this should be used to delete sessions on the etherpadlite server) 58 | 7. Verify HTTPS cert: This lets curl check, if the https cert of the etherpadlite server is valid, to prevent man in the middle attacks 59 | 8. Guests allowed to write?: As described 60 | 61 | 62 | ## [Sponsors](id:sponsors) 63 | Browsertesting is kindly provided by [BrowserStack](https://browserstack.com) 64 | -------------------------------------------------------------------------------- /amd/build/copy_to_clipboard.min.js: -------------------------------------------------------------------------------- 1 | define("mod_etherpadlite/copy_to_clipboard",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;var etherpadliteFullPadURL=null; 2 | /** 3 | * This function gets called by the lib script and gets the etherpad url. 4 | * Be aware the navigator.clipboard object is only available in https. 5 | * 6 | * @module mod_etherpadlite 7 | * @copyright 2022 André Menrath 8 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 9 | */function copyToClipboard(){navigator.clipboard.writeText(etherpadliteFullPadURL),require(["core/str","core/notification"],(function(str,notification){str.get_strings([{key:"link_copied",component:"mod_etherpadlite"},{key:"etherpadlite_link_copied_to_clipboard",component:"mod_etherpadlite"}]).done((function(strings){notification.alert(strings[0],strings[1])}))}))} 10 | /** 11 | * This function gets called by the lib script and gets the etherpad url. 12 | * 13 | * @module mod_etherpadlite 14 | * @copyright 2022 André Menrath 15 | * @param {string} url the full link to the etherpadlite url. 16 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 17 | */_exports.init=url=>{etherpadliteFullPadURL=url,document.getElementsByClassName("copy_etherpadlink_to_clipboard_button").item(0).addEventListener("click",copyToClipboard)}})); 18 | 19 | //# sourceMappingURL=copy_to_clipboard.min.js.map -------------------------------------------------------------------------------- /amd/build/copy_to_clipboard.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"copy_to_clipboard.min.js","sources":["../src/copy_to_clipboard.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n\n// Global variable which stores the url to be copied.\nvar etherpadliteFullPadURL = null;\n\n\n/**\n * This function gets called by the lib script and gets the etherpad url.\n * Be aware the navigator.clipboard object is only available in https.\n *\n * @module mod_etherpadlite\n * @copyright 2022 André Menrath \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nfunction copyToClipboard() {\n navigator.clipboard.writeText(etherpadliteFullPadURL);\n require(['core/str', 'core/notification'], function(str, notification) {\n str.get_strings([\n {'key': 'link_copied', component: 'mod_etherpadlite'},\n {'key': 'etherpadlite_link_copied_to_clipboard', component: 'mod_etherpadlite'},\n ]).done(function(strings) {\n notification.alert(strings[0], strings[1]);\n }\n );\n });\n}\n\n\n/**\n * This function gets called by the lib script and gets the etherpad url.\n *\n * @module mod_etherpadlite\n * @copyright 2022 André Menrath \n * @param {string} url the full link to the etherpadlite url.\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport const init = (url) => {\n etherpadliteFullPadURL = url;\n let button = document.getElementsByClassName('copy_etherpadlink_to_clipboard_button').item(0);\n button.addEventListener('click', copyToClipboard);\n};\n"],"names":["etherpadliteFullPadURL","copyToClipboard","navigator","clipboard","writeText","require","str","notification","get_strings","component","done","strings","alert","url","document","getElementsByClassName","item","addEventListener"],"mappings":"6JAiBIA,uBAAyB;;;;;;;;cAWpBC,kBACLC,UAAUC,UAAUC,UAAUJ,wBAC9BK,QAAQ,CAAC,WAAY,sBAAsB,SAASC,IAAKC,cACrDD,IAAIE,YAAY,CACR,KAAQ,cAAeC,UAAW,oBAClC,KAAQ,wCAAyCA,UAAW,sBAC7DC,MAAK,SAASC,SACbJ,aAAaK,MAAMD,QAAQ,GAAIA,QAAQ;;;;;;;;mBAelCE,MACjBb,uBAAyBa,IACZC,SAASC,uBAAuB,yCAAyCC,KAAK,GACpFC,iBAAiB,QAAShB"} -------------------------------------------------------------------------------- /amd/build/modal_iframe.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module mod_etherpadlite/modal_iframe 3 | * @author Andreas Grabs 4 | * @copyright 2019 Humboldt-Universität zu Berlin 5 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6 | */ 7 | define("mod_etherpadlite/modal_iframe",["jquery"],(function($){return{init:function(frameurl,id){$("#etherpadmodal_"+id).on("show.bs.modal",(function(){$("#etherpadiframe_"+id).attr("src","about:blank"),$("#etherpadiframe2_"+id).attr("src",frameurl),$("body").addClass("modal-open")})),$("#etherpadmodal_"+id).on("hide.bs.modal",(function(){$("#etherpadiframe2_"+id).attr("src","about:blank"),$("#etherpadiframe_"+id).attr("src",frameurl),$("body").removeClass("modal-open")})),$("#etherpadiframe_"+id).attr("src",frameurl)}}})); 8 | 9 | //# sourceMappingURL=modal_iframe.min.js.map -------------------------------------------------------------------------------- /amd/build/modal_iframe.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"modal_iframe.min.js","sources":["../src/modal_iframe.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * @module mod_etherpadlite/modal_iframe\n * @author Andreas Grabs \n * @copyright 2019 Humboldt-Universität zu Berlin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n return {\n 'init': function(frameurl, id) {\n $(\"#etherpadmodal_\" + id).on(\"show.bs.modal\", function() {\n $(\"#etherpadiframe_\" + id).attr(\"src\", \"about:blank\");\n $(\"#etherpadiframe2_\" + id).attr(\"src\", frameurl);\n $(\"body\").addClass(\"modal-open\");\n });\n $(\"#etherpadmodal_\" + id).on(\"hide.bs.modal\", function() {\n $(\"#etherpadiframe2_\" + id).attr(\"src\", \"about:blank\");\n $(\"#etherpadiframe_\" + id).attr(\"src\", frameurl);\n $(\"body\").removeClass(\"modal-open\");\n });\n\n $(\"#etherpadiframe_\" + id).attr(\"src\", frameurl);\n }\n };\n\n});\n"],"names":["define","$","frameurl","id","on","attr","addClass","removeClass"],"mappings":";;;;;;AAsBAA,uCAAO,CAAC,WAAW,SAASC,SACjB,MACK,SAASC,SAAUC,IACvBF,EAAE,kBAAoBE,IAAIC,GAAG,iBAAiB,WAC1CH,EAAE,mBAAqBE,IAAIE,KAAK,MAAO,eACvCJ,EAAE,oBAAsBE,IAAIE,KAAK,MAAOH,UACxCD,EAAE,QAAQK,SAAS,iBAEvBL,EAAE,kBAAoBE,IAAIC,GAAG,iBAAiB,WAC1CH,EAAE,oBAAsBE,IAAIE,KAAK,MAAO,eACxCJ,EAAE,mBAAqBE,IAAIE,KAAK,MAAOH,UACvCD,EAAE,QAAQM,YAAY,iBAG1BN,EAAE,mBAAqBE,IAAIE,KAAK,MAAOH"} -------------------------------------------------------------------------------- /amd/src/copy_to_clipboard.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | 17 | // Global variable which stores the url to be copied. 18 | var etherpadliteFullPadURL = null; 19 | 20 | 21 | /** 22 | * This function gets called by the lib script and gets the etherpad url. 23 | * Be aware the navigator.clipboard object is only available in https. 24 | * 25 | * @module mod_etherpadlite 26 | * @copyright 2022 André Menrath 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | function copyToClipboard() { 30 | navigator.clipboard.writeText(etherpadliteFullPadURL); 31 | require(['core/str', 'core/notification'], function(str, notification) { 32 | str.get_strings([ 33 | {'key': 'link_copied', component: 'mod_etherpadlite'}, 34 | {'key': 'etherpadlite_link_copied_to_clipboard', component: 'mod_etherpadlite'}, 35 | ]).done(function(strings) { 36 | notification.alert(strings[0], strings[1]); 37 | } 38 | ); 39 | }); 40 | } 41 | 42 | 43 | /** 44 | * This function gets called by the lib script and gets the etherpad url. 45 | * 46 | * @module mod_etherpadlite 47 | * @copyright 2022 André Menrath 48 | * @param {string} url the full link to the etherpadlite url. 49 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 | */ 51 | export const init = (url) => { 52 | etherpadliteFullPadURL = url; 53 | let button = document.getElementsByClassName('copy_etherpadlink_to_clipboard_button').item(0); 54 | button.addEventListener('click', copyToClipboard); 55 | }; 56 | -------------------------------------------------------------------------------- /amd/src/modal_iframe.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - http://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * @module mod_etherpadlite/modal_iframe 18 | * @author Andreas Grabs 19 | * @copyright 2019 Humboldt-Universität zu Berlin 20 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 21 | */ 22 | 23 | define(['jquery'], function($) { 24 | return { 25 | 'init': function(frameurl, id) { 26 | $("#etherpadmodal_" + id).on("show.bs.modal", function() { 27 | $("#etherpadiframe_" + id).attr("src", "about:blank"); 28 | $("#etherpadiframe2_" + id).attr("src", frameurl); 29 | $("body").addClass("modal-open"); 30 | }); 31 | $("#etherpadmodal_" + id).on("hide.bs.modal", function() { 32 | $("#etherpadiframe2_" + id).attr("src", "about:blank"); 33 | $("#etherpadiframe_" + id).attr("src", frameurl); 34 | $("body").removeClass("modal-open"); 35 | }); 36 | 37 | $("#etherpadiframe_" + id).attr("src", frameurl); 38 | } 39 | }; 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /backup/moodle2/backup_etherpadlite_activity_task.class.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | defined('MOODLE_INTERNAL') || die; 18 | 19 | require_once($CFG->dirroot . '/mod/etherpadlite/backup/moodle2/backup_etherpadlite_stepslib.php'); 20 | 21 | /** 22 | * Etherpadlite backup task that provides all the settings and steps to perform one complete backup of the activity. 23 | * 24 | * @package mod_etherpadlite 25 | * @author Timo Welde 26 | * @copyright 2012 Humboldt-Universität zu Berlin 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class backup_etherpadlite_activity_task extends backup_activity_task { 30 | /** 31 | * Define (add) particular settings this activity can have. 32 | */ 33 | protected function define_my_settings() { 34 | // No particular settings for this activity. 35 | } 36 | 37 | /** 38 | * Define (add) particular steps this activity can have. 39 | */ 40 | protected function define_my_steps() { 41 | // Etherpadlite only has one structure step. 42 | $this->add_step(new backup_etherpadlite_activity_structure_step('etherpadlite_structure', 'etherpadlite.xml')); 43 | } 44 | 45 | /** 46 | * Code the transformations to perform in the activity in order to get transportable (encoded) links. 47 | * 48 | * @param string $content some HTML text that eventually contains URLs to the activity instance scripts 49 | * @return string the content with the URLs encoded 50 | */ 51 | public static function encode_content_links($content) { 52 | global $CFG; 53 | 54 | // No links to encode. 55 | return $content; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /backup/moodle2/backup_etherpadlite_stepslib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Define the complete etherpadlite structure for backup, with file and id annotations. 19 | * 20 | * @package mod_etherpadlite 21 | * @author Timo Welde 22 | * @copyright 2012 Humboldt-Universität zu Berlin 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | class backup_etherpadlite_activity_structure_step extends backup_activity_structure_step { 26 | /** 27 | * Define the plugin structure for backup. 28 | * 29 | * @return \backup_nested_element 30 | */ 31 | protected function define_structure() { 32 | global $DB; 33 | 34 | $config = get_config('etherpadlite'); 35 | 36 | try { 37 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 38 | } catch (\mod_etherpadlite\api\api_exception $e) { 39 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 40 | } 41 | 42 | // To know if we are including userinfo. 43 | $userinfo = $this->get_setting_value('userinfo'); 44 | 45 | // Define each element separated. 46 | $eplite = new backup_nested_element( 47 | 'etherpadlite', 48 | ['id'], 49 | [ 50 | 'name', 51 | 'intro', 52 | 'introformat', 53 | 'guestsallowed', 54 | 'timecreated', 55 | 'timemodified', 56 | ] 57 | ); 58 | 59 | // Define elements of the content structure. 60 | // The elements "html" and "text" get the content of the main etherpad. 61 | // The element grouppaddata get the serialized data as array with objects (->text and ->html). 62 | $content = new backup_nested_element('content', null, ['html', 'text', 'grouppaddata']); 63 | 64 | // Build the tree. 65 | $eplite->add_child($content); 66 | 67 | // Define sources. 68 | $eplite->set_source_table('etherpadlite', ['id' => backup::VAR_ACTIVITYID]); 69 | $eplite->annotate_files('mod_etherpadlite', 'intro', null); // This file area hasn't an itemid. 70 | 71 | // All the rest of elements only happen if we are including user info. 72 | if ($userinfo) { 73 | $modid = $this->task->get_activityid(); 74 | if (!empty($client)) { 75 | if ($etherpadlite = $DB->get_record('etherpadlite', ['id' => $modid])) { 76 | $padid = $etherpadlite->uri; 77 | // Get all groups. 78 | $groups = groups_get_all_groups($etherpadlite->course); 79 | $grouppaddata = []; 80 | // Get group pads if exist. 81 | $cm = get_coursemodule_from_instance('etherpadlite', $modid); 82 | if ($cm->groupmode != 0 && $groups) { 83 | // Get the HTML content of the group pads. 84 | foreach ($groups as $group) { 85 | $grouppadid = $padid . $group->id; 86 | $html = $client->get_html($grouppadid); 87 | $text = $client->get_text($grouppadid); 88 | $groupcontent = new \stdClass(); 89 | $groupcontent->html = $html->html; 90 | $groupcontent->text = $text->text; 91 | $grouppaddata[$group->id] = $groupcontent; 92 | } 93 | } 94 | 95 | $data = new \stdClass(); 96 | $data->grouppaddata = serialize($grouppaddata); 97 | // The HTML content of the main pad. 98 | $html = $client->get_html($padid); 99 | $text = $client->get_text($padid); 100 | $data->html = $html->html; 101 | $data->text = $text->text; 102 | $content->set_source_array([$data]); 103 | } 104 | } 105 | } 106 | 107 | // Define id annotations. 108 | // We have none. 109 | 110 | // Define file annotations. 111 | 112 | // Return the root element (etherpadlite), wrapped into standard activity structure. 113 | return $this->prepare_activity_structure($eplite); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /backup/moodle2/restore_etherpadlite_activity_task.class.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | defined('MOODLE_INTERNAL') || die; 18 | 19 | require_once($CFG->dirroot . '/mod/etherpadlite/backup/moodle2/restore_etherpadlite_stepslib.php'); 20 | 21 | /** 22 | * Etherpadlite restore task that provides all the settings and steps to perform one complete restore of the activity. 23 | * 24 | * @package mod_etherpadlite 25 | * @author Timo Welde 26 | * @copyright 2012 Humboldt-Universität zu Berlin 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | class restore_etherpadlite_activity_task extends restore_activity_task { 30 | /** 31 | * Define (add) particular settings this activity can have. 32 | */ 33 | protected function define_my_settings() { 34 | // No particular settings for this activity. 35 | } 36 | 37 | /** 38 | * Define (add) particular steps this activity can have. 39 | */ 40 | protected function define_my_steps() { 41 | // Etherpadlite only has one structure step. 42 | $this->add_step(new restore_etherpadlite_activity_structure_step('etherpadlite_structure', 'etherpadlite.xml')); 43 | } 44 | 45 | /** 46 | * Define the contents in the activity that must be 47 | * processed by the link decoder. 48 | */ 49 | public static function define_decode_contents() { 50 | $contents = []; 51 | 52 | // Nothing to decode. 53 | 54 | return $contents; 55 | } 56 | 57 | /** 58 | * Define the decoding rules for links belonging 59 | * to the activity to be executed by the link decoder. 60 | */ 61 | public static function define_decode_rules() { 62 | global $DB; 63 | $rules = []; 64 | 65 | // Nothing to decode. 66 | 67 | return $rules; 68 | } 69 | 70 | /** 71 | * Define the restore log rules that will be applied 72 | * by the {@see restore_logs_processor} when restoring 73 | * etherpadlite logs. It must return one array 74 | * of {@see restore_log_rule} objects. 75 | */ 76 | public static function define_restore_log_rules() { 77 | $rules = []; 78 | 79 | $rules[] = new restore_log_rule('etherpadlite', 'add', 'view.php?id={course_module}', '{etherpadlite}'); 80 | $rules[] = new restore_log_rule('etherpadlite', 'update', 'view.php?id={course_module}', '{etherpadlite}'); 81 | $rules[] = new restore_log_rule('etherpadlite', 'view', 'view.php?id={course_module}', '{etherpadlite}'); 82 | $rules[] = new restore_log_rule('etherpadlite', 'choose', 'view.php?id={course_module}', '{etherpadlite}'); 83 | $rules[] = new restore_log_rule('etherpadlite', 'choose again', 'view.php?id={course_module}', '{etherpadlite}'); 84 | $rules[] = new restore_log_rule('etherpadlite', 'report', 'report.php?id={course_module}', '{etherpadlite}'); 85 | 86 | return $rules; 87 | } 88 | 89 | /** 90 | * Define the restore log rules that will be applied 91 | * by the {@see restore_logs_processor} when restoring 92 | * course logs. It must return one array 93 | * of {@see restore_log_rule} objects. 94 | * 95 | * Note this rules are applied when restoring course logs 96 | * by the restore final task, but are defined here at 97 | * activity level. All them are rules not linked to any module instance (cmid = 0) 98 | */ 99 | public static function define_restore_log_rules_for_course() { 100 | $rules = []; 101 | 102 | // Fix old wrong uses (missing extension). 103 | $rules[] = new restore_log_rule('etherpadlite', 'view all', 'index?id={course}', null, 104 | null, null, 'index.php?id={course}'); 105 | $rules[] = new restore_log_rule('etherpadlite', 'view all', 'index.php?id={course}', null); 106 | 107 | return $rules; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /backup/moodle2/restore_etherpadlite_stepslib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Structure step to restore one etherpadlite activity. 19 | * 20 | * @package mod_etherpadlite 21 | * @author Timo Welde 22 | * @copyright 2012 Humboldt-Universität zu Berlin 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | class restore_etherpadlite_activity_structure_step extends restore_activity_structure_step { 26 | /** 27 | * Define the restore structure for the etherpadlite plugin. 28 | * 29 | * @return array 30 | */ 31 | protected function define_structure() { 32 | $paths = []; 33 | $userinfo = $this->get_setting_value('userinfo'); 34 | 35 | $paths[] = new restore_path_element('etherpadlite', '/activity/etherpadlite'); 36 | 37 | if ($userinfo) { 38 | $paths[] = new restore_path_element('etherpadlite_content', '/activity/etherpadlite/content'); 39 | } 40 | 41 | // Return the paths wrapped into standard activity structure. 42 | return $this->prepare_activity_structure($paths); 43 | } 44 | 45 | /** 46 | * Process the restore. 47 | * 48 | * @param \stdClass|array $data 49 | * @return void 50 | */ 51 | protected function process_etherpadlite($data) { 52 | global $DB; 53 | $config = get_config('etherpadlite'); 54 | $data = (object) $data; 55 | $oldid = $data->id; 56 | $data->course = $this->get_courseid(); 57 | 58 | try { 59 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 60 | } catch (\mod_etherpadlite\api\api_exception $e) { 61 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 62 | 63 | return; 64 | } 65 | 66 | if (!empty($client)) { 67 | $epgroupid = $client->create_group(); 68 | } 69 | 70 | if (!$epgroupid) { 71 | \core\notification::add('Could not create etherpad group', \core\notification::ERROR); 72 | 73 | return; 74 | } 75 | $padid = $client->create_group_pad($epgroupid, $config->padname); 76 | 77 | if (!$padid) { 78 | \core\notification::add('Could not create etherpad group pad', \core\notification::ERROR); 79 | 80 | return; 81 | } 82 | 83 | $data->uri = $padid; 84 | 85 | $data->timecreated = $this->apply_date_offset($data->timecreated); 86 | $data->timemodified = $this->apply_date_offset($data->timemodified); 87 | 88 | // Insert the etherpadlite record. 89 | $newitemid = $DB->insert_record('etherpadlite', $data); 90 | // Immediately after inserting "activity" record, call this. 91 | $this->apply_activity_instance($newitemid); 92 | 93 | $cm = get_coursemodule_from_instance('etherpadlite', $newitemid); 94 | // Get all groups. 95 | $groups = groups_get_all_groups($data->course, 0, $cm->groupingid); 96 | 97 | if ($cm->groupmode != 0 && $groups) { 98 | $mgroupdb = []; 99 | foreach ($groups as $group) { 100 | $mgroup = []; 101 | $mgroup['padid'] = $newitemid; 102 | $mgroup['groupid'] = $group->id; 103 | $mgroupdb[] = $mgroup; 104 | 105 | try { 106 | $padid = $client->create_group_pad($epgroupid, $config->padname . $group->id); 107 | } catch (Exception $e) { 108 | continue; 109 | } 110 | } 111 | $DB->insert_records('etherpadlite_mgroups', $mgroupdb); 112 | } 113 | } 114 | 115 | /** 116 | * Restore the etherpadlite content. 117 | * 118 | * @param \stdClass|array $data 119 | * @return void 120 | */ 121 | protected function process_etherpadlite_content($data) { 122 | global $DB; 123 | $config = get_config('etherpadlite'); 124 | $data = (object) $data; 125 | 126 | try { 127 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 128 | } catch (\mod_etherpadlite\api\api_exception $e) { 129 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 130 | 131 | return; 132 | } 133 | 134 | $newid = $this->get_new_parentid('etherpadlite'); 135 | $etherpadlite = $DB->get_record('etherpadlite', ['id' => $newid]); 136 | $padid = $etherpadlite->uri; 137 | 138 | // Set the content for the main pad. 139 | try { 140 | $client->set_text($padid, $data->text); 141 | $client->set_html($padid, $data->html); 142 | } catch (Exception $e) { 143 | // Something went wrong. 144 | echo "\n\nsetHTML Failed with message " . $e->getMessage(); 145 | } 146 | 147 | if (!empty($data->grouppaddata)) { 148 | $grouppaddata = unserialize($data->grouppaddata); 149 | foreach ($grouppaddata as $oldgroupid => $grouppadcontent) { 150 | $newgroupid = $this->get_mappingid('group', $oldgroupid); 151 | $grouppadid = $padid . $newgroupid; 152 | try { 153 | $client->set_text($grouppadid, $grouppadcontent->text); 154 | $client->set_html($grouppadid, $grouppadcontent->html); 155 | } catch (Exception $e) { 156 | // Something went wrong. 157 | echo "\n\nsetHTML Failed with message " . $e->getMessage(); 158 | } 159 | } 160 | } 161 | } 162 | 163 | /** 164 | * Add files after the database structure is restored. 165 | * 166 | * @return void 167 | */ 168 | protected function after_execute() { 169 | $this->add_related_files('mod_etherpadlite', 'intro', null); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /classes/api/api_exception.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\api; 18 | 19 | /** 20 | * This is a dummy class for testing purpose to simulate the communicate with the etherpadlite server. 21 | * 22 | * @package mod_etherpadlite 23 | * @author Andreas Grabs 24 | * @see https://github.com/TomNomNom/etherpad-lite-client 25 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | class api_exception extends \moodle_exception { 29 | /** 30 | * Constructor. 31 | * 32 | * @param string $errorcode 33 | */ 34 | public function __construct($errorcode) { 35 | parent::__construct($errorcode, 'mod_etherpadlite', '', null, null); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/api/client.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\api; 18 | 19 | /** 20 | * This is a helper class to communicate with the etherpadlite server. 21 | * 22 | * @package mod_etherpadlite 23 | * @author Andreas Grabs 24 | * @see https://github.com/TomNomNom/etherpad-lite-client 25 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | class client { 29 | /** The default api version if none is set in configuration */ 30 | public const DEFAULT_API_VERSION = '1.2'; 31 | 32 | /** Return value for success */ 33 | public const CODE_OK = 0; 34 | /** Return value for invalid parameters */ 35 | public const CODE_INVALID_PARAMETERS = 1; 36 | /** Return value for internal error */ 37 | public const CODE_INTERNAL_ERROR = 2; 38 | /** Return value for invalid function */ 39 | public const CODE_INVALID_FUNCTION = 3; 40 | /** Return value for invalid api key */ 41 | public const CODE_INVALID_API_KEY = 4; 42 | 43 | /** The default value for connecttimeout */ 44 | public const DEFAULT_CONNECTTIMEOUT = 300; 45 | /** The default value for timeout */ 46 | public const DEFAULT_TIMEOUT = 0; 47 | 48 | /** @var string */ 49 | protected $apikey = ''; 50 | /** @var string */ 51 | protected $baseurl = ''; 52 | /** @var string */ 53 | protected $apiurl = ''; 54 | /** @var \curl */ 55 | protected $curl; // Use the moodle curl class. 56 | /** @var \stdClass */ 57 | protected $config; 58 | /** @var array */ 59 | protected $curloptions; // The curl options. 60 | 61 | /** 62 | * Constructor. 63 | * 64 | * @param string $apikey 65 | * @param string $baseurl 66 | */ 67 | protected function __construct($apikey, $baseurl) { 68 | global $CFG; 69 | require_once($CFG->libdir . '/filelib.php'); 70 | 71 | $this->config = get_config('etherpadlite'); 72 | 73 | if ($apikey === '') { 74 | throw new api_exception('error_config_has_no_api_key'); 75 | } 76 | $this->apikey = $apikey; 77 | 78 | $this->baseurl = trim($baseurl, '/'); 79 | $this->apiurl = $this->baseurl . '/api'; 80 | 81 | if (!filter_var($this->apiurl, FILTER_VALIDATE_URL)) { 82 | throw new api_exception('error_config_has_no_valid_baseurl'); 83 | } 84 | 85 | // Sometimes the etherpad host is located on an internal network like 127.0.0.1 or 10.0.0.0/8. 86 | // Since Moodle 4.0 this kind of host are blocked by default. 87 | $settings = []; 88 | if (!empty($this->config->ignoresecurity)) { 89 | $settings['ignoresecurity'] = true; 90 | } 91 | 92 | $this->curl = new \curl($settings); 93 | $this->curloptions = []; 94 | 95 | // Set the connect and connection timeout from settings. 96 | if (!isset($this->config->connecttimeout)) { 97 | $this->config->connecttimeout = static::DEFAULT_CONNECTTIMEOUT; 98 | } 99 | if (!isset($this->config->timeout)) { 100 | $this->config->timeout = static::DEFAULT_TIMEOUT; 101 | } 102 | $this->curloptions['CURLOPT_CONNECTTIMEOUT'] = $this->config->connecttimeout; 103 | $this->curloptions['CURLOPT_TIMEOUT'] = $this->config->timeout; 104 | 105 | // Should the certificate be verified. 106 | if (empty($this->config->check_ssl)) { 107 | $curloptions['CURLOPT_SSL_VERIFYHOST'] = 0; 108 | $curloptions['CURLOPT_SSL_VERIFYPEER'] = 0; 109 | } 110 | 111 | if (empty($this->config->apiversion)) { 112 | $this->config->apiversion = self::DEFAULT_API_VERSION; 113 | } 114 | if (!$this->check_version($this->config->apiversion)) { 115 | throw new api_exception('error_wrong_api_version'); 116 | } 117 | 118 | if ($this->check_version('1.2', $this->config->apiversion)) { 119 | if (!$this->check_token()) { 120 | throw new api_exception('error_invalid_api_key'); 121 | } 122 | } 123 | } 124 | 125 | /** 126 | * Get the base url. 127 | * 128 | * @return string The url 129 | */ 130 | public function get_baseurl() { 131 | return $this->baseurl; 132 | } 133 | 134 | /** 135 | * Start a get request. 136 | * 137 | * @param string $function 138 | * @param array $arguments 139 | * @return \stdClass|bool The returned data or false 140 | */ 141 | protected function get($function, array $arguments = []) { 142 | return $this->call($function, $arguments, 'GET'); 143 | } 144 | 145 | /** 146 | * Start a post request. 147 | * 148 | * @param string $function 149 | * @param array $arguments 150 | * @return \stdClass|bool The returned data or false 151 | */ 152 | protected function post($function, array $arguments = []) { 153 | return $this->call($function, $arguments, 'POST'); 154 | } 155 | 156 | /** 157 | * Start a request. 158 | * 159 | * @param string $function 160 | * @param array $arguments 161 | * @param string $method 162 | * @return \stdClass|bool The returned data or false 163 | */ 164 | protected function call($function, array $arguments = [], $method = 'GET') { 165 | $arguments['apikey'] = $this->apikey; 166 | $url = $this->apiurl . '/' . $this->config->apiversion . '/' . $function; 167 | 168 | if ($method === 'POST') { 169 | $result = $this->curl->post($url, $arguments, $this->curloptions); 170 | } else { 171 | $result = $this->curl->get($url, $arguments, $this->curloptions); 172 | } 173 | 174 | if (!$result) { 175 | return false; 176 | } 177 | 178 | $result = json_decode($result); 179 | if ($result === null) { 180 | return false; 181 | } 182 | 183 | return $this->handle_result($result); 184 | } 185 | 186 | /** 187 | * Checks the result by looking at $result->code 188 | * If the code is ok the data are returned. 189 | * 190 | * @param \stdClass|array|null $result 191 | * @return \stdClass|array|bool|null 192 | */ 193 | protected function handle_result($result) { 194 | if (!isset($result->code)) { 195 | return false; 196 | } 197 | if (!isset($result->message)) { 198 | return false; 199 | } 200 | if (!isset($result->data)) { 201 | $result->data = null; 202 | } 203 | 204 | switch ($result->code) { 205 | case self::CODE_OK: 206 | return $result->data; 207 | case self::CODE_INVALID_PARAMETERS: 208 | case self::CODE_INVALID_API_KEY: 209 | return false; 210 | case self::CODE_INTERNAL_ERROR: 211 | return false; 212 | case self::CODE_INVALID_FUNCTION: 213 | return false; 214 | default: 215 | return false; 216 | } 217 | } 218 | 219 | /** 220 | * Get the api version from the etherpadlite server. 221 | * 222 | * @throws \mod_etherpadlite\api\api_exception 223 | * @return string 224 | */ 225 | public function get_version() { 226 | $url = $this->apiurl; 227 | $result = $this->curl->get($url, [], $this->curloptions); 228 | 229 | $result = json_decode($result); 230 | if (!empty($result->currentVersion)) { 231 | return $result->currentVersion; 232 | } 233 | throw new api_exception('error_could_not_get_api_version'); 234 | } 235 | 236 | /** 237 | * Check the needed api version. 238 | * 239 | * @param string $neededversion 240 | * @param string $usedversion 241 | * @return bool 242 | */ 243 | public function check_version($neededversion, $usedversion = null) { 244 | if (null === $usedversion) { 245 | $currentversion = $this->get_version(); 246 | } else { 247 | $currentversion = $usedversion; 248 | } 249 | 250 | return version_compare($currentversion, $neededversion, '>='); 251 | } 252 | 253 | /** 254 | * Check the API key on the etherpadlite server. 255 | * 256 | * @return bool 257 | */ 258 | public function check_token() { 259 | return $this->get('checkToken') !== false; 260 | } 261 | 262 | // GROUPS 263 | // Pads can belong to a group. 264 | // There will always be public pads that doesnt belong to a group (or we give this group the id 0). 265 | 266 | /** 267 | * Create a new group. 268 | * 269 | * @return string|bool The new group id or false 270 | */ 271 | public function create_group() { 272 | $group = $this->post('createGroup'); 273 | if ($group) { 274 | return $group->groupID; 275 | } 276 | 277 | return false; 278 | } 279 | 280 | /** 281 | * This functions helps you to map your application group ids to etherpad lite group ids. 282 | * 283 | * @param string $groupmapper 284 | * @return string|bool The new group id or false 285 | */ 286 | public function create_group_if_not_exists_for($groupmapper) { 287 | $group = $this->post('createGroupIfNotExistsFor', [ 288 | 'groupMapper' => $groupmapper, 289 | ]); 290 | if ($group) { 291 | return $group->groupID; 292 | } 293 | 294 | return false; 295 | } 296 | 297 | /** 298 | * Deletes a group. 299 | * 300 | * @param string $epgroupid 301 | * @return bool 302 | */ 303 | public function delete_group($epgroupid) { 304 | return $this->post('deleteGroup', [ 305 | 'groupID' => $epgroupid, 306 | ]); 307 | } 308 | 309 | /** 310 | * Returns all pads of this group. 311 | * 312 | * @param string $epgroupid 313 | * @return array 314 | */ 315 | public function list_pads($epgroupid) { 316 | return $this->get('listPads', [ 317 | 'groupID' => $epgroupid, 318 | ]); 319 | } 320 | 321 | /** 322 | * Creates a new pad in this group. 323 | * 324 | * @param string $epgroupid 325 | * @param string $padname 326 | * @param string $text 327 | * @return string|bool The new pad id or false 328 | */ 329 | public function create_group_pad($epgroupid, $padname, $text = null) { 330 | $pad = $this->post('createGroupPad', [ 331 | 'groupID' => $epgroupid, 332 | 'padName' => $padname, 333 | 'text' => $text, 334 | ]); 335 | if ($pad) { 336 | return $pad->padID; 337 | } 338 | 339 | return false; 340 | } 341 | 342 | /** 343 | * List all groups. 344 | * 345 | * @return array 346 | */ 347 | public function list_all_groups() { 348 | return $this->get('listAllGroups'); 349 | } 350 | 351 | // AUTHORS 352 | // Theses authors are bind to the attributes the users choose (color and name). 353 | 354 | /** 355 | * Create a new author. 356 | * 357 | * @param string $name 358 | * @return string|bool The new author id or false 359 | */ 360 | public function create_author($name) { 361 | $author = $this->post('createAuthor', [ 362 | 'name' => $name, 363 | ]); 364 | if ($author) { 365 | return $author->authorID; 366 | } 367 | 368 | return false; 369 | } 370 | 371 | /** 372 | * This functions helps you to map your application author ids to etherpad lite author ids. 373 | * 374 | * @param string $authormapper 375 | * @param string $name 376 | * @return string|bool the new author id or false 377 | */ 378 | public function create_author_if_not_exists_for($authormapper, $name) { 379 | $author = $this->post('createAuthorIfNotExistsFor', [ 380 | 'authorMapper' => $authormapper, 381 | 'name' => $name, 382 | ]); 383 | if ($author) { 384 | return $author->authorID; 385 | } 386 | 387 | return false; 388 | } 389 | 390 | /** 391 | * Returns the ids of all pads this author has edited. 392 | * 393 | * @param string $authorid 394 | * @return array 395 | */ 396 | public function list_pads_of_author($authorid) { 397 | return $this->get('listPadsOfAuthor', [ 398 | 'authorID' => $authorid, 399 | ]); 400 | } 401 | 402 | /** 403 | * Gets an author's name. 404 | * 405 | * @param string $authorid 406 | * @return string 407 | */ 408 | public function get_author_name($authorid) { 409 | return $this->get('getAuthorName', [ 410 | 'authorID' => $authorid, 411 | ]); 412 | } 413 | 414 | // SESSIONS 415 | // Sessions can be created between a group and a author. This allows 416 | // an author to access more than one group. The sessionID will be set as 417 | // a cookie to the client and is valid until a certian date. 418 | 419 | /** 420 | * Creates a new session. 421 | * 422 | * @param string $epgroupid 423 | * @param string $authorid 424 | * @return string|bool the new session id or false 425 | */ 426 | public function create_session($epgroupid, $authorid) { 427 | $validuntil = time() + $this->config->cookietime; 428 | $session = $this->post('createSession', [ 429 | 'groupID' => $epgroupid, 430 | 'authorID' => $authorid, 431 | 'validUntil' => $validuntil, 432 | ]); 433 | 434 | if ($session) { 435 | // If we reach the etherpadlite server over https, then the cookie should only be delivered over ssl. 436 | $ssl = (stripos($this->config->url, 'https://') === 0) ? true : false; 437 | setcookie('sessionID', $session->sessionID, $validuntil, '/', $this->config->cookiedomain, $ssl); // Set a cookie. 438 | 439 | return true; 440 | } 441 | 442 | return false; 443 | } 444 | 445 | /** 446 | * Deletes a session. 447 | * 448 | * @param string $sessionid 449 | * @return bool 450 | */ 451 | public function delete_session($sessionid) { 452 | return $this->post('deleteSession', [ 453 | 'sessionID' => $sessionid, 454 | ]); 455 | } 456 | 457 | /** 458 | * Returns informations about a session. 459 | * 460 | * @param string $sessionid 461 | * @return array|\stdClass 462 | */ 463 | public function get_session_info($sessionid) { 464 | return $this->get('getSessionInfo', [ 465 | 'sessionID' => $sessionid, 466 | ]); 467 | } 468 | 469 | /** 470 | * Returns all sessions of a group. 471 | * 472 | * @param string $epgroupid 473 | * @return array 474 | */ 475 | public function list_sessions_of_group($epgroupid) { 476 | return $this->get('listSessionsOfGroup', [ 477 | 'groupID' => $epgroupid, 478 | ]); 479 | } 480 | 481 | /** 482 | * Returns all sessions of an author. 483 | * 484 | * @param string $authorid 485 | * @return array 486 | */ 487 | public function list_sessions_of_author($authorid) { 488 | return $this->get('listSessionsOfAuthor', [ 489 | 'authorID' => $authorid, 490 | ]); 491 | } 492 | 493 | // PAD CONTENT 494 | // Pad content can be updated and retrieved through the API. 495 | 496 | /** 497 | * Returns the text of a pad. 498 | * 499 | * @param string $padid 500 | * @param string $rev 501 | * @return \stdClass the text is defined in $obj->text 502 | */ 503 | public function get_text($padid, $rev = null) { 504 | $params = ['padID' => $padid]; 505 | if (isset($rev)) { 506 | $params['rev'] = $rev; 507 | } 508 | 509 | return $this->get('getText', $params); 510 | } 511 | 512 | /** 513 | * Returns the text of a pad as html. 514 | * 515 | * @param string $padid 516 | * @param string $rev 517 | * @return \stdClass the html is defined in $obj->html 518 | */ 519 | public function get_html($padid, $rev = null) { 520 | $params = ['padID' => $padid]; 521 | if (isset($rev)) { 522 | $params['rev'] = $rev; 523 | } 524 | 525 | return $this->get('getHTML', $params); 526 | } 527 | 528 | /** 529 | * Sets the text for a pad. 530 | * 531 | * @param string $padid 532 | * @param string $text 533 | * @return bool 534 | */ 535 | public function set_text($padid, $text) { 536 | return $this->post('setText', [ 537 | 'padID' => $padid, 538 | 'text' => $text, 539 | ]); 540 | } 541 | 542 | /** 543 | * Sets the html text of a pad. 544 | * 545 | * @param string $padid 546 | * @param string $html 547 | * @return bool 548 | */ 549 | public function set_html($padid, $html) { 550 | return $this->post('setHTML', [ 551 | 'padID' => $padid, 552 | 'html' => $html, 553 | ]); 554 | } 555 | 556 | // PAD 557 | // Group pads are normal pads, but with the name schema 558 | // GROUPID$padname. A security manager controls access of them and its 559 | // forbidden for normal pads to include a $ in the name. 560 | 561 | /** 562 | * Create a new pad. 563 | * 564 | * @param string $padid 565 | * @param string $text 566 | * @return bool 567 | */ 568 | public function create_pad($padid, $text) { 569 | return $this->post('createPad', [ 570 | 'padID' => $padid, 571 | 'text' => $text, 572 | ], 'POST'); 573 | } 574 | 575 | /** 576 | * Returns the number of revisions of this pad. 577 | * 578 | * @param string $padid 579 | * @return int 580 | */ 581 | public function get_revisions_count($padid) { 582 | return $this->get('getRevisionsCount', [ 583 | 'padID' => $padid, 584 | ]); 585 | } 586 | 587 | /** 588 | * Returns the number of users currently editing this pad. 589 | * 590 | * @param string $padid 591 | * @return int 592 | */ 593 | public function pad_users_count($padid) { 594 | return $this->get('padUsersCount', [ 595 | 'padID' => $padid, 596 | ]); 597 | } 598 | 599 | /** 600 | * Return the time the pad was last edited as a Unix timestamp. 601 | * 602 | * @param string $padid 603 | * @return int 604 | */ 605 | public function get_last_edited($padid) { 606 | return $this->get('getLastEdited', [ 607 | 'padID' => $padid, 608 | ]); 609 | } 610 | 611 | /** 612 | * Deletes a pad. 613 | * 614 | * @param string $padid 615 | * @return bool 616 | */ 617 | public function delete_pad($padid) { 618 | return $this->post('deletePad', [ 619 | 'padID' => $padid, 620 | ]); 621 | } 622 | 623 | /** 624 | * Returns the read only link of a pad. 625 | * 626 | * @param string $padid 627 | * @return string|bool The readonly id or false 628 | */ 629 | public function get_readonly_id($padid) { 630 | $id = $this->get('getReadOnlyID', [ 631 | 'padID' => $padid, 632 | ]); 633 | if ($id) { 634 | return $id->readOnlyID; 635 | } 636 | 637 | return false; 638 | } 639 | 640 | /** 641 | * Returns the ids of all authors who've edited this pad. 642 | * 643 | * @param string $padid 644 | * @return array 645 | */ 646 | public function list_authors_of_pad($padid) { 647 | return $this->get('listAuthorsOfPad', [ 648 | 'padID' => $padid, 649 | ]); 650 | } 651 | 652 | /** 653 | * Sets a boolean for the public status of a pad. 654 | * 655 | * @param string $padid 656 | * @param bool $publicstatus 657 | * @return bool 658 | */ 659 | public function set_public_status($padid, $publicstatus) { 660 | if (is_bool($publicstatus)) { 661 | $publicstatus = $publicstatus ? 'true' : 'false'; 662 | } 663 | 664 | return $this->post('setPublicStatus', [ 665 | 'padID' => $padid, 666 | 'publicStatus' => $publicstatus, 667 | ]); 668 | } 669 | 670 | /** 671 | * Get the public status. 672 | * 673 | * @param string $padid 674 | * @return bool 675 | */ 676 | public function get_public_status($padid) { 677 | return $this->get('getPublicStatus', [ 678 | 'padID' => $padid, 679 | ]); 680 | } 681 | 682 | /** 683 | * Set a password for a pad. 684 | * 685 | * @param string $padid 686 | * @param string $password 687 | * @return string|bool 688 | */ 689 | public function set_password($padid, $password) { 690 | return $this->post('setPassword', [ 691 | 'padID' => $padid, 692 | 'password' => $password, 693 | ]); 694 | } 695 | 696 | /** 697 | * Check whether or not a pad is protected by a password. 698 | * 699 | * @param string $padid 700 | * @return bool 701 | */ 702 | public function is_password_protected($padid) { 703 | return $this->get('isPasswordProtected', [ 704 | 'padID' => $padid, 705 | ]); 706 | } 707 | 708 | /** 709 | * Get all pad users. 710 | * 711 | * @param string $padid 712 | * @return array 713 | */ 714 | public function pad_users($padid) { 715 | return $this->get('padUsers', [ 716 | 'padID' => $padid, 717 | ]); 718 | } 719 | 720 | /** 721 | * Send all clients a message. 722 | * 723 | * @param string $padid 724 | * @param string $msg 725 | * @return bool 726 | */ 727 | public function send_clients_message($padid, $msg) { 728 | return $this->post('sendClientsMessage', [ 729 | 'padID' => $padid, 730 | 'msg' => $msg, 731 | ]); 732 | } 733 | 734 | /** 735 | * Checks whether or not the server url is blocked by moodle settings. 736 | * 737 | * @param string $urlstring 738 | * @return string|bool The blocked host or false 739 | */ 740 | public static function is_url_blocked($urlstring) { 741 | $curl = new \curl(['ignoresecurity' => true]); 742 | $url = new \moodle_url($urlstring); 743 | if ($curl->get_security()->url_is_blocked($url)) { 744 | $ipstring = ''; 745 | if ($ips = gethostbynamel($url->get_host())) { 746 | $ipstring = implode(', ', $ips); 747 | } 748 | 749 | return $url->get_host() . '(' . $ipstring . ')'; 750 | } 751 | 752 | return false; 753 | } 754 | 755 | /** 756 | * Get an instance of the client communicating with the etherpad server 757 | * If the site is in testing mode (behat or unit test) a dummy client is created, which only pretend to communicate. 758 | * 759 | * @param string $apikey 760 | * @param string $apiurl 761 | * @return static 762 | */ 763 | public static function get_instance($apikey, $apiurl = null) { 764 | static $client; 765 | if (empty($client)) { 766 | if (static::is_testing()) { 767 | $client = new dummy_client($apikey, $apiurl); 768 | } else { 769 | $client = new static($apikey, $apiurl); 770 | } 771 | } 772 | 773 | return $client; 774 | } 775 | 776 | /** 777 | * Checks whether or not the current site is running a test (behat or unit test). 778 | * 779 | * @return bool 780 | */ 781 | public static function is_testing() { 782 | $mycfg = get_config('etherpadlite'); 783 | 784 | if (empty($mycfg->url)) { 785 | return true; 786 | } 787 | 788 | if (defined('BEHAT_SITE_RUNNING')) { 789 | return true; 790 | } 791 | if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) { 792 | return true; 793 | } 794 | 795 | return false; 796 | } 797 | } 798 | -------------------------------------------------------------------------------- /classes/api/dummy_client.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\api; 18 | 19 | /** 20 | * This is a dummy class for testing purpose to simulate the communicate with the etherpadlite server. 21 | * 22 | * @package mod_etherpadlite 23 | * @author Andreas Grabs 24 | * @see https://github.com/TomNomNom/etherpad-lite-client 25 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | class dummy_client extends client { 29 | /** 30 | * Constructor. 31 | * 32 | * @param string $apikey 33 | * @param string $apiurl 34 | */ 35 | protected function __construct($apikey, $apiurl) { 36 | global $CFG; 37 | require_once($CFG->libdir . '/filelib.php'); 38 | 39 | $this->config = get_config('etherpadlite'); 40 | 41 | if ($apikey === '') { 42 | throw new api_exception('error_config_has_no_api_key'); 43 | } 44 | $this->apikey = $apikey; 45 | 46 | if (!empty($apiurl)) { 47 | $this->apiurl = trim($apiurl, '/'); 48 | $this->apiurl .= '/api'; 49 | if (!filter_var($this->apiurl, FILTER_VALIDATE_URL)) { 50 | throw new api_exception('error_config_has_no_valid_baseurl'); 51 | } 52 | } 53 | 54 | // Sometimes the etherpad host is located on an internal network like 127.0.0.1 or 10.0.0.0/8. 55 | // Since Moodle 4.0 this kind of host are blocked by default. 56 | $settings = []; 57 | if (!empty($this->config->ignoresecurity)) { 58 | $settings['ignoresecurity'] = true; 59 | } 60 | 61 | if (empty($this->config->apiversion)) { 62 | $this->config->apiversion = self::DEFAULT_API_VERSION; 63 | } 64 | } 65 | 66 | /** 67 | * Creates a new session. 68 | * 69 | * @param string $epgroupid 70 | * @param string $authorid 71 | * @return string|bool the new session id or false 72 | */ 73 | public function create_session($epgroupid, $authorid) { 74 | return true; 75 | } 76 | 77 | /** 78 | * Create a new group. 79 | * 80 | * @return string|bool The new group id or false 81 | */ 82 | public function create_group() { 83 | return random_string(20); 84 | } 85 | 86 | /** 87 | * Creates a new pad in this group. 88 | * 89 | * @param string $epgroupid 90 | * @param string $padname 91 | * @param string $text 92 | * @return string|bool The new pad id or false 93 | */ 94 | public function create_group_pad($epgroupid, $padname, $text = null) { 95 | return 'g.' . random_string(20) . '$' . $padname; 96 | } 97 | 98 | /** 99 | * Deletes a group. 100 | * 101 | * @param string $epgroupid 102 | * @return bool 103 | */ 104 | public function delete_group($epgroupid) { 105 | return true; 106 | } 107 | 108 | /** 109 | * Deletes a pad. 110 | * 111 | * @param string $padid 112 | * @return bool 113 | */ 114 | public function delete_pad($padid) { 115 | return true; 116 | } 117 | 118 | /** 119 | * Returns the read only link of a pad. 120 | * 121 | * @param string $padid 122 | * @return string|bool The readonly id or false 123 | */ 124 | public function get_readonly_id($padid) { 125 | return random_string(20); 126 | } 127 | 128 | /** 129 | * Create a new author. 130 | * 131 | * @param string $name 132 | * @return string|bool The new author id or false 133 | */ 134 | public function create_author($name) { 135 | return random_string(20); 136 | } 137 | 138 | /** 139 | * This functions helps you to map your application author ids to etherpad lite author ids. 140 | * 141 | * @param string $authormapper 142 | * @param string $name 143 | * @return string|bool the new author id or false 144 | */ 145 | public function create_author_if_not_exists_for($authormapper, $name) { 146 | return $this->create_author($name); 147 | } 148 | 149 | /** 150 | * Returns the text of a pad. 151 | * 152 | * @param string $padid 153 | * @param string $rev 154 | * @return string 155 | */ 156 | public function get_text($padid, $rev = null) { 157 | return html_to_text($this->get_html($padid, $rev)); 158 | } 159 | 160 | /** 161 | * Returns the text of a pad as html. 162 | * 163 | * @param string $padid 164 | * @param string $rev 165 | * @return string 166 | */ 167 | public function get_html($padid, $rev = null) { 168 | return '
something formatted
'; 169 | } 170 | 171 | /** 172 | * Sets the text for a pad. 173 | * 174 | * @param string $padid 175 | * @param string $text 176 | * @return bool 177 | */ 178 | public function set_text($padid, $text) { 179 | return true; 180 | } 181 | 182 | /** 183 | * Sets the html text of a pad. 184 | * 185 | * @param string $padid 186 | * @param string $html 187 | * @return bool 188 | */ 189 | public function set_html($padid, $html) { 190 | return true; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /classes/dates.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Contains the class for fetching the important dates in mod_etherpadlite for a given module instance and a user. 19 | * 20 | * @package mod_etherpadlite 21 | * @copyright 2022 Adrian Czermak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | declare(strict_types=1); 26 | 27 | namespace mod_etherpadlite; 28 | 29 | use core\activity_dates; 30 | 31 | /** 32 | * Class for fetching the important dates in mod_etherpadlite for a given module instance and a user. 33 | * 34 | * @copyright 2022 Adrian Czermak 35 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 | */ 37 | class dates extends activity_dates { 38 | /** 39 | * Returns a list of important dates in mod_wordcloud. 40 | * 41 | * @return array 42 | */ 43 | protected function get_dates(): array { 44 | global $DB; 45 | 46 | $timeopen = $this->cm->customdata['timeopen'] ?? null; 47 | $timeclose = $this->cm->customdata['timeclose'] ?? null; 48 | 49 | $now = time(); 50 | $dates = []; 51 | 52 | if ($timeopen) { 53 | $openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened'; 54 | $dates[] = [ 55 | 'label' => get_string($openlabelid, 'core_course'), 56 | 'timestamp' => (int) $timeopen, 57 | ]; 58 | } 59 | 60 | if ($timeclose) { 61 | $closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed'; 62 | $dates[] = [ 63 | 'label' => get_string($closelabelid, 'core_course'), 64 | 'timestamp' => (int) $timeclose, 65 | ]; 66 | } 67 | 68 | return $dates; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /classes/event/course_module_viewed.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\event; 18 | 19 | /** 20 | * Event class for course_module_viewed. 21 | * 22 | * @package mod_etherpadlite 23 | * @author Andreas Grabs 24 | * @copyright 2019 Humboldt-Universität zu Berlin 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class course_module_viewed extends \core\event\course_module_viewed { 28 | /** 29 | * Init method. 30 | * 31 | * @return void 32 | */ 33 | protected function init() { 34 | $this->data['objecttable'] = 'etherpadlite'; 35 | parent::init(); 36 | } 37 | 38 | /** 39 | * Get URL related to the action. 40 | * 41 | * @return \moodle_url 42 | */ 43 | public function get_url() { 44 | return new \moodle_url('/mod/etherpadlite/view.php', ['id' => $this->contextinstanceid]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /classes/observer/group.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\observer; 18 | 19 | use mod_etherpadlite\task\delete_moodle_group_pad; 20 | 21 | /** 22 | * Event observer for mod_etherpadlite. 23 | * 24 | * @package mod_etherpadlite 25 | * @copyright 2022 Andreas Grabs 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | class group { 29 | /** 30 | * Triggered via group_created core event. 31 | * 32 | * @param \core\event\group_created $event 33 | * @return void 34 | */ 35 | public static function group_created(\core\event\group_created $event) { 36 | global $DB; 37 | 38 | $data = $event->get_data(); 39 | $etherpads = $DB->get_records('etherpadlite', ['course' => $data['courseid']]); 40 | if (!$etherpads) { 41 | return; 42 | } 43 | $config = get_config('etherpadlite'); 44 | 45 | try { 46 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 47 | } catch (\mod_etherpadlite\api\api_exception $e) { 48 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 49 | 50 | return; 51 | } 52 | 53 | $etherpads = $DB->get_records('etherpadlite', ['course' => $data['courseid']]); 54 | $mgroupdb = []; 55 | foreach ($etherpads as $etherpad) { 56 | $padid = $etherpad->uri; 57 | $epgroupid = explode('$', $padid); 58 | $epgroupid = $epgroupid[0]; 59 | 60 | $cm = get_coursemodule_from_instance('etherpadlite', $etherpad->id); 61 | 62 | if ($cm->groupmode != 0) { 63 | $mgroup = []; 64 | $mgroup['padid'] = $etherpad->id; 65 | $mgroup['groupid'] = $event->objectid; 66 | try { 67 | $padid = $client->create_group_pad($epgroupid, $config->padname . $event->objectid); 68 | } catch (\Exception $e) { 69 | continue; 70 | } 71 | $mgroupdb[] = $mgroup; 72 | } 73 | } 74 | $DB->insert_records('etherpadlite_mgroups', $mgroupdb); 75 | } 76 | 77 | /** 78 | * Triggered via group_deleted core event. 79 | * 80 | * @param \core\event\group_deleted $event 81 | */ 82 | public static function group_deleted(\core\event\group_deleted $event) { 83 | global $DB; 84 | 85 | $config = get_config('etherpadlite'); 86 | 87 | $sql = 'SELECT mg.id, mg.groupid, e.id, e.uri 88 | FROM {etherpadlite_mgroups} mg 89 | LEFT JOIN {etherpadlite} e ON e.id = mg.padid 90 | WHERE e.course = :courseid AND mg.groupid = :groupid'; 91 | $records = $DB->get_records_sql($sql, ['courseid' => $event->courseid, 'groupid' => $event->objectid]); 92 | $nextruntime = self::get_next_runtime($config->deletemgrouppad); 93 | 94 | foreach ($records as $record) { 95 | self::delete_mgroup_pad_adhoc_task($record->uri, $record->id, $record, $nextruntime); 96 | } 97 | } 98 | 99 | /** 100 | * Triggered via grouping_group_assigned core event. 101 | * 102 | * @param \core\event\grouping_group_assigned $event 103 | */ 104 | public static function grouping_group_assigned(\core\event\grouping_group_assigned $event) { 105 | global $DB; 106 | 107 | $etherpads = $DB->get_records('etherpadlite', ['course' => $event->courseid]); 108 | if (!$etherpads) { 109 | return; 110 | } 111 | $other = $event->other; 112 | $config = get_config('etherpadlite'); 113 | 114 | try { 115 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 116 | } catch (\mod_etherpadlite\api\api_exception $e) { 117 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 118 | 119 | return; 120 | } 121 | 122 | foreach ($etherpads as $etherpad) { 123 | $padid = $etherpad->uri; 124 | $epgroupid = explode('$', $padid); 125 | $epgroupid = $epgroupid[0]; 126 | $cm = get_coursemodule_from_instance('etherpadlite', $etherpad->id); 127 | if ($cm->groupmode != 0 && $cm->groupingid == $event->objectid) { 128 | $mgroup = []; 129 | $mgroup['padid'] = $etherpad->id; 130 | $mgroup['groupid'] = $other['groupid']; 131 | try { 132 | $padid = $client->create_group_pad($epgroupid, $config->padname . $event->objectid); 133 | } catch (\Exception $e) { 134 | continue; 135 | } 136 | $DB->insert_record('etherpadlite_mgroups', $mgroup); 137 | } 138 | } 139 | } 140 | 141 | /** 142 | * Triggered via grouping_group_unassigned core event. 143 | * 144 | * @param \core\event\grouping_group_unassigned $event 145 | */ 146 | public static function grouping_group_unassigned(\core\event\grouping_group_unassigned $event) { 147 | global $DB; 148 | 149 | $config = get_config('etherpadlite'); 150 | 151 | $other = $event->other; 152 | $sql = 'SELECT mg.id, mg.groupid, e.id, e.uri 153 | FROM {etherpadlite_mgroups} mg 154 | LEFT JOIN {etherpadlite} e ON e.id = mg.padid 155 | LEFT JOIN {course_modules} cm ON e.id = cm.instance 156 | WHERE e.course = :courseid AND mg.groupid = :groupid AND cm.groupingid = :groupingid'; 157 | $records = $DB->get_records_sql($sql, 158 | ['courseid' => $event->courseid, 'groupid' => $other['groupid'], 'groupingid' => $event->objectid]); 159 | $nextruntime = self::get_next_runtime($config->deletemgrouppad); 160 | foreach ($records as $record) { 161 | self::delete_mgroup_pad_adhoc_task($record->uri, $record->id, $record, $nextruntime); 162 | } 163 | } 164 | 165 | /** 166 | * Triggered via course_module_updated core event. 167 | * 168 | * @param \core\event\course_module_updated $event 169 | */ 170 | public static function course_module_updated(\core\event\course_module_updated $event) { 171 | global $DB; 172 | 173 | $eventdata = $event->get_data(); 174 | $other = $eventdata['other']; 175 | 176 | if ($other['modulename'] == 'etherpadlite') { 177 | $cm = $DB->get_record('course_modules', ['id' => $eventdata['objectid']]); 178 | $etherpadlite = $DB->get_record('etherpadlite', ['id' => $other['instanceid']]); 179 | // If groupmode is not set anymore, delete mgroupspads if exist. 180 | $data = []; 181 | $data['groupmode'] = $cm->groupmode; 182 | $data['course'] = $eventdata['courseid']; 183 | $config = get_config('etherpadlite'); 184 | 185 | try { 186 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 187 | } catch (\mod_etherpadlite\api\api_exception $e) { 188 | return; 189 | } 190 | 191 | if ($data['groupmode'] == 0) { 192 | $mgrouppads = $DB->get_records('etherpadlite_mgroups', ['padid' => $etherpadlite->id]); 193 | if ($mgrouppads) { 194 | $nextruntime = self::get_next_runtime($config->deletemgrouppad); 195 | if ($nextruntime) { 196 | foreach ($mgrouppads as $mgrouppad) { 197 | self::delete_mgroup_pad_adhoc_task($etherpadlite->uri, 198 | $etherpadlite->id, $mgrouppad, $nextruntime); 199 | } 200 | } 201 | } 202 | } else { 203 | $config = get_config('etherpadlite'); 204 | $groups = groups_get_all_groups($data['course'], 0, $cm->groupingid); 205 | 206 | $epgroupid = explode('$', $etherpadlite->uri); 207 | $epgroupid = $epgroupid[0]; 208 | 209 | $mgroupdb = []; 210 | foreach ($groups as $group) { 211 | $mgroup = new \stdClass(); 212 | if (!$DB->record_exists('etherpadlite_mgroups', ['padid' => $etherpadlite->id, 'groupid' => $group->id])) { 213 | $mgroup->padid = $etherpadlite->id; 214 | $mgroup->groupid = $group->id; 215 | $mgroupdb[] = $mgroup; 216 | try { 217 | $padid = $client->create_group_pad($epgroupid, $config->padname . $group->id); 218 | } catch (\Exception $e) { 219 | continue; 220 | } 221 | } 222 | } 223 | $DB->insert_records('etherpadlite_mgroups', $mgroupdb); 224 | } 225 | } 226 | } 227 | 228 | /** 229 | * Get the next runtime for deletion. 230 | * 231 | * @param int $configtime 232 | * @return int|null 233 | */ 234 | public static function get_next_runtime(int $configtime) { 235 | $timenow = time(); 236 | $nextruntime = 0; 237 | switch ($configtime) { 238 | case 0: 239 | $nextruntime = null; 240 | break; 241 | case 1: 242 | $nextruntime = $timenow; 243 | // No break here! 244 | case 2: 245 | $nextruntime = $timenow + 3600; 246 | break; 247 | case 3: 248 | $nextruntime = $timenow + 12 * 3600; 249 | break; 250 | case 4: 251 | $nextruntime = $timenow + 24 * 3600; 252 | break; 253 | } 254 | 255 | return $nextruntime; 256 | } 257 | 258 | /** 259 | * Create a new adhoc task for deletion of an mgrouppad. 260 | * 261 | * @param string $paduri 262 | * @param string $padid 263 | * @param \stdClass $mgrouppad 264 | * @param int $nextruntime 265 | * @return void 266 | */ 267 | public static function delete_mgroup_pad_adhoc_task($paduri, $padid, $mgrouppad, $nextruntime) { 268 | $deletepad = new delete_moodle_group_pad(); 269 | $deletepad->set_custom_data([ 270 | 'paduri' => $paduri . $mgrouppad->groupid, 271 | 'etherpadliteid' => $padid, 272 | 'mrouppadid' => $mgrouppad->id, 273 | 'mgroupid' => $mgrouppad->groupid, 274 | ]); 275 | $deletepad->set_next_run_time($nextruntime); 276 | \core\task\manager::queue_adhoc_task($deletepad, true); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /classes/output/component/urlsettingsnote.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\output\component; 18 | 19 | /** 20 | * Output component to render a notification. 21 | * 22 | * @package mod_etherpadlite 23 | * @author Andreas Grabs 24 | * @copyright 2019 Humboldt-Universität zu Berlin 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class urlsettingsnote implements \renderable, \templatable { 28 | /** @var array */ 29 | protected $data = []; 30 | 31 | /** 32 | * Constructor. 33 | * 34 | * @param \stdClass $config The plugin configuration 35 | */ 36 | public function __construct(\stdClass $config) { 37 | if (!empty($config->url)) { 38 | // Is the current host blocked? 39 | $blockedhost = \mod_etherpadlite\api\client::is_url_blocked($config->url); 40 | if ($blockedhost && empty($config->ignoresecurity)) { 41 | $connected = false; 42 | } else { 43 | // Check the connection with the current config, but only if the host is not blocked. 44 | try { 45 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 46 | $connected = true; 47 | } catch (\mod_etherpadlite\api\api_exception $e) { 48 | $connected = false; 49 | $infotext = $e->getMessage(); 50 | } 51 | } 52 | 53 | if ($connected) { 54 | $connectiontext = get_string('connected', 'etherpadlite'); 55 | $connectiontextclass = 'success'; 56 | $connectionicon = 'fa-check-square-o'; 57 | } else { 58 | $connectiontext = get_string('not_connected', 'etherpadlite'); 59 | $connectiontextreason = $infotext ?? ''; 60 | $connectiontextclass = 'danger'; 61 | $connectionicon = 'fa-times-circle-o'; 62 | } 63 | 64 | $blockedhostinfo = ''; 65 | if ($blockedhost) { 66 | $blockingicon = 'fa-exclamation-triangle'; 67 | if (empty($config->ignoresecurity)) { 68 | $blockedhostinfo = get_string('urlisblocked', 'etherpadlite', $blockedhost); 69 | $blockingtextclass = 'danger'; 70 | } else { 71 | $blockedhostinfo = get_string('urlisblocked_but_ignored', 'etherpadlite', $blockedhost); 72 | $blockingtextclass = 'warning'; 73 | } 74 | } 75 | 76 | $this->data['urlinfo'] = get_string('urldesc', 'etherpadlite'); 77 | $this->data['blockingmsg'] = $blockedhostinfo ?? ''; 78 | $this->data['blockingicon'] = $blockingicon ?? ''; 79 | $this->data['blockingtextclass'] = $blockingtextclass ?? ''; 80 | $this->data['connectiontext'] = $connectiontext; 81 | $this->data['connectiontextreason'] = $connectiontextreason ?? ''; 82 | $this->data['connectionicon'] = $connectionicon; 83 | $this->data['connectiontextclass'] = $connectiontextclass; 84 | 85 | } 86 | } 87 | 88 | /** 89 | * Get the mustache context data. 90 | * 91 | * @param \renderer_base $output 92 | * @return \stdClass|array 93 | */ 94 | public function export_for_template(\renderer_base $output) { 95 | return $this->data; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /classes/output/renderer.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\output; 18 | 19 | /** 20 | * Renderer class for this plugin. 21 | * 22 | * @package mod_etherpadlite 23 | * @author Andreas Grabs 24 | * @copyright 2019 Humboldt-Universität zu Berlin 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class renderer extends \plugin_renderer_base { 28 | /** 29 | * Renders the etherpad frame. 30 | * 31 | * @param \stdClass $etherpadlite 32 | * @param \stdClass $cm 33 | * @param string $frameurl 34 | * @return string The rendered html output 35 | */ 36 | public function render_etherpad($etherpadlite, $cm, $frameurl) { 37 | $config = get_config('etherpadlite'); 38 | 39 | $summaryguest = ''; 40 | if (isguestuser() && !etherpadlite_guestsallowed($etherpadlite)) { 41 | $summaryguest = get_string('summaryguest', 'etherpadlite'); 42 | } 43 | 44 | $content = new \stdClass(); 45 | $content->id = $cm->id; 46 | $content->name = $etherpadlite->name; 47 | $content->summaryguest = $summaryguest; 48 | $content->frameurl = $frameurl; 49 | $content->emptyurl = new \moodle_url('/mod/etherpadlite/empty.html'); 50 | $content->minwidth = (empty($config->minwidth) ? 10 : $config->minwidth) . 'px'; 51 | $content->courseurl = new \moodle_url('/course/view.php', ['id' => $etherpadlite->course]); 52 | 53 | // Add a warning notice. 54 | if (\mod_etherpadlite\api\client::is_testing()) { 55 | $content->hasnotice = true; 56 | $content->noticetype = \core\notification::WARNING; 57 | $content->notice = get_string('urlnotset', 'mod_etherpadlite'); 58 | } 59 | 60 | return $this->render_from_template('mod_etherpadlite/content', $content); 61 | } 62 | 63 | /** 64 | * Checks whether or not the current theme is based on boost. 65 | * 66 | * @return bool 67 | */ 68 | public function is_boost_based() { 69 | if (strcmp($this->page->theme->name, 'boost') === 0) { 70 | return true; 71 | } else if (!empty($this->page->theme->parents)) { 72 | if (in_array('boost', $this->page->theme->parents) === true) { 73 | return true; 74 | } 75 | } 76 | 77 | return false; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /classes/privacy/provider.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace mod_etherpadlite\privacy; 18 | 19 | /** 20 | * Privacy Subsystem implementation for mod_etherpadlite. 21 | * 22 | * @package mod_etherpadlite 23 | * @copyright Catalyst IT Canada LTD 24 | * @author Karl Michael Reyes 25 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | class provider implements \core_privacy\local\metadata\null_provider { 28 | /** 29 | * Get the language string identifier with the component's language 30 | * file to explain why this plugin stores no data. 31 | * 32 | * @return string 33 | */ 34 | public static function get_reason(): string { 35 | return 'privacy:metadata'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /classes/task/delete_moodle_group_pad.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file defines an adhoc task to send notifications. 19 | * 20 | * @package mod_etherpadlite 21 | * @copyright 2022 University of Vienna 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace mod_etherpadlite\task; 26 | 27 | /** 28 | * Adhoc task to delete moodle group mode pads. 29 | * 30 | * @package mod_etherpadlite 31 | * @copyright 2022 University of Vienna 32 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 | */ 34 | class delete_moodle_group_pad extends \core\task\adhoc_task { 35 | /** 36 | * Execute this task. 37 | * 38 | * @return void 39 | */ 40 | public function execute() { 41 | global $DB; 42 | 43 | $data = $this->get_custom_data(); 44 | $etherpad = $DB->get_record('etherpadlite', ['id' => $data->etherpadliteid]); 45 | try { 46 | list($course, $cm) = get_course_and_cm_from_instance($data->etherpadliteid, 'etherpadlite'); 47 | if ($cm->groupmode == 1 || $cm->groupmode == 2) { 48 | $mgroups = groups_get_all_groups($etherpad->course, 0, $cm->groupingid); 49 | if (in_array($data->mgroupid, array_keys($mgroups))) { 50 | return; 51 | } 52 | } 53 | } catch (\moodle_exception $e) { 54 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 55 | } 56 | 57 | $config = get_config('etherpadlite'); 58 | try { 59 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 60 | $client->delete_pad($data->paduri); 61 | } catch (\mod_etherpadlite\api\api_exception $e) { 62 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 63 | } 64 | 65 | $DB->delete_records('etherpadlite_mgroups', ['id' => $data->mrouppadid]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /classes/util.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Contains utility functions. 19 | * 20 | * @package mod_etherpadlite 21 | * @copyright 2022 Andreas Grabs 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | namespace mod_etherpadlite; 26 | 27 | /** 28 | * Class for fetching the important dates in mod_etherpadlite for a given module instance and a user. 29 | * 30 | * @copyright 2022 Adrian Czermak 31 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 | */ 33 | class util { 34 | /** 35 | * Get an existing activity instance. 36 | * An array with the following elements will be returned. 37 | * [ 38 | * $course, 39 | * $cm, 40 | * $etherpadlite, 41 | * ]. 42 | * 43 | * @param int $id The coursemodule id 44 | * @param int $a The instance id from table etherpadlite 45 | * @return array 46 | */ 47 | public static function get_coursemodule($id, $a) { 48 | global $DB; 49 | 50 | if ($id) { 51 | $cm = get_coursemodule_from_id('etherpadlite', $id, 0, false, MUST_EXIST); 52 | $course = $DB->get_record('course', ['id' => $cm->course], '*', MUST_EXIST); 53 | $etherpadlite = $DB->get_record('etherpadlite', ['id' => $cm->instance], '*', MUST_EXIST); 54 | } else if ($a) { 55 | $etherpadlite = $DB->get_record('etherpadlite', ['id' => $a], '*', MUST_EXIST); 56 | $course = $DB->get_record('course', ['id' => $etherpadlite->course], '*', MUST_EXIST); 57 | $cm = get_coursemodule_from_instance('etherpadlite', $etherpadlite->id, $course->id, false, MUST_EXIST); 58 | } else { 59 | throw new \moodle_exception('You must specify a course_module ID or an instance ID'); 60 | } 61 | 62 | return [$course, $cm, $etherpadlite]; 63 | } 64 | 65 | /** 66 | * Reset the content of an etherpadlite instance. This affects the main pad and also all related group pads. 67 | * 68 | * @param \stdClass $etherpadlite 69 | * @param \mod_etherpadlite\api\client $client 70 | * @return bool 71 | */ 72 | public static function reset_etherpad_content(\stdClass $etherpadlite, api\client $client) { 73 | $config = get_config('etherpadlite'); 74 | 75 | $padid = $etherpadlite->uri; 76 | $groups = groups_get_all_groups($etherpadlite->course); 77 | 78 | $epgroupid = explode('$', $padid); 79 | $epgroupid = $epgroupid[0]; 80 | 81 | try { 82 | if ($groups) { 83 | // Empty the content of the group pads. 84 | foreach ($groups as $group) { 85 | $grouppadid = $padid . $group->id; 86 | $client->delete_pad($grouppadid); 87 | $client->create_group_pad($epgroupid, $config->padname . $group->id, ''); 88 | } 89 | } 90 | $client->delete_pad($padid); 91 | $client->create_group_pad($epgroupid, $config->padname); 92 | $result = true; 93 | } catch (\Exception $e) { 94 | $result = false; 95 | } 96 | 97 | return $result; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Plugin capabilities. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | $capabilities = [ 29 | 'mod/etherpadlite:addinstance' => [ 30 | 'riskbitmask' => RISK_XSS, 31 | 32 | 'captype' => 'write', 33 | 'contextlevel' => CONTEXT_COURSE, 34 | 'archetypes' => [ 35 | 'editingteacher' => CAP_ALLOW, 36 | 'manager' => CAP_ALLOW, 37 | ], 38 | 'clonepermissionsfrom' => 'moodle/course:manageactivities', 39 | ], 40 | ]; 41 | -------------------------------------------------------------------------------- /db/events.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Mod etherpadlite events. 19 | * 20 | * @package mod_etherpadlite 21 | * @copyright 2022 University of vienna 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | defined('MOODLE_INTERNAL') || die; 25 | 26 | $observers = [ 27 | [ 28 | 'eventname' => '\core\event\group_created', 29 | 'callback' => '\mod_etherpadlite\observer\group::group_created', 30 | ], 31 | [ 32 | 'eventname' => '\core\event\group_deleted', 33 | 'callback' => '\mod_etherpadlite\observer\group::group_deleted', 34 | 'internal' => true, 35 | ], 36 | [ 37 | 'eventname' => '\core\event\grouping_group_assigned', 38 | 'callback' => '\mod_etherpadlite\observer\group::grouping_group_assigned', 39 | 'internal' => true, 40 | ], 41 | [ 42 | 'eventname' => '\core\event\grouping_group_unassigned', 43 | 'callback' => '\mod_etherpadlite\observer\group::grouping_group_unassigned', 44 | 'internal' => true, 45 | ], 46 | [ 47 | 'eventname' => '\core\event\course_module_updated', 48 | 'callback' => '\mod_etherpadlite\observer\group::course_module_updated', 49 | 'internal' => true, 50 | ], 51 | ]; 52 | -------------------------------------------------------------------------------- /db/install.xml: -------------------------------------------------------------------------------- 1 | 2 | 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 |
41 | -------------------------------------------------------------------------------- /db/log.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Definition of log events. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | global $DB; 29 | 30 | $logs = [ 31 | ['module' => 'etherpadlite', 'action' => 'add', 'mtable' => 'etherpadlite', 'field' => 'name'], 32 | ['module' => 'etherpadlite', 'action' => 'update', 'mtable' => 'etherpadlite', 'field' => 'name'], 33 | ['module' => 'etherpadlite', 'action' => 'view', 'mtable' => 'etherpadlite', 'field' => 'name'], 34 | ['module' => 'etherpadlite', 'action' => 'view all', 'mtable' => 'etherpadlite', 'field' => 'name'], 35 | ]; 36 | -------------------------------------------------------------------------------- /db/upgrade.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file keeps track of upgrades to the etherpadlite module. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | * @param mixed $oldversion 26 | */ 27 | 28 | // This file keeps track of upgrades to 29 | // the etherpadlite module 30 | // 31 | // Sometimes, changes between versions involve 32 | // alterations to database structures and other 33 | // major things that may break installations. 34 | // 35 | // The upgrade function in this file will attempt 36 | // to perform all the necessary actions to upgrade 37 | // your older installtion to the current version. 38 | // 39 | // If there's something it cannot do itself, it 40 | // will tell you what you need to do. 41 | // 42 | // The commands in here will all be database-neutral, 43 | // using the functions defined in lib/ddllib.php. 44 | 45 | /** 46 | * Process the upgrade of this plugin. 47 | * 48 | * @param int $oldversion 49 | * @return bool 50 | */ 51 | function xmldb_etherpadlite_upgrade($oldversion = 0) { 52 | global $CFG, $THEME, $DB; 53 | 54 | $dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes. 55 | 56 | if ($oldversion < 2013042901) { 57 | set_config('url', $CFG->etherpadlite_url, 'etherpadlite'); 58 | set_config('apikey', $CFG->etherpadlite_apikey, 'etherpadlite'); 59 | set_config('padname', $CFG->etherpadlite_padname, 'etherpadlite'); 60 | set_config('cookiedomain', $CFG->etherpadlite_cookiedomain, 'etherpadlite'); 61 | set_config('cookietime', $CFG->etherpadlite_cookietime, 'etherpadlite'); 62 | set_config('ssl', $CFG->etherpadlite_ssl, 'etherpadlite'); 63 | set_config('check_ssl', $CFG->etherpadlite_check_ssl, 'etherpadlite'); 64 | set_config('adminguests', $CFG->etherpadlite_adminguests, 'etherpadlite'); 65 | 66 | $DB->delete_records_select('config', "name LIKE 'etherpadlite_%'"); 67 | 68 | upgrade_mod_savepoint(true, 2013042901, 'etherpadlite'); 69 | } 70 | 71 | if ($oldversion < 2022041400) { 72 | // Define table etherpadlite_mgroups to be created. 73 | $table = new xmldb_table('etherpadlite_mgroups'); 74 | 75 | // Adding fields to table etherpadlite_mgroups. 76 | $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 77 | $table->add_field('padid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 78 | $table->add_field('groupid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 79 | 80 | // Adding keys to table etherpadlite_mgroups. 81 | $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); 82 | // Define key unique key over padid and groupid to be added to etherpadlite_mgroups. 83 | $table->add_key('padid-groupid', XMLDB_KEY_UNIQUE, ['padid', 'groupid']); 84 | 85 | // Conditionally launch create table for etherpadlite_mgroups. 86 | if (!$dbman->table_exists($table)) { 87 | $dbman->create_table($table); 88 | } 89 | 90 | // Etherpadlite savepoint reached. 91 | upgrade_mod_savepoint(true, 2022041400, 'etherpadlite'); 92 | } 93 | 94 | if ($oldversion < 2022083102) { 95 | // Define field timeopen to be added to etherpadlite. 96 | $table = new xmldb_table('etherpadlite'); 97 | $field = new xmldb_field('timeopen', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'timemodified'); 98 | 99 | // Conditionally launch add field timeopen. 100 | if (!$dbman->field_exists($table, $field)) { 101 | $dbman->add_field($table, $field); 102 | } 103 | 104 | // Define field timeclose to be added to etherpadlite. 105 | $field = new xmldb_field('timeclose', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'timeopen'); 106 | 107 | // Conditionally launch add field timeclose. 108 | if (!$dbman->field_exists($table, $field)) { 109 | $dbman->add_field($table, $field); 110 | } 111 | 112 | // Etherpadlite savepoint reached. 113 | upgrade_mod_savepoint(true, 2022083102, 'etherpadlite'); 114 | } 115 | 116 | return true; 117 | } 118 | -------------------------------------------------------------------------------- /empty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Empty page 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This page lists all the instances of etherpadlite in a particular course. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | require_once(dirname(__DIR__, 2) . '/config.php'); 28 | require_once(__DIR__ . '/lib.php'); 29 | 30 | global $OUTPUT, $PAGE; 31 | 32 | $id = required_param('id', PARAM_INT); // The course id. 33 | 34 | $PAGE->set_url('/mod/etherpadlite/index.php', ['id' => $id]); 35 | 36 | if (!$course = $DB->get_record('course', ['id' => $id])) { 37 | throw new \moodle_exception('invalidcourseid'); 38 | } 39 | 40 | require_course_login($course); 41 | 42 | // Get all required stringsetherpadlite. 43 | 44 | $stretherpadlites = get_string('modulenameplural', 'etherpadlite'); 45 | $stretherpadlite = get_string('modulename', 'etherpadlite'); 46 | 47 | // Get all the appropriate data. 48 | 49 | if (!$etherpadlites = get_all_instances_in_course('etherpadlite', $course)) { 50 | notice('There are no instances of etherpadlite', "../../course/view.php?id=$course->id"); 51 | die; 52 | } 53 | 54 | // Print the list of instances (your module will probably extend this). 55 | 56 | $timenow = time(); 57 | $strname = get_string('name'); 58 | $strsummary = get_string('summary'); 59 | $strweek = get_string('week'); 60 | $strtopic = get_string('topic'); 61 | 62 | $table = new html_table(); 63 | 64 | if ($course->format == 'weeks') { 65 | $table->head = [$strweek, $strname, $strsummary]; 66 | $table->align = ['center', 'left', 'left']; 67 | } else if ($course->format == 'topics') { 68 | $table->head = [$strtopic, $strname, $strsummary]; 69 | $table->align = ['center', 'left', 'left']; 70 | } else { 71 | $table->head = [$strname, $strsummary]; 72 | $table->align = ['left', 'left', 'left']; 73 | } 74 | 75 | foreach ($etherpadlites as $etherpadlite) { 76 | if (!$etherpadlite->visible) { 77 | // Show dimmed if the mod is hidden. 78 | $class = 'dimmed'; 79 | } else { 80 | // Show normal if the mod is visible. 81 | $class = ''; 82 | } 83 | $linkdata = (object) [ 84 | 'class' => $class, 85 | 'url' => new \moodle_url('/mod/etherpadlite/view.php', ['id' => $etherpadlite->coursemodule]), 86 | 'text' => format_string($etherpadlite->name), 87 | ]; 88 | $link = $OUTPUT->render_from_template('mod_etherpadlite/instance_link', $linkdata); 89 | 90 | if ($course->format == 'weeks' || $course->format == 'topics') { 91 | $table->data[] = [$etherpadlite->section, $link, format_text($etherpadlite->intro, FORMAT_MOODLE, 'para = false')]; 92 | } else { 93 | $table->data[] = [$link, format_text($etherpadlite->intro, FORMAT_MOODLE, 'para = false')]; 94 | } 95 | } 96 | 97 | // Output the page. 98 | $PAGE->navbar->add($stretherpadlites); 99 | $PAGE->set_title("$course->shortname: $stretherpadlites"); 100 | $PAGE->set_heading($course->fullname); 101 | 102 | echo $OUTPUT->header(); 103 | echo $OUTPUT->heading($stretherpadlites, 2); 104 | 105 | echo html_writer::table($table); 106 | 107 | echo $OUTPUT->footer($course); 108 | -------------------------------------------------------------------------------- /lang/en/etherpadlite.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file holds the english language. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | $string['activityclose'] = 'Allow editing to'; 27 | $string['activityopen'] = 'Allow editing from'; 28 | $string['activityopenclose'] = 'Allow editing from/to'; 29 | $string['activityopenclose_help'] = 'If activated, students can edit the etherpad only in the specified period.'; 30 | $string['adminguests'] = 'Guests allowed to write?'; 31 | $string['adminguestsdesc'] = 'With this set, users who are allowed to configure a specific etherpadlite module can allow guests to write in this specific etherpadlite module'; 32 | $string['apikey'] = 'API Key'; 33 | $string['apikeydesc'] = 'This is the API Key which this module needs to communicate with your etherpadlite server. This API key is stored on your etherpadliste server and can be copied from there.'; 34 | $string['apiversion'] = 'API version'; 35 | $string['apiversiondesc'] = 'The default version is "1.2". You only should use version "1.1" if you use a very old version of etherpad lite server!'; 36 | $string['checkssl'] = 'Verify HTTPS cert'; 37 | $string['checkssldesc'] = 'With this set, the HTTPS certificate of the etherpadlite server will be checked, to prevent man in the middle attacks'; 38 | $string['connected'] = 'Connected'; 39 | $string['connecttimeout'] = 'Connect Timeout'; 40 | $string['connecttimeoutdesc'] = 'Connect timeout is the maximum amount of time in seconds allowed to connect to the server. It can be set to 0 to disable this limit, but this is not advisable in a production environment.
41 | See also: CURLOPT_CONNECTTIMEOUT'; 42 | $string['cookiedomain'] = 'Cookie Domain'; 43 | $string['cookiedomaindesc'] = 'Here you can enter the domain, which should be stored in the session cookie, so that the etherpadlite server recognize it. When moodle runs on the domain moodle.example.com and your etherpadlite server on etherpadlite.example.com, then your cookie domain should be .example.com.'; 44 | $string['cookietime'] = 'Session elapse time'; 45 | $string['cookietimedesc'] = 'Here you have to enter the time (in seconds) until the etherpadlite session should be valid'; 46 | $string['copylink'] = 'Copy pad link'; 47 | $string['copylinkdesc'] = 'Add a button to the module navigation settings, which allows teachers and managers to easily get the full link for the current pad.'; 48 | $string['deletein24hours'] = '24 hours'; 49 | $string['deleteinonehour'] = '1 hour'; 50 | $string['deleteintwelvehours'] = '12 hours'; 51 | $string['deletemgroupads'] = 'Delete moodle group pads in'; 52 | $string['deletemgroupadsdesc'] = 'Delete all group pads created in association with moodle group mode'; 53 | $string['deletenow'] = 'Immediately'; 54 | $string['donotdelete'] = 'Do not delete'; 55 | $string['error_config_has_no_api_key'] = 'The api key is not configured yet! Please check your configuration!'; 56 | $string['error_config_has_no_valid_baseurl'] = 'The configured server url is not valid! Please check your configuration!'; 57 | $string['error_could_not_get_api_version'] = 'The Moodle server could not get the api version! Your Moodle server may not be able to connect to the Etherpad server. Please check your configuration!'; 58 | $string['error_invalid_api_key'] = 'The api key is not valid! Please check your configuration!'; 59 | $string['error_wrong_api_version'] = 'The api version is not compatible! Please check the version of your Etherpad server!'; 60 | $string['etherpadlite'] = 'Etherpad Lite'; 61 | $string['etherpadlite:addinstance'] = 'Add new pad'; 62 | $string['etherpadlite_link_copied_to_clipboard'] = 'The link of this etherpad was copied to your clipboard.'; 63 | $string['etherpadliteintro'] = 'Etherpadlite Intro'; 64 | $string['etherpadlitename'] = 'Etherpadlite Name'; 65 | $string['fullscreen'] = 'Fullscreen'; 66 | $string['guestsallowed'] = 'Guests allowed to write?'; 67 | $string['guestsallowed_help'] = 'This determines if guests are allowed to write in this pad. If not, they will be only able to read the content of the pad.'; 68 | $string['ignoresecurity'] = 'Ignore security'; 69 | $string['ignoresecuritydesc'] = 'If you activate this the url will be allowed despite the settings for "cURL blocked hosts list" (see: curlsecurityblockedhosts).'; 70 | $string['link_copied'] = 'Link copied!'; 71 | $string['minwidth'] = 'Minimum width'; 72 | $string['minwidthdesc'] = 'If you don\'t want a very small etherpad on small displays like cell phones, you can set a minimum width that will keep the pad.'; 73 | $string['modulename'] = 'Etherpad Lite'; 74 | $string['modulename_help'] = 'The Etherpad Lite module enables students and teachers to write text in a collaborative way. The text is synced automatically as they type. 75 | 76 | (The etherpadlite server, which stands behind this module, is still in beta stage. That\'s why problems might occur in rare circumstances, but aren\'t expected)'; 77 | $string['modulenameplural'] = 'Etherpad Lites'; 78 | $string['not_connected'] = 'Not connected'; 79 | $string['padname'] = 'Padname for all instances'; 80 | $string['padnamedesc'] = 'A general padname can be helpful, if you want to find all pads from this Moodle installation on your etherpadlite server. Pad groups are generated autmatically.'; 81 | $string['pluginadministration'] = 'Etherpad Lite administration'; 82 | $string['pluginname'] = 'Etherpad Lite'; 83 | $string['privacy:metadata'] = 'Ethepadlite plugin does not store any personal data.'; 84 | $string['resetting_data'] = 'Reset Etherpad Lite data'; 85 | $string['restorewindowsize'] = 'Restore window size'; 86 | $string['ssl'] = 'HTTPS Redirect'; 87 | $string['ssldesc'] = 'With this set, your site will redirect itself to HTTPS, if an etherpadlite is opened (eye candy for the user)'; 88 | $string['summaryguest'] = 'You are logged in as guest. That\'s why you can only see the readonly version of this Pad. Reload the page to get new changes.'; 89 | $string['timeout'] = 'Timeout'; 90 | $string['timeoutdesc'] = 'Timeout is the total time in seconds that we wait for a response to a given request, including the time it takes to establish the connection and the time it takes for the server to respond. It can be set to 0 to disable this limit, but this is not advisable in a production environment.
91 | See also: CURLOPT_TIMEOUT'; 92 | $string['url'] = 'Server URL'; 93 | $string['urldesc'] = 'This is the URL to your Etherpadlite server in the form: http[s]://host[:port]/[subDir/]'; 94 | $string['urlisblocked'] = 'The current host "{$a}" is blocked'; 95 | $string['urlisblocked_but_ignored'] = 'The current host "{$a}" is blocked but "ignoresecurity" is activated.'; 96 | $string['urlnotset'] = 'The etherpad server url is not configured for this site'; 97 | -------------------------------------------------------------------------------- /lib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Library of functions and constants for module etherpadlite. 19 | * 20 | * This file should have two well differenced parts: 21 | * - All the core Moodle functions, neeeded to allow 22 | * the module to work integrated in Moodle. 23 | * - All the etherpadlite specific functions, needed 24 | * to implement all the module logic. Please, note 25 | * that, if the module become complex and this lib 26 | * grows a lot, it's HIGHLY recommended to move all 27 | * these module specific functions to a new php file, 28 | * called "locallib.php" (see forum, quiz...). This will 29 | * help to save some memory when Moodle is performing 30 | * actions across all modules. 31 | * 32 | * @package mod_etherpadlite 33 | * 34 | * @author Timo Welde 35 | * @copyright 2012 Humboldt-Universität zu Berlin 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | define('ETHERPADLITE_RESETFORM_RESET', 'etherpadlite_reset_data_'); 39 | 40 | /** 41 | * Create a new etherpadlite instance. 42 | * 43 | * Given an object containing all the necessary data, (defined by the form in mod_form.php) this function 44 | * will create a new instance and return the id number of the new instance. 45 | * 46 | * @param stdClass $etherpadlite An object from the form in mod_form.php 47 | * @param mod_etherpadlite_mod_form $mform 48 | * @return int The id of the newly inserted etherpadlite record 49 | */ 50 | function etherpadlite_add_instance(stdClass $etherpadlite, $mform = null) { 51 | global $DB; 52 | $config = get_config('etherpadlite'); 53 | 54 | try { 55 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 56 | } catch (\mod_etherpadlite\api\api_exception $e) { 57 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 58 | 59 | return false; 60 | } 61 | 62 | if (!$epgroupid = $client->create_group()) { 63 | // The group already exists or something else went wrong. 64 | throw new \moodle_exception('could not create etherpad group'); 65 | } 66 | 67 | if (!$padid = $client->create_group_pad($epgroupid, $config->padname)) { 68 | // The pad already exists or something else went wrong. 69 | throw new \moodle_exception('could not create etherpad group pad'); 70 | } 71 | 72 | $etherpadlite->uri = $padid; 73 | 74 | $etherpadlite->timecreated = time(); 75 | 76 | $padinstanceid = $DB->insert_record('etherpadlite', $etherpadlite); 77 | 78 | // Get all groups. 79 | $groups = groups_get_all_groups($etherpadlite->course, 0, $etherpadlite->groupingid); 80 | 81 | if ($etherpadlite->groupmode != 0 && $groups) { 82 | $mgroupdb = []; 83 | foreach ($groups as $group) { 84 | $mgroup = new stdClass(); 85 | $mgroup->padid = $padinstanceid; 86 | $mgroup->groupid = $group->id; 87 | $mgroupdb[] = $mgroup; 88 | 89 | try { 90 | $padid = $client->create_group_pad($epgroupid, $config->padname . $group->id); 91 | } catch (Exception $e) { 92 | continue; 93 | } 94 | } 95 | $DB->insert_records('etherpadlite_mgroups', $mgroupdb); 96 | } 97 | 98 | return $padinstanceid; 99 | } 100 | 101 | /** 102 | * Update an existing etherpadlite instance. 103 | * 104 | * Given an object containing all the necessary data, (defined by the form in mod_form.php) this function 105 | * will update an existing instance with new data. 106 | * 107 | * @param stdClass $etherpadlite An object from the form in mod_form.php 108 | * @param mod_etherpadlite_mod_form $mform 109 | * @return bool Success/Fail 110 | */ 111 | function etherpadlite_update_instance(stdClass $etherpadlite, $mform = null) { 112 | global $DB; 113 | require_once(__DIR__ . '/locallib.php'); 114 | 115 | $etherpadlite->timemodified = time(); 116 | $etherpadlite->id = $etherpadlite->instance; 117 | 118 | // You may have to add extra stuff in here. 119 | if (empty($etherpadlite->guestsallowed)) { 120 | $etherpadlite->guestsallowed = 0; 121 | } 122 | // If groupmode is not set anymore, delete mgroupspads if exist. 123 | $formdata = $mform->get_data(); 124 | $etherpadliteuri = $DB->get_field('etherpadlite', 'uri', ['id' => $etherpadlite->id]); 125 | $config = get_config('etherpadlite'); 126 | try { 127 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 128 | } catch (\mod_etherpadlite\api\api_exception $e) { 129 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 130 | 131 | return false; 132 | } 133 | if ($formdata->groupmode != 0) { 134 | // Deletion will be done by adhoc task triggered by cm_update. 135 | mod_etherpadlite_add_mgrouppads($formdata, $etherpadlite->id, $etherpadliteuri, $client); 136 | } 137 | 138 | return $DB->update_record('etherpadlite', $etherpadlite); 139 | } 140 | 141 | /** 142 | * Given an ID of an instance of this module, 143 | * this function will permanently delete the instance 144 | * and any data that depends on it. 145 | * 146 | * @param int $id Id of the module instance 147 | * @return bool Success/Failure 148 | */ 149 | function etherpadlite_delete_instance($id) { 150 | global $DB; 151 | require_once(__DIR__ . '/locallib.php'); 152 | 153 | if (!$etherpadlite = $DB->get_record('etherpadlite', ['id' => $id])) { 154 | return false; 155 | } 156 | 157 | $result = true; 158 | 159 | // Delete any dependent records here. 160 | 161 | $config = get_config('etherpadlite'); 162 | try { 163 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 164 | 165 | $padid = $etherpadlite->uri; 166 | $epgroupid = explode('$', $padid); 167 | $epgroupid = $epgroupid[0]; 168 | 169 | // Delete pads for moodle groups and respective DB entry. 170 | mod_etherpadlite_delete_all_mgrouppads($id, $padid, $client); 171 | 172 | $client->delete_pad($padid); 173 | $client->delete_group($epgroupid); 174 | } catch (\mod_etherpadlite\api\api_exception $e) { 175 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 176 | } 177 | 178 | if (!$DB->delete_records('etherpadlite', ['id' => $etherpadlite->id])) { 179 | $result = false; 180 | } 181 | 182 | return $result; 183 | } 184 | 185 | /** 186 | * Return a small object with summary information about what a 187 | * user has done with a given particular instance of this module 188 | * Used for user activity reports. 189 | * $return->time = the time they did it 190 | * $return->info = a short text description. 191 | * 192 | * @param \stdClass $course 193 | * @param \stdClass $user 194 | * @param \stdClass $mod 195 | * @param \stdClass $etherpadlite 196 | * @return \stdClass|null 197 | */ 198 | function etherpadlite_user_outline($course, $user, $mod, $etherpadlite) { 199 | return null; 200 | } 201 | 202 | /** 203 | * Print a detailed representation. 204 | * 205 | * Print a reprensentation of what a user has done with a given particular instance of this module, for user activity reports. 206 | * 207 | * @param \stdClass $course 208 | * @param \stdClass $user 209 | * @param \stdClass $mod 210 | * @param \stdClass $etherpadlite 211 | * @return bool 212 | */ 213 | function etherpadlite_user_complete($course, $user, $mod, $etherpadlite) { 214 | return true; 215 | } 216 | 217 | /** 218 | * Return true if there was output, or false is there was none. 219 | * 220 | * Given a course and a time, this module should find recent activity 221 | * that has occurred in etherpadlite activities and print it out. 222 | * 223 | * @param \stdClass $course 224 | * @param bool $isteacher 225 | * @param int $timestart 226 | * @return bool 227 | */ 228 | function etherpadlite_print_recent_activity($course, $isteacher, $timestart) { 229 | return false; // True if anything was printed, otherwise false. 230 | } 231 | 232 | /** 233 | * Function to be run periodically according to the moodle cron 234 | * This function searches for things that need to be done, such 235 | * as sending out mail, toggling flags etc ... 236 | * 237 | * @return bool 238 | **/ 239 | function etherpadlite_cron() { 240 | return true; 241 | } 242 | 243 | /** 244 | * Must return an array of user records (all data) who are participants 245 | * for a given instance of etherpadlite. Must include every user involved 246 | * in the instance, independient of his role (student, teacher, admin...) 247 | * See other modules as example. 248 | * 249 | * @param int $etherpadliteid ID of an instance of this module 250 | * @return mixed boolean/array of students 251 | */ 252 | function etherpadlite_get_participants($etherpadliteid) { 253 | return false; 254 | } 255 | 256 | /** 257 | * Execute post-install custom actions for the module 258 | * This function was added in 1.9. 259 | * 260 | * @return bool true if success, false on error 261 | */ 262 | function etherpadlite_install() { 263 | return true; 264 | } 265 | 266 | /** 267 | * Execute post-uninstall custom actions for the module 268 | * This function was added in 1.9. 269 | * 270 | * @return bool true if success, false on error 271 | */ 272 | function etherpadlite_uninstall() { 273 | return true; 274 | } 275 | 276 | /** 277 | * Checks whether or not a given feature is supported. 278 | * 279 | * @param string $feature FEATURE_xx constant for requested feature 280 | * @return mixed True if module supports feature, false if not, null if doesn't know or string for the module purpose. 281 | */ 282 | function etherpadlite_supports($feature) { 283 | switch ($feature) { 284 | case FEATURE_GROUPS: 285 | case FEATURE_GROUPINGS: 286 | case FEATURE_MOD_INTRO: 287 | case FEATURE_BACKUP_MOODLE2: 288 | case FEATURE_SHOW_DESCRIPTION: 289 | return true; 290 | 291 | case FEATURE_GROUPMEMBERSONLY: 292 | case FEATURE_COMPLETION_TRACKS_VIEWS: 293 | case FEATURE_COMPLETION_HAS_RULES: 294 | case FEATURE_GRADE_HAS_GRADE: 295 | case FEATURE_GRADE_OUTCOMES: 296 | return false; 297 | 298 | case FEATURE_MOD_PURPOSE: 299 | return MOD_PURPOSE_COLLABORATION; 300 | default: 301 | return null; 302 | } 303 | } 304 | 305 | /** 306 | * Optionally extend the module settings menu for teachers and managers: 307 | * add a button which copies the url of the current pad to the clipboard. 308 | * 309 | * @param settings_navigation $settingsnav The settings navigation object 310 | * @param navigation_node $navigationnode The node to add module settings to 311 | * @return bool true if success, false on error 312 | */ 313 | function etherpadlite_extend_settings_navigation($settingsnav, $navigationnode) { 314 | global $USER, $PAGE; 315 | 316 | if (has_capability('mod/etherpadlite:addinstance', $PAGE->cm->context)) { 317 | $config = get_config('etherpadlite'); 318 | 319 | // Check if getting the pad url via the menu is enabled in the plugin settings. 320 | if ($config->copylink) { 321 | // Create navigation item with pseudo link. 322 | // It's just used as a button which triggers some javascript to copy the 323 | // pad url to the clipboard. 324 | $url = new moodle_url('#'); 325 | $copytoclipboardbutton = navigation_node::create( 326 | get_string('copylink', 'mod_etherpadlite'), 327 | $url, 328 | navigation_node::TYPE_SETTING, 329 | null, 330 | 'testkey', 331 | new pix_icon('t/copy', '') 332 | ); 333 | $copytoclipboardbutton->classes = ['copy_etherpadlink_to_clipboard_button']; 334 | 335 | // Add the copy to clipboard button to the module menu navigation. 336 | $navigationnode->add_node($copytoclipboardbutton); 337 | 338 | // Get the full etherpad url and pass it as a variable to the 339 | // javascript which handles the copying and the notification. 340 | global $DB; 341 | $paduri = $DB->get_record('etherpadlite', ['id' => $PAGE->cm->instance], 'uri', MUST_EXIST); 342 | $url = trim($config->url, '/'); 343 | $url .= '/p/' . $paduri->uri; 344 | 345 | // Include the javascript file, which handles the copy-to-clipboard process. 346 | $PAGE->requires->js_call_amd( 347 | 'mod_etherpadlite/copy_to_clipboard', 348 | 'init', 349 | [$url] 350 | ); 351 | } 352 | } 353 | } 354 | 355 | // Any other etherpadlite functions go here. Each of them must have a name that 356 | // starts with etherpadlite_ 357 | // Remember (see note in first lines) that, if this section grows, it's HIGHLY 358 | // recommended to move all funcions below to a new "localib.php" file. 359 | 360 | /** 361 | * A funtion to generate a random name if something doesn't already exist. 362 | * 363 | * @return string 364 | */ 365 | function etherpadlite_gen_random_string() { 366 | $length = 5; 367 | $characters = '0123456789'; 368 | $string = ''; 369 | for ($p = 0; $p < $length; ++$p) { 370 | $string .= $characters[mt_rand(0, strlen($characters) - 1)]; 371 | } 372 | 373 | return $string; 374 | } 375 | 376 | /** 377 | * Check whether or not guests are allowed. 378 | * 379 | * @param \stdClass $etherpadlite 380 | * @return void 381 | */ 382 | function etherpadlite_guestsallowed($etherpadlite) { 383 | global $CFG; 384 | 385 | if (get_config('etherpadlite', 'adminguests') == 1) { 386 | if ($etherpadlite->guestsallowed) { 387 | return true; 388 | } 389 | } 390 | 391 | return false; 392 | } 393 | 394 | /** 395 | * Add a get_coursemodule_info function in case any etherpad type wants to add 'extra' information 396 | * for the course (see resource). 397 | * 398 | * Given a course_module object, this function returns any "extra" information that may be needed 399 | * when printing this activity in a course listing. See get_array_of_activities() in course/lib.php. 400 | * 401 | * @param stdClass $coursemodule the coursemodule object (record) 402 | * @return cached_cm_info an object on information that the courses 403 | * will know about (most noticeably, an icon) 404 | */ 405 | function etherpadlite_get_coursemodule_info($coursemodule) { 406 | global $DB; 407 | 408 | $dbparams = ['id' => $coursemodule->instance]; 409 | $fields = 'id, course, name, timeopen, timeclose'; 410 | if (!$etherpad = $DB->get_record('etherpadlite', $dbparams)) { 411 | return false; 412 | } 413 | 414 | $result = new cached_cm_info(); 415 | $result->name = $etherpad->name; 416 | 417 | if ($coursemodule->showdescription) { 418 | // Convert intro to html. Do not filter cached version, filters run at display time. 419 | $result->content = format_module_intro('etherpadlite', $etherpad, $coursemodule->id, false); 420 | } 421 | 422 | // Populate some other values that can be used in calendar or on dashboard. 423 | if ($etherpad->timeopen) { 424 | $result->customdata['timeopen'] = $etherpad->timeopen; 425 | } 426 | if ($etherpad->timeclose) { 427 | $result->customdata['timeclose'] = $etherpad->timeclose; 428 | } 429 | 430 | return $result; 431 | } 432 | 433 | /** 434 | * This function is used by the reset_course_userdata function in moodlelib. 435 | * This function will remove all data from the specified etherpadlite. 436 | * 437 | * @param object $data the data submitted from the reset course 438 | * @return array status array 439 | */ 440 | function etherpadlite_reset_userdata($data) { 441 | global $DB; 442 | $config = get_config('etherpadlite'); 443 | 444 | $resetetherpadlites = []; 445 | $status = []; 446 | $componentstr = get_string('modulenameplural', 'etherpadlite'); 447 | 448 | // Get the relevant entries from $data. 449 | foreach ($data as $key => $value) { 450 | switch (true) { 451 | case substr($key, 0, strlen(ETHERPADLITE_RESETFORM_RESET)) == ETHERPADLITE_RESETFORM_RESET: 452 | if ($value == 1) { 453 | $templist = explode('_', $key); 454 | if (isset($templist[3])) { 455 | $resetetherpadlites[] = (int) $templist[3]; 456 | } 457 | } 458 | break; 459 | } 460 | } 461 | 462 | if (empty($resetetherpadlites)) { 463 | return $status; 464 | } 465 | 466 | try { 467 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 468 | } catch (\mod_etherpadlite\api\api_exception $e) { 469 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 470 | 471 | return $status; 472 | } 473 | 474 | // Reset the selected etherpadlites. 475 | foreach ($resetetherpadlites as $id) { 476 | $etherpadlite = $DB->get_record('etherpadlite', ['id' => $id]); 477 | // Delete etherpad lite data. 478 | $result = \mod_etherpadlite\util::reset_etherpad_content($etherpadlite, $client); 479 | $status[] = [ 480 | 'component' => $componentstr . ': ' . $etherpadlite->name, 481 | 'item' => get_string('resetting_data', 'etherpadlite'), 482 | 'error' => !$result, 483 | ]; 484 | } 485 | 486 | // Updating dates - shift may be negative too. 487 | if ($data->timeshift) { 488 | // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset. 489 | // See MDL-9367. 490 | $shifterror = !shift_course_mod_dates('etherpadlite', ['timeopen', 'timeclose'], $data->timeshift, $data->courseid); 491 | $status[] = ['component' => $componentstr, 'item' => get_string('datechanged'), 'error' => $shifterror]; 492 | } 493 | 494 | return $status; 495 | } 496 | 497 | /** 498 | * Called by course/reset.php. 499 | * 500 | * @param object $mform form passed by reference 501 | */ 502 | function etherpadlite_reset_course_form_definition(&$mform) { 503 | global $COURSE, $DB; 504 | 505 | $mform->addElement('header', 'etherpadliteheader', get_string('modulenameplural', 'etherpadlite')); 506 | 507 | if (!$etherpadlites = $DB->get_records('etherpadlite', ['course' => $COURSE->id], 'name')) { 508 | return; 509 | } 510 | 511 | $mform->addElement('static', 'hint', get_string('resetting_data', 'etherpadlite')); 512 | foreach ($etherpadlites as $etherpadlite) { 513 | $mform->addElement('checkbox', ETHERPADLITE_RESETFORM_RESET . $etherpadlite->id, $etherpadlite->name); 514 | } 515 | } 516 | 517 | /** 518 | * Course reset form defaults. 519 | * 520 | * @param object $course 521 | */ 522 | function etherpadlite_reset_course_form_defaults($course) { 523 | global $DB; 524 | 525 | $return = []; 526 | if (!$etherpadlites = $DB->get_records('etherpadlite', ['course' => $course->id], 'name')) { 527 | return; 528 | } 529 | foreach ($etherpadlites as $etherpadlite) { 530 | $return[ETHERPADLITE_RESETFORM_RESET . $etherpadlite->id] = true; 531 | } 532 | 533 | return $return; 534 | } 535 | -------------------------------------------------------------------------------- /locallib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Etherpadlite locallib. 19 | * 20 | * @package mod_etherpadlite 21 | * @copyright 20222 University of Vienna 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | * @param mixed $padid 24 | * @param mixed $paduri 25 | * @param mixed $client 26 | */ 27 | 28 | /** 29 | * Delete all grouppads. 30 | * 31 | * @param string $padid 32 | * @param string $paduri 33 | * @param \mod_etherpadlite\api\client $client 34 | * @return void 35 | */ 36 | function mod_etherpadlite_delete_all_mgrouppads($padid, $paduri, $client) { 37 | global $DB; 38 | 39 | $mgrouppads = $DB->get_records('etherpadlite_mgroups', ['padid' => $padid]); 40 | if ($mgrouppads) { 41 | foreach ($mgrouppads as $mgrouppad) { 42 | $client->delete_pad($paduri . $mgrouppad->groupid); 43 | } 44 | $DB->delete_records('etherpadlite_mgroups', ['padid' => $padid]); 45 | } 46 | } 47 | 48 | /** 49 | * Add an etherpadlite grouppad. 50 | * 51 | * @param \stdClass $formdata 52 | * @param string $mpadid 53 | * @param string $paduri 54 | * @param \mod_etherpadlite\api\client $client 55 | * @return void 56 | */ 57 | function mod_etherpadlite_add_mgrouppads($formdata, $mpadid, $paduri, $client) { 58 | global $DB; 59 | 60 | $config = get_config('etherpadlite'); 61 | $groups = groups_get_all_groups($formdata->course, 0, $formdata->groupingid); 62 | 63 | $epgroupid = explode('$', $paduri); 64 | $epgroupid = $epgroupid[0]; 65 | 66 | $mgroupdb = []; 67 | foreach ($groups as $group) { 68 | $mgroup = new stdClass(); 69 | if (!$DB->record_exists('etherpadlite_mgroups', ['padid' => $mpadid, 'groupid' => $group->id])) { 70 | $mgroup->padid = $mpadid; 71 | $mgroup->groupid = $group->id; 72 | $mgroupdb[] = $mgroup; 73 | try { 74 | $padid = $client->create_group_pad($epgroupid, $config->padname . $group->id); 75 | } catch (Exception $e) { 76 | continue; 77 | } 78 | } 79 | } 80 | $DB->insert_records('etherpadlite_mgroups', $mgroupdb); 81 | } 82 | -------------------------------------------------------------------------------- /mod_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This file defines the main etherpadlite configuration form. 19 | * 20 | * It uses the standard core Moodle (>1.8) formslib. For 21 | * more info about them, please visit: 22 | * 23 | * http://docs.moodle.org/en/Development:lib/formslib.php 24 | * 25 | * The form must provide support for, at least these fields: 26 | * - name: text element of 64cc max 27 | * 28 | * Also, it's usual to use these fields: 29 | * - intro: one htmlarea element to describe the activity 30 | * (will be showed in the list of activities of 31 | * etherpadlite type (index.php) and in the header 32 | * of the etherpadlite main page (view.php). 33 | * - introformat: The format used to write the contents 34 | * of the intro field. It automatically defaults 35 | * to HTML when the htmleditor is used and can be 36 | * manually selected if the htmleditor is not used 37 | * (standard formats are: MOODLE, HTML, PLAIN, MARKDOWN) 38 | * See lib/weblib.php Constants and the format_text() 39 | * function for more info 40 | * 41 | * @package mod_etherpadlite 42 | * 43 | * @author Timo Welde 44 | * @copyright 2012 Humboldt-Universität zu Berlin 45 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 46 | */ 47 | defined('MOODLE_INTERNAL') || die; 48 | 49 | require_once($CFG->dirroot . '/course/moodleform_mod.php'); 50 | 51 | /** 52 | * Etherpadlite configuration form. 53 | * 54 | * @author Timo Welde 55 | * @copyright 2012 Humboldt-Universität zu Berlin 56 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 57 | */ 58 | class mod_etherpadlite_mod_form extends moodleform_mod { 59 | /** 60 | * Defines the mform elements. 61 | * 62 | * @return void 63 | */ 64 | public function definition() { 65 | global $COURSE, $CFG; 66 | $mform = $this->_form; 67 | $config = get_config('etherpadlite'); 68 | 69 | try { 70 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 71 | } catch (\mod_etherpadlite\api\api_exception $e) { 72 | \core\notification::add($e->getMessage(), \core\notification::ERROR); 73 | $url = course_get_url($COURSE->id); 74 | redirect($url); 75 | } 76 | 77 | // Adding the "general" fieldset, where all the common settings are showed. 78 | $mform->addElement('header', 'general', get_string('general', 'form')); 79 | 80 | // Adding the standard "name" field. 81 | $mform->addElement('text', 'name', get_string('etherpadlitename', 'etherpadlite'), ['size' => '64']); 82 | if (!empty($CFG->formatstringstriptags)) { 83 | $mform->setType('name', PARAM_TEXT); 84 | } else { 85 | $mform->setType('name', PARAM_CLEANHTML); 86 | } 87 | $mform->addRule('name', null, 'required', null, 'client'); 88 | $mform->addRule('name', get_string('maximumchars', '', 150), 'maxlength', 150, 'client'); 89 | 90 | // Adding the required "intro" field to hold the description of the instance. 91 | $this->standard_intro_elements(get_string('etherpadliteintro', 'mod_etherpadlite')); 92 | 93 | // Is writing for guests allowed? 94 | if (get_config('etherpadlite', 'adminguests') == 1) { 95 | $mform->addElement('checkbox', 'guestsallowed', get_string('guestsallowed', 'etherpadlite')); 96 | $mform->addHelpButton('guestsallowed', 'guestsallowed', 'etherpadlite'); 97 | } 98 | 99 | $mform->addElement('header', 'availabilityhdr', get_string('availability')); 100 | $mform->addElement('date_time_selector', 'timeopen', get_string('activityopen', 'etherpadlite'), 101 | ['optional' => true]); 102 | $mform->addHelpButton('timeopen', 'activityopenclose', 'etherpadlite'); 103 | $mform->addElement('date_time_selector', 'timeclose', get_string('activityclose', 'etherpadlite'), 104 | ['optional' => true]); 105 | 106 | $this->standard_coursemodule_elements(); 107 | 108 | // Add standard buttons, common to all modules. 109 | $this->add_action_buttons(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /pix/monologo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * These are the settings for this module. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | defined('MOODLE_INTERNAL') || die; 27 | 28 | $urlblockinfo = ''; 29 | $config = get_config('etherpadlite'); 30 | $infotext = ''; 31 | if (!empty($config->url)) { 32 | $urldescwidget = new \mod_etherpadlite\output\component\urlsettingsnote($config); 33 | $infotext = $OUTPUT->render($urldescwidget); 34 | } 35 | /** @var \admin_settingpage $settings */ 36 | $settings->add(new admin_setting_configtext('etherpadlite/url', get_string('url', 'etherpadlite'), 37 | $infotext, '', PARAM_URL, 40)); 38 | 39 | $settings->add(new admin_setting_configcheckbox('etherpadlite/ignoresecurity', get_string('ignoresecurity', 'etherpadlite'), 40 | get_string('ignoresecuritydesc', 'etherpadlite'), false)); 41 | 42 | $settings->add(new admin_setting_configtext('etherpadlite/apikey', get_string('apikey', 'etherpadlite'), 43 | get_string('apikeydesc', 'etherpadlite'), 'Enter your API Key', PARAM_RAW, 40)); 44 | 45 | $settings->add(new admin_setting_configselect('etherpadlite/apiversion', get_string('apiversion', 'etherpadlite'), 46 | get_string('apiversiondesc', 'etherpadlite'), '1.2', ['1.1' => '1.1', '1.2' => '1.2'])); 47 | 48 | $settings->add(new admin_setting_configtext('etherpadlite/connecttimeout', get_string('connecttimeout', 'etherpadlite'), 49 | get_string('connecttimeoutdesc', 'etherpadlite'), 300, PARAM_INT)); 50 | 51 | $settings->add(new admin_setting_configtext('etherpadlite/timeout', get_string('timeout', 'etherpadlite'), 52 | get_string('timeoutdesc', 'etherpadlite'), 0, PARAM_INT)); 53 | 54 | $settings->add(new admin_setting_configtext('etherpadlite/padname', get_string('padname', 'etherpadlite'), 55 | get_string('padnamedesc', 'etherpadlite'), 'mymoodle2')); 56 | 57 | $settings->add(new admin_setting_configtext('etherpadlite/cookiedomain', get_string('cookiedomain', 'etherpadlite'), 58 | get_string('cookiedomaindesc', 'etherpadlite'), '.mydomain.local')); 59 | $settings->add(new admin_setting_configtext('etherpadlite/cookietime', get_string('cookietime', 'etherpadlite'), 60 | get_string('cookietimedesc', 'etherpadlite'), '10800', PARAM_INT)); 61 | 62 | $settings->add(new admin_setting_configcheckbox('etherpadlite/ssl', get_string('ssl', 'etherpadlite'), 63 | get_string('ssldesc', 'etherpadlite'), '0')); 64 | $settings->add(new admin_setting_configcheckbox('etherpadlite/check_ssl', get_string('checkssl', 'etherpadlite'), 65 | get_string('checkssldesc', 'etherpadlite'), 0)); 66 | 67 | $settings->add(new admin_setting_configcheckbox('etherpadlite/adminguests', get_string('adminguests', 'etherpadlite'), 68 | get_string('adminguestsdesc', 'etherpadlite'), '0')); 69 | 70 | $settings->add(new admin_setting_configtext('etherpadlite/minwidth', get_string('minwidth', 'etherpadlite'), 71 | get_string('minwidthdesc', 'etherpadlite'), '400', PARAM_INT)); 72 | 73 | $settings->add(new admin_setting_configcheckbox('etherpadlite/copylink', get_string('copylink', 'etherpadlite'), 74 | get_string('copylinkdesc', 'etherpadlite'), '0')); 75 | $options = []; 76 | $options[0] = get_string('donotdelete', 'etherpadlite'); 77 | $options[1] = get_string('deletenow', 'etherpadlite'); 78 | $options[2] = get_string('deleteinonehour', 'etherpadlite'); 79 | $options[3] = get_string('deleteintwelvehours', 'etherpadlite'); 80 | $options[4] = get_string('deletein24hours', 'etherpadlite'); 81 | 82 | $settings->add(new admin_setting_configselect('etherpadlite/deletemgrouppad', get_string('deletemgroupads', 'etherpadlite'), 83 | get_string('deletemgroupadsdesc', 'etherpadlite'), 2, $options)); 84 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* Etherpad should be in fullscreen if it is opened in a modal dialog */ 2 | .etherpadmodal.modal.in { 3 | width: 100%; 4 | max-width: 100%; 5 | height: 100%; 6 | margin: 0; 7 | padding: 0; 8 | left: 0; 9 | top: 0; 10 | } 11 | .etherpadmodal.modal.in .modal-body { 12 | position: absolute; 13 | width: 100%; 14 | max-width: 100%; 15 | height: 100%; 16 | min-height: 100%; 17 | } 18 | .etherpadmodal .modal-dialog { 19 | width: 100%; 20 | max-width: 100%; 21 | height: 100%; 22 | margin: 0; 23 | padding: 0; 24 | } 25 | .etherpadmodal .modal-header { 26 | padding-top: 0; 27 | padding-bottom: 0; 28 | } 29 | .etherpadmodal .modal-content { 30 | width: 100%; 31 | height: auto; 32 | min-height: 100%; 33 | border-radius: 0; 34 | } 35 | .etherpadmodal iframe { 36 | height: 100%; 37 | width: 100%; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | } 42 | -------------------------------------------------------------------------------- /templates/closebutton.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template mod_etherpadlite/urlsettingsnote 19 | 20 | Displays etherpad content 21 | 22 | Example context (json): 23 | { 24 | "courseurl" : "https://example.com/course/view.php?id=2" 25 | } 26 | 27 | }} 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /templates/content.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template mod_etherpadlite/content 19 | 20 | Displays etherpad content 21 | 22 | Example context (json): 23 | { 24 | "id" : 46, 25 | "name" : "Test pad", 26 | "summaryguest" : "...", 27 | "responsiveiframe" : 1, 28 | "minwidth" : "400px", 29 | "frameurl" : "https://example.com", 30 | "emptyurl" : "https://example.com", 31 | "hasnotice" : 1, 32 | "noticetype" : "warning", 33 | "courseurl" : "https://example.com/course/view.php?id=2", 34 | "notice" : "The etherpad server url is not configured for this site" 35 | } 36 | 37 | }} 38 | {{#hasnotice}} 39 | 42 | {{/hasnotice}} 43 |

44 | 47 | {{name}} 48 | {{> mod_etherpadlite/closebutton }} 49 |

50 | 51 | {{#summaryguest}} 52 |
53 |
54 | {{{.}}} 55 |
56 |
57 | {{/summaryguest}} 58 | {{> mod_etherpadlite/iframe}} 59 | {{> mod_etherpadlite/modal}} 60 | {{#js}} 61 | require(['mod_etherpadlite/modal_iframe'], function(mod) { 62 | mod.init("{{frameurl}}", {{{id}}}); 63 | }); 64 | {{/js}} 65 | -------------------------------------------------------------------------------- /templates/iframe.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template mod_etherpadlite/iframe 19 | 20 | Displays etherpad iframe on boost based themes 21 | 22 | Example context (json): 23 | { 24 | "id" : 1, 25 | "minwidth" : "400px", 26 | "emptyurl" : "https://example.com" 27 | } 28 | 29 | }} 30 | 31 |
32 | 33 |
34 | -------------------------------------------------------------------------------- /templates/instance_link.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template mod_etherpadlite/instance_link 19 | 20 | Displays etherpad content 21 | 22 | Example context (json): 23 | { 24 | "class" : "dimmed", 25 | "url" : "https://example.com/mod/etherpadlite/view.php?id=2", 26 | "text" : "Etherpadinstance" 27 | } 28 | 29 | }} 30 | 31 | {{text}} 32 | -------------------------------------------------------------------------------- /templates/modal.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template mod_etherpadlite/modal 19 | 20 | Displays the modal dialog with the etherpad iframe 21 | 22 | Example context (json): 23 | { 24 | "id" : 1, 25 | "name" : "Test pad", 26 | "courseurl" : "https://example.com/course/view.php?id=2" 27 | } 28 | 29 | }} 30 | 31 | 54 | -------------------------------------------------------------------------------- /templates/urlsettingsnote.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - http://moodle.org/ 3 | 4 | Moodle is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | Moodle is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with Moodle. If not, see . 16 | }} 17 | {{! 18 | @template mod_etherpadlite/urlsettingsnote 19 | 20 | Displays etherpad content 21 | 22 | Example context (json): 23 | { 24 | "msg" : "Description of something", 25 | "msginfo" : "Additional info", 26 | "msgtype" : "warning", 27 | "icon" : "exclamation-triangle" 28 | } 29 | 30 | }} 31 | 32 |
33 | {{urlinfo}} 34 | {{#blockingmsg}} 35 |
36 | 37 | {{{.}}} 38 |
39 | {{/blockingmsg}} 40 | 41 |
42 | 43 | {{connectiontext}} 44 | {{connectiontextreason}} 45 |
46 | 47 |
48 | -------------------------------------------------------------------------------- /tests/behat/behat_mod_etherpadlite.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Steps definitions related to mod_etherpadlite. 19 | * 20 | * @package mod_etherpadlite 21 | * @category test 22 | * @author Andreas Grabs 23 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. 28 | 29 | require_once(__DIR__ . '/../../../../lib/behat/behat_base.php'); 30 | 31 | /** 32 | * Steps definitions related to mod_feedback. 33 | * 34 | * @copyright 2022 Andreas Grabs 35 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 | */ 37 | class behat_mod_etherpadlite extends behat_base { 38 | } 39 | -------------------------------------------------------------------------------- /tests/behat/etherpadlite_create.feature: -------------------------------------------------------------------------------- 1 | @mod @mod_etherpadlite 2 | Feature: Show the etherpadlite activity link on course page 3 | In order to edit the etherpadlite content 4 | # As a teacher 5 | # I need to see the button "Edit content" 6 | 7 | Background: 8 | Given the following "users" exist: 9 | | username | firstname | lastname | 10 | | teacher1 | Teacher | 1 | 11 | | student1 | Student | 1 | 12 | And the following "courses" exist: 13 | | fullname | shortname | 14 | | Course 1 | C1 | 15 | And the following "course enrolments" exist: 16 | | user | course | role | 17 | | teacher1 | C1 | editingteacher | 18 | | student1 | C1 | student | 19 | 20 | @javascript 21 | Scenario: See the etherpadlite activity link as teacher 22 | # Set up a etherpadlite. 23 | When I log in as "teacher1" 24 | And I am on "Course 1" course homepage with editing mode on 25 | And I add a etherpadlite activity to course "Course 1" section "1" and I fill the form with: 26 | | Name | testpad1 | 27 | | Etherpadlite Intro | Intro to Testpad2 | 28 | 29 | # Should See the etherpadlite and the button 30 | Then I should see "testpad1" 31 | -------------------------------------------------------------------------------- /tests/behat/etherpadlite_show_fullscreen.feature: -------------------------------------------------------------------------------- 1 | @mod @mod_etherpadlite 2 | Feature: Show the etherpadlite activity in fullscreen 3 | In order to edit the etherpadlite content 4 | 5 | Background: 6 | Given the following "users" exist: 7 | | username | firstname | lastname | 8 | | teacher1 | Teacher | 1 | 9 | | student1 | Student | 1 | 10 | And the following "courses" exist: 11 | | fullname | shortname | 12 | | Course 1 | C1 | 13 | And the following "course enrolments" exist: 14 | | user | course | role | 15 | | teacher1 | C1 | editingteacher | 16 | | student1 | C1 | student | 17 | 18 | @javascript 19 | Scenario: See the etherpadlite activity link as teacher 20 | # Set up a etherpadlite. 21 | When I log in as "teacher1" 22 | And I am on "Course 1" course homepage with editing mode on 23 | And I add a etherpadlite activity to course "Course 1" section "1" and I fill the form with: 24 | | Name | testpad1 | 25 | | Etherpadlite Intro | Intro to Testpad2 | 26 | 27 | # Should See the etherpadlite and the button 28 | Then I should see "testpad1" 29 | And I click on "testpad1" "link" in the "#section-1" "css_element" 30 | 31 | # See that the iframe is shown. 32 | And I switch to "etherpadiframe" class iframe 33 | And I should see "dummyoutput" 34 | 35 | And I switch to the main frame 36 | And I click on "Fullscreen" "button" 37 | # See that the iframe is shown. 38 | And I switch to "etherpadiframe2" class iframe 39 | And I should see "dummyoutput" 40 | And I switch to the main frame 41 | And I click on "Restore window size" "button" 42 | 43 | # And I am on "Course 1" course homepage 44 | -------------------------------------------------------------------------------- /tests/fixtures/dummyoutput.html: -------------------------------------------------------------------------------- 1 | dummyoutput 2 | -------------------------------------------------------------------------------- /tests/generator/behat_mod_etherpadlite_generator.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Behat data generator for mod_etherpadlite. 19 | * 20 | * @package mod_etherpadlite 21 | * @category test 22 | * @author Andreas Grabs 23 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | class behat_mod_etherpadlite_generator extends behat_generator_base { 27 | /** 28 | * Get a list of the entities that Behat can create using the generator step. 29 | * 30 | * @return array 31 | */ 32 | protected function get_creatable_entities(): array { 33 | return []; 34 | } 35 | 36 | /** 37 | * Get the assignment CMID using an activity idnumber. 38 | * 39 | * @param string $idnumber 40 | * @return int The cmid 41 | */ 42 | protected function get_etherpadlite_id(string $idnumber): int { 43 | return $this->get_activity_id($idnumber); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/generator/lib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * etherpadlite module data generator class. 19 | * 20 | * @package mod_etherpadlite 21 | * @category test 22 | * @author Andreas Grabs 23 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | /** 28 | * etherpadlite module data generator class. 29 | * 30 | * @package mod_etherpadlite 31 | * @category test 32 | * @author Andreas Grabs 33 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class mod_etherpadlite_generator extends testing_module_generator { 37 | /** 38 | * Create a new instance of the etherpadlite activity. 39 | * 40 | * @param array|stdClass|null $record 41 | * @param array|null $options 42 | * @return stdClass 43 | */ 44 | public function create_instance($record = null, ?array $options = null) { 45 | $record = (object) (array) $record; 46 | 47 | return parent::create_instance($record, (array) $options); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/generator_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unit tests for generating instances. 19 | * 20 | * @package mod_etherpadlite 21 | * @category test 22 | * @author Andreas Grabs 23 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | namespace mod_etherpadlite; 28 | 29 | /** 30 | * Unit tests for generating instances. 31 | * 32 | * @package mod_etherpadlite 33 | * @category test 34 | * @author Andreas Grabs 35 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | final class generator_test extends \advanced_testcase { 39 | /** 40 | * Test create an instance. 41 | * 42 | * @covers ::etherpadlite_add_instance() 43 | * @return void 44 | */ 45 | public function test_create_instance(): void { 46 | global $DB; 47 | $this->resetAfterTest(); 48 | $this->setAdminUser(); 49 | 50 | $course = $this->getDataGenerator()->create_course(); 51 | 52 | $this->assertFalse($DB->record_exists('etherpadlite', ['course' => $course->id])); 53 | $etherpadlite = $this->getDataGenerator()->create_module( 54 | 'etherpadlite', 55 | [ 56 | 'course' => $course->id, 57 | 'idnumber' => 'mh1', 58 | 'name' => 'testpad1', 59 | 'intro' => 'Intro to Testpad1', 60 | 'guestsallowed' => false, 61 | 'timeopen' => 0, 62 | 'timeclose' => 0, 63 | ] 64 | ); 65 | $records = $DB->get_records('etherpadlite', ['course' => $course->id], 'id'); 66 | $this->assertEquals(1, count($records)); 67 | $this->assertTrue(array_key_exists($etherpadlite->id, $records)); 68 | 69 | $params = [ 70 | 'course' => $course->id, 71 | 'idnumber' => 'mh2', 72 | 'name' => 'testpad2', 73 | 'intro' => 'Intro to Testpad2', 74 | 'guestsallowed' => false, 75 | 'timeopen' => 0, 76 | 'timeclose' => 0, 77 | ]; 78 | $etherpadlite = $this->getDataGenerator()->create_module('etherpadlite', $params); 79 | $records = $DB->get_records('etherpadlite', ['course' => $course->id], 'id'); 80 | $this->assertEquals(2, count($records)); 81 | $this->assertEquals('testpad2', $records[$etherpadlite->id]->name); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tests/lib_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unit tests for general etherpadlite features. 19 | * 20 | * @package mod_etherpadlite 21 | * @category test 22 | * @author Andreas Grabs 23 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | namespace mod_etherpadlite; 28 | 29 | /** 30 | * Unit tests for general etherpadlite features. 31 | * 32 | * @package mod_etherpadlite 33 | * @category test 34 | * @author Andreas Grabs 35 | * @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de} 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | final class lib_test extends \advanced_testcase { 39 | /** 40 | * Test create an instance. 41 | * 42 | * @covers ::etherpadlite_add_instance() 43 | * @return void 44 | */ 45 | public function test_etherpadlite_initialise(): void { 46 | global $DB; 47 | $this->resetAfterTest(); 48 | $this->setAdminUser(); 49 | 50 | // First create the course. 51 | $course = $this->getDataGenerator()->create_course(); 52 | 53 | // Now create a etherpadlite instance. 54 | $params['course'] = $course->id; 55 | $params['timeopen'] = 0; 56 | $params['timeclose'] = 0; 57 | $params['name'] = 'testpad1'; 58 | $params['intro'] = 'Intro to Testpad1'; 59 | $etherpadlite = $this->getDataGenerator()->create_module('etherpadlite', $params); 60 | 61 | // Test different ways to construct the structure object. 62 | $pseudocm = get_coursemodule_from_instance('etherpadlite', $etherpadlite->id); // Object similar to cm_info. 63 | $cm = get_fast_modinfo($course)->instances['etherpadlite'][$etherpadlite->id]; // Instance of cm_info. 64 | 65 | $this->assertTrue($cm->instance == $etherpadlite->id); 66 | $this->assertTrue($DB->count_records('etherpadlite', null) == 1); 67 | $this->assertNotEmpty($etherpadlite->uri); 68 | } 69 | 70 | /** 71 | * Try to get an existing instance. 72 | * 73 | * @covers \mod_etherpadlite\util::get_coursemodule() 74 | * @return void 75 | */ 76 | public function test_etherpadlite_get_instance(): void { 77 | global $DB; 78 | $this->resetAfterTest(); 79 | $this->setAdminUser(); 80 | 81 | // First create the course. 82 | $coursenew = $this->getDataGenerator()->create_course(); 83 | 84 | // Now create a new etherpadlite instance. 85 | $params['course'] = $coursenew->id; 86 | $params['timeopen'] = 0; 87 | $params['timeclose'] = 0; 88 | $params['name'] = 'testpad1'; 89 | $params['intro'] = 'Intro to Testpad1'; 90 | $etherpadlitenew = $this->getDataGenerator()->create_module('etherpadlite', $params); 91 | $cmnew = get_coursemodule_from_instance('etherpadlite', $etherpadlitenew->id); 92 | 93 | $course = $cm = $etherpadlite = null; 94 | list($course, $cm, $etherpadlite) = \mod_etherpadlite\util::get_coursemodule($cmnew->id, 0); 95 | $this->assertIsObject($course); 96 | $this->assertIsObject($cm); 97 | $this->assertIsObject($etherpadlite); 98 | 99 | $course = $cm = $etherpadlite = null; 100 | list($course, $cm, $etherpadlite) = \mod_etherpadlite\util::get_coursemodule(0, $etherpadlitenew->id); 101 | $this->assertIsObject($course); 102 | $this->assertIsObject($cm); 103 | $this->assertIsObject($etherpadlite); 104 | } 105 | 106 | /** 107 | * Create a new course group and check whether or not a new group pad is created. 108 | * 109 | * @covers \mod_etherpadlite\observer\group::group_created() 110 | * @return void 111 | */ 112 | public function test_etherpadlite_add_group(): void { 113 | global $DB; 114 | $this->resetAfterTest(); 115 | $this->setAdminUser(); 116 | 117 | // First create the course. 118 | $course = $this->getDataGenerator()->create_course(); 119 | 120 | // Now create the first etherpadlite instance. 121 | $params['course'] = $course->id; 122 | $params['timeopen'] = 0; 123 | $params['timeclose'] = 0; 124 | $params['name'] = 'testpad1'; 125 | $params['intro'] = 'Intro to Testpad1'; 126 | $params['groupmode'] = 2; 127 | $etherpadlite1 = $this->getDataGenerator()->create_module('etherpadlite', $params); 128 | 129 | // Now create the second etherpadlite instance. 130 | $params['course'] = $course->id; 131 | $params['timeopen'] = 0; 132 | $params['timeclose'] = 0; 133 | $params['name'] = 'testpad2'; 134 | $params['intro'] = 'Intro to Testpad2'; 135 | $params['groupmode'] = 2; 136 | $etherpadlite2 = $this->getDataGenerator()->create_module('etherpadlite', $params); 137 | 138 | $this->assertFalse($DB->record_exists('etherpadlite_mgroups', ['padid' => $etherpadlite1->id])); 139 | $this->assertFalse($DB->record_exists('etherpadlite_mgroups', ['padid' => $etherpadlite2->id])); 140 | 141 | // We create a group and after that there should be a group pad for each pad. 142 | $group1 = $this->getDataGenerator()->create_group( 143 | [ 144 | 'courseid' => $course->id, 145 | 'name' => 'testgroup-1', 146 | ] 147 | ); 148 | $this->assertTrue($DB->count_records('etherpadlite_mgroups', ['padid' => $etherpadlite1->id]) == 1); 149 | $this->assertTrue($DB->count_records('etherpadlite_mgroups', ['padid' => $etherpadlite2->id]) == 1); 150 | 151 | // We create a group and after that there should be a group pad for each pad. 152 | $group2 = $this->getDataGenerator()->create_group( 153 | [ 154 | 'courseid' => $course->id, 155 | 'name' => 'testgroup-2', 156 | ] 157 | ); 158 | $this->assertTrue($DB->count_records('etherpadlite_mgroups', ['padid' => $etherpadlite1->id]) == 2); 159 | $this->assertTrue($DB->count_records('etherpadlite_mgroups', ['padid' => $etherpadlite2->id]) == 2); 160 | 161 | // Now create the third etherpadlite instance afterwards and check the group pad is created too. 162 | $params['course'] = $course->id; 163 | $params['timeopen'] = 0; 164 | $params['timeclose'] = 0; 165 | $params['name'] = 'testpad3'; 166 | $params['intro'] = 'Intro to Testpad3'; 167 | $params['groupmode'] = 2; 168 | $etherpadlite3 = $this->getDataGenerator()->create_module('etherpadlite', $params); 169 | $this->assertTrue($DB->count_records('etherpadlite_mgroups', ['padid' => $etherpadlite3->id]) == 2); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Code fragment to define the version of etherpadlite. 19 | * 20 | * This fragment is called by moodle_needs_upgrading() and /admin/index.php 21 | * 22 | * @package mod_etherpadlite 23 | * @author Timo Welde 24 | * @copyright 2012 Humboldt-Universität zu Berlin 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | defined('MOODLE_INTERNAL') || die; 28 | 29 | $plugin->version = 2025041500; // The current module version (Date: YYYYMMDDXX). 30 | $plugin->release = 'v5.0 (2025041500)'; 31 | $plugin->requires = 2025040800; 32 | $plugin->component = 'mod_etherpadlite'; 33 | $plugin->maturity = MATURITY_STABLE; 34 | -------------------------------------------------------------------------------- /view.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * This page prints a particular instance of etherpadlite. 19 | * 20 | * @package mod_etherpadlite 21 | * 22 | * @author Timo Welde 23 | * @copyright 2012 Humboldt-Universität zu Berlin 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | require_once(dirname(__DIR__, 2) . '/config.php'); 28 | require_once(__DIR__ . '/lib.php'); 29 | 30 | $id = optional_param('id', 0, PARAM_INT); // The course_module id. 31 | $a = optional_param('a', 0, PARAM_INT); // The etherpadlite instance id. 32 | 33 | list($course, $cm, $etherpadlite) = \mod_etherpadlite\util::get_coursemodule($id, $a); 34 | 35 | $context = context_module::instance($cm->id); 36 | 37 | // This must be here, so that require login doesn't throw a warning. 38 | $url = new moodle_url('/mod/etherpadlite/view.php', ['id' => $cm->id]); 39 | 40 | $PAGE->set_url($url); 41 | require_login($course, true, $cm); 42 | $config = get_config('etherpadlite'); 43 | 44 | if ($config->ssl) { 45 | // The https_required doesn't work, if $CFG->loginhttps doesn't work. 46 | $CFG->httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); 47 | if (!isset($_SERVER['HTTPS'])) { 48 | $url = $CFG->httpswwwroot . '/mod/etherpadlite/view.php?id=' . $id; 49 | 50 | redirect($url); 51 | } 52 | } 53 | 54 | // START of Initialise the session for the Author. 55 | // Set vars. 56 | $padid = $etherpadlite->uri; 57 | 58 | // Make a new intance from the etherpadlite client. It might throw an exception. 59 | $client = \mod_etherpadlite\api\client::get_instance($config->apikey, $config->url); 60 | 61 | // Get group mode. 62 | $groupmode = groups_get_activity_groupmode($cm); 63 | $canaddinstance = has_capability('mod/etherpadlite:addinstance', $context); 64 | $isgroupmember = true; 65 | $urlpadid = $padid; 66 | 67 | if ($groupmode) { 68 | $activegroup = groups_get_activity_group($cm, true); 69 | if ($activegroup != 0) { 70 | $urlpadid .= $activegroup; 71 | $isgroupmember = groups_is_member($activegroup); 72 | } 73 | } 74 | 75 | // Check if Activity is in the open timeframe. 76 | $time = time(); 77 | $openrestricted = !empty($etherpadlite->timeopen) && ($etherpadlite->timeopen >= $time); 78 | $closerestricted = !empty($etherpadlite->timeclose) && ($etherpadlite->timeclose <= $time); 79 | $timerestricted = ($openrestricted || $closerestricted) && !$canaddinstance; 80 | 81 | // Are there some guest restrictions? 82 | $guestrestricted = isguestuser() && !etherpadlite_guestsallowed($etherpadlite); 83 | 84 | // Are there some groups restrictions? 85 | $grouprestricted = !$isgroupmember && !$canaddinstance; 86 | 87 | // Fullurl generation depending on the restrictions. 88 | if ($guestrestricted || $grouprestricted || $timerestricted) { 89 | if (!$readonlyid = $client->get_readonly_id($urlpadid)) { 90 | throw new \moodle_exception('could not get readonly id'); 91 | } 92 | $fullurl = $client->get_baseurl() . '/p/' . $readonlyid; 93 | } else { 94 | $fullurl = $client->get_baseurl() . '/p/' . $urlpadid; 95 | } 96 | 97 | // Get the groupID. 98 | $epgroupid = explode('$', $padid); 99 | $epgroupid = $epgroupid[0]; 100 | 101 | // Create author if not exists for logged in user (with full name as it is obtained from Moodle core library). 102 | if ((isguestuser() && etherpadlite_guestsallowed($etherpadlite)) || !$isgroupmember) { 103 | $authorid = $client->create_author('Guest-' . etherpadlite_gen_random_string()); 104 | } else { 105 | $authorid = $client->create_author_if_not_exists_for($USER->id, fullname($USER)); 106 | } 107 | if (!$authorid) { 108 | throw new \moodle_exception('could not create etherpad author'); 109 | } 110 | 111 | // Create a browser session to the etherpad lite server. 112 | if (!$client->create_session($epgroupid, $authorid)) { 113 | throw new \moodle_exception('could not create etherpad session'); 114 | } 115 | 116 | // END of Etherpad Lite init. 117 | // Display the etherpadlite and possibly results. 118 | $eventparams = [ 119 | 'context' => $context, 120 | 'objectid' => $etherpadlite->id, 121 | ]; 122 | $event = \mod_etherpadlite\event\course_module_viewed::create($eventparams); 123 | $event->add_record_snapshot('course_modules', $cm); 124 | $event->add_record_snapshot('course', $course); 125 | $event->add_record_snapshot('etherpadlite', $etherpadlite); 126 | $event->trigger(); 127 | 128 | $PAGE->set_title(get_string('modulename', 'mod_etherpadlite') . ': ' . format_string($etherpadlite->name)); 129 | $PAGE->set_heading(format_string($course->fullname)); 130 | $PAGE->set_context($context); 131 | 132 | // Add the keepalive system to keep checking for a connection. 133 | \core\session\manager::keepalive(); 134 | 135 | $renderer = $PAGE->get_renderer('mod_etherpadlite'); 136 | 137 | // Print the page header. 138 | echo $renderer->header(); 139 | 140 | require_once($CFG->libdir . '/grouplib.php'); 141 | $groupselecturl = new moodle_url($CFG->wwwroot . '/mod/etherpadlite/view.php', 142 | ['id' => $cm->id, 143 | ]); 144 | 145 | groups_print_activity_menu($cm, $groupselecturl); 146 | 147 | // Print the etherpad content. 148 | if (\mod_etherpadlite\api\client::is_testing()) { 149 | $fullurl = new \moodle_url('/mod/etherpadlite/tests/fixtures/dummyoutput.html'); 150 | } 151 | echo $renderer->render_etherpad($etherpadlite, $cm, $fullurl); 152 | 153 | // Close the page. 154 | echo $renderer->footer(); 155 | --------------------------------------------------------------------------------