127 Commits

Author SHA1 Message Date
Benjamin Walker
f2b7d1c7dd Display creation info on manage outage page 2026-01-25 13:41:57 +11:00
abhinavgandham
f0f3112ea1 Fixed PHPdoc. 2026-01-22 22:15:35 +11:00
abhinavgandham
0ac6b14ed1 Fixed examples table syntax in default settings behat file. 2026-01-22 22:15:35 +11:00
abhinavgandham
b2c3fbd7b2 Removed more tests that are not needed. 2026-01-22 22:15:35 +11:00
abhinavgandham
840d2c39d9 Removed maintanance mode tests. 2026-01-22 22:15:35 +11:00
abhinavgandham
ddd56cd93f Removed maintenance later code in outagelib so that site does not go into hard cli mode when outage starts. 2026-01-22 22:15:35 +11:00
abhinavgandham
7d779f8fe9 Fixed PHPUnit test errors and failures. 2026-01-22 22:15:35 +11:00
abhinavgandham
ac29a02402 Modified unit tests to remove autostart, and quickstart guide and readme. 2026-01-22 22:15:35 +11:00
abhinavgandham
ffda33329c Codechecker fixes and removed other comments. 2026-01-22 22:15:35 +11:00
abhinavgandham
0bff6f5145 Issue #390: Reverted new 'Force off' option and dropdown conversion. 2026-01-22 22:15:35 +11:00
Matthew Hilton
bffd5f39f9 Merge pull request #388 from catalyst/issue-387
Issue 387: Add metadata header to outage pages
2026-01-19 12:47:02 +10:00
Peter Burnett
fcb2a681e7 Issue 387: Add metadata header to outage pages 2026-01-19 12:19:21 +10:00
abhinavgandham
59a62c4aa4 micro-bumped version in version.php. 2026-01-16 12:52:05 +11:00
abhinavgandham
073a682b01 CI codesniffer fixes. 2026-01-16 12:52:05 +11:00
abhinavgandham
da18e31dab Fixed default parameter in configselect and updated 'Force off' lang string. 2026-01-16 12:52:05 +11:00
abhinavgandham
1ef47a6233 Included logic for 'Force off' setting. 2026-01-16 12:52:05 +11:00
abhinavgandham
551a97142c Issue #390: Changed outage auto-start default parameter from checkbox to dropdown with option to fully disable. 2026-01-16 12:52:05 +11:00
Brendan Heywood
ceee32d889 Merge pull request #397 from catalyst/MOODLE_39_branchtable_unsupportedversion_fix
#Issue 390: Fixed branch table in readme and added version 501 as incompatible.
2026-01-13 16:51:12 +11:00
abhinavgandham
149223d10f #Issue 390: Fixed branch table in readme and added version 501 as incompatible. 2026-01-13 14:51:54 +10:00
Brendan Heywood
42b6ec3c87 Fix code standards 2026-01-13 14:50:05 +11:00
Brendan Heywood
6792b03ab0 Merge pull request #394 from catalyst/MOODLE_39_codecleanup
Issue #390: Code cleanup to fix code standards codesniffer errors.
2026-01-12 18:34:15 +11:00
abhinavgandham
7f968b2365 Issue #390: Code cleanup to fix code standards codesniffer errors. 2026-01-12 17:18:47 +10:00
Guillaume
42dbec5b4d Fix #314: Switch optional_param starttime to optional_param_array (#383)
* Fix #314: Switch optional_param starttime to optional_param_array

* convert default startime to timestamp

* fix error debug warning
2025-12-03 17:39:53 +11:00
Brendan Heywood
5d064a0bec Merge pull request #353 from catalyst/fix-define
Fix bootstrap define #352
2025-11-29 16:46:21 +11:00
Brendan Heywood
d16ae47a5f Fix bootstrap define #352 2025-11-29 15:51:50 +11:00
Brendan Heywood
1e3572be9f Improve comments when blocked 2025-10-22 16:09:21 +11:00
Jason den Dulk
0222741c3d Merge pull request #379 from catalyst/378-add-install-check
Issue #378: Add install check to before_standard_top_of_body_html_generation()
2025-09-16 18:25:43 +10:00
jasondendulk
611c7a000c Issue #378: Add install check to before_standard_top_of_body_html_generation() 2025-09-16 08:54:15 +10:00
Kevin Pham
f994a97a7d Merge pull request #369 from catalyst/368-clean-ob
[#368] Ensure output buffer is empty before serving files
2025-02-27 11:27:51 +11:00
Matthew Hilton
da0f7c5865 [#368] Ensure output buffer is empty before serving files 2025-02-27 08:30:43 +10:00
Dan Marsden
da080f8d65 Merge pull request #367 from SashaAnastasi/MOODLE_39_STABLE-phpunit10
outagelib: adjust exception trigger for phpunit10
2025-02-20 14:33:42 +13:00
Sasha Anastasi
b01dd8723e remove superfluous conditional 2025-02-20 14:17:15 +13:00
Sasha Anastasi
99452cb4f3 outagelib: adjust exception trigger for phpunit10 2025-02-20 13:01:11 +13:00
Dan Marsden
6e70c45547 Update README.md with latest supported branches. 2025-02-19 10:59:04 +13:00
Matthew Hilton
03362c084c Merge pull request #362 from catalyst/fix-blocked-url
Fix URL is blocked message when not behind a proxy/load balancer
2025-02-11 09:02:02 +10:00
Rossco Hellmans
51db933862 Fix URL is blocked message when not behind a proxy/load balancer
When there is no proxy/load balancer and curlsecurityblockedhosts is set to
default (i.e. has 127.0.0.1 in it) fetching the outage page will result in
a "The URL is blocked." message. This resolves that issue by passing
ignoresecurity to the curl object.
2025-02-10 16:36:34 +10:00
Misha Golenkov
11fbe64c46 Merge pull request #361 from catalyst/359-MOODLE_39_STABLE-fix
Issue #358: Use pix_icon to render icons
2025-01-30 13:32:00 +11:00
djarrancotleanu
0114db359b Issue #358: Use pix_icon to render icons 2025-01-30 10:50:43 +10:00
Djarran Cotleanu
aedf7a5a17 Merge pull request #359 from niko-hoogeveen/MOODLE_39_STABLE-issue358
Issue #358: Added some extra styling for outage icons
2025-01-30 09:25:36 +10:00
Niko Hoogeveen
3f79129774 Issue #358: Added some extra styling for outage icons 2025-01-28 15:02:34 -05:00
Dmitrii Metelkin
43d82333ca Merge pull request #355 from catalyst/issue354-39
Fix #354
2025-01-15 15:40:55 +11:00
david adamson
0dfede6084 Fix #354 2025-01-15 14:09:28 +11:00
Brendan Heywood
7f55f5d403 Merge pull request #351 from catalyst/issue-350-MOODLE_39_STABLE-test-errors
Fix #350: Do not setcookie during unit tests
2024-11-28 07:32:42 +11:00
djarrancotleanu
2a55769a86 Fix #350: Update unit tests with changes made to climaintenance.php 2024-11-27 09:55:51 +10:00
djarrancotleanu
68959e336d Fix #350: Do not setcookie during unit tests 2024-11-25 15:42:36 +10:00
Scott Verbeek
386a623bb3 Merge pull request #348 from catalyst/issue-347
[#347] Fix typo in the title link
2024-10-16 11:07:39 +10:00
Jwalit Shah
e01fa3797e [#347] Fix typo in the title link 2024-10-16 11:49:56 +11:00
Dan Marsden
5aac578c98 Merge pull request #346 from catalyst/fix343
Fix343
2024-10-01 11:44:20 +13:00
Dan Marsden
329e92445c Add supported 405 branch. 2024-10-01 10:27:31 +13:00
Dan Marsden
07d4fc89b8 Fix #343 - rename function to prevent unit test failure in 4.5 2024-10-01 10:25:56 +13:00
Matthew Hilton
a40e198d3e Merge pull request #345 from catalyst/access-key-fix
Fix: make access key and allowed ips OR conditional
2024-09-30 10:44:32 +10:00
Rossco Hellmans
c5485849d5 Fix: make access key and allowed ips OR conditional
Access keys and allowed IPs should be indpendant of each other and when
at least one of them is satisfied the user should be allowed in.
2024-09-27 12:34:47 +10:00
Rossco Hellmans
cba55cdc69 Merge pull request #341 from catalyst/access-key
[#340] Access key exclusion method
2024-08-20 10:26:53 +10:00
Matthew Hilton
bfa9569e3b [#340] Access key exclusion method 2024-08-19 14:31:14 +10:00
Rossco Hellmans
abcb087a58 Merge pull request #342 from catalyst/fixup-ci-and-codingstandards
cleanup: fixup to match coding standards
2024-08-09 10:59:49 +10:00
Matthew Hilton
97b118593d cleanup: fixup to match coding standards 2024-08-05 15:22:03 +10:00
Benjamin Walker
dae83f45ba Change hook callback to string #336 (#337) 2024-05-24 15:56:32 +10:00
Brendan Heywood
c9b5a9ad92 Merge pull request #335 from catalyst/add-hook-callbacks-334
Implement new hook callbacks #334
2024-05-21 00:32:53 -05:00
Benjamin Walker
541b2d4407 Implement new hook callbacks #334 2024-05-21 14:13:16 +10:00
Dan Marsden
e281b59eb4 Merge pull request #331 from catalyst/330-fix-grammar-in-langstring
fix: small grammatical issue for default description
2024-03-22 18:39:27 +13:00
Kevin Pham
b90d17d84a fix: small grammatical issue for default description
In speaking, we use a /ə/ before a consonant sound.

Resolves #330
2024-03-22 16:32:41 +11:00
Kevin Pham
24678ab31e Merge pull request #329 from catalyst/issue-328-remove-fontawesome-map
issue #328: remove unused fontawesome icon map
2024-01-05 14:33:54 +11:00
ScottVerbeek
94afadac9d issue #146: display icon for calendar event 2024-01-05 13:22:12 +10:00
ScottVerbeek
57b5a229c0 issue #328: remove unused fontawesome icon map 2024-01-04 15:32:56 +10:00
Brendan Heywood
bf7e2e5a97 Merge pull request #324 from cybernotic/MOODLE_39_STABLE
Comment out line 33 as this is killing behat test runs
2023-08-03 15:52:38 +10:00
David Kwaw
3f64f702c4 Remove MOODLE_INTERNAL test as it is killing the behat test runs 2023-08-03 15:43:09 +10:00
Brendan Heywood
cf0969c933 Merge pull request #322 from catalyst/MOODLE_39_STABLE-issue_321-no-require-config.php
Issue#321 bootstrap.php: remove require(config.php)
2023-07-05 10:54:16 +10:00
Srdjan
4aa5976ea8 Make Moodle linter happy. Mainly tests. 2023-07-04 15:04:30 +10:00
Srdjan
143862e25a Issue#321 bootstrap.php: remove require(config.php)
bootstrap.php is supposed to be required from config.php, so it makes
no sense to require config.php in bootstrap.php.
2023-07-03 15:50:30 +10:00
Dan Marsden
bed817cd5e Merge pull request #320 from catalyst/date-format-support-php8.1
Improve date format method compatiability for PHP 8.1
2023-06-13 10:22:51 +12:00
michaelkotlyar
755dcf8152 Improve date format method compatiability for PHP 8.1 2023-06-12 16:12:11 +12:00
Dan Marsden
5cebaf9227 Merge pull request #307 from sarahjcotton/moodle-41-fixes
Fix for Moodle 4.1 [#306]
2023-02-09 09:15:36 +13:00
Sarah Cotton
a8aa5c49c5 Version bump 2023-02-08 10:23:01 +00:00
Sarah Cotton
85bcca8d38 Update ci workflow and badge 2023-02-08 10:23:01 +00:00
Sarah Cotton
3b87330bb1 Fix for Moodle 4.1
The settings page is ignored on install so when the admin settings page
is next loaded, the default settings cannot be found and undefined offset
warnings are thrown.
2023-02-08 10:22:54 +00:00
Brendan Heywood
1627bced86 Merge pull request #311 from QihuiChan/logging-maint-mode
Add logging for maint mode and fix failed CI tests
2022-12-21 12:23:09 +11:00
Qihui Chan
89e2621bf6 Fix failed Github CI tests 2022-12-20 22:12:05 +10:00
Qihui Chan
9473db8c6e Modify unit tests to declare expected output 2022-12-20 16:58:57 +10:00
Qihui Chan
b9cbf015ac Add logging for maint mode config 2022-12-20 16:58:57 +10:00
Brendan Heywood
42d4744534 Merge pull request #309 from QihuiChan/Issue-308-patch
Issue#308 force autostart off when maint mode managed by CI
2022-12-20 15:53:25 +11:00
Qihui Chan
b95fb97af5 Issue#308 Freeze the autostart in form if it is set in admin level config 2022-12-20 14:48:48 +10:00
Brendan Heywood
bbde2f74d3 Merge pull request #310 from QihuiChan/update-doc
Update the maintenance description in QUICKGUIDE.md
2022-12-20 11:59:20 +11:00
Qihui Chan
0791b39744 Update the maintenance description in QUICKGUIDE.md 2022-12-16 17:57:06 +10:00
Matt Dawson
21fe888862 ISSUE304 - css change for warning box header and title (#305)
* ISSUE304 - css change for warning box header and title

* Issue304-css to hopefully override existing theme hacks

* Issue#304 - updated README file with additional scss and css code to fix issues with custom themes

Co-authored-by: Matt Dawson <mattd@catalyst-au.net>
2022-12-07 08:58:30 +11:00
Peterburnett
12f43447b7 Merge pull request #299 from catalyst/MOODLE_39_STABLE_ISSUE209
Issue#209 Remove as much db calls as possible in outage plugin
2022-07-26 14:21:45 +10:00
Qihui Chan
5465e5fe40 Issue#209 Remove db calls in get_active of outage 2022-07-25 11:13:25 +10:00
Dan Marsden
8eb86bce44 Merge pull request #302 from catalyst/MOODLE_39_STABLE_issue_301
clibase.php: replace hardcoded admin userid
2022-07-25 13:12:43 +12:00
Sasha Anastasi
fe18931334 clibase.php: replace hardcoded admin userid 2022-07-22 10:34:52 +12:00
Brendan Heywood
0344500671 Merge pull request #293 from catalyst/split-ip-settings
Issue #288: Split allowed IPs settings into two.
2022-04-28 13:38:06 +10:00
Jason den Dulk
1f08a44a1a Change builtinallowediplist_desc.
Move the new lang strings to be in alphabetical order.
2022-04-28 13:16:40 +10:00
Jason den Dulk
c64a33c345 Issue #288: Split allowed IPs settings into two.
Merge two settings when accessing config.
Bump version.
2022-04-28 09:47:58 +10:00
Brendan Heywood
afc96d347d Merge pull request #290 from catalyst/ci-workflow-alignment-for-MOODLE_39_STABLE
[#289] chore: ci usage alignment and cleanup
2022-03-24 19:38:46 +11:00
Kevin Pham
59fbdee57b [#289] chore: ci usage alignment and cleanup 2022-03-23 14:58:42 +11:00
Dan Marsden
2b554967d9 Merge pull request #285 from aneno-m-e/fix-deprecation-warnings
Update assertFileNotExists() to assertFileDoesNotExist()
2022-02-28 10:45:21 +13:00
Noemie Ariste
5bf0496c79 Update assertFileNotExists() to assertFileDoesNotExist() 2022-02-25 20:17:58 +13:00
golenkovm
015300d098 Slow down warning bar checkfinished calls (#280)
* Slow down warning bar checkfinished calls

* Fix coding style after review
2022-01-28 09:58:03 +11:00
Brendan Heywood
ddfb17f5f8 Update README.md 2022-01-17 17:19:13 +11:00
Anupama Dharmajan
a98029f41e Fix workflow status badge and bump version number 2022-01-14 12:46:32 +11:00
Anupama Thundiyil Dharmajan
6dc2227034 Merge pull request #277 from catalyst/MOODLE_39_STABLE-fix-unsupported-versions
Moodle 39 stable fix supported versions
2022-01-13 17:49:31 +11:00
Anupama Dharmajan
c628cd8218 Add plugin supported moodle branches and update readme file 2022-01-13 17:27:56 +11:00
Brendan Heywood
c47d102326 Merge pull request #273 from catalyst/MOODLE_39_STABLE-reusing-workflows
Add reusable workflows
2022-01-13 16:42:12 +11:00
Anupama Dharmajan
1f5927c394 Add reusable workflows 2022-01-13 15:38:29 +11:00
tuanngocnguyen
6e8aa6d007 Merge pull request #275 from SashaAnastasi/auth_outage_totara15_runtest_fixes_39_STABLE
Issue#271: Fix refactored phpunit_util causing unit test error
2021-12-29 13:01:46 +11:00
Sasha Anastasi
3181522f63 Issue#271: Fix refactored phpunit_util causing unit test error
substitute in platform-agnostic functions ->getDebuggingMessages and ->resetDebugging
2021-12-23 08:59:35 +13:00
Andrew Madden
690d90f3bc Merge pull request #264 from catalyst/issue263
Bypass permissions check when generating a static page to fix #263
2021-09-02 16:09:48 +10:00
Dmitrii Metelkin
94fc6fb540 Bypass permissions check when generating a static page to fix #263 2021-09-02 15:44:37 +10:00
Kevin Pham
43ff1f9301 Merge pull request #262 from catalyst/generation-time
Raised time limit for page generation
2021-07-28 10:30:08 +10:00
Peter Burnett
34a716226e Raised time limit for page generation 2021-07-28 10:09:00 +10:00
Dmitrii Metelkin
6af9317240 Fix ci issues (#260)
* Fix mariadb version

* Fix coding style

* Actually fix mariadb version
2021-07-22 12:38:27 +10:00
Dmitrii Metelkin
eea4029f57 Fix build status in readme for MOODLE_39_STABLE 2021-07-22 11:51:35 +10:00
Kevin Pham
f95fd64871 Merge pull request #258 from catalyst/issue253-MOODLE_39_STABLE
Rename test classes to fix #253
2021-07-06 13:19:03 +10:00
Dmitrii Metelkin
cf8c88aefc Rename test classes to fix #253 2021-07-06 12:51:47 +10:00
Brendan Heywood
c0fa33a559 Merge pull request #257 from catalyst/issue256_outage_view_info_cap
Closes #256 Add capability to control viewing outage info
2021-06-23 17:27:11 +10:00
Andrew Madden
7c9bb96f1a Closes #256 Add capability to control viewing outage info 2021-06-23 11:23:08 +10:00
Brendan Heywood
45987b9259 Merge pull request #255 from catalyst/MOODLE_39_STABLE-fix-234-inline-background-image
Fix #234 pick up images from inline style background image.
2021-05-31 14:55:04 +10:00
Kateryna Degtyariova
17463f0d3e Fix #234 pick up images from inline style background image. 2021-05-31 14:15:00 +10:00
Tomo
2aec03911a Merge pull request #252 from catalyst/cherry-pick-update_test_using_set_config
Replace admin_write_settings to set_config
2021-05-11 16:46:40 +10:00
Tomo Tsuyuki
a262467d58 Replace admin_write_settings to set_config 2021-05-11 16:29:55 +10:00
Dan Marsden
207223fc2d Merge pull request #250 from jcharaoui/fix-class-curl-not-found
Include filelib.php to fix class not found exception, fix #247
2021-05-06 10:22:40 +12:00
Dan Marsden
b40fb047e2 Bump requires version to T13. 2021-05-06 10:14:10 +12:00
Dan Marsden
4bda39434d update gitub workflow - 3.9 and higher branch. 2021-05-06 10:03:33 +12:00
Dan Marsden
50511d1e46 Merge remote-tracking branch 'origin/fixphpunit' into MOODLE_39_STABLE 2021-05-06 10:03:06 +12:00
Dan Marsden
baa7138a01 Update Readme with new branch naming. 2021-05-06 09:44:03 +12:00
Dan Marsden
802acbd26c PHPunit 9 fixes. 2021-05-05 14:09:09 +12:00
Gus Auwerda
9c2b529831 auth_outage: Use assertStringNotContainsString() for PHPUnit 9 compliance 2021-05-05 13:54:26 +12:00
Gus Auwerda
e1d7d2d3f4 auth_outage: Update function calls to conform to PHPUnit 9 deprecations 2021-05-05 13:53:50 +12:00
Jerome Charaoui
84ad9759f4 Include filelib.php to fix class not found exception 2021-04-11 11:44:44 -04:00
86 changed files with 2899 additions and 2471 deletions

View File

@@ -1,126 +1,12 @@
name: Moodle plugin CI for master
# .github/workflows/ci.yml
name: ci
# Controls when the action will run.
on: [push, pull_request]
jobs:
citest:
name: CI test
runs-on: 'ubuntu-latest'
services:
postgres:
image: postgres
env:
POSTGRES_USER: 'postgres'
POSTGRES_HOST_AUTH_METHOD: 'trust'
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 3
ports:
- 5432:5432
mariadb:
image: mariadb
env:
MYSQL_USER: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 3
strategy:
fail-fast: false
matrix:
database: ['mariadb']
moodle-branch: ['MOODLE_35_STABLE']
node: ['14.15.1']
php: ['7.1']
include:
- {moodle-branch: 'MOODLE_38_STABLE', php: '7.1', node: '14.15.1', database: 'mariadb'}
- {moodle-branch: 'MOODLE_38_STABLE', php: '7.4', node: '14.15.1', database: 'pgsql'}
steps:
- name: Check out repository code
uses: actions/checkout@v2
with:
path: plugin
- name: Install node ${{ matrix.node }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- name: Setup PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: pgsql, zip, gd, xmlrpc, soap
coverage: none
- name: Initialise moodle-plugin-ci
run: |
composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
# Add dirs to $PATH
echo $(cd ci/bin; pwd) >> $GITHUB_PATH
echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
# PHPUnit depends on en_AU.UTF-8 locale
sudo locale-gen en_AU.UTF-8
- name: Install Moodle
run: |
mkdir ~/.npm-global
export PATH=~/.npm-global/bin:$PATH
source ~/.profile
moodle-plugin-ci install -vvv --plugin ./plugin --db-host=127.0.0.1
env:
DB: ${{ matrix.database }}
MOODLE_BRANCH: ${{ matrix.moodle-branch }}
- name: Run phplint
if: ${{ always() }}
run: moodle-plugin-ci phplint
- name: Run phpcpd
if: ${{ always() }}
run: moodle-plugin-ci phpcpd || true
- name: Run phpmd
if: ${{ always() }}
run: moodle-plugin-ci phpmd
- name: Run codechecker
if: ${{ always() }}
run: moodle-plugin-ci codechecker
- name: Run validate
if: ${{ always() }}
run: moodle-plugin-ci validate
- name: Run savepoints
if: ${{ always() }}
run: moodle-plugin-ci savepoints
- name: Run mustache
if: ${{ always() }}
run: moodle-plugin-ci phpcpd
- name: Run grunt
if: ${{ always() }}
run: moodle-plugin-ci grunt
- name: Run phpdoc
if: ${{ always() }}
run: moodle-plugin-ci phpdoc
- name: Run phpunit
if: ${{ always() }}
run: moodle-plugin-ci phpunit
# - name: Run behat
# if: ${{ always() }}
# run: moodle-plugin-ci behat --profile chrome
ci:
uses: catalyst/catalyst-moodle-workflows/.github/workflows/ci.yml@main
secrets:
moodle_org_token: ${{ secrets.MOODLE_ORG_TOKEN }}
with:
disable_behat: true

View File

@@ -59,6 +59,9 @@ Basic Usage
_Anyone who tries to use Moodle now will receive a maintenance message._
**Note:** If the outage starts with the option `Auto start maintenance mode` checked, the maint mode won't exit automatically.
It will need to be disabled through command line.
1) Perform the maintenance activities.
At this point it is safe to perform the required maintenance as no one can use the system, not even admins.
@@ -95,9 +98,8 @@ Execute the commands from your Moodle instalation directory and leave the browse
1) Create an outage
This command will create an outage starting in 30 seconds, with a warning period of 15 seconds.
It will automatically start (trigger maintenance mode).
`php auth/outage/cli/create.php -w=20 -s=30 --autostart=Y`
`php auth/outage/cli/create.php -w=20 -s=30`
Refresh the page but you will not see anything yet.

110
README.md
View File

@@ -1,18 +1,21 @@
<a href="https://travis-ci.org/catalyst/moodle-auth_outage">
<img src="https://travis-ci.org/catalyst/moodle-auth_outage.svg?branch=master">
</a>
[![ci](https://github.com/catalyst/moodle-auth_outage/actions/workflows/ci.yml/badge.svg?branch=MOODLE_39_STABLE)](https://github.com/catalyst/moodle-auth_outage/actions/workflows/ci.yml?branch=MOODLE_39_STABLE)
# Moodle Outage manager plugin
* [Version Support](#version-support)
* [What is this?](#what-is-this)
* [Moodle Requirements](#moodle-requirements)
* [Screenshots](#screenshots)
* [Installation](#installation)
* [Theme configuration](#theme-configuration)
* [How to use](#how-to-use)
* [Quick Guide](#quick-guide)
* [Why is it an auth plugin?](#why-it-is-an-auth-plugin)
* [Feedback and issues](#feedback-and-issues)
- [Moodle Outage manager plugin](#moodle-outage-manager-plugin)
- [What is this?](#what-is-this)
- [Moodle Requirements](#moodle-requirements)
- [Branches](#branches)
- [Screenshots](#screenshots)
- [Installation](#installation)
- [Theme configuration](#theme-configuration)
- [Custom Theme Additional SCSS](#custom-theme-additional-scss)
- [How to use](#how-to-use)
- [Quick Guide](#quick-guide)
- [Why it is an auth plugin?](#why-it-is-an-auth-plugin)
- [Tester restriction options](#tester-restriction-options)
- [IP restriction](#ip-restriction)
- [Access key](#access-key)
- [Feedback and issues](#feedback-and-issues)
What is this?
-------------
@@ -26,10 +29,7 @@ this plugin creates the concept of graduated outages where at predefined times b
an outage and after, different levels of warning and access can be provided to students
and testers letting them know what is about to happen and why.
<img alt="Default" src="https://cdn.rawgit.com/catalyst/moodle-auth_outage/master/docs/default.svg" width="520">
<img alt="Manager" src="https://cdn.rawgit.com/catalyst/moodle-auth_outage/master/docs/outage.svg" width="520">
![image](https://user-images.githubusercontent.com/187449/149717343-1d2c5237-dbc6-4d2a-a08c-2bdb343e87d2.png)
Moodle Requirements
-------------------
@@ -40,16 +40,19 @@ If you have an older version of Moodle you can still make it work but you will
need to manually add one extra plugin, please check:
* https://github.com/catalyst/moodle-local_outage
Branches
Moodle supported branches
--------
| Moodle version | Branch | PHP |
| ----------------- | ----------- | ---- |
| Moodle 2.7 to 3.2 | MOODLE_32_STABLE | 5.5+ |
| Totara up to 10 | TOTARA_10 | 5.5+ |
| Moodle 3.3 to 3.8 | MOODLE_38_STABLE | 7.1+ |
| Totara 11 to 12 | MOODLE_38_STABLE | 7.1+ |
| Moodle 3.9+ | MOODLE_39_STABLE | 7.2+ |
| Totara 13+ | MOODLE_39_STABLE | 7.2+ |
| Version | Branch | PHP |
|-------------|-------------------|------|
| Moodle 3.9+ | MOODLE_39_STABLE | 7.2+ |
Totara supported branches
--------
| Version | Branch | PHP |
| --------------- | ---------------- | ---- |
| Totara 19 | TOTARA_19 | 7.2+ |
| Totara 13-18 | MOODLE_39_STABLE | 7.2+ |
Screenshots
-----------
@@ -107,6 +110,48 @@ This can be found at:
Custom Theme Additional SCSS
-------------------
Custom themes generally do not have the same `$navbar-height` variable set to 80px (MOODLE), therefore custom themes will not calculate the change in navbar height with page elements that calculate the navbar total height.
Add the following SCSS For Moodle 3.11+
```
body.auth_outage {
#page-wrapper {
#nav-drawer {
top: $navbar-height + 100px;
height: calc(100% - (#{$navbar-height} + 100px));
}
#page {
margin-top: $navbar-height + 100px;
}
}
[data-region=right-hand-drawer].drawer {
top: $navbar-height + 100px;
height: calc(100% - (#{$navbar-height} + 100px));
}
}
```
Totara is a little different with version 13+ and no variables are used to set the `totaraNav` height
Add the following CSS For Totara 13+
```
.totaraNav {
margin-top: 100px;
}
.local_envbar .totaraNav {
margin-top: 50px;
}
body.auth_outage #page {
margin-top: 0;
}
```
How to use
----------
@@ -122,7 +167,6 @@ Creates a new outage.
-h, --help shows parameters help.
-c, --clone clone another outage except for the start time.
-a, --autostart must be Y or N, sets if the outage automatically triggers maintenance mode.
-w, --warn how many seconds before it starts to display a warning.
-s, --start in how many seconds should this outage start or unix time to start outage. Required.
-d, --duration how many seconds should the outage last.
@@ -143,6 +187,18 @@ Why it is an auth plugin?
One of the graduated stages this plugin introduces is a 'tester only' mode which disables login for most normal users. This is conceptually similar to the maintenance mode but enables testers to login and confirm the state after an upgrade without needing full admin privileges.
Tester restriction options
------------
Two options are available to restrict the site to only let testers in during the tester phase.
Note: these restrictions build on each other; If both are enabled, users must meet both criteria to be allowed in.
## IP restriction
Only allow users from a certain IP or range of ips to enter.
## Access key
Users provide an access key in the URL params on first page load, which is then stored as a cookie for 24 hours. If the access key matches the one setup for the outage, they are allowed in.
## Using IP restriction with access key
Users will be allowed if they are from the configured allowed ips OR if they provide the correct access key.
Feedback and issues
-------------------

View File

@@ -32,7 +32,7 @@ use auth_outage\local\outagelib;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/authlib.php');
require_once($CFG->libdir . '/authlib.php');
/**
* auth_plugin_outage class.

View File

@@ -13,6 +13,7 @@
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file should run before config.php requires '/lib/setup.php'.
*
@@ -28,20 +29,19 @@
*
* @var stdClass $CFG
*/
define('MOODLE_INTERNAL', true);
// This call is required by Moodle, but this script should have been called by config.php anyway.
// @codingStandardsIgnoreStart
require_once(__DIR__.'/../../config.php');
// @codingStandardsIgnoreEnd
defined('MOODLE_INTERNAL') || die();
//
// We need the CFG->dataroot, if not set yet this script is called too early in config.php file.
if (!isset($CFG->dataroot)) {
return;
}
// 1) Make sure we replace the configurations for behat as we have not ran 'lib/setup.php' yet.
if (!empty($CFG->behat_wwwroot) or !empty($CFG->behat_dataroot) or !empty($CFG->behat_prefix)) {
require_once(__DIR__.'/../../lib/behat/lib.php');
if (!empty($CFG->behat_wwwroot) || !empty($CFG->behat_dataroot) || !empty($CFG->behat_prefix)) {
require_once(__DIR__ . '/../../lib/behat/lib.php');
behat_update_vars_for_process();
if (behat_is_test_site()) {
$beforebehatcfg = $CFG;
@@ -75,18 +75,20 @@ if (!empty($_SERVER['REQUEST_URI'])) {
if (array_key_exists('path', $rooturl) && !empty($rooturl['path'])) {
$path = $rooturl['path'];
}
$url = $path.'/auth/outage/info.php';
$url = $path . '/auth/outage/info.php';
$outageinfo = strpos($_SERVER['REQUEST_URI'], $url) === 0 ? true : false;
}
$allowed = !file_exists($CFG->dataroot.'/climaintenance.php') // Not in maintenance mode.
$allowed = !file_exists($CFG->dataroot . '/climaintenance.php') // Not in maintenance mode.
|| (defined('ABORT_AFTER_CONFIG') && ABORT_AFTER_CONFIG) // Only config requested.
|| (defined('CLI_SCRIPT') && CLI_SCRIPT) // Allow CLI scripts.
|| $outageinfo // Allow outage info requests.
|| (defined('NO_AUTH_OUTAGE') && NO_AUTH_OUTAGE); // Allow any page should not be blocked by maintenance mode.
if (!$allowed) {
// Call the climaintenance.php which will check for allowed IPs.
// Call the climaintenance.php which will check for the conditions
// that have been baked into it from the frontend (ip, accesskey, etc...).
$CFG->dirroot = dirname(dirname(dirname(__FILE__))); // It is not defined yet but the script below needs it.
require($CFG->dataroot.'/climaintenance.php'); // This call may terminate the script here or not.
require($CFG->dataroot . '/climaintenance.php'); // This call may terminate the script here or not.
}
// 4) Set flag this file was loaded.

View File

@@ -31,7 +31,7 @@ header('Cache-Control: public, max-age=10,s-maxage=10');
// @codingStandardsIgnoreEnd
define('NO_AUTH_OUTAGE', true);
require_once(__DIR__.'/../../config.php');
require_once(__DIR__ . '/../../config.php');
$active = outagedb::get_active();

View File

@@ -14,22 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* calendar class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\calendar;
use auth_outage\local\outage;
use calendar_event;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/calendar/lib.php');
require_once($CFG->dirroot . '/calendar/lib.php');
/**
* calendar class.
@@ -62,7 +53,7 @@ class calendar {
$event = self::load($outage->id);
if (is_null($event)) {
debugging('Cannot update calendar entry for outage #'.$outage->id.', event not found. Creating it...');
debugging('Cannot update calendar entry for outage #' . $outage->id . ', event not found. Creating it...');
self::create($outage);
} else {
$event->update(self::create_data($outage), false);
@@ -78,7 +69,7 @@ class calendar {
// If not found (was not created before) ignore it.
if (is_null($event)) {
debugging('Cannot delete calendar entry for outage #'.$outageid.', event not found. Ignoring it...');
debugging('Cannot delete calendar entry for outage #' . $outageid . ', event not found. Ignoring it...');
} else {
$event->delete();
}
@@ -98,6 +89,7 @@ class calendar {
'userid' => 0,
'modulename' => '',
'instance' => $outage->id,
'component' => 'auth_outage',
'eventtype' => 'auth_outage',
'timestart' => $outage->starttime,
'visible' => true,

View File

@@ -14,18 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outagedb class.
*
* The DB Context to manipulate Outages.
* It will also commit changes to the calendar as you change outages.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\dml;
use auth_outage\calendar\calendar;
@@ -38,7 +26,7 @@ use coding_exception;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/calendar/lib.php');
require_once($CFG->dirroot . '/calendar/lib.php');
/**
* outagedb class.
@@ -130,7 +118,6 @@ class outagedb {
// Create calendar entry.
calendar::create($outage);
} else {
$other = (array) $outage;
$other['title'] = $outage->get_title();
$event = outage_updated::create([
@@ -211,21 +198,22 @@ class outagedb {
throw new coding_exception('$time must be null or a positive int.', $time);
}
$select = ':datetime2 <= stoptime AND (finished IS NULL OR :datetime3 <= finished)'; // End condition.
$select = "(warntime <= :datetime1 AND (${select}))"; // Full select part.
$data = $DB->get_records_select(
'auth_outage',
$select,
['datetime1' => $time, 'datetime2' => $time, 'datetime3' => $time],
'starttime ASC, stoptime DESC, title ASC',
'*',
0,
1
);
// Get cached outage, or null.
$outageinfo = get_config('moodle', 'auth_outage_active_outage');
// Not using $DB->get_record_select instead because there is no 'limit' parameter.
// Allowing multiple records still raises an internal error.
return (count($data) == 0) ? null : new outage(array_shift($data));
if (!$outageinfo) {
return null;
} else {
$outagecache = new outage(json_decode($outageinfo));
}
if (
$outagecache && $outagecache->warntime <= $time && $outagecache->stoptime >= $time
&& (!$outagecache->finished || $outagecache->finished >= $time)
) {
return $outagecache;
}
return null;
}
/**
@@ -251,7 +239,8 @@ class outagedb {
':datetime1 < stoptime AND (finished IS NULL OR :datetime2 < finished)',
['datetime1' => $time, 'datetime2' => $time],
'starttime ASC, stoptime DESC, title ASC',
'*');
'*'
);
foreach ($rs as $r) {
$outages[] = new outage($r);
}
@@ -283,7 +272,8 @@ class outagedb {
'NOT (:datetime1 < stoptime AND (finished IS NULL OR :datetime2 < finished))',
['datetime1' => $time, 'datetime2' => $time],
'stoptime DESC, starttime DESC, title ASC',
'*');
'*'
);
foreach ($rs as $r) {
$outages[] = new outage($r);
}
@@ -308,12 +298,12 @@ class outagedb {
$outage = self::get_by_id($id);
if (is_null($outage)) {
debugging('Cannot finish outage #'.$id.': outage not found.');
debugging('Cannot finish outage #' . $id . ': outage not found.');
return;
}
if (!$outage->is_ongoing($time)) {
debugging('Cannot finish outage #'.$id.': outage not ongoing.');
debugging('Cannot finish outage #' . $id . ': outage not ongoing.');
return;
}
@@ -352,37 +342,6 @@ class outagedb {
return (count($data) == 0) ? null : new outage(array_shift($data));
}
/**
* Gets the next outage which has not started yet and has the autostart flag set to true.
* @param null $time Timestamp reference for current time.
* @return outage|null The outage or null if not found.
* @throws coding_exception
*/
public static function get_next_autostarting($time = null) {
global $DB;
if ($time === null) {
$time = time();
}
if (!is_int($time) || ($time <= 0)) {
throw new coding_exception('$time must be null or a positive int.', $time);
}
$data = $DB->get_records_select(
'auth_outage',
'(:datetime <= starttime) AND (autostart = 1)',
['datetime' => $time],
'starttime ASC',
'*',
0,
1
);
// Not using $DB->get_record_select instead because there is no 'limit' parameter.
// Allowing multiple records still raises an internal error.
return (count($data) == 0) ? null : new outage(array_shift($data));
}
/**
* Gets an ongoing outage (between start and stop time but not finished).
* @param int|null $time Timestamp considered to check for outages, null for current date/time.

View File

@@ -14,22 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Toutage_created class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\event;
use core\event\base;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* outage_created class.
*

View File

@@ -14,21 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outage_deleted class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\event;
use core\event\base;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* outage_deleted class.
*

View File

@@ -14,22 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outage_updated class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\event;
use core\event\base;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* outage_updated class.
*

View File

@@ -14,22 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* delete class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\form\outage;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
/**
* delete class.

View File

@@ -14,15 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* edit class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\form\outage;
use auth_outage\local\outage;
@@ -31,7 +22,7 @@ use moodleform;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
require_once($CFG->libdir . '/formslib.php');
/**
* edit class.
@@ -57,9 +48,6 @@ class edit extends moodleform {
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('checkbox', 'autostart', get_string('autostart', 'auth_outage'));
$mform->addHelpButton('autostart', 'autostart', 'auth_outage');
$mform->addElement('duration', 'warningduration', get_string('warningduration', 'auth_outage'));
$mform->addHelpButton('warningduration', 'warningduration', 'auth_outage');
@@ -73,7 +61,7 @@ class edit extends moodleform {
'text',
'title',
get_string('title', 'auth_outage'),
'maxlength="'.self::TITLE_MAX_CHARS.'" size="60"'
'maxlength="' . self::TITLE_MAX_CHARS . '" size="60"'
);
$mform->setType('title', PARAM_TEXT);
$mform->addHelpButton('title', 'title', 'auth_outage');
@@ -84,6 +72,23 @@ class edit extends moodleform {
$mform->addElement('static', 'usagehints', '', get_string('textplaceholdershint', 'auth_outage'));
$mform->addElement('static', 'warningreenablemaintenancemode', '');
$mform->addElement(
'advcheckbox',
'useaccesskey',
get_string('useaccesskey', 'auth_outage'),
get_string('useaccesskey:desc', 'auth_outage'),
0
);
$mform->addElement('text', 'accesskey', get_string('accesskey', 'auth_outage'));
$mform->setType('accesskey', PARAM_TEXT);
$mform->disabledIf('accesskey', 'useaccesskey');
$mform->addHelpButton('accesskey', 'accesskey', 'auth_outage');
$mform->addElement('text', 'metadata', get_string('metadata', 'auth_outage'));
$mform->setType('metadata', PARAM_TEXT);
$mform->addHelpButton('metadata', 'metadata', 'auth_outage');
$this->add_action_buttons();
}
@@ -126,17 +131,18 @@ class edit extends moodleform {
return null;
}
if ($data->description['format'] != '1') {
debugging('Not implemented for format '.$data->description['format'], DEBUG_DEVELOPER);
debugging('Not implemented for format ' . $data->description['format'], DEBUG_DEVELOPER);
return null;
}
$outagedata = [
'id' => ($data->id === 0) ? null : $data->id,
'autostart' => (isset($data->autostart) && ($data->autostart == 1)),
'starttime' => $data->starttime,
'stoptime' => $data->starttime + $data->outageduration,
'warntime' => $data->starttime - $data->warningduration,
'title' => $data->title,
'description' => $data->description['text'],
'accesskey' => $data->useaccesskey ? $data->accesskey : null,
'metadata' => $data->metadata ?? null,
];
return new outage($outagedata);
}
@@ -147,25 +153,40 @@ class edit extends moodleform {
* @throws coding_exception
*/
public function set_data($outage) {
global $OUTPUT;
global $OUTPUT, $CFG;
$mform = $this->_form;
// Cannot change method signature, check type.
if ($outage instanceof outage) {
$this->_form->setDefaults([
'id' => $outage->id,
'autostart' => $outage->autostart,
'starttime' => $outage->starttime,
'outageduration' => $outage->get_duration_planned(),
'warningduration' => $outage->get_warning_duration(),
'title' => $outage->title,
'description' => ['text' => $outage->description, 'format' => '1'],
'accesskey' => $outage->accesskey,
'useaccesskey' => !empty($outage->accesskey),
'metadata' => $outage->metadata,
]);
// If the default_autostart is configured in config, then force autostart to be the default value.
if (
array_key_exists('auth_outage', $CFG->forced_plugin_settings)
&& array_key_exists('default_autostart', $CFG->forced_plugin_settings['auth_outage'])
) {
$this->_form->setDefaults([
'autostart' => $CFG->forced_plugin_settings['auth_outage']['default_autostart'],
]);
$mform->freeze('autostart');
}
if (!empty($outage->id) && $outage->autostart && $outage->starttime < time() && $outage->stoptime > time()) {
$warning = $mform->getElement('warningreenablemaintenancemode');
$warning->setValue($OUTPUT->notification(get_string('warningreenablemaintenancemode', 'auth_outage'),
'notifywarning'));
$warning->setValue($OUTPUT->notification(
get_string('warningreenablemaintenancemode', 'auth_outage'),
'notifywarning'
));
}
} else {
throw new coding_exception('$outage must be an outage object.', $outage);

View File

@@ -14,22 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* finish class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\form\outage;
use moodleform;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
require_once($CFG->libdir . '/formslib.php');
/**
* finish class.

View File

@@ -0,0 +1,47 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace auth_outage;
use auth_outage\local\outagelib;
use core\hook\output\before_standard_top_of_body_html_generation;
/**
* Hook callbacks for auth_outage.
*
* @package auth_outage
* @author Benjamin Walker (benjaminwalker@catalyst-au.net)
* @copyright 2024 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class hook_callbacks {
/**
* Inject the warning bar into the page if there is currently an outage.
*
* @param before_standard_top_of_body_html_generation $hook
*/
public static function before_standard_top_of_body_html_generation(before_standard_top_of_body_html_generation $hook): void {
global $CFG;
if (during_initial_install() || isset($CFG->upgraderunning)) {
// Do nothing during installation or upgrade.
return;
}
// Get code to inject.
$hook->add_html(outagelib::get_inject_code());
}
}

View File

@@ -14,21 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* cli_exception class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use Exception;
defined('MOODLE_INTERNAL') || die();
/**
* cli_exception class.
*
@@ -87,9 +76,9 @@ class cli_exception extends Exception {
* cliexception constructor.
* @param string $message An explanation of the exception.
* @param int $code Exit code to be used.
* @param Exception $previous Another exception as reference or null.
* @param Exception|null $previous Another exception as reference or null.
*/
public function __construct($message, $code = 1, Exception $previous = null) {
parent::__construct('*ERROR* '.$message, $code, $previous = null);
public function __construct($message, $code = 1, ?Exception $previous = null) {
parent::__construct('*ERROR* ' . $message, $code, $previous = null);
}
}

View File

@@ -14,23 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* clibase class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\local\outagelib;
use coding_exception;
use core\session\manager;
defined('MOODLE_INTERNAL') || die();
/**
* clibase class.
*
@@ -52,12 +41,12 @@ abstract class clibase {
/**
* clibase constructor.
* @param array $options The parameters to use.
* @param array|null $options The parameters to use.
* @throws cli_exception
*/
public function __construct(array $options = null) {
public function __construct(?array $options = null) {
global $CFG;
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->libdir . '/clilib.php');
$warning = outagelib::generate_plugin_configuration_warning();
if ($warning) {
@@ -68,11 +57,13 @@ abstract class clibase {
if (is_null($options)) {
// Using Moodle CLI API to read the parameters.
list($options, $unrecognized) = cli_get_params($this->generate_options(), $this->generate_shortcuts());
[$options, $unrecognized] = cli_get_params($this->generate_options(), $this->generate_shortcuts());
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
throw new cli_exception(get_string('cliunknowoption', 'admin', $unrecognized),
cli_exception::ERROR_PARAMETER_UNKNOWN);
throw new cli_exception(
get_string('cliunknowoption', 'admin', $unrecognized),
cli_exception::ERROR_PARAMETER_UNKNOWN
);
}
} else {
// If not using Moodle CLI API to read parameters, ensure all keys exist.
@@ -124,7 +115,7 @@ abstract class clibase {
*/
protected function become_admin_user() {
global $DB;
$user = $DB->get_record('user', ['id' => 2]);
$user = get_admin();
unset($user->description);
unset($user->access);
unset($user->preference);
@@ -140,13 +131,13 @@ abstract class clibase {
$options = $this->generate_options();
$shorts = array_flip($this->generate_shortcuts());
printf("%s\n\n", get_string('cli'.$cliname.'help', 'auth_outage'));
printf("%s\n\n", get_string('cli' . $cliname . 'help', 'auth_outage'));
foreach (array_keys($options) as $long) {
$text = get_string('cli'.$cliname.'param'.$long, 'auth_outage');
$short = isset($shorts[$long]) ? ('-'.$shorts[$long].',') : '';
$long = '--'.$long;
$text = get_string('cli' . $cliname . 'param' . $long, 'auth_outage');
$short = isset($shorts[$long]) ? ('-' . $shorts[$long] . ',') : '';
$long = '--' . $long;
printf(" %-4s %-20s %s\n", $short, $long, $text);
}
printf("\n%s\n\n", get_string('cli'.$cliname.'examples', 'auth_outage'));
printf("\n%s\n\n", get_string('cli' . $cliname . 'examples', 'auth_outage'));
}
}

View File

@@ -14,23 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* create class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\dml\outagedb;
use auth_outage\local\outage;
use coding_exception;
defined('MOODLE_INTERNAL') || die();
/**
* create class.
*
@@ -39,7 +28,8 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class create extends clibase {
class create extends clibase
{
/**
* @var mixed[] Defaults to use if given option is null.
*/
@@ -54,7 +44,6 @@ class create extends clibase {
return [
'help' => false,
'clone' => null,
'autostart' => null,
'warn' => null,
'start' => null,
'duration' => null,
@@ -71,7 +60,6 @@ class create extends clibase {
*/
public function generate_shortcuts() {
return [
'a' => 'autostart',
'b' => 'block',
'c' => 'clone',
'd' => 'duration',
@@ -94,7 +82,7 @@ class create extends clibase {
// Check if any extra parameter was given.
foreach (array_keys($defaults) as $key) {
if (!array_key_exists($key, $missing)) {
throw new coding_exception('$default['.$key.'] is not valid.');
throw new coding_exception('$default[' . $key . '] is not valid.');
}
unset($missing[$key]);
}
@@ -102,7 +90,7 @@ class create extends clibase {
// Check if any required parameter is missing.
foreach (array_keys($missing) as $k => $v) {
if (is_null($v)) {
throw new coding_exception('$default[] missing: '.$k);
throw new coding_exception('$default[] missing: ' . $k);
}
}
@@ -121,8 +109,10 @@ class create extends clibase {
// If not help mode, 'start' is required and cannot use default.
if (is_null($this->options['start'])) {
throw new cli_exception(get_string('clierrormissingparamaters', 'auth_outage'),
cli_exception::ERROR_PARAMETER_MISSING);
throw new cli_exception(
get_string('clierrormissingparamaters', 'auth_outage'),
cli_exception::ERROR_PARAMETER_MISSING
);
}
// If cloning, set defaults to outage being cloned.
@@ -134,7 +124,7 @@ class create extends clibase {
$options = $this->merge_options();
$id = $this->create_outage($options);
if ($options['block']) {
if ($options['block'] && !$options['onlyid']) {
$block = new waitforit(['outageid' => $id]);
$block->execute();
}
@@ -174,7 +164,6 @@ class create extends clibase {
// an outage 47 years in advance.
$start = $options['start'] > 1500000000 ? $options['start'] : $this->time + $options['start'];
$outage = new outage([
'autostart' => $options['autostart'],
'warntime' => $start - $options['warn'],
'starttime' => $start,
'stoptime' => $start + $options['duration'],
@@ -200,13 +189,14 @@ class create extends clibase {
private function clone_defaults() {
$id = $this->options['clone'];
if (!is_number($id) || ($id <= 0)) {
throw new cli_exception(get_string('clierrorinvalidvaluenotid', 'auth_outage', ['param' => 'clone']),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvaluenotid', 'auth_outage', ['param' => 'clone']),
cli_exception::ERROR_PARAMETER_INVALID
);
}
$outage = outagedb::get_by_id((int)$id);
$this->set_defaults([
'autostart' => $outage->autostart,
'warn' => $outage->get_warning_duration(),
'duration' => $outage->get_duration_planned(),
'title' => $outage->title,
@@ -229,10 +219,6 @@ class create extends clibase {
$options[$param] = $this->merge_options_check_parameters_string_nonempty($options[$param], $param);
}
foreach (['autostart'] as $param) {
$options[$param] = $this->merge_options_check_parameters_bool($options[$param], $param);
}
return $options;
}
@@ -245,13 +231,17 @@ class create extends clibase {
*/
private function merge_options_check_parameters_int_nonnegative($option, $param) {
if (!is_number($option)) {
throw new cli_exception(get_string('clierrorinvalidvaluenotnumber', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvaluenotnumber', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID
);
}
$option = (int)$option;
if ($option < 0) {
throw new cli_exception(get_string('clierrorinvalidvaluenegativenumber', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvaluenegativenumber', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID
);
}
return $option;
}
@@ -265,13 +255,17 @@ class create extends clibase {
*/
private function merge_options_check_parameters_string_nonempty($option, $param) {
if (!is_string($option)) {
throw new cli_exception(get_string('clierrorinvalidvaluenotstring', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvaluenotstring', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID
);
}
$option = trim($option);
if (strlen($option) == 0) {
throw new cli_exception(get_string('clierrorinvalidvalueemptystring', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvalueemptystring', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID
);
}
return $option;
}
@@ -298,7 +292,9 @@ class create extends clibase {
}
}
throw new cli_exception(get_string('clierrorinvalidvaluenotbool', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvaluenotbool', 'auth_outage', ['param' => $param]),
cli_exception::ERROR_PARAMETER_INVALID
);
}
}

View File

@@ -14,22 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* finish class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\dml\outagedb;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
/**
* finish class.
*
@@ -77,16 +66,20 @@ class finish extends clibase {
// Cannot run during CLI_MAINTENANCE mode.
if (CLI_MAINTENANCE) {
throw new cli_exception(get_string('cliinmaintenancemode', 'auth_outage'),
cli_exception::ERROR_MAINTENANCE_MODE);
throw new cli_exception(
get_string('cliinmaintenancemode', 'auth_outage'),
cli_exception::ERROR_MAINTENANCE_MODE
);
}
// Requires outageid or active but not both at the same time.
$byid = !is_null($this->options['outageid']);
$byactive = $this->options['active'];
if ($byid == $byactive) {
throw new cli_exception(get_string('cliwaitforiterroridxoractive', 'auth_outage'),
cli_exception::ERROR_PARAMETER_MISSING);
throw new cli_exception(
get_string('cliwaitforiterroridxoractive', 'auth_outage'),
cli_exception::ERROR_PARAMETER_MISSING
);
}
$outage = $this->get_outage();
@@ -108,8 +101,10 @@ class finish extends clibase {
} else {
$id = $this->options['outageid'];
if (!is_number($id) || ($id <= 0)) {
throw new cli_exception(get_string('clierrorinvalidvalue', 'auth_outage', ['param' => 'outageid']),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvalue', 'auth_outage', ['param' => 'outageid']),
cli_exception::ERROR_PARAMETER_INVALID
);
}
$outage = outagedb::get_by_id((int)$id);
}

View File

@@ -14,22 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* waitforit class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\dml\outagedb;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
/**
* waitforit class.
*
@@ -101,8 +90,10 @@ class waitforit extends clibase {
$byid = !is_null($this->options['outageid']);
$byactive = $this->options['active'];
if ($byid == $byactive) {
throw new cli_exception(get_string('cliwaitforiterroridxoractive', 'auth_outage'),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('cliwaitforiterroridxoractive', 'auth_outage'),
cli_exception::ERROR_PARAMETER_INVALID
);
}
$this->verbose('Verbose mode activated.');
@@ -111,11 +102,11 @@ class waitforit extends clibase {
while ($sleep = $this->wait_for_outage_to_start($outage)) {
if (is_null($this->sleepcallback)) {
$this->verbose('Sleeping for '.$sleep.' second(s).');
$this->verbose('Sleeping for ' . $sleep . ' second(s).');
sleep($sleep);
$this->time = time();
} else {
$this->verbose('Calling callback to sleep '.$sleep.' second(s).');
$this->verbose('Calling callback to sleep ' . $sleep . ' second(s).');
$callback = $this->sleepcallback;
$this->time = $callback($sleep);
}
@@ -131,7 +122,7 @@ class waitforit extends clibase {
return;
}
$time = strftime('%F %T %Z');
$time = date('Y-m-d H:i:s T', time());
printf("[%s] %s\n", $time, $message);
}
@@ -147,10 +138,12 @@ class waitforit extends clibase {
} else {
$id = $this->options['outageid'];
if (!is_number($id) || ($id <= 0)) {
throw new cli_exception(get_string('clierrorinvalidvalue', 'auth_outage', ['param' => 'outageid']),
cli_exception::ERROR_PARAMETER_INVALID);
throw new cli_exception(
get_string('clierrorinvalidvalue', 'auth_outage', ['param' => 'outageid']),
cli_exception::ERROR_PARAMETER_INVALID
);
}
$this->verbose('Querying database for outage #'.$id.'...');
$this->verbose('Querying database for outage #' . $id . '...');
$outage = outagedb::get_by_id((int)$id);
}
@@ -158,7 +151,7 @@ class waitforit extends clibase {
throw new cli_exception(get_string('clierroroutagenotfound', 'auth_outage'), cli_exception::ERROR_OUTAGE_NOT_FOUND);
}
$this->verbose('Found outage #'.$outage->id.': '.$outage->get_title());
$this->verbose('Found outage #' . $outage->id . ': ' . $outage->get_title());
return $outage;
}

View File

@@ -14,15 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* infopage class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\controllers;
use auth_outage\dml\outagedb;
@@ -32,8 +23,6 @@ use coding_exception;
use context_system;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* infopage class.
*
@@ -49,10 +38,15 @@ class infopage {
private $outage;
/**
* infopage_controller constructor.
* @param array $params Parameters to use or null to get from Moodle API (request).
* @var bool|null Defines if the page is generated for a static outage page.
*/
public function __construct(array $params = null) {
private $static;
/**
* infopage_controller constructor.
* @param array|null $params Parameters to use or null to get from Moodle API (request).
*/
public function __construct(?array $params = null) {
global $CFG;
// Enable SVG support here to make sure all SVG files
// used in the current theme are served properly.
@@ -62,11 +56,13 @@ class infopage {
$params = [
'id' => optional_param('id', null, PARAM_INT),
'outage' => null,
'static' => optional_param('static', false, PARAM_BOOL),
];
} else {
$defaults = [
'id' => null,
'outage' => null,
'static' => false,
];
$params = array_merge($defaults, $params);
}
@@ -101,6 +97,10 @@ class infopage {
redirect(new moodle_url('/'));
}
// If it's not static outage page, then check access, then redirect if not allowed.
if (!$this->static && !has_capability('auth/outage:viewinfo', context_system::instance())) {
redirect(new moodle_url('/'));
}
$PAGE->set_context(context_system::instance());
$PAGE->set_title($this->outage->get_title());
$PAGE->set_heading($this->outage->get_title());
@@ -109,12 +109,19 @@ class infopage {
// No hooks injecting into this page, do it manually.
echo outagelib::get_inject_code();
// Inject metadata into the header before output.
if (!empty($this->outage->metadata)) {
header('X-Outage-Metadata: ' . $this->outage->metadata);
header('X-Outage-StartTime: ' . $this->outage->starttime);
header('X-Outage-EndTime: ' . $this->outage->stoptime);
}
echo $OUTPUT->header();
$viewbag = [
'admin' => is_siteadmin(),
'outage' => $this->outage,
];
require($CFG->dirroot.'/auth/outage/views/info/content.php');
require($CFG->dirroot . '/auth/outage/views/info/content.php');
// Moodle 2.7 did not check for CLI mode, which was fixed later.
if (!($CFG->branch == '27' && CLI_SCRIPT)) {
@@ -133,7 +140,7 @@ class infopage {
}
if (!is_null($params['id']) && !is_null($params['outage']) && ($params['id'] !== $params['outage']->id)) {
throw new coding_exception('Provided id and outage->id do not match.', $params['id'].'/'.$params['outage']->id);
throw new coding_exception('Provided id and outage->id do not match.', $params['id'] . '/' . $params['outage']->id);
}
if (is_null($params['id']) && is_null($params['outage'])) {
@@ -143,5 +150,6 @@ class infopage {
}
$this->outage = $params['outage'];
$this->static = $params['static'];
}
}

View File

@@ -14,23 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* maintenance_static_page class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\controllers;
use auth_outage\local\outage;
use coding_exception;
use DOMDocument;
defined('MOODLE_INTERNAL') || die();
/**
* maintenance_static_page class.
*
@@ -60,8 +49,15 @@ class maintenance_static_page {
} else if (PHPUNIT_TEST || defined('BEHAT_SITE_RUNNING')) {
$html = '<html></html>';
} else {
// Inject metadata into the header before output.
if (!empty($outage->metadata)) {
header('X-Outage-Metadata: ' . $outage->metadata);
header('X-Outage-StartTime: ' . $outage->starttime);
header('X-Outage-EndTime: ' . $outage->stoptime);
}
$data = maintenance_static_page_io::file_get_data(
$CFG->wwwroot.'/auth/outage/info.php?auth_outage_hide_warning=1&id='.$outage->id);
$CFG->wwwroot . '/auth/outage/info.php?auth_outage_hide_warning=1&static=1&id=' . $outage->id
);
$html = $data['contents'];
}

View File

@@ -14,26 +14,16 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* maintenance_static_page_generator class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\controllers;
use auth_outage\local\outagelib;
use coding_exception;
use core_php_time_limit;
use DOMDocument;
use DOMElement;
use invalid_state_exception;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* maintenance_static_page_generator class.
*
@@ -43,6 +33,15 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class maintenance_static_page_generator {
/** PATTERN
* The pattern should match the attribute values that
* go as 'url(xxxxx)', but make sure 'url(data:xxxxx)' is not
* rewritten. Must be case insensitive to match 'URL(xxxxx)'.
* It should be possible to specify other background attributes as
* 'background: color url(xxxxx) no-repeat'.
*/
protected const PATTERN = '/url\s*\(\s*[\'"]?(?![\'"]?data:)([^\s\'"]+)[\'"]?\s*\)/i';
/** @var DOMDocument */
protected $dom;
@@ -75,6 +74,9 @@ class maintenance_static_page_generator {
$this->io->cleanup();
if (!is_null($this->dom)) {
// This can take a while to process using repeated curls.
core_php_time_limit::raise();
$this->io->create_resources_path();
$this->remove_script_tags();
@@ -83,6 +85,7 @@ class maintenance_static_page_generator {
$this->update_link_favicon();
$this->update_images();
$this->remove_configured_css_selectors();
$this->update_inline_background_images();
$html = $this->dom->saveHTML();
if (trim($html) == '') {
@@ -150,6 +153,18 @@ class maintenance_static_page_generator {
return $matches;
}
/**
* Retrieves a URL from inline style using regular expressions.
*
* @param string $style Content of the style attribute
* @return array Array containing match
*/
public function get_url_from_inline_style($style) {
preg_match(self::PATTERN, $style, $match);
return $match;
}
/**
* Checks for urls inside filename.
*
@@ -168,9 +183,9 @@ class maintenance_static_page_generator {
$fullurl = $originalurl;
} else if ($originalurl[0] == '/') {
$rooturl = parse_url($CFG->wwwroot);
$fullurl = $rooturl['scheme'].'://'.$rooturl['host'].$originalurl;
$fullurl = $rooturl['scheme'] . '://' . $rooturl['host'] . $originalurl;
} else {
$fullurl = $baseref.'/'.$originalurl;
$fullurl = $baseref . '/' . $originalurl;
}
$saved = $this->io->save_url_file($fullurl);
@@ -218,6 +233,31 @@ class maintenance_static_page_generator {
}
}
/**
* Fetch and fixes all inline background images.
*/
private function update_inline_background_images() {
global $CFG;
$xpath = new \DOMXPath($this->dom);
$elements = $xpath->query("//*[contains(@style,'background')]");
foreach ($elements as $element) {
$style = $element->getAttribute("style");
$matches = $this->get_url_from_inline_style($style);
if (isset($matches[1])) {
// Allow incomplete URLs in style, assume it is from moodle root.
if (maintenance_static_page_io::is_url($matches[1])) {
$fullurl = $matches[1];
} else {
$fullurl = (string) new moodle_url($matches[1]);
}
$newurl = $this->io->generate_file_url($fullurl);
$updated = preg_replace(self::PATTERN, ' url(' . $newurl . ') ', $style);
$element->setAttribute('style', $updated);
}
}
}
/**
* Remove from DOM the CSS selectores defined in the plugin settings.
*/

View File

@@ -14,15 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* maintenance_static_page_io class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\controllers;
use auth_outage\local\outagelib;
@@ -31,8 +22,6 @@ use finfo;
use invalid_parameter_exception;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* maintenance_static_page_io class.
*
@@ -72,7 +61,7 @@ class maintenance_static_page_io {
}
if ($result['contents'] === false) {
debugging('Cannot fetch: '.$file);
debugging('Cannot fetch: ' . $file);
$result = ['contents' => '', 'mime' => 'unknown'];
}
return $result;
@@ -96,9 +85,9 @@ class maintenance_static_page_io {
public function get_template_file() {
global $CFG;
if ($this->preview) {
return $this->get_resources_folder().'/climaintenance.html';
return $this->get_resources_folder() . '/climaintenance.html';
} else {
return $CFG->dataroot.'/climaintenance.template.html';
return $CFG->dataroot . '/climaintenance.template.html';
}
}
@@ -113,10 +102,10 @@ class maintenance_static_page_io {
global $CFG;
// If you change the path, also change file auth/outage/bootstrap.php as it does not use this reference.
$dir = $CFG->dataroot.'/auth_outage/climaintenance';
$dir = $CFG->dataroot . '/auth_outage/climaintenance';
if ($this->preview) {
$dir = $dir.'/preview';
$dir = $dir . '/preview';
}
return $dir;
}
@@ -166,18 +155,18 @@ class maintenance_static_page_io {
$dir = realpath($dir);
$safedir = $this->get_resources_folder();
if (substr($dir, 0, strlen($safedir)) !== $safedir) {
throw new invalid_parameter_exception('Unsafe to delete: '.$dir);
throw new invalid_parameter_exception('Unsafe to delete: ' . $dir);
}
if (!is_dir($dir)) {
throw new coding_exception('Not a directory: '.$dir);
throw new coding_exception('Not a directory: ' . $dir);
}
$files = scandir($dir);
foreach ($files as $file) {
if (($file == '.') || ($file == '..')) {
continue;
}
$file = $dir.'/'.$file;
$file = $dir . '/' . $file;
if (is_file($file)) {
unlink($file);
continue;
@@ -186,7 +175,7 @@ class maintenance_static_page_io {
$this->delete_directory_recursively($file);
continue;
}
throw new coding_exception('Not a file or directory: '.$file);
throw new coding_exception('Not a file or directory: ' . $file);
}
rmdir($dir);
}
@@ -222,7 +211,7 @@ class maintenance_static_page_io {
global $CFG;
if (!self::is_url($url)) {
debugging('Found a relative url ('.$url.') -- is it using moodle_url()?');
debugging('Found a relative url (' . $url . ') -- is it using moodle_url()?');
return null; // Leave hardcoded URLs as it is.
}
@@ -238,12 +227,12 @@ class maintenance_static_page_io {
$data = self::file_get_data($url);
$mime = trim(base64_encode($data['mime']), '=');
$url = sha1($data['contents']).'.'.$mime;
$filepath = $this->get_resources_folder().'/'.$url;
$url = sha1($data['contents']) . '.' . $mime;
$filepath = $this->get_resources_folder() . '/' . $url;
file_put_contents($filepath, $data['contents']);
if ($this->preview) {
$url = 'preview/'.$url;
$url = 'preview/' . $url;
}
return ['file' => $filepath, 'url' => $url];

View File

@@ -14,22 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outage class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local;
use coding_exception;
use stdClass;
defined('MOODLE_INTERNAL') || die();
/**
* outage class.
*
@@ -69,11 +58,6 @@ class outage {
*/
public $id = null;
/**
* @var bool|null Maintenance mode auto start flag.
*/
public $autostart = null;
/**
* @var int|null Start Time timestamp.
*/
@@ -119,6 +103,16 @@ class outage {
*/
public $lastmodified = null;
/**
* @var string|null access key, or null if not enabled.
*/
public $accesskey = null;
/**
* @var string|null metadata string, or null if not enabled.
*/
public $metadata = null;
/**
* outage constructor.
* @param stdClass|array|null $data The data for the outage.
@@ -295,8 +289,13 @@ class outage {
foreach ($fs as $f) {
$this->$f = ($this->$f === null) ? null : (int)$this->$f;
}
}
// Adjust bool fields.
$this->autostart = ($this->autostart === null) ? null : (bool)$this->autostart;
/**
* Return json encoded outage.
* @return string Json string.
*/
public function __toString() {
return json_encode(get_object_vars($this));
}
}

View File

@@ -14,20 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outagelib class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local;
use auth_outage\dml\outagedb;
use auth_outage\local\controllers\maintenance_static_page;
use auth_outage\output\renderer;
use coding_exception;
use curl;
use Exception;
@@ -36,8 +26,7 @@ use invalid_parameter_exception;
use stdClass;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../../lib.php');
require_once(__DIR__ . '/../../lib.php');
/**
* outagelib class.
@@ -48,7 +37,6 @@ require_once(__DIR__.'/../../lib.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class outagelib {
/** Outage start. */
const OUTAGE_START = '<!-- OUTAGESTART -->';
@@ -68,7 +56,7 @@ class outagelib {
global $CFG;
require_once($CFG->libdir . '/filelib.php');
$curl = new curl();
$curl = new curl(['ignoresecurity' => true]);
$contents = $curl->get($file);
$info = $curl->get_info();
if (!empty($info['content_type'])) {
@@ -146,7 +134,7 @@ class outagelib {
$renderer = $PAGE->get_renderer('auth_outage');
return $renderer->render_warningbar($active, $time, false, $preview);
} catch (Exception $e) {
debugging('Exception occured while injecting our code: '.$e->getMessage());
debugging('Exception occured while injecting our code: ' . $e->getMessage());
debugging($e->getTraceAsString(), DEBUG_DEVELOPER);
}
}
@@ -168,7 +156,12 @@ class outagelib {
}
}
return (object)array_merge(self::get_config_defaults(), $config);
$config = array_merge(self::get_config_defaults(), $config);
// Combine allowed IPs config values together.
if (isset($config['allowedips_forced'])) {
$config['allowedips'] = trim($config['allowedips'] . "\n" . $config['allowedips_forced']);
}
return (object)$config;
}
/**
@@ -180,7 +173,6 @@ class outagelib {
'allowedips' => '',
'css' => '',
'default_time' => '',
'default_autostart' => '0',
'default_duration' => (string)(60 * 60),
'default_warning_duration' => (string)(60 * 60),
'default_title' => get_string('defaulttitlevalue', 'auth_outage'),
@@ -201,15 +193,13 @@ class outagelib {
$outage = outagedb::get_ongoing();
if (is_null($outage)) {
$outage = outagedb::get_next_starting();
$ongoingoutage = false;
} else {
$ongoingoutage = true;
}
// Set json formatted outage string to cache.
set_config('auth_outage_active_outage', (string)$outage);
maintenance_static_page::create_from_outage($outage)->generate();
self::update_climaintenance_code($outage);
if (!$ongoingoutage || $reenablemaint || is_null($outage)) {
self::update_maintenance_later($outage);
}
}
/**
@@ -221,24 +211,6 @@ class outagelib {
return (!empty($result['contents']));
}
/**
* Calls Moodle API - set_maintenance_later() to set when the next outage starts.
* @param outage|null $outage Outage or null if no scheduled outage.
*/
private static function update_maintenance_later($outage) {
if (is_null($outage) || !$outage->autostart) {
unset_config('maintenance_later');
} else {
$message = get_config('moodle', 'maintenance_message');
if ($message) {
debugging('Disabling $CFG->maintenance_message to allow our template page to take place.');
debugging('Previous value: '.$message);
// We cannot do much if forced config, but the logs will show the error.
unset_config('maintenance_message');
}
set_config('maintenance_later', $outage->starttime);
}
}
/**
* Checks if we should try to inject an warning bar.
@@ -254,7 +226,7 @@ class outagelib {
self::$injectcalled = true;
// Do not inject into admin/settings.php.
if ($_SERVER['SCRIPT_NAME'] == '/'.$CFG->admin.'/settings.php') {
if ($_SERVER['SCRIPT_NAME'] == '/' . $CFG->admin . '/settings.php') {
if (optional_param('section', '', PARAM_RAW) === 'additionalhtml') {
return false;
}
@@ -267,7 +239,7 @@ class outagelib {
// Used to test the try block in case of errors.
if (PHPUNIT_TEST && optional_param('auth_outage_break_code', false, PARAM_INT)) {
(new stdClass())->invalidfield; // Triggers an exception.
throw new coding_exception('Exception triggered');
}
// Nothing preventing the injection.
@@ -280,57 +252,102 @@ class outagelib {
* @param int $starttime Outage start time.
* @param int $stoptime Outage stop time.
* @param string $allowedips List of IPs allowed.
* @param string|null $accesskey access key, or null if no access key set.
* @param string|null $metadata metadata to be added to the outage headers, or null if none.
*
* @return string
* @throws invalid_parameter_exception
*/
public static function create_climaintenancephp_code($starttime, $stoptime, $allowedips) {
public static function create_climaintenancephp_code($starttime, $stoptime, $allowedips, $accesskey = null, $metadata = null) {
global $CFG;
if (!is_int($starttime) || !is_int($stoptime)) {
throw new invalid_parameter_exception('Make sure $startime and $stoptime are integers.');
}
if (!is_string($allowedips) || (trim($allowedips) == '')) {
throw new invalid_parameter_exception('$allowedips must be a valid string.');
}
// I know Moodle validation would clean up this field, but just in case, let's ensure no
// single-quotes (and double for the sake of it) are present otherwise it would break the code.
$allowedips = addslashes($allowedips);
$cookiesecure = is_moodle_cookie_secure();
// Since Moodle 4.3 cookiehttponly is default to true and this CFG is not set.
// so if not set, default to true.
$cookiehttponly = isset($CFG->cookiehttponly) ? (bool) $CFG->cookiehttponly : true;
$code = <<<'EOT'
<?php
if ((time() >= {{STARTTIME}}) && (time() < {{STOPTIME}})) {
define('MOODLE_INTERNAL', true);
if (!defined('MOODLE_INTERNAL')) {
define('MOODLE_INTERNAL', true);
}
require_once($CFG->dirroot.'/lib/moodlelib.php');
if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) {
require_once($CFG->dirroot.'/lib/classes/ip_utils.php');
}
if (!remoteip_in_list('{{ALLOWEDIPS}}')) {
header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance');
header('Status: 503 Moodle under maintenance');
header('Retry-After: 300');
header('Content-type: text/html; charset=utf-8');
header('X-UA-Compatible: IE=edge');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Accept-Ranges: none');
header('X-Moodle-Maintenance: manager');
if ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER)) {
// Put access key as a cookie if given. This stops the need to put it as a url param on every request.
$urlaccesskey = optional_param('accesskey', null, PARAM_TEXT);
$isphpunit = defined('PHPUNIT_TEST');
if (!empty($urlaccesskey) && !$isphpunit) {
setcookie('auth_outage_accesskey', $urlaccesskey, time() + 86400, '/', '', {{COOKIESECURE}}, {{COOKIEHTTPONLY}});
}
// Use url access key if given, else the cookie, else null.
$useraccesskey = $urlaccesskey ?: $_COOKIE['auth_outage_accesskey'] ?? null;
$ipblocked = !remoteip_in_list('{{ALLOWEDIPS}}');
$accesskeyblocked = $useraccesskey != '{{ACCESSKEY}}';
$allowed = ({{USEACCESSKEY}} && !$accesskeyblocked) || ({{USEALLOWEDIPS}} && !$ipblocked);
if (!$allowed) {
if (!$isphpunit) {
header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance');
header('Status: 503 Moodle under maintenance');
header('Retry-After: 300');
header('Content-type: text/html; charset=utf-8');
header('X-UA-Compatible: IE=edge');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Accept-Ranges: none');
header('X-Moodle-Maintenance: manager');
if (!empty({{METADATA}})) {
header('X-Outage-Metadata: ' . {{METADATA}});
}
header('X-Outage-StartTime: ' . '{{STARTTIME}}');
header('X-Outage-EndTime: ' . '{{STOPTIME}}');
}
if (!$isphpunit && ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER))) {
exit(0);
}
echo '<!-- Blocked by ip, your ip: '.getremoteaddr('n/a').' -->';
if (file_exists($CFG->dataroot.'/climaintenance.template.html')) {
require($CFG->dataroot.'/climaintenance.template.html');
exit(0);
if ({{USEALLOWEDIPS}} && $ipblocked) {
echo '<!-- auth_outage blocked your ip: '.getremoteaddr('n/a').' -->';
}
if ({{USEACCESSKEY}} && $accesskeyblocked) {
echo '<!-- auth_outage blocked by missing or incorrect access key, access key given: '. $useraccesskey .' -->';
}
if (!$isphpunit) {
if (file_exists($CFG->dataroot.'/climaintenance.template.html')) {
require($CFG->dataroot.'/climaintenance.template.html');
exit(0);
}
// The file above should always exist, but just in case...
die('We are currently under maintentance, please try again later.');
}
// The file above should always exist, but just in case...
die('We are currently under maintentance, please try again later.');
}
}
EOT;
$search = ['{{STARTTIME}}', '{{STOPTIME}}', '{{ALLOWEDIPS}}', '{{YOURIP}}'];
$replace = [$starttime, $stoptime, $allowedips, getremoteaddr('n/a')];
$search = ['{{STARTTIME}}', '{{STOPTIME}}', '{{USEALLOWEDIPS}}', '{{ALLOWEDIPS}}', '{{USEACCESSKEY}}', '{{ACCESSKEY}}',
'{{YOURIP}}', '{{COOKIESECURE}}', '{{COOKIEHTTPONLY}}', '{{METADATA}}'];
// Note that var_export is required because (string) false == '', not 'false'.
$replace = [$starttime, $stoptime, var_export(!empty($allowedips), true), $allowedips, var_export(!empty($accesskey), true),
$accesskey, getremoteaddr('n/a'), var_export($cookiesecure, true),
var_export($cookiehttponly, true), var_export($metadata, true)];
return str_replace($search, $replace, $code);
}
@@ -344,7 +361,7 @@ EOT;
*/
public static function update_climaintenance_code($outage) {
global $CFG;
$file = $CFG->dataroot.'/climaintenance.php';
$file = $CFG->dataroot . '/climaintenance.php';
if (!is_null($outage) && !($outage instanceof outage)) {
throw new coding_exception('$outage must be null or an outage object.');
@@ -352,17 +369,20 @@ EOT;
$config = self::get_config();
$allowedips = trim($config->allowedips);
$accesskey = $outage->accesskey ?? null;
$metadata = $outage->metadata ?? null;
if (is_null($outage) || ($allowedips == '')) {
// If no outage, or allowed ips is null and access key is null (i.e. no blocking required).
if (is_null($outage) || ($allowedips == '' && empty($accesskey))) {
if (file_exists($file)) {
unlink($file);
}
} else {
$code = self::create_climaintenancephp_code($outage->starttime, $outage->stoptime, $allowedips);
$code = self::create_climaintenancephp_code($outage->starttime, $outage->stoptime, $allowedips, $accesskey, $metadata);
$dir = dirname($file);
if (!file_exists($dir) || !is_dir($dir)) {
throw new file_exception('Directory must exists: '.$dir);
throw new file_exception('Directory must exists: ' . $dir);
}
file_put_contents($file, $code);
}
@@ -378,8 +398,10 @@ EOT;
$message = [];
if (trim(self::get_config()->allowedips) != ''
&& (!isset($CFG->auth_outage_bootstrap_loaded) || !$CFG->auth_outage_bootstrap_loaded)) {
if (
trim(self::get_config()->allowedips) != ''
&& (!isset($CFG->auth_outage_bootstrap_loaded) || !$CFG->auth_outage_bootstrap_loaded)
) {
$message[] = get_string('configurationwarning', 'auth_outage');
}
@@ -405,4 +427,18 @@ EOT;
return $message;
}
/**
* Logging for maintenance mode configuration.
*
* @param outage|null $outage Outage or null if no scheduled outage.
*/
private static function maintenance_config_log(outage $outage) {
mtrace(get_string('logformaintmodeconfig', 'auth_outage'));
$timezone = ' (Timezone ' . \core_date::get_server_timezone_object()->getName() . ')';
mtrace('... updated at ' . date('H:i:s'));
$time = date("Y-m-d H:i:s", $outage->starttime);
mtrace("... enable maintenance mode at $time $timezone");
mtrace(get_string('logformaintmodeconfigcomplete', 'auth_outage'));
}
}

View File

@@ -14,15 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* base_table class.
*
* @package auth_outage
* @author Daniel Thee Roperto <danielroperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\output\manage;
use auth_outage\local\outage;
@@ -31,8 +22,7 @@ use html_writer;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir . '/tablelib.php');
/**
* base_table class.
@@ -68,12 +58,47 @@ class base_table extends flexible_table {
global $PAGE;
$id = (is_null($id) ? self::$autoid++ : $id);
parent::__construct('auth_outage_manage_'.$id);
parent::__construct('auth_outage_manage_' . $id);
$this->define_baseurl($PAGE->url);
$this->set_attribute('class', 'generaltable admintable');
}
/**
* Displays a user by their fullname with a link to a profile.
* @param int $userid
* @return string HTML link to user profile
*/
private function format_user(int $userid): string {
if ($userid == 0 || !$user = \core_user::get_user($userid)) {
return get_string('na', 'auth_outage');
}
$url = new moodle_url('/user/profile.php', ['id' => $userid]);
return html_writer::link($url, fullname($user));
}
/**
* Formats created by column.
* @param outage $outage
* @return string The user who created the outage.
*/
protected function format_created(outage $outage): string {
return $this->format_user($outage->createdby);
}
/**
* Formats modified by column.
* @param outage $outage
* @return string The user who last modiifed the outage and the last modified time.
*/
protected function format_modified(outage $outage): string {
$timestamp = html_writer::div(
userdate($outage->lastmodified, get_string('datetimeformat', 'auth_outage')),
'small text-muted'
);
return $this->format_user($outage->modifiedby) . $timestamp;
}
/**
* Create the action buttons HTML code for a specific outage.
* @param outage $outage The outage to generate the buttons.
@@ -87,12 +112,7 @@ class base_table extends flexible_table {
// View button.
$buttons .= html_writer::link(
new moodle_url('/auth/outage/info.php', ['id' => $outage->id]),
html_writer::empty_tag('img', [
'src' => $OUTPUT->image_url('t/preview'),
'alt' => get_string('view'),
'class' => 'iconsmall',
]),
$OUTPUT->pix_icon('t/preview', get_string('view'), 'moodle', ['class' => 'iconsmall']),
[
'title' => get_string('view'),
'target' => '_blank',
@@ -103,11 +123,7 @@ class base_table extends flexible_table {
if ($editdelete) {
$buttons .= html_writer::link(
new moodle_url('/auth/outage/edit.php', ['edit' => $outage->id]),
html_writer::empty_tag('img', [
'src' => $OUTPUT->image_url('t/edit'),
'alt' => get_string('edit'),
'class' => 'iconsmall',
]),
$OUTPUT->pix_icon('t/edit', get_string('edit'), 'moodle', ['class' => 'iconsmall']),
['title' => get_string('edit')]
);
}
@@ -115,12 +131,7 @@ class base_table extends flexible_table {
// Clone button.
$buttons .= html_writer::link(
new moodle_url('/auth/outage/edit.php', ['clone' => $outage->id]),
html_writer::empty_tag('img', [
'src' => $OUTPUT->image_url('t/copy'),
'alt' => get_string('clone', 'auth_outage'),
'class' => 'iconsmall',
]),
$OUTPUT->pix_icon('t/copy', get_string('clone', 'auth_outage'), 'moodle', ['class' => 'iconsmall']),
['title' => get_string('clone', 'auth_outage')]
);
@@ -128,11 +139,7 @@ class base_table extends flexible_table {
if ($outage->is_ongoing()) {
$buttons .= html_writer::link(
new moodle_url('/auth/outage/finish.php', ['id' => $outage->id]),
html_writer::empty_tag('img', [
'src' => $OUTPUT->image_url('t/check'),
'alt' => get_string('finish', 'auth_outage'),
'class' => 'iconsmall',
]),
$OUTPUT->pix_icon('t/check', get_string('finish', 'auth_outage'), 'moodle', ['class' => 'iconsmall']),
['title' => get_string('finish', 'auth_outage')]
);
}
@@ -141,11 +148,7 @@ class base_table extends flexible_table {
if ($editdelete) {
$buttons .= html_writer::link(
new moodle_url('/auth/outage/delete.php', ['id' => $outage->id]),
html_writer::empty_tag('img', [
'src' => $OUTPUT->image_url('t/delete'),
'alt' => get_string('delete'),
'class' => 'iconsmall',
]),
$OUTPUT->pix_icon('t/delete', get_string('delete'), 'moodle', ['class' => 'iconsmall']),
['title' => get_string('delete')]
);
}

View File

@@ -14,22 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* history_table class.
*
* @package auth_outage
* @author Daniel Thee Roperto <danielroperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\output\manage;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir . '/tablelib.php');
/**
* history_table class.
@@ -46,7 +36,7 @@ class history_table extends base_table {
public function __construct() {
parent::__construct();
$this->define_columns(['warning', 'starts', 'durationplanned', 'durationactual', 'title', 'actions']);
$this->define_columns(['warning', 'starts', 'duration', 'durationactual', 'title', 'created', 'modified', 'actions']);
$this->define_headers([
get_string('tableheaderwarnbefore', 'auth_outage'),
@@ -54,9 +44,10 @@ class history_table extends base_table {
get_string('tableheaderdurationplanned', 'auth_outage'),
get_string('tableheaderdurationactual', 'auth_outage'),
get_string('tableheadertitle', 'auth_outage'),
get_string('tableheadercreatedby', 'auth_outage'),
get_string('tableheadermodifiedby', 'auth_outage'),
get_string('actions'),
]
);
]);
$this->setup();
}
@@ -75,6 +66,8 @@ class history_table extends base_table {
format_time($outage->get_duration_planned()),
$finished,
$outage->get_title(),
$this->format_created($outage),
$this->format_modified($outage),
$this->create_data_buttons($outage, false),
]);
}

View File

@@ -14,15 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* planned_table class.
*
* @package auth_outage
* @author Daniel Thee Roperto <danielroperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\output\manage;
use auth_outage\local\outage;
@@ -30,8 +21,7 @@ use html_writer;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir . '/tablelib.php');
/**
* planned_table class.
@@ -48,13 +38,15 @@ class planned_table extends base_table {
public function __construct() {
parent::__construct();
$this->define_columns(['warning', 'starts', 'duration', 'title', 'actions']);
$this->define_columns(['warning', 'starts', 'duration', 'title', 'created', 'modified', 'actions']);
$this->define_headers([
get_string('tableheaderwarnbefore', 'auth_outage'),
get_string('tableheaderstarttime', 'auth_outage'),
get_string('tableheaderduration', 'auth_outage'),
get_string('tableheadertitle', 'auth_outage'),
get_string('tableheadercreatedby', 'auth_outage'),
get_string('tableheadermodifiedby', 'auth_outage'),
get_string('actions'),
]);
@@ -68,7 +60,7 @@ class planned_table extends base_table {
public function show_data(array $outages) {
foreach ($outages as $outage) {
$title = html_writer::link(
new moodle_url('/auth/outage/edit.php', ['id' => $outage->id]),
new moodle_url('/auth/outage/edit.php', ['edit' => $outage->id]),
$outage->get_title(),
['title' => get_string('edit')]
);
@@ -78,6 +70,8 @@ class planned_table extends base_table {
self::create_starttime_string($outage->starttime),
format_time($outage->get_duration_planned()),
$title,
$this->format_created($outage),
$this->format_modified($outage),
$this->create_data_buttons($outage, true),
]);
}

View File

@@ -14,15 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* auth_outage_renderer class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\output;
use auth_outage\local\outage;
@@ -33,8 +24,6 @@ use html_writer;
use moodle_url;
use plugin_renderer_base;
defined('MOODLE_INTERNAL') || die();
/**
* auth_outage_renderer class.
*
@@ -56,7 +45,7 @@ class renderer extends plugin_renderer_base {
$viewbag['viewfile'] = $view;
unset($view);
require($CFG->dirroot.'/auth/outage/views/'.$viewbag['viewfile']);
require($CFG->dirroot . '/auth/outage/views/' . $viewbag['viewfile']);
}
/**
@@ -99,8 +88,8 @@ class renderer extends plugin_renderer_base {
* @return string HTML for the page.
*/
public function renderdeleteconfirmation(outage $outage) {
return $this->rendersubtitle('outagedelete').
html_writer::tag('p', get_string('outagedeletewarning', 'auth_outage')).
return $this->rendersubtitle('outagedelete') .
html_writer::tag('p', get_string('outagedeletewarning', 'auth_outage')) .
$this->renderoutage($outage, false);
}
@@ -110,8 +99,8 @@ class renderer extends plugin_renderer_base {
* @return string HTML for the page.
*/
public function renderfinishconfirmation(outage $outage) {
return $this->rendersubtitle('outagefinish').
html_writer::tag('p', get_string('outagefinishwarning', 'auth_outage')).
return $this->rendersubtitle('outagefinish') .
html_writer::tag('p', get_string('outagefinishwarning', 'auth_outage')) .
$this->renderoutage($outage, false);
}
@@ -158,7 +147,7 @@ class renderer extends plugin_renderer_base {
$created = core_user::get_user($outage->createdby, 'firstname,lastname', MUST_EXIST);
$created = html_writer::link(
new moodle_url('/user/profile.php', ['id' => $outage->createdby]),
trim($created->firstname.' '.$created->lastname)
trim($created->firstname . ' ' . $created->lastname)
);
}
@@ -168,7 +157,7 @@ class renderer extends plugin_renderer_base {
$modified = core_user::get_user($outage->modifiedby, 'firstname,lastname', MUST_EXIST);
$modified = html_writer::link(
new moodle_url('/user/profile.php', ['id' => $outage->modifiedby]),
trim($modified->firstname.' '.$modified->lastname)
trim($modified->firstname . ' ' . $modified->lastname)
);
}
@@ -196,33 +185,35 @@ class renderer extends plugin_renderer_base {
$start = outagelib::OUTAGE_START;
$end = outagelib::OUTAGE_END;
$outagehtml = html_writer::div(
html_writer::tag('blockquote',
html_writer::div(html_writer::tag('b', $outage->get_title(), ['data-id' => $outage->id])).
html_writer::div(html_writer::tag('i', $outage->get_description())).
html_writer::tag(
'blockquote',
html_writer::div(html_writer::tag('b', $outage->get_title(), ['data-id' => $outage->id])) .
html_writer::div(html_writer::tag('i', $outage->get_description())) .
html_writer::div(
html_writer::tag('b', get_string('tableheaderwarnbefore', 'auth_outage').': ').
html_writer::tag('b', get_string('tableheaderwarnbefore', 'auth_outage') . ': ') .
format_time($outage->get_warning_duration())
).
) .
html_writer::div(
html_writer::tag('b', get_string('tableheaderstarttime', 'auth_outage').': ').
html_writer::tag('b', get_string('tableheaderstarttime', 'auth_outage') . ': ') .
userdate($outage->starttime, get_string('datetimeformat', 'auth_outage'))
).
) .
html_writer::div(
html_writer::tag('b', get_string('tableheaderdurationplanned', 'auth_outage').': ').
html_writer::tag('b', get_string('tableheaderdurationplanned', 'auth_outage') . ': ') .
format_time($outage->get_duration_planned())
).
) .
html_writer::div(
html_writer::tag('b', get_string('tableheaderdurationactual', 'auth_outage').': ').
html_writer::tag('b', get_string('tableheaderdurationactual', 'auth_outage') . ': ') .
$finished
).
) .
html_writer::div(
html_writer::tag('small',
'Created by '.$created.
', modified by '.$modified.' on '.
html_writer::tag(
'small',
'Created by ' . $created .
', modified by ' . $modified . ' on ' .
userdate($outage->lastmodified, get_string('datetimeformat', 'auth_outage'))
)
).
($buttons ? html_writer::div($linkedit.$linkdelete) : '')
) .
($buttons ? html_writer::div($linkedit . $linkdelete) : '')
)
);
return $start . $outagehtml . $end;

View File

@@ -13,6 +13,9 @@
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace auth_outage\privacy;
/**
* Privacy Subsystem implementation for auth_outage.
*
@@ -20,24 +23,8 @@
* @copyright 2018 Olivier SECRET <olivier.secret@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\legacy_polyfill;
/**
* Privacy provider for the authentication manual.
*
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\null_provider {
use legacy_polyfill;
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
@@ -47,8 +34,7 @@ class provider implements
*
* @return string
*/
public static function _get_reason() {
public static function get_reason(): string {
return 'privacy:no_data_reason';
}
}

View File

@@ -14,23 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* update_static_page class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\task;
use auth_outage\local\controllers\infopage;
use auth_outage\local\outagelib;
use core\task\scheduled_task;
defined('MOODLE_INTERNAL') || die();
/**
* update_static_page class.
*

View File

@@ -28,8 +28,8 @@ use auth_outage\local\cli\create;
use auth_outage\local\outagelib;
define('CLI_SCRIPT', true);
require_once(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/clilib.php');
try {
$cli = new create();

View File

@@ -27,8 +27,8 @@ use auth_outage\local\cli\cli_exception;
use auth_outage\local\cli\finish;
define('CLI_SCRIPT', true);
require_once(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/clilib.php');
try {
$cli = new finish();
@@ -36,4 +36,3 @@ try {
} catch (cli_exception $e) {
cli_error($e->getMessage());
}

View File

@@ -27,8 +27,8 @@ use auth_outage\local\cli\cli_exception;
use auth_outage\local\cli\waitforit;
define('CLI_SCRIPT', true);
require_once(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/clilib.php');
try {
$cli = new waitforit();
@@ -36,4 +36,3 @@ try {
} catch (cli_exception $e) {
cli_error($e->getMessage());
}

37
db/access.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Define capabilities for plugin.
*
* @package auth_outage
* @author Andrew Madden <andrewmadden@catalyst-au.net>
* @copyright 2021 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = [
'auth/outage:viewinfo' => [
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => [
'guest' => CAP_ALLOW,
'user' => CAP_ALLOW,
],
],
];

34
db/hooks.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Hook callbacks for auth_outage
*
* @package auth_outage
* @author Benjamin Walker (benjaminwalker@catalyst-au.net)
* @copyright 2024 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$callbacks = [
[
'hook' => \core\hook\output\before_standard_top_of_body_html_generation::class,
'callback' => '\auth_outage\hook_callbacks::before_standard_top_of_body_html_generation',
'priority' => 0,
],
];

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="auth/outage/db" VERSION="20160922" COMMENT="XMLDB file for Moodle auth/outage"
<XMLDB PATH="auth/outage/db" VERSION="20260120" COMMENT="XMLDB file for Moodle auth/outage"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
@@ -7,7 +7,6 @@
<TABLE NAME="auth_outage" COMMENT="Table used for auth/outage plugin. Holds information about all past, current and future outages.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="autostart" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false" COMMENT="If the maintenance mode should be automatically triggered once this outage startes."/>
<FIELD NAME="warntime" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="When the outage will start showing a warning for that outage."/>
<FIELD NAME="starttime" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="When outage starts."/>
<FIELD NAME="stoptime" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="When outage ends."/>
@@ -17,6 +16,8 @@
<FIELD NAME="modifiedby" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Who last modified this entry."/>
<FIELD NAME="lastmodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="When was this entry last modified."/>
<FIELD NAME="finished" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Timestamp of when the outage really finished."/>
<FIELD NAME="accesskey" TYPE="char" LENGTH="16" NOTNULL="false" SEQUENCE="false" COMMENT="Unique key used to access during outage"/>
<FIELD NAME="metadata" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="metadata to be exposed in outage page headers"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View File

@@ -24,8 +24,6 @@
*/
use auth_outage\local\controllers\maintenance_static_page;
defined('MOODLE_INTERNAL') || die();
/**
* Auth Outage plugin uninstall code.
* @return bool result

View File

@@ -22,7 +22,6 @@
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Outage plugin upgrade code
@@ -48,5 +47,50 @@ function xmldb_auth_outage_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2016092200, 'auth', 'outage');
}
if ($oldversion < 2024081900) {
// Define field accesskey to be added to auth_outage.
$table = new xmldb_table('auth_outage');
$field = new xmldb_field('accesskey', XMLDB_TYPE_CHAR, '16', null, null, null, null, 'finished');
// Conditionally launch add field accesskey.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Outage savepoint reached.
upgrade_plugin_savepoint(true, 2024081900, 'auth', 'outage');
}
if ($oldversion < 2024081901) {
// Define field metadata to be added to auth_outage.
$table = new xmldb_table('auth_outage');
$field = new xmldb_field('metadata', XMLDB_TYPE_TEXT, null, null, null, null, null, 'accesskey');
// Conditionally launch add field metadata.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Outage savepoint reached.
upgrade_plugin_savepoint(true, 2024081901, 'auth', 'outage');
}
if ($oldversion < 2024081902) {
// Getting the table auth_outage and target field to remove from the table.
$table = new xmldb_table('auth_outage');
$field = new xmldb_field('autostart');
// Conditionally launch drop field autostart.
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// Removing the default_autostart config as it is no longer used.
unset_config('default_autostart', 'auth_outage');
// Outage savepoint reached.
upgrade_plugin_savepoint(true, 2024081902, 'auth', 'outage');
}
return true;
}

View File

@@ -27,9 +27,9 @@ use auth_outage\dml\outagedb;
use auth_outage\form\outage\delete;
use auth_outage\output\renderer;
require_once(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/formslib.php');
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/formslib.php');
admin_externalpage_setup('auth_outage_manage');
$PAGE->set_url(new moodle_url('/auth/outage/manage.php'));
@@ -46,7 +46,7 @@ if ($mform->is_cancelled()) {
$id = required_param('id', PARAM_INT);
$outage = outagedb::get_by_id($id);
if ($outage == null) {
throw new invalid_parameter_exception('Outage #'.$id.' not found.');
throw new invalid_parameter_exception('Outage #' . $id . ' not found.');
}
$dataid = new stdClass();

View File

@@ -27,11 +27,11 @@ use auth_outage\dml\outagedb;
use auth_outage\form\outage\edit;
use auth_outage\local\outage;
use auth_outage\local\outagelib;
use auth_outage\output\renderer;
require_once(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/formslib.php');
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/formslib.php');
admin_externalpage_setup('auth_outage_manage');
$output = $PAGE->get_renderer('auth_outage');
@@ -48,7 +48,24 @@ if ($mform->is_cancelled()) {
$clone = optional_param('clone', 0, PARAM_INT);
$edit = optional_param('edit', 0, PARAM_INT);
$time = optional_param('starttime', 0, PARAM_INT);
if (array_key_exists('starttime', $_POST) && is_array($_POST['starttime'])) {
$start = optional_param_array('starttime', [], PARAM_INT);
} else {
$start = optional_param('starttime', 0, PARAM_INT);
}
if (!empty($start['year']) && !empty($start['month']) && !empty($start['day'])) {
$hour = $start['hour'] ?? 0;
$minute = $start['minute'] ?? 0;
$time = make_timestamp(
(int) $start['year'],
(int) $start['month'],
(int) $start['day'],
(int) $hour,
(int) $minute
);
} else {
$time = 0;
}
if ($clone && $edit) {
throw new invalid_parameter_exception('Cannot provide both clone and edit ids.');
}
@@ -67,12 +84,12 @@ if ($clone) {
}
$outage = new outage([
'autostart' => $config->default_autostart,
'starttime' => $time,
'stoptime' => $time + $config->default_duration,
'warntime' => $time - $config->default_warning_duration,
'title' => $config->default_title,
'description' => $config->default_description,
'metadata' => $config->default_metadata,
]);
$action = 'outagecreate';
}
@@ -83,7 +100,7 @@ if ($outage == null) {
$mform->set_data($outage);
$PAGE->navbar->add(get_string($action.'crumb', 'auth_outage'));
$PAGE->navbar->add(get_string($action . 'crumb', 'auth_outage'));
echo $output->header();
echo $output->rendersubtitle($action);
$mform->display();

View File

@@ -45,13 +45,13 @@ if (count($parts) != 2) {
$mime = base64_decode($parts[1]);
// Detect type, we only support css or PNG images.
header('Content-Type: '.$mime);
header('Content-Type: ' . $mime);
// Use cache.
$lifetime = 60 * 60 * 24; // 1 day.
header('Expires: '.gmdate('D, d M Y H:i:s', time() + $lifetime).' GMT');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT');
header('Pragma: ');
header('Cache-Control: public, max-age='.$lifetime);
header('Cache-Control: public, max-age=' . $lifetime);
header('Accept-Ranges: none');
@@ -60,7 +60,7 @@ header('Accept-Ranges: none');
*/
function auth_outage_bootstrap_callback() {
// Not using classes as classloader has not been initialized yet. Keep it minimalist.
require_once(__DIR__.'/lib.php');
require_once(__DIR__ . '/lib.php');
$file = auth_outage_get_climaintenance_resource_file($_GET['file']);
if (is_null($file)) {
// @codingStandardsIgnoreStart
@@ -69,6 +69,11 @@ function auth_outage_bootstrap_callback() {
http_response_code(404);
die('File not found.');
}
// Ensure there is nothing in the output buffer.
// otherwise the file will not be read correctly.
ob_clean();
readfile($file);
exit(0);
}
@@ -79,5 +84,5 @@ require_once(__DIR__.'/../../config.php');
// We should never reach here if config.php and auth/outage/bootstrap.php intercepted it correctly.
// If config.php did not execute the callback function we can use the debugging function here.
debugging('Your config.php is not properly configured for auth/outage plugin. '.
debugging('Your config.php is not properly configured for auth/outage plugin. ' .
'Please check the plugin settings for information.');

View File

@@ -27,9 +27,9 @@ use auth_outage\dml\outagedb;
use auth_outage\form\outage\finish;
use auth_outage\output\renderer;
require_once(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/formslib.php');
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/formslib.php');
admin_externalpage_setup('auth_outage_manage');
$PAGE->set_url(new moodle_url('/auth/outage/manage.php'));
@@ -46,7 +46,7 @@ if ($mform->is_cancelled()) {
$id = required_param('id', PARAM_INT);
$outage = outagedb::get_by_id($id);
if ($outage == null) {
throw new invalid_parameter_exception('Outage #'.$id.' not found.');
throw new invalid_parameter_exception('Outage #' . $id . ' not found.');
}
$dataid = new stdClass();

View File

@@ -23,11 +23,19 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['accesskey'] = 'Access key';
$string['accesskey_help'] = 'Testers should pass the access key initially in the url parameters e.g. ?accesskey=xyz. This will then be stored in a cookie for 24 hours, during which the url parameter will not be necessary.<br /><b>Note:</b> the access key is in addition to any IP restrictions setup.';
$string['allowedipsempty'] = 'No one will be blocked by IP because the list is empty. You can add your own IP address (<i>{$a->ip}</i>) and block all other IPs. IP blocking is in addition to access key blocking (if setup in outage)';
$string['allowedipshasmyip'] = 'Your IP (<i>{$a->ip}</i>) is in the list and your IP will not be blocked out during an Outage.';
$string['allowedipshasntmyip'] = 'Your IP (<i>{$a->ip}</i>) is not in the list and your IP will be blocked out during an outage.';
$string['allowedipsnoconfig'] = 'Your config.php does not have the extra setup to allow blocking via IP.<br />Please refer to our <a href="https://github.com/catalyst/moodle-auth_outage#installation" target="_blank">README.md</a> file for more information.';
$string['auth_outagedescription'] = 'Auxiliary plugin that warns users about a future outage and prevents them from logging in once the outage starts.';
$string['autostart'] = 'Auto start maintenance mode.';
$string['autostart_help'] = 'If selected, when the outage starts it will automatically turn on Moodle maintenance mode.';
$string['clicreatehelp'] = 'Creates a new outage.';
$string['builtinallowediplist'] = 'Builtin Allowed IP List';
$string['builtinallowediplist_desc'] = 'A second allowed IP list which makes it easier to have some IPs forced in config.php and others editable in the UI';
$string['clicreateexamples'] = "Create an outage starting in 10 seconds\n\n> php create.php -s=10";
$string['clicreatehelp'] = 'Creates a new outage.';
$string['clicreateparamautostart'] = 'must be Y or N, sets if the outage automatically triggers maintenance mode.';
$string['clicreateparamblock'] = 'blocks until outage starts.';
$string['clicreateparamclone'] = 'clone another outage except for the start time.';
@@ -38,16 +46,28 @@ $string['clicreateparamonlyid'] = 'only outputs the new outage id, useful for sc
$string['clicreateparamstart'] = 'in how many seconds should this outage start or unix time to start outage. Required.';
$string['clicreateparamtitle'] = 'the title of the outage.';
$string['clicreateparamwarn'] = 'how many seconds before it starts to display a warning.';
$string['clifinishhelp'] = 'Finishes an ongoing outage.';
$string['clierrorinvalidvalue'] = 'Invalid value for parameter: {$a->param}';
$string['clierrorinvalidvalueemptystring'] = 'Param --{$a->param} must not be an empty string';
$string['clierrorinvalidvaluenegativenumber'] = 'Param --{$a->param} must be a positive number';
$string['clierrorinvalidvaluenotbool'] = 'Param --{$a->param} must be set to either Y or N';
$string['clierrorinvalidvaluenotid'] = 'Param --{$a->param} must be an id number';
$string['clierrorinvalidvaluenotnumber'] = 'Param --{$a->param} must be a number';
$string['clierrorinvalidvaluenotstring'] = 'Param --{$a->param} must be a string';
$string['clierrormissingparamaters'] = 'You must specify the start time, use --help for more information.';
$string['clierroroutagechanged'] = 'Outage was changed while waiting.';
$string['clierroroutageended'] = 'Outage has already ended.';
$string['clierroroutagenotfound'] = 'Outage not found.';
$string['clifinishexamples'] = '';
$string['clifinishhelp'] = 'Finishes an ongoing outage.';
$string['clifinishnotongoing'] = 'Outage is not ongoing.';
$string['clifinishparamhelp'] = 'shows parameters help.';
$string['clifinishparamactive'] = 'finishes the currently active outage.';
$string['clifinishparamhelp'] = 'shows parameters help.';
$string['clifinishparamoutageid'] = 'the id of the outage to finish.';
$string['cliinmaintenancemode'] = 'Moodle maintenance mode is on. Use "php admin/cli/maintenance.php --disable" to disable it before finishing the outage.';
$string['clioutagecreated'] = 'Outage created, id: {$a->id}';
$string['cliwaitforiterroridxoractive'] = 'You must use --outageid=# or --active parameter but not both.';
$string['cliwaitforithelp'] = 'Waits until an outage starts.';
$string['cliwaitforitexamples'] = '';
$string['cliwaitforithelp'] = 'Waits until an outage starts.';
$string['cliwaitforitoutagestarted'] = 'Outage started!';
$string['cliwaitforitoutagestartingin'] = 'Outage starting in {$a->countdown}.';
$string['cliwaitforitparamactive'] = 'wait for the currently active outage.';
@@ -55,39 +75,29 @@ $string['cliwaitforitparamhelp'] = 'shows parameters help.';
$string['cliwaitforitparamoutageid'] = 'the id of the outage to wait until it starts.';
$string['cliwaitforitparamsleep'] = 'maximum amount of seconds before status output.';
$string['cliwaitforitparamverbose'] = 'enable verbose mode.';
$string['clierrorinvalidvalue'] = 'Invalid value for parameter: {$a->param}';
$string['clierrorinvalidvaluenotid'] = 'Param --{$a->param} must be an id number';
$string['clierrorinvalidvaluenotbool'] = 'Param --{$a->param} must be set to either Y or N';
$string['clierrorinvalidvaluenotnumber'] = 'Param --{$a->param} must be a number';
$string['clierrorinvalidvaluenegativenumber'] = 'Param --{$a->param} must be a positive number';
$string['clierrorinvalidvaluenotstring'] = 'Param --{$a->param} must be a string';
$string['clierrorinvalidvalueemptystring'] = 'Param --{$a->param} must not be an empty string';
$string['clierrormissingparamaters'] = 'You must specify the start time, use --help for more information.';
$string['clierroroutagechanged'] = 'Outage was changed while waiting.';
$string['clierroroutageended'] = 'Outage has already ended.';
$string['clierroroutagenotfound'] = 'Outage not found.';
$string['clioutagecreated'] = 'Outage created, id: {$a->id}';
$string['clone'] = 'Clone';
$string['configurationwarning'] = 'The outage plugin is not properly configured, please refer to <a href="https://github.com/catalyst/moodle-auth_outage/blob/master/README.md#installation" target="_blank">README.md</a> for more information.';
$string['configurationdisabled'] = 'The authentication plugin \'Outage\' is disabled. Please enable it in the site administration it and try again.';
$string['configurationinaccessiblewwwroot'] = 'Could not access <b>{$a->wwwroot}</b> from the server, creation of outages may fail.';
$string['configurationwarning'] = 'The outage plugin is not properly configured, please refer to <a href="https://github.com/catalyst/moodle-auth_outage/blob/master/README.md#installation" target="_blank">README.md</a> for more information.';
$string['datetimeformat'] = '%a %d %h %Y at %I:%M%P %Z';
$string['defaultdescription'] = 'Description';
$string['defaultdescriptiondescription'] = 'Default warning message for outages. Use {{start}} and {{stop}} placeholders as required.';
$string['defaultdescriptionvalue'] = 'There is maintenance scheduled from {{start}} to {{stop}} and our system will not be available during that time.';
$string['defaultlayoutcss'] = 'Layout CSS';
$string['defaultlayoutcssdescription'] = 'This CSS code can be used to override the Outage Warning Bar CSS.';
$string['defaultmetadata'] = 'Default metadata';
$string['defaultmetadatadescription'] = 'The default metadata to include in the outage settings, to be used as a template for new outages. E.g. JIRA-{ticketno}.';
$string['defaultoutageautostart'] = 'Outage auto start';
$string['defaultoutageautostartdescription'] = 'If the outage should automatically trigger maintenance mode once it starts, locking down the whole site.';
$string['defaultoutageduration'] = 'Outage duration';
$string['defaultoutagedurationdescription'] = 'Default duration (in minutes) of an outage.';
$string['defaultwarningduration'] = 'Warning duration';
$string['defaultwarningdurationdescription'] = 'Default warning time (in minutes) for outages.';
$string['defaulttime'] = 'Default time';
$string['defaulttimedescription'] = 'The default time for the next outage, expressed in natural language eg "next Thursday 7pm". See <a target=_blank href="https://www.php.net/manual/en/datetime.formats.relative.php">PHP relative dates</a>';
$string['defaulttitle'] = 'Title';
$string['defaulttitledescription'] = 'Default title for outages. Use {{start}} and {{stop}} placeholders as required.';
$string['defaulttitlevalue'] = 'System down from {{start}} for {{duration}}';
$string['defaultdescription'] = 'Description';
$string['defaultdescriptiondescription'] = 'Default warning message for outages. Use {{start}} and {{stop}} placeholders as required.';
$string['defaultdescriptionvalue'] = 'There is an scheduled maintenance from {{start}} to {{stop}} and our system will not be available during that time.';
$string['defaultwarningduration'] = 'Warning duration';
$string['defaultwarningdurationdescription'] = 'Default warning time (in minutes) for outages.';
$string['description'] = 'Public Description';
$string['description_help'] = 'A full description of the outage, publicly visible by all users.';
$string['finish'] = 'Finish';
@@ -95,25 +105,26 @@ $string['info15secondsbefore'] = '15 seconds before';
$string['infoendofoutage'] = 'end of outage';
$string['infofrom'] = 'From:';
$string['infohidewarning'] = 'no warning bar';
$string['infountil'] = 'Until:';
$string['infopagestaticgenerated'] = 'This warning was generated on {$a->time}.';
$string['infostart'] = 'start';
$string['infostartofwarning'] = 'start of warning';
$string['infostaticpage'] = 'static page';
$string['infopagestaticgenerated'] = 'This warning was generated on {$a->time}.';
$string['allowedipsempty'] = 'When the allowed IPs list is empty we will not block anyone. You can add your own IP address (<i>{$a->ip}</i>) and block all other IPs.';
$string['allowedipshasmyip'] = 'Your IP (<i>{$a->ip}</i>) is in the list and you will not be blocked out during an Outage.';
$string['allowedipshasntmyip'] = 'Your IP (<i>{$a->ip}</i>) is not in the list and you will be blocked out during an outage.';
$string['allowedipsnoconfig'] = 'Your config.php does not have the extra setup to allow blocking via IP.<br />Please refer to our <a href="https://github.com/catalyst/moodle-auth_outage#installation" target="_blank">README.md</a> file for more information.';
$string['menusettings'] = 'Settings';
$string['infountil'] = 'Until:';
$string['ips_combine'] = 'The IPs listed above will be combined with the IPs listed below.';
$string['logformaintmodeconfig'] = 'Update maintenance mode configuration.';
$string['logformaintmodeconfigcomplete'] = 'Updating maintenance mode configuration complete.';
$string['menumanage'] = 'Manage outages';
$string['menusettings'] = 'Settings';
$string['messageoutagebackonline'] = 'We are back online!';
$string['messageoutagebackonlinedescription'] = 'You may resume browsing safely.';
$string['messageoutageongoing'] = 'Back online at {$a->stop}.';
$string['messageoutagewarning'] = 'Shutting down in {{countdown}}';
$string['metadata'] = 'Outage metadata';
$string['metadata_help'] = 'Data here will be output in the outage page as a meta tag in the header of the outage page.';
$string['na'] = 'n/a';
$string['notfound'] = 'No outages found.';
$string['outageedit'] = 'Edit outage';
$string['outageeditcrumb'] = 'Edit';
$string['outage:updatenotify'] = '';
$string['outage:viewinfo'] = 'View outage info';
$string['outageclone'] = 'Clone outage';
$string['outageclonecrumb'] = 'Clone';
$string['outagecreate'] = 'Create outage';
@@ -121,13 +132,14 @@ $string['outagecreatecrumb'] = 'Create';
$string['outagedelete'] = 'Delete outage';
$string['outagedeletewarning'] = 'You are about to permanently delete the outage below. This cannot be undone.';
$string['outageduration'] = 'Outage duration';
$string['outagedurationerrorinvalid'] = 'Outage duration must be positive.';
$string['outageduration_help'] = 'How long the outage lasts after it starts.';
$string['outagedurationerrorinvalid'] = 'Outage duration must be positive.';
$string['outageedit'] = 'Edit outage';
$string['outageeditcrumb'] = 'Edit';
$string['outagefinish'] = 'Finish outage';
$string['outagefinishwarning'] = 'You are about to mark this outage as finished. The system will be immediately back online.';
$string['outageslistfuture'] = 'Planned outages';
$string['outageslistpast'] = 'Outage history';
$string['outage:updatenotify'] = '';
$string['pluginname'] = 'Outage manager';
$string['removeselectors'] = 'Remove selectors';
$string['removeselectorsdescription'] = 'CSS selectors to remove when rendering a static themed maintenance page. One selector per line.';
@@ -137,24 +149,28 @@ $string['settingssectionplugin'] = 'Plugin Configuration';
$string['settingssectionplugindescription'] = 'General outage management plugin settings.';
$string['starttime'] = 'Start date and time';
$string['starttime_help'] = 'At which date and time the outage starts, preventing general access to the system.';
$string['tableheadercreatedby'] = 'Created by';
$string['tableheaderduration'] = 'Duration';
$string['tableheaderdurationplanned'] = 'Planned duration';
$string['tableheaderdurationactual'] = 'Actual duration';
$string['tableheaderstarttime'] = 'Starts on';
$string['tableheaderdurationplanned'] = 'Planned duration';
$string['tableheadermodifiedby'] = 'Last modified by';
$string['tableheaderstartedtime'] = 'Started on';
$string['tableheaderwarnbefore'] = 'Warns before';
$string['tableheaderstarttime'] = 'Starts on';
$string['tableheadertitle'] = 'Title';
$string['tableheaderwarnbefore'] = 'Warns before';
$string['tablestarttimefutureformat'] = '{$a->absolute}<br />In {$a->relative}';
$string['tablestarttimepastformat'] = '{$a->absolute}<br />{$a->relative} ago';
$string['taskupdatestaticpage'] = 'Update static outage page';
$string['textplaceholdershint'] = 'You can use {{start}}, {{stop}} and {{duration}} as placeholders on the title and description.';
$string['titleerrorinvalid'] = 'Title cannot be left blank.';
$string['titleerrortoolong'] = 'Title cannot have more than {$a} characters.';
$string['title'] = 'Title';
$string['title_help'] = 'A short title to for this outage. It will be displayed on the warning bar and on the calendar.';
$string['warningdurationerrorinvalid'] = 'Warning duration must be positive.';
$string['titleerrorinvalid'] = 'Title cannot be left blank.';
$string['titleerrortoolong'] = 'Title cannot have more than {$a} characters.';
$string['useaccesskey'] = 'Use access key';
$string['useaccesskey:desc'] = 'Require testers to access site during outage by providing the access key below';
$string['warningduration'] = 'Warning duration';
$string['warningduration_help'] = 'How long before the start of the outage should the warning be displayed.';
$string['warningdurationerrorinvalid'] = 'Warning duration must be positive.';
$string['warningreenablemaintenancemode'] = 'Please note that saving this outage will re-enable maintenance mode.<br />Untick "Auto start maintenance mode" if you want to prevent this.';
/*

22
lib.php
View File

@@ -25,8 +25,6 @@
use auth_outage\local\outagelib;
defined('MOODLE_INTERNAL') || die;
/**
* Used for adminlib::set_updatedcallback which requires a string that resolves to a function.
*
@@ -50,7 +48,7 @@ function auth_outage_get_climaintenance_resource_file($file) {
// We are not using any external libraries or references in this file (we have not gully loaded config.php yet).
// If you change the path below maybe you need to change maintenance_static_page::get_resources_folder() as well.
$resourcedir = rtrim($CFG->dataroot, '/'); // In case the configuration has a trailing slash.
$resourcedir = $resourcedir.'/auth_outage/climaintenance';
$resourcedir = $resourcedir . '/auth_outage/climaintenance';
// Protect against path traversal attacks.
$basename = basename($file);
@@ -63,27 +61,19 @@ function auth_outage_get_climaintenance_resource_file($file) {
return null;
}
$realpath = realpath($resourcedir.'/'.$file);
$realpath = realpath($resourcedir . '/' . $file);
return ($realpath == false) ? null : $realpath;
}
/**
* Display required icon for the calendar events.
*
* @return array
*/
function auth_outage_get_fontawesome_icon_map() {
return [
'core:i/auth_outageevent' => 'fa-power-off',
];
}
/**
* Inject the warning bar into the page if there is currently an outage.
*
* This is a legacy callback that is used for compatibility with older Moodle versions.
* Moodle 4.4+ will use auth_outage\hook_callbacks::before_standard_top_of_body_html_generation instead.
*
* @return string|void
*/
function auth_outage_before_standard_top_of_body_html() {
// Get code to inject.
return outagelib::get_inject_code();
}
}

View File

@@ -27,8 +27,8 @@ use auth_outage\dml\outagedb;
use auth_outage\output\renderer;
use auth_outage\local\outagelib;
require_once(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
admin_externalpage_setup('auth_outage_manage');
$PAGE->set_url(new moodle_url('/auth/outage/manage.php'));

BIN
pix/auth_outage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

1
pix/auth_outage.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512" preserveAspectRatio="xMinYMid meet"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V256c0 17.7 14.3 32 32 32s32-14.3 32-32V32zM143.5 120.6c13.6-11.3 15.4-31.5 4.1-45.1s-31.5-15.4-45.1-4.1C49.7 115.4 16 181.8 16 256c0 132.5 107.5 240 240 240s240-107.5 240-240c0-74.2-33.8-140.6-86.6-184.6c-13.6-11.3-33.8-9.4-45.1 4.1s-9.4 33.8 4.1 45.1c38.9 32.3 63.5 81 63.5 135.4c0 97.2-78.8 176-176 176s-176-78.8-176-176c0-54.4 24.7-103.1 63.5-135.4z"/></svg>

After

Width:  |  Height:  |  Size: 686 B

View File

@@ -26,8 +26,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* auth_outage_renderer class.
*

View File

@@ -26,25 +26,23 @@
* @var admin_settingpage $settings
* @var bootstrap_renderer $OUTPUT
* @var admin_root $ADMIN
* @var moodle_page $PAGE
*/
use auth_outage\local\outagelib;
defined('MOODLE_INTERNAL') || die;
if ($hassiteconfig && is_enabled_auth('outage')) {
if ($hassiteconfig) {
$defaults = outagelib::get_config_defaults();
$settings->visiblename = get_string('menusettings', 'auth_outage');
$description = outagelib::generate_plugin_configuration_warning();
$settings->add(new admin_setting_heading(
'defaults',
get_string('settingssectiondefaults', 'auth_outage'),
get_string('settingssectiondefaultsdescription', 'auth_outage')));
$settings->add(new admin_setting_configcheckbox(
'auth_outage/default_autostart',
get_string('defaultoutageautostart', 'auth_outage'),
get_string('defaultoutageautostartdescription', 'auth_outage'),
$defaults['default_autostart']
get_string('settingssectiondefaultsdescription', 'auth_outage') . $description
));
$settings->add(new admin_setting_configduration(
'auth_outage/default_warning_duration',
get_string('defaultwarningduration', 'auth_outage'),
@@ -73,6 +71,16 @@ if ($hassiteconfig && is_enabled_auth('outage')) {
$defaults['default_title'],
PARAM_TEXT
));
$settings->add(
new admin_setting_configtext(
'auth_outage/default_metadata',
get_string('defaultmetadata', 'auth_outage'),
get_string('defaultmetadatadescription', 'auth_outage'),
'',
PARAM_TEXT
)
);
$settings->add(new admin_setting_configtextarea(
'auth_outage/default_description',
get_string('defaultdescription', 'auth_outage'),
@@ -84,7 +92,8 @@ if ($hassiteconfig && is_enabled_auth('outage')) {
$settings->add(new admin_setting_heading(
'plugin',
get_string('settingssectionplugin', 'auth_outage'),
get_string('settingssectionplugindescription', 'auth_outage')));
get_string('settingssectionplugindescription', 'auth_outage')
));
$settings->add(new admin_setting_configtextarea(
'auth_outage/css',
@@ -96,20 +105,23 @@ if ($hassiteconfig && is_enabled_auth('outage')) {
// Create 'Allowed IPs' settings.
$allowedips = outagelib::get_config()->allowedips;
$description = outagelib::generate_plugin_configuration_warning();
if (trim($allowedips) == '') {
$message = 'allowedipsempty';
$type = 'notifymessage';
} else if (remoteip_in_list($allowedips)) {
$message = 'allowedipshasmyip';
$type = 'notifysuccess';
} else {
$message = 'allowedipshasntmyip';
$type = 'notifyerror';
if (remoteip_in_list($allowedips)) {
$message = 'allowedipshasmyip';
$type = 'notifysuccess';
} else {
$message = 'allowedipshasntmyip';
$type = 'notifyerror';
}
};
$description .= $OUTPUT->notification(get_string($message, 'auth_outage', ['ip' => getremoteaddr()]), $type);
$description = $OUTPUT->notification(get_string($message, 'auth_outage', ['ip' => getremoteaddr()]), $type);
$description .= '<p>' . get_string('ipblockersyntax', 'admin') . '</p>';
$description .= '<p>' . get_string('ips_combine', 'auth_outage') . '</p>';
$description .= '<p>'.get_string('ipblockersyntax', 'admin').'</p>';
$iplist = new admin_setting_configiplist(
'auth_outage/allowedips',
get_string('allowediplist', 'admin'),
@@ -119,6 +131,14 @@ if ($hassiteconfig && is_enabled_auth('outage')) {
$iplist->set_updatedcallback('auth_outage_outagelib_prepare_next_outage');
$settings->add($iplist);
$iplist = new admin_setting_configiplist(
'auth_outage/allowedips_forced',
get_string('builtinallowediplist', 'auth_outage'),
get_string('builtinallowediplist_desc', 'auth_outage'),
''
);
$settings->add($iplist);
// Create 'Static Page - Elements to Remove' settings.
$toremove = new admin_setting_configtextarea(
'auth_outage/remove_selectors',
@@ -136,11 +156,12 @@ if ($hassiteconfig && is_enabled_auth('outage')) {
// Clear '$settings' to prevent adding again outsite category.
$settings = null;
// Add options.
$ADMIN->add('auth_outage',
$ADMIN->add(
'auth_outage',
new admin_externalpage(
'auth_outage_manage',
get_string('menumanage', 'auth_outage'),
new moodle_url($CFG->wwwroot.'/auth/outage/manage.php')
new moodle_url($CFG->wwwroot . '/auth/outage/manage.php')
)
);
}

View File

@@ -14,6 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace auth_outage;
use auth_outage\dml\outagedb;
use auth_outage\local\outage;
/**
* Base testcase for auth outage tests.
*
@@ -30,20 +35,7 @@
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\dml\outagedb;
defined('MOODLE_INTERNAL') || die();
/**
* auth_outage_base_testcase class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class auth_outage_base_testcase extends advanced_testcase {
abstract class base_testcase extends \advanced_testcase {
/**
* Checks PHPUnit version and calls the functions accordingly.
* @param string $exception Expected exception class.
@@ -65,6 +57,36 @@ abstract class auth_outage_base_testcase extends advanced_testcase {
}
}
/**
* Revoke permission to see info page.
*/
protected function revoke_info_page_permissions() {
global $DB;
$guestrole = $DB->get_record('role', ['shortname' => 'guest']);
role_change_permission($guestrole->id, \context_system::instance(), 'auth/outage:viewinfo', CAP_PREVENT);
$this->setGuestUser();
}
/**
* Get an outage object.
*
* @return \auth_outage\local\outage
*/
protected function get_dummy_outage() {
$now = time();
return new outage([
'id' => 1,
'warntime' => $now - 100,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
}
/**
* Setup testcase.
*/

View File

@@ -30,7 +30,7 @@ use auth_outage\local\outage;
use Behat\Gherkin\Node\TableNode;
use Behat\Mink\Exception\ExpectationException;
require_once(__DIR__.'/../../../../lib/behat/behat_base.php');
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
/**
* Steps definitions related to auth_outage.
@@ -88,10 +88,9 @@ class behat_auth_outage extends behat_base {
*/
public function there_is_a_outage($type) {
$data = [
'autostart' => false,
'finished' => null,
'title' => 'Example of '.$type.' outage',
'description' => 'An outage: '.$type,
'title' => 'Example of ' . $type . ' outage',
'description' => 'An outage: ' . $type,
];
switch ($type) {
case 'waiting':
@@ -121,7 +120,7 @@ class behat_auth_outage extends behat_base {
$data['stoptime'] = time() - (60 * 60 * 2); // Stopped 1 hour ago.
break;
default:
throw new InvalidArgumentException('$type='.$type.' is not valid.');
throw new InvalidArgumentException('$type=' . $type . ' is not valid.');
}
outagedb::save(new outage($data));
}
@@ -136,8 +135,8 @@ class behat_auth_outage extends behat_base {
$expected = ($action == 'Edit') ? 2 : 1; // Edit is an action through the title or button.
$found = $this->how_many_times_can_i_see_action($action);
if ($found != $expected) {
throw new ExpectationException('"'.$action.'" action not found, expected '.$expected.
' but found '.$found.'.', $this->getSession());
throw new ExpectationException('"' . $action . '" action not found, expected ' . $expected .
' but found ' . $found . '.', $this->getSession());
}
}
@@ -149,7 +148,7 @@ class behat_auth_outage extends behat_base {
*/
public function i_should_not_see_the_action($action) {
if ($this->how_many_times_can_i_see_action($action) != 0) {
throw new ExpectationException('"'.$action.'" action was found', $this->getSession());
throw new ExpectationException('"' . $action . '" action was found', $this->getSession());
}
}
@@ -159,7 +158,7 @@ class behat_auth_outage extends behat_base {
* @param string $name
*/
public function i_should_see_an_empty_settings_text_area($name) {
$this->assertSession()->fieldValueEquals('s_auth_outage_'.$name, '');
$this->assertSession()->fieldValueEquals('s_auth_outage_' . $name, '');
}
/**
@@ -177,7 +176,7 @@ class behat_auth_outage extends behat_base {
*/
private function how_many_times_can_i_see_action($action) {
$selector = 'css';
$locator = "div[role='main'] a[title='".$action."']";
$locator = "div[role='main'] a[title='" . $action . "']";
$items = $this->getSession()->getPage()->findAll($selector, $locator);
return count($items);
}
@@ -188,7 +187,7 @@ class behat_auth_outage extends behat_base {
* @param string $action Action button to click.
*/
public function i_click_on_the_action_button($action) {
$node = $this->get_selected_node('css_element', "div[role='main'] table nobr a[title='".$action."']");
$node = $this->get_selected_node('css_element', "div[role='main'] table nobr a[title='" . $action . "']");
$this->ensure_node_is_visible($node);
$node->click();
}
@@ -205,7 +204,7 @@ class behat_auth_outage extends behat_base {
$count = count($this->getSession()->getWindowNames());
if ($count != 2) {
throw new ExpectationException('Number of windows: '.$count, $this->getSession());
throw new ExpectationException('Number of windows: ' . $count, $this->getSession());
}
}
@@ -220,17 +219,18 @@ class behat_auth_outage extends behat_base {
$container = $this->getSession()->getPage()->findAll('css', $element);
if (count($container) == 0) {
throw new ExpectationException('"'.$element.'" element not found', $this->getSession());
throw new ExpectationException('"' . $element . '" element not found', $this->getSession());
}
$container = $container[0];
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]".
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
$found = $this->find_all('xpath', $xpath, false, $container);
if (count($found) == 0) {
throw new ExpectationException('"'.$text.'" text was not found in the "'.$element.'" element', $this->getSession());
throw new ExpectationException('"' . $text . '" text was not found in the "' . $element .
'" element', $this->getSession());
}
foreach ($found as $node) {
@@ -238,8 +238,10 @@ class behat_auth_outage extends behat_base {
return;
}
}
throw new ExpectationException('"'.$text.'" text was found in the "'.$element.'" element but was not visible',
$this->getSession());
throw new ExpectationException(
'"' . $text . '" text was found in the "' . $element . '" element but was not visible',
$this->getSession()
);
}
/**
@@ -252,7 +254,7 @@ class behat_auth_outage extends behat_base {
$locator = "#auth_outage_warningbar_box";
$items = $this->getSession()->getPage()->findAll($selector, $locator);
if (count($items) > 0) {
throw new ExpectationException($locator.' found, not expected.', $this->getSession());
throw new ExpectationException($locator . ' found, not expected.', $this->getSession());
}
}
@@ -269,7 +271,6 @@ class behat_auth_outage extends behat_base {
// Set defaults.
$row = array_merge(
[
'autostart' => 'no',
'warnbefore' => 60,
'startsin' => 0,
'stopsafter' => 60,
@@ -279,16 +280,12 @@ class behat_auth_outage extends behat_base {
],
$row
);
if (($row['autostart'] != 'yes') && ($row['autostart'] != 'no')) {
throw new Exception('autostart must be yes or no, found: '.$row['autostart']);
}
if ($row['finished'] == '') {
$row['finished'] = null;
}
$starttime = $time + $row['startsin'];
$this->outage = new outage([
'autostart' => ($row['autostart'] == 'yes'),
'warntime' => $starttime - $row['warnbefore'],
'starttime' => $starttime,
'stoptime' => $starttime + $row['stopsafter'],
@@ -318,7 +315,7 @@ class behat_auth_outage extends behat_base {
$seconds += 5; // Give it some extra time to pool the server.
break;
default:
throw new Exception('Invalid $what='.$what);
throw new Exception('Invalid $what=' . $what);
}
if ($seconds >= 0) {
$seconds++; // Give one extra second for things to happen.
@@ -332,7 +329,7 @@ class behat_auth_outage extends behat_base {
*/
private function is_behat_3() {
global $version;
list($behat) = explode('.', $version);
[$behat] = explode('.', $version);
return ($behat >= 3);
}
}

View File

@@ -14,7 +14,6 @@ Feature: Change the default settings
Scenario Outline: Check if I can save the default settings.
When I navigate to "Plugins > Authentication > Outage manager > Settings" in site administration
And I set the following fields to these values:
| s_auth_outage_default_autostart | <autostart> |
| s_auth_outage_default_warning_duration[v] | <warning> |
| s_auth_outage_default_warning_duration[u] | 60 |
| s_auth_outage_default_duration[v] | <duration> |
@@ -26,7 +25,6 @@ Feature: Change the default settings
Then I should see "Changes saved"
When I visit the Create Outage Page
Then the following fields match these values:
| autostart | <autostart> |
| warningduration[number] | <warning> |
| warningduration[timeunit] | 60 |
| outageduration[number] | <duration> |
@@ -35,6 +33,6 @@ Feature: Change the default settings
| description[text] | <description> |
Examples:
| autostart | warning | duration | title | description | css |
| 1 | 15 | 30 | An Outage | My outage until {stop}. | /* Some CSS. */ |
| 0 | 30 | 45 | My Behat Outage {start} | My outage with <b>HTML</b>. | /* More CSS. */ |
| warning | duration | title | description | css |
| 15 | 30 | An Outage | My outage until {stop}. | /* Some CSS. */ |
| 30 | 45 | My Behat Outage {start} | My outage with <b>HTML</b>. | /* More CSS. */ |

View File

@@ -23,10 +23,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\calendar\calendar;
use auth_outage\local\outage;
namespace auth_outage\calendar;
defined('MOODLE_INTERNAL') || die();
use auth_outage\local\outage;
/**
* calendar_test test class.
@@ -38,8 +37,9 @@ defined('MOODLE_INTERNAL') || die();
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\calendar\calendar
*/
class calendar_test extends advanced_testcase {
final class calendar_test extends \advanced_testcase {
/**
* @var outage|null The calendar entry owner.
*/
@@ -48,14 +48,13 @@ class calendar_test extends advanced_testcase {
/**
* Creates an outage and checks if its in the calendar.
*/
public function test_create() {
public function test_create(): void {
$this->resetAfterTest(true);
self::setAdminUser();
$time = time();
self::$outage = new outage([
'id' => 1,
'autostart' => false,
'warntime' => $time - 100,
'starttime' => $time,
'stoptime' => $time + (2 * 60 * 60),
@@ -69,14 +68,13 @@ class calendar_test extends advanced_testcase {
/**
* Updates an outage and checks the calendar.
*/
public function test_update() {
public function test_update(): void {
$this->resetAfterTest(true);
self::setAdminUser();
$time = time();
self::$outage = new outage([
'id' => 1,
'autostart' => false,
'warntime' => $time - 100,
'starttime' => $time,
'stoptime' => $time + (2 * 60 * 60),
@@ -93,14 +91,13 @@ class calendar_test extends advanced_testcase {
/**
* Deletes an outage and checks the calendar.
*/
public function test_delete() {
public function test_delete(): void {
$this->resetAfterTest(true);
self::setAdminUser();
$time = time();
self::$outage = new outage([
'id' => 1,
'autostart' => false,
'warntime' => $time - 100,
'starttime' => $time,
'stoptime' => $time + (2 * 60 * 60),
@@ -118,14 +115,13 @@ class calendar_test extends advanced_testcase {
/**
* Try to update a non existing outage.
*/
public function test_update_notfound() {
public function test_update_notfound(): void {
$this->resetAfterTest(true);
self::setAdminUser();
$time = time();
$outage = new outage([
'id' => 1,
'autostart' => false,
'warntime' => $time - 100,
'starttime' => $time,
'stoptime' => $time + (2 * 60 * 60),
@@ -134,20 +130,20 @@ class calendar_test extends advanced_testcase {
]);
calendar::update($outage);
self::assertCount(1, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
self::assertCount(1, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**
* Try to delete a non existing outage.
*/
public function test_delete_notfound() {
public function test_delete_notfound(): void {
$this->resetAfterTest(true);
self::setAdminUser();
calendar::delete(1);
self::assertCount(1, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
self::assertCount(1, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**

View File

@@ -23,10 +23,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\dml\outagedb;
use auth_outage\local\outage;
namespace auth_outage\dml;
defined('MOODLE_INTERNAL') || die();
use auth_outage\local\outage;
/**
* events_test tests class.
@@ -38,8 +37,9 @@ defined('MOODLE_INTERNAL') || die();
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\dml\outagedb
*/
class events_test extends advanced_testcase {
final class events_test extends \advanced_testcase {
/**
* @var outage|null Outage used in the tests.
*/
@@ -54,7 +54,7 @@ class events_test extends advanced_testcase {
* Saves an outage and check if the event was created.
* @return array With the outage id and the event id.
*/
public function test_save() {
public function test_save(): void {
global $DB;
self::setAdminUser();
$this->resetAfterTest(true);
@@ -62,7 +62,6 @@ class events_test extends advanced_testcase {
// Save new outage.
$now = time();
$outage = new outage([
'autostart' => false,
'warntime' => $now - 60,
'starttime' => 60,
'stoptime' => 120,
@@ -86,7 +85,7 @@ class events_test extends advanced_testcase {
/**
* Updates an outage and checks if the event was updated.
*/
public function test_update() {
public function test_update(): void {
global $DB;
self::setAdminUser();
@@ -95,7 +94,6 @@ class events_test extends advanced_testcase {
// Save new outage.
$now = time();
$outage = new outage([
'autostart' => false,
'warntime' => $now - 60,
'starttime' => 60,
'stoptime' => 120,
@@ -124,7 +122,7 @@ class events_test extends advanced_testcase {
/**
* Deletes an outage and checks if the event was deleted.
*/
public function test_delete() {
public function test_delete(): void {
global $DB;
self::setAdminUser();
@@ -133,7 +131,6 @@ class events_test extends advanced_testcase {
// Save new outage.
$now = time();
$outage = new outage([
'autostart' => false,
'warntime' => $now - 60,
'starttime' => 60,
'stoptime' => 120,

View File

@@ -25,10 +25,12 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\dml;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/base_testcase.php');
require_once(__DIR__ . '/../base_testcase.php');
/**
* installation_test test class.
@@ -39,52 +41,64 @@ require_once(__DIR__.'/base_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\dml\outagedb
*/
class installation_test extends auth_outage_base_testcase {
final class installation_test extends \auth_outage\base_testcase {
/**
* Checks if plugin cleans up data after uninstall.
*
* See Issue #57.
*/
public function test_uninstall() {
public function test_uninstall(): void {
global $CFG, $DB;
$this->resetAfterTest();
static::setAdminUser();
$dbman = $DB->get_manager();
// Create a future outage with autostart.
// Create a future outage.
$now = time();
$outage = new outage([
'autostart' => true,
'starttime' => $now + (1 * 60 * 60),
'stoptime' => $now + (2 * 60 * 60),
'warntime' => $now - (2 * 60 * 60),
'title' => 'Title',
'description' => 'Description',
]);
\auth_outage\dml\outagedb::save($outage);
ob_start();
outagedb::save($outage);
$text = trim(ob_get_contents());
ob_end_clean();
self::assertSame(1, $DB->count_records_select('event', "eventtype = 'auth_outage'", null));
// Uninstall plugin.
require_once($CFG->libdir.'/adminlib.php');
$progress = new progress_trace_buffer(new text_progress_trace(), false);
core_plugin_manager::instance()->uninstall_plugin('auth_outage', $progress);
require_once($CFG->libdir . '/adminlib.php');
$progress = new \progress_trace_buffer(new \text_progress_trace(), false);
\core_plugin_manager::instance()->uninstall_plugin('auth_outage', $progress);
$progress->finished();
self::assertContains('++ Success ++', $progress->get_buffer());
self::assertStringContainsString('++ Success ++', $progress->get_buffer());
// Check ...
self::assertSame(0, $DB->count_records_select('event', "eventtype = 'auth_outage'", null),
'The outage events were not removed.');
self::assertFalse(file_exists($CFG->dataroot.'/climaintenance.php'),
'The maintenance template file was not deleted.');
self::assertFalse(get_config('moodle', 'maintenance_later'),
'Maintenance later must not be set.'); // Issue #57.
self::assertFalse($dbman->table_exists('auth_outage'),
'Table "auth_outage" was not dropped.');
self::assertSame(
0,
$DB->count_records_select('event', "eventtype = 'auth_outage'", null),
'The outage events were not removed.'
);
self::assertFalse(
file_exists($CFG->dataroot . '/climaintenance.php'),
'The maintenance template file was not deleted.'
);
self::assertFalse(
get_config('moodle', 'maintenance_later'),
'Maintenance later must not be set.'
); // Issue #57.
self::assertFalse(
$dbman->table_exists('auth_outage'),
'Table "auth_outage" was not dropped.'
);
// Create tables back so tests do not fail with MySQL ...
require_once($CFG->libdir.'/upgradelib.php');
$DB->get_manager()->install_from_xmldb_file($CFG->dirroot.'/auth/outage/db/install.xml');
require_once($CFG->libdir . '/upgradelib.php');
$DB->get_manager()->install_from_xmldb_file($CFG->dirroot . '/auth/outage/db/install.xml');
}
}

View File

@@ -23,11 +23,12 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\dml\outagedb;
namespace auth_outage\dml;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../base_testcase.php');
require_once(__DIR__ . '/../base_testcase.php');
/**
* outagedb_test tests class.
@@ -36,8 +37,9 @@ require_once(__DIR__.'/../base_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\dml\outagedb
*/
class outagedb_test extends auth_outage_base_testcase {
final class outagedb_test extends \auth_outage\base_testcase {
/**
* Creates an array of ids in from the given outages array.
* @param outage[] $outages An array of outages.
@@ -54,7 +56,6 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Helper function to create an outage then save it to the database.
*
* @param bool $autostart If outage should automatically start.
* @param int $now Timestamp for now, such as time().
* @param int $warning In how many hours the warning starts. Can be negative.
* @param int $start In how many hours this outage starts. Can be negative.
@@ -63,9 +64,8 @@ class outagedb_test extends auth_outage_base_testcase {
* @param int|null $finished In how many hours this outage is marked as finished. Can be negative or null.
* @return int Id the of created outage.
*/
private static function saveoutage($autostart, $now, $warning, $start, $stop, $title, $finished = null) {
private static function saveoutage($now, $warning, $start, $stop, $title, $finished = null) {
return outagedb::save(new outage([
'autostart' => $autostart,
'warntime' => $now + ($warning * 60 * 60),
'starttime' => $now + ($start * 60 * 60),
'stoptime' => $now + ($stop * 60 * 60),
@@ -86,7 +86,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Make sure we can save and update.
*/
public function test_save() {
public function test_save(): void {
$this->resetAfterTest(true);
// Save new outage.
$id = outagedb::save($this->createoutage(1));
@@ -99,7 +99,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Create a few outages, fetch them and check if fields match.
*/
public function test_saved_fields() {
public function test_saved_fields(): void {
$this->resetAfterTest(true);
for ($i = 0; $i < 4; $i++) {
$expected = $this->createoutage($i);
@@ -110,14 +110,14 @@ class outagedb_test extends auth_outage_base_testcase {
$expected->createdby = $actual->createdby;
$expected->modifiedby = $actual->modifiedby;
// Check if fields are the same.
self::assertEquals($expected, $actual, 'Failed for $i='.$i);
self::assertEquals($expected, $actual, 'Failed for $i=' . $i);
}
}
/**
* Make sure we can get existing entries and null if not found.
*/
public function test_getbyid() {
public function test_getbyid(): void {
$this->resetAfterTest(true);
// Create something.
$id = outagedb::save($this->createoutage(1));
@@ -134,7 +134,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Make sure we can delete stuff.
*/
public function test_delete() {
public function test_delete(): void {
$this->resetAfterTest(true);
// Create something.
$id = outagedb::save($this->createoutage(1));
@@ -147,11 +147,11 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Make sure we can finish outages.
*/
public function test_finish() {
public function test_finish(): void {
$now = time();
$this->resetAfterTest(true);
// Create it.
$id = self::saveoutage(false, $now, -3, -2, 2, 'An ongoing outage.');
$id = self::saveoutage($now, -3, -2, 2, 'An ongoing outage.');
$outage = outagedb::get_by_id($id);
self::assertTrue($outage->is_active($now));
self::assertTrue($outage->is_ongoing($now));
@@ -167,7 +167,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Make sure getall brings all entries.
*/
public function test_getall() {
public function test_getall(): void {
$this->resetAfterTest(true);
$amount = 10;
// Should start empty.
@@ -184,7 +184,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Perform some tests on the data itself, checking values after inserted and updated.
*/
public function test_basiccrud() {
public function test_basiccrud(): void {
$this->resetAfterTest(true);
// Create some outages.
@@ -202,7 +202,7 @@ class outagedb_test extends auth_outage_base_testcase {
self::assertNotNull($inserted);
// Check its data.
foreach (['starttime', 'stoptime', 'warntime', 'title', 'description'] as $field) {
self::assertSame($outage->$field, $inserted->$field, 'Field '.$field.' does not match.');
self::assertSame($outage->$field, $inserted->$field, 'Field ' . $field . ' does not match.');
}
// Check generated data.
self::assertGreaterThan(0, $inserted->id);
@@ -210,11 +210,11 @@ class outagedb_test extends auth_outage_base_testcase {
self::assertNotNull($inserted->createdby);
self::assertNotNull($inserted->modifiedby);
// Change it.
$inserted->title = 'Title ID'.$id;
$inserted->title = 'Title ID' . $id;
outagedb::save($inserted);
// Get it again and check data.
$updated = outagedb::get_by_id($id);
self::assertSame('Title ID'.$id, $updated->title);
self::assertSame('Title ID' . $id, $updated->title);
self::assertSame($inserted->description, $updated->description);
// Delete it.
outagedb::delete($id);
@@ -226,7 +226,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::get_active() method.
*/
public function test_getactive() {
public function test_getactive(): void {
$this->resetAfterTest(true);
// Have a consistent time for now (no seconds variation), helps debugging.
@@ -235,37 +235,56 @@ class outagedb_test extends auth_outage_base_testcase {
self::assertEquals([], outagedb::get_all(), 'Ensure there are no other outages that can affect the test.');
self::assertNull(outagedb::get_active($now), 'There should be no active outage at this point.');
self::saveoutage(false, $now, 1, 2, 3, 'An outage that starts in the future and is not in warning period.');
self::saveoutage($now, 1, 2, 3, 'An outage that starts in the future and is not in warning period.');
self::assertNull(outagedb::get_active($now), 'No active outages yet.');
self::saveoutage(false, $now, -3, -2, -1, 'An outage that is already in the past.');
self::saveoutage($now, -3, -2, -1, 'An outage that is already in the past.');
self::assertNull(outagedb::get_active($now), 'No active outages yet.');
$activeid = self::saveoutage(false, $now, -2, 1, 2, 'An outage in warning period.');
$activeid = self::saveoutage($now, -2, 1, 2, 'An outage in warning period.');
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
self::saveoutage(false, $now, -1, 2, 3,
'Another outage in warning period, but ignored as it starts after the previous one.');
$activeid = self::saveoutage($now, -2, 0, 2, 'An outage starts now.');
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
self::saveoutage(false, $now, -3, -2, 2, 'An finished outage.', -1);
self::saveoutage($now, -2, 0, -1, 'Invalid outage.');
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
$activeid = self::saveoutage(false, $now, -3, -2, 2, 'An ongoing outage.');
self::saveoutage($now, -2, 0, 0, 'Invalid outage.');
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
self::saveoutage(false, $now, -3, -1, 1, 'Another ongoing outage but ignored because it started after the previous one.');
self::saveoutage(
$now,
-1,
2,
3,
'Another outage in warning period, but ignored as it starts after the previous one.'
);
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
self::saveoutage(false, $now, -3, -2, 1,
'Another ongoing outage starting at the same time, but ignored as it stops before the previous one.');
self::saveoutage($now, -3, -2, 2, 'An finished outage.', -1);
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
$activeid = self::saveoutage($now, -3, -2, 2, 'An ongoing outage.');
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
self::saveoutage($now, -3, -1, 1, 'Another ongoing outage but ignored because it started after the previous one.');
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
self::saveoutage(
$now,
-3,
-2,
1,
'Another ongoing outage starting at the same time, but ignored as it stops before the previous one.'
);
self::assertSame($activeid, outagedb::get_active($now)->id, 'Wrong active outage picked.');
}
/**
* Tests the outagedb::get_all_unended() method.
*/
public function test_getallunended() {
public function test_getallunended(): void {
$this->resetAfterTest(true);
// Have a consistent time for now (no seconds variation), helps debugging.
@@ -274,49 +293,73 @@ class outagedb_test extends auth_outage_base_testcase {
self::assertEquals([], outagedb::get_all(), 'Ensure there are no other outages that can affect the test.');
self::assertEquals([], outagedb::get_all_unended($now), 'There should be no future outages at this point.');
self::saveoutage(false, $now, -3, -2, -1, 'A past outage.');
self::saveoutage($now, -3, -2, -1, 'A past outage.');
self::assertEquals([], outagedb::get_all_unended($now), 'No future outages yet.');
self::saveoutage(false, $now, -3, -2, 2, 'A finished outage.', -1);
self::saveoutage($now, -3, -2, 2, 'A finished outage.', -1);
self::assertEquals([], outagedb::get_all_unended($now), 'No future outages yet.');
$id1 = self::saveoutage(false, $now, 2, 3, 4, 'A future outage.');
self::assertEquals([$id1],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id1 = self::saveoutage($now, 2, 3, 4, 'A future outage.');
self::assertEquals(
[$id1],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id2 = self::saveoutage(false, $now, 1, 4, 5, 'Another future outage.');
self::assertEquals([$id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id2 = self::saveoutage($now, 1, 4, 5, 'Another future outage.');
self::assertEquals(
[$id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id3 = self::saveoutage(false, $now, 1, 3, 5, 'Yet another future outage.');
self::assertEquals([$id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id3 = self::saveoutage($now, 1, 3, 5, 'Yet another future outage.');
self::assertEquals(
[$id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id4 = self::saveoutage(false, $now, -2, 1, 2, 'An outage in warning period.');
self::assertEquals([$id4, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id4 = self::saveoutage($now, -2, 1, 2, 'An outage in warning period.');
self::assertEquals(
[$id4, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id5 = self::saveoutage(false, $now, -1, 2, 3, 'Another outage in warning period.');
self::assertEquals([$id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id5 = self::saveoutage($now, -1, 2, 3, 'Another outage in warning period.');
self::assertEquals(
[$id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id6 = self::saveoutage(false, $now, -3, -2, 2, 'An ongoing outage.');
self::assertEquals([$id6, $id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id6 = self::saveoutage($now, -3, -2, 2, 'An ongoing outage.');
self::assertEquals(
[$id6, $id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id7 = self::saveoutage(false, $now, -3, -1, 1, 'Another ongoing outage.');
self::assertEquals([$id6, $id7, $id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id7 = self::saveoutage($now, -3, -1, 1, 'Another ongoing outage.');
self::assertEquals(
[$id6, $id7, $id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
$id8 = self::saveoutage(false, $now, -3, -2, 1, 'Yet another ongoing outage.');
self::assertEquals([$id6, $id8, $id7, $id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)), 'Wrong future data.');
$id8 = self::saveoutage($now, -3, -2, 1, 'Yet another ongoing outage.');
self::assertEquals(
[$id6, $id8, $id7, $id4, $id5, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_unended($now)),
'Wrong future data.'
);
}
/**
* Tests the outagedb::get_all_ended() method.
*/
public function test_getallended() {
public function test_getallended(): void {
$this->resetAfterTest(true);
// Have a consistent time for now (no seconds variation), helps debugging.
@@ -325,36 +368,48 @@ class outagedb_test extends auth_outage_base_testcase {
self::assertEquals([], outagedb::get_all(), 'Ensure there are no other outages that can affect the test.');
self::assertEquals([], outagedb::get_all_ended($now), 'There should be no future outages at this point.');
self::saveoutage(false, $now, -2, 1, 2, 'An outage in warning period.');
self::saveoutage($now, -2, 1, 2, 'An outage in warning period.');
self::assertEquals([], outagedb::get_all_ended($now), 'No past outages yet.');
self::saveoutage(false, $now, -3, -2, 2, 'An ongoing outage.');
self::saveoutage($now, -3, -2, 2, 'An ongoing outage.');
self::assertEquals([], outagedb::get_all_ended($now), 'No past outages yet.');
self::saveoutage(false, $now, 2, 3, 4, 'A future outage.');
self::saveoutage($now, 2, 3, 4, 'A future outage.');
self::assertEquals([], outagedb::get_all_ended($now), 'No past outages yet.');
$id1 = self::saveoutage(false, $now, -8, -6, -4, 'A past outage.');
self::assertEquals([$id1],
self::createidarray(outagedb::get_all_ended($now)), 'Wrong past data.');
$id1 = self::saveoutage($now, -8, -6, -4, 'A past outage.');
self::assertEquals(
[$id1],
self::createidarray(outagedb::get_all_ended($now)),
'Wrong past data.'
);
$id2 = self::saveoutage(false, $now, -8, -7, -5, 'Another past outage.');
self::assertEquals([$id1, $id2],
self::createidarray(outagedb::get_all_ended($now)), 'Wrong past data.');
$id2 = self::saveoutage($now, -8, -7, -5, 'Another past outage.');
self::assertEquals(
[$id1, $id2],
self::createidarray(outagedb::get_all_ended($now)),
'Wrong past data.'
);
$id3 = self::saveoutage(false, $now, -8, -5, -3, 'Yet another past outage.');
self::assertEquals([$id3, $id1, $id2],
self::createidarray(outagedb::get_all_ended($now)), 'Wrong past data.');
$id3 = self::saveoutage($now, -8, -5, -3, 'Yet another past outage.');
self::assertEquals(
[$id3, $id1, $id2],
self::createidarray(outagedb::get_all_ended($now)),
'Wrong past data.'
);
$id4 = self::saveoutage(false, $now, -3, -2, 2, 'A finished outage.', -1);
self::assertEquals([$id4, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_ended($now)), 'Wrong past data.');
$id4 = self::saveoutage($now, -3, -2, 2, 'A finished outage.', -1);
self::assertEquals(
[$id4, $id3, $id1, $id2],
self::createidarray(outagedb::get_all_ended($now)),
'Wrong past data.'
);
}
/**
* Tests the outagedb::get_by_id() with an invalid parameter.
*/
public function test_getbyid_invalid() {
public function test_getbyid_invalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::get_by_id(-1);
@@ -363,7 +418,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::delete() with an invalid parameter.
*/
public function test_delete_invalid() {
public function test_delete_invalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::delete(-1);
@@ -372,7 +427,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::get_active() with an invalid parameter.
*/
public function test_getactive_invalid() {
public function test_getactive_invalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::get_active(-1);
@@ -381,7 +436,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::get_all_unended() with an invalid parameter.
*/
public function test_getallunended_invalid() {
public function test_getallunended_invalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::get_all_unended(-1);
@@ -390,7 +445,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Checks we can execute outagedb::get_all_unended() without parameters (now).
*/
public function test_getallunended_now() {
public function test_getallunended_now(): void {
$this->resetAfterTest(true);
self::assertEmpty(outagedb::get_all_unended());
}
@@ -398,7 +453,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::get_all_ended() with an invalid parameter.
*/
public function test_getallended_invalid() {
public function test_getallended_invalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::get_all_ended(-1);
@@ -407,7 +462,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Checks we can execute outagedb::test_getallended_now() without parameters (now).
*/
public function test_getallended_now() {
public function test_getallended_now(): void {
$this->resetAfterTest(true);
self::assertEmpty(outagedb::get_all_ended());
}
@@ -415,7 +470,7 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::finish() with an invalid parameter.
*/
public function test_finish_invalid() {
public function test_finish_invalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::finish(1, -1);
@@ -424,21 +479,20 @@ class outagedb_test extends auth_outage_base_testcase {
/**
* Tests the outagedb::finish() with a non existing outage.
*/
public function test_finish_now_notfound() {
public function test_finish_now_notfound(): void {
$this->resetAfterTest(true);
outagedb::finish(1);
self::assertCount(1, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
self::assertCount(1, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**
* Try to finish not ongoing outages.
*/
public function test_finish_notongoing() {
public function test_finish_notongoing(): void {
$this->resetAfterTest(true);
$time = time();
$outage = new outage([
'autostart' => false,
'warntime' => $time + (60 * 60 * 24 * 1),
'starttime' => $time + (60 * 60 * 24 * 2),
'stoptime' => $time + (60 * 60 * 24 * 3),
@@ -449,28 +503,19 @@ class outagedb_test extends auth_outage_base_testcase {
self::assertTrue(!$outage->is_ongoing($time));
outagedb::finish($id, $time);
self::assertCount(1, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
self::assertCount(1, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**
* Tests the outagedb::get_next_starting() with an invalid parameter.
*/
public function test_getnextstartinginvalid() {
public function test_getnextstartinginvalid(): void {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::get_next_starting(-1);
}
/**
* Tests the outagedb::get_next_autostarting() with an invalid parameter.
*/
public function test_getnextautostartinginvalid() {
$this->resetAfterTest(true);
$this->set_expected_exception('coding_exception');
outagedb::get_next_autostarting(-1);
}
/**
* Helper function to create an outage for tests.
* @param int $i Used to populate the information.
@@ -478,11 +523,10 @@ class outagedb_test extends auth_outage_base_testcase {
*/
private function createoutage($i) {
return new outage([
'autostart' => ($i % 2 == 0),
'starttime' => $i * 100,
'stoptime' => $i * 100 + 50,
'warntime' => $i * 60,
'title' => 'The Title '.$i,
'title' => 'The Title ' . $i,
'description' => 'A <b>description</b> in HTML.',
]);
}

View File

@@ -23,13 +23,12 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\form\outage\delete;
use auth_outage\form\outage\edit;
use auth_outage\form\outage\finish;
namespace auth_outage\form\outage;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../base_testcase.php');
require_once(__DIR__ . '/../../base_testcase.php');
/**
* forms_test test class.
@@ -38,26 +37,27 @@ require_once(__DIR__.'/../base_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\form\outage\edit
*/
class forms_test extends auth_outage_base_testcase {
final class forms_test extends \auth_outage\base_testcase {
/**
* Create a delete form.
*/
public function test_delete() {
public function test_delete(): void {
new delete();
}
/**
* Create a finish form.
*/
public function test_finish() {
public function test_finish(): void {
new finish();
}
/**
* Mock some data and check values.
*/
public function test_edit_valid() {
public function test_edit_valid(): void {
if ($this->skip_because_moodle_is_below_30('Moodle POST mocking was fixed in Moodle 30.')) {
return;
}
@@ -67,7 +67,6 @@ class forms_test extends auth_outage_base_testcase {
self::assertFalse($edit->is_cancelled());
$outage = $edit->get_data();
self::assertInstanceOf('\\auth_outage\\local\\outage', $outage);
self::assertSame(false, $outage->autostart);
self::assertSame(60, $outage->get_warning_duration());
self::assertSame(mktime(14, 15, 0, 2, 1, 2013), $outage->starttime);
self::assertSame(2 * 60 * 60, $outage->get_duration_planned());
@@ -78,7 +77,7 @@ class forms_test extends auth_outage_base_testcase {
/**
* Check invalid warning duration.
*/
public function test_edit_invalid_warning() {
public function test_edit_invalid_warning(): void {
if ($this->skip_because_moodle_is_below_30('Moodle POST mocking was fixed in Moodle 30.')) {
return;
}
@@ -93,7 +92,7 @@ class forms_test extends auth_outage_base_testcase {
/**
* Check invalid outage duration.
*/
public function test_edit_invalid_duration() {
public function test_edit_invalid_duration(): void {
if ($this->skip_because_moodle_is_below_30('Moodle POST mocking was fixed in Moodle 30.')) {
return;
}
@@ -107,7 +106,7 @@ class forms_test extends auth_outage_base_testcase {
/**
* Check invalid title (empty).
*/
public function test_edit_invalid_title() {
public function test_edit_invalid_title(): void {
if ($this->skip_because_moodle_is_below_30('Moodle POST mocking was fixed in Moodle 30.')) {
return;
}
@@ -121,14 +120,14 @@ class forms_test extends auth_outage_base_testcase {
/**
* Check invalid title (too long).
*/
public function test_edit_invalid_title_toolong() {
public function test_edit_invalid_title_toolong(): void {
if ($this->skip_because_moodle_is_below_30('Moodle POST mocking was fixed in Moodle 30.')) {
return;
}
$this->mock_edit_post();
$_POST['title'] = 'This is a very long time, it is so long that at some point it should not be valid. '.
'With a very long title used in this place we should get a form validation error. '.
$_POST['title'] = 'This is a very long time, it is so long that at some point it should not be valid. ' .
'With a very long title used in this place we should get a form validation error. ' .
'Do you think this title is long enough?';
$edit = new edit();
self::assertNull($edit->get_data());
@@ -137,7 +136,7 @@ class forms_test extends auth_outage_base_testcase {
/**
* Check invalid format for description.
*/
public function test_edit_description_invalid_format() {
public function test_edit_description_invalid_format(): void {
if ($this->skip_because_moodle_is_below_30('Moodle POST mocking was fixed in Moodle 30.')) {
return;
}
@@ -146,16 +145,15 @@ class forms_test extends auth_outage_base_testcase {
$_POST['description'] = ['text' => 'The <b>description</b>.', 'format' => '2'];
$edit = new edit();
self::assertNull($edit->get_data());
self::assertCount(1, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
self::assertCount(1, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**
* Check if set data works properly.
*/
public function test_setdata() {
public function test_setdata(): void {
$outage = new outage([
'autostart' => false,
'warntime' => time() - 60,
'starttime' => time(),
'stoptime' => time() + 60,
@@ -169,7 +167,7 @@ class forms_test extends auth_outage_base_testcase {
/**
* Check edit::set_data() with invalid parameter.
*/
public function test_setdata_invalid() {
public function test_setdata_invalid(): void {
$edit = new edit();
$this->set_expected_exception('coding_exception');
$edit->set_data(null);
@@ -208,7 +206,7 @@ class forms_test extends auth_outage_base_testcase {
// The bugfix MDL-56250 in only applies to Moodle 30+.
// Before that the form validation test is meaningless (results are cached), so skip it.
if ($CFG->branch < 30) {
$this->markTestSkipped('Some tests can only run in Moodle 30+. '.$reason);
$this->markTestSkipped('Some tests can only run in Moodle 30+. ' . $reason);
return true;
}

View File

@@ -14,18 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* tests for lib.php
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/base_testcase.php');
require_once(__DIR__.'/../../lib.php');
require_once(__DIR__ . '/base_testcase.php');
require_once(__DIR__ . '/../lib.php');
/**
* tests for lib.php
@@ -34,18 +27,19 @@ require_once(__DIR__.'/../../lib.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers ::auth_outage_get_climaintenance_resource_file
*/
class lib_test extends auth_outage_base_testcase {
final class lib_test extends base_testcase {
/**
* Test this plugin gets climaintenance resource file.
*/
public function test_auth_outage_get_climaintenance_resource_file_resolves_a_file() {
public function test_auth_outage_get_climaintenance_resource_file_resolves_a_file(): void {
global $CFG;
$dir = $CFG->dataroot.'/auth_outage/climaintenance';
$dir = $CFG->dataroot . '/auth_outage/climaintenance';
mkdir($dir, 0777, true);
// Create a file.
$expected = $dir.'/example.txt';
$expected = $dir . '/example.txt';
file_put_contents($expected, 'Outage Unit Test Message');
// Get that file.
@@ -61,17 +55,17 @@ class lib_test extends auth_outage_base_testcase {
/**
* Regression test for issue #104.
*/
public function test_auth_outage_get_climaintenance_resource_file_resolves_a_file_with_symlink() {
public function test_auth_outage_get_climaintenance_resource_file_resolves_a_file_with_symlink(): void {
global $CFG;
// Create a file.
$realdir = $CFG->dataroot.'/auth_outage/climaintenance_real';
$realdir = $CFG->dataroot . '/auth_outage/climaintenance_real';
mkdir($realdir, 0777, true);
$realfile = $realdir.'/example.txt';
$realfile = $realdir . '/example.txt';
file_put_contents($realfile, 'Outage Unit Test Message');
// Create a symlink.
$symdir = $CFG->dataroot.'/auth_outage/climaintenance';
$symdir = $CFG->dataroot . '/auth_outage/climaintenance';
if (!symlink($realdir, $symdir)) {
unlink($realfile);
rmdir($realdir);
@@ -93,18 +87,18 @@ class lib_test extends auth_outage_base_testcase {
/**
* Test this plugin gets climaintenance resource file and prevents path traversal attack.
*/
public function test_auth_outage_get_climaintenance_resource_file_prevent_path_traversal() {
public function test_auth_outage_get_climaintenance_resource_file_prevent_path_traversal(): void {
global $CFG;
$dir = $CFG->dataroot.'/auth_outage/climaintenance';
$dir = $CFG->dataroot . '/auth_outage/climaintenance';
mkdir($dir, 0777, true);
// Create a file.
$expected = $dir.'/example.txt';
$expected = $dir . '/example.txt';
file_put_contents($expected, 'Outage Unit Test Message');
// Create a sensitive file.
$sensitivefile = $CFG->dataroot.'/auth_outage/nuclear_silo_passwords.txt';
$sensitivefile = $CFG->dataroot . '/auth_outage/nuclear_silo_passwords.txt';
file_put_contents($sensitivefile, 'The password to launch the ICBM: 123456');
// Path Traversal Attack.

View File

@@ -23,11 +23,10 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\local\cli\cli_exception;
use auth_outage\local\cli\create;
namespace auth_outage\local\cli;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/cli_testcase.php');
require_once(__DIR__ . '/cli_testcase.php');
/**
* cli_test test class.
@@ -36,12 +35,13 @@ require_once(__DIR__.'/cli_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\cli\create
*/
class cli_test extends auth_outage_cli_testcase {
final class cli_test extends cli_testcase {
/**
* Tests providing an unknown parameter.
*/
public function test_invalidargumentparam() {
public function test_invalidargumentparam(): void {
$this->set_parameters(['--aninvalidparameter']);
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_UNKNOWN);
new create();
@@ -50,7 +50,7 @@ class cli_test extends auth_outage_cli_testcase {
/**
* Tests providing another unknow parameter (without --).
*/
public function test_invalidargumentgiven() {
public function test_invalidargumentgiven(): void {
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_UNKNOWN);
new create(['anotherinvalidparameter']);
}
@@ -58,7 +58,7 @@ class cli_test extends auth_outage_cli_testcase {
/**
* Tests setting reference times.
*/
public function test_setreferencetime() {
public function test_setreferencetime(): void {
$cli = new create(['start' => 0]);
$cli->set_referencetime(1);
$cli->set_referencetime(60 * 60 * 24 * 7);
@@ -67,7 +67,7 @@ class cli_test extends auth_outage_cli_testcase {
/**
* Tests setting an invalid reference time.
*/
public function test_setreferencetime_invalid() {
public function test_setreferencetime_invalid(): void {
$this->set_parameters(['--start=60']);
$cli = new create();
$this->set_expected_exception('coding_exception');
@@ -77,18 +77,18 @@ class cli_test extends auth_outage_cli_testcase {
/**
* Tests the help.
*/
public function test_help() {
public function test_help(): void {
$this->set_parameters(['-h']);
$cli = new create();
$output = $this->execute($cli);
self::assertContains('-h', $output);
self::assertContains('--help', $output);
self::assertStringContainsString('-h', $output);
self::assertStringContainsString('--help', $output);
}
/**
* Tests the cli_exception.
*/
public function test_exception() {
public function test_exception(): void {
$this->set_expected_cli_exception(cli_exception::ERROR_UNDEFINED);
throw new cli_exception('An CLI exception.');
}
@@ -96,11 +96,11 @@ class cli_test extends auth_outage_cli_testcase {
/**
* Tests with the auth_outage plugin disabled.
*/
public function test_authdisabled() {
public function test_authdisabled(): void {
// Disable all auth plugins.
set_config('auth', '');
\core\session\manager::gc(); // Remove stale sessions.
core_plugin_manager::reset_caches();
\core_plugin_manager::reset_caches();
// Try to create an CLI object.
$this->set_expected_cli_exception(cli_exception::ERROR_PLUGIN_CONFIGURATION);
new create();

View File

@@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* auth_outage_cli_testcase class.
* cli_testcase class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
@@ -23,20 +23,20 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\local\cli\clibase;
namespace auth_outage\local\cli;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../../base_testcase.php');
require_once(__DIR__ . '/../../base_testcase.php');
/**
* auth_outage_cli_testcase class.
* cli_testcase class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class auth_outage_cli_testcase extends auth_outage_base_testcase {
abstract class cli_testcase extends \auth_outage\base_testcase {
/**
* Always enable the auth outage plugin, resets after test and set no parameters.
*/
@@ -49,7 +49,7 @@ abstract class auth_outage_cli_testcase extends auth_outage_base_testcase {
// Enable auth plugins.
set_config('auth', 'outage');
\core\session\manager::gc(); // Remove stale sessions.
core_plugin_manager::reset_caches();
\core_plugin_manager::reset_caches();
$this->set_parameters([]);
parent::setUp();

View File

@@ -23,13 +23,13 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\dml\outagedb;
use auth_outage\local\cli\cli_exception;
use auth_outage\local\cli\create;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/cli_testcase.php');
require_once(__DIR__ . '/cli_testcase.php');
/**
* create_test test class.
@@ -38,12 +38,13 @@ require_once(__DIR__.'/cli_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\cli\create
*/
class create_test extends auth_outage_cli_testcase {
final class create_test extends cli_testcase {
/**
* Tests without any arguments.
*/
public function test_noarguments() {
public function test_noarguments(): void {
$cli = new create();
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_MISSING);
$this->execute($cli);
@@ -52,7 +53,7 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests when the start time is not a valid number.
*/
public function test_invalidparam_notanumber() {
public function test_invalidparam_notanumber(): void {
$cli = new create(['start' => 'some day']);
$cli->set_defaults([
'warn' => 50,
@@ -68,7 +69,7 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests when providing a negative start time.
*/
public function test_invalidparam_negative() {
public function test_invalidparam_negative(): void {
$cli = new create(['start' => -1]);
$cli->set_defaults([
'warn' => 50,
@@ -84,7 +85,7 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests providing an empty title.
*/
public function test_invalidparam_emptystring() {
public function test_invalidparam_emptystring(): void {
$cli = new create(['start' => 0, 'title' => '']);
$cli->set_defaults([
'warn' => 50,
@@ -100,7 +101,7 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests if not providing the title (it will be send as true).
*/
public function test_invalidparam_notastring() {
public function test_invalidparam_notastring(): void {
$cli = new create(['start' => 0, 'title' => true]);
$cli->set_defaults([
'warn' => 50,
@@ -116,18 +117,18 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests the help.
*/
public function test_help() {
public function test_help(): void {
$this->set_parameters(['--help']);
$cli = new create();
$output = $this->execute($cli);
self::assertContains('Creates', $output);
self::assertContains('--help', $output);
self::assertStringContainsString('Creates', $output);
self::assertStringContainsString('--help', $output);
}
/**
* Tests the options and shortcuts.
*/
public function test_options() {
public function test_options(): void {
$cli = new create();
$options = $cli->generate_options();
@@ -144,9 +145,8 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests creating with all given options.
*/
public function test_create_withoptions() {
public function test_create_withoptions(): void {
$this->set_parameters([
'--autostart=true',
'--warn=10',
'--start=0',
'--duration=30',
@@ -157,10 +157,10 @@ class create_test extends auth_outage_cli_testcase {
$cli = new create();
$cli->set_referencetime($now);
$text = $this->execute($cli);
self::assertContains('created', $text);
self::assertStringContainsString('created', $text);
// Check creted outage.
list(, $id) = explode(':', $text);
$id = (int)$id;
$clioutput = explode(':', $text);
$id = (int)end($clioutput);
$outage = outagedb::get_by_id($id);
self::assertSame($now, $outage->starttime);
self::assertSame(10, $outage->get_warning_duration());
@@ -173,10 +173,9 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests creating with the onlyid parameter.
*/
public function test_create_onlyid() {
public function test_create_onlyid(): void {
$this->set_parameters([
'--onlyid',
'--autostart=N',
'--warn=10',
'--start=0',
'--duration=30',
@@ -187,8 +186,10 @@ class create_test extends auth_outage_cli_testcase {
$cli = new create();
$cli->set_referencetime($now);
$id = $this->execute($cli);
// Check if the id contains is only a number (parameter onlyid).
$id = trim($id);
// Extracting only the id digits from the output.
preg_match('/(\d+)\s*$/', $id, $matches);
// Passing the proper id into the id variable.
$id = $matches ? $matches[1] : null;
self::assertTrue(is_number($id));
$id = (int)$id;
// Check creted outage.
@@ -204,7 +205,7 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests creating using some default values.
*/
public function test_create_withdefaults() {
public function test_create_withdefaults(): void {
$this->set_parameters([
'--warn=100',
'--start=50',
@@ -213,7 +214,6 @@ class create_test extends auth_outage_cli_testcase {
$cli = new create();
$cli->set_referencetime($now);
$cli->set_defaults([
'autostart' => false,
'warn' => 50,
'start' => 200,
'duration' => 300,
@@ -221,10 +221,10 @@ class create_test extends auth_outage_cli_testcase {
'description' => 'Default Description',
]);
$text = $this->execute($cli);
self::assertContains('created', $text);
self::assertStringContainsString('created', $text);
// Check creted outage.
list(, $id) = explode(':', $text);
$id = (int)$id;
$clioutput = explode(':', $text);
$id = (int)end($clioutput);
$outage = outagedb::get_by_id($id);
self::assertSame($now + 50, $outage->starttime, 'Wrong starttime.');
self::assertSame($outage->starttime - 100, $outage->warntime, 'Wrong warntime.');
@@ -237,12 +237,11 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests creating with clone.
*/
public function test_create_withclone() {
public function test_create_withclone(): void {
self::setAdminUser();
$now = time();
// Create the outage to clone.
$original = new outage([
'autostart' => false,
'warntime' => $now - 120,
'starttime' => $now,
'stoptime' => $now + 120,
@@ -254,14 +253,17 @@ class create_test extends auth_outage_cli_testcase {
$this->set_parameters([
'--onlyid',
'--start=60',
'--clone='.$id,
'--clone=' . $id,
]);
$cli = new create();
$cli->set_referencetime($now);
$id = trim($this->execute($cli));
// Extracting only the id digits from the output.
preg_match('/(\d+)\s*$/', $id, $matches);
// Passing the proper id into the id variable.
$id = $matches ? (int)$matches[1] : null;
// Check cloned data.
$cloned = outagedb::get_by_id((int)$id);
self::assertSame($now + 60, $cloned->starttime);
self::assertSame($now, $cloned->starttime);
self::assertSame($original->get_warning_duration(), $cloned->get_warning_duration());
self::assertSame($original->get_duration_planned(), $cloned->get_duration_planned());
self::assertSame($original->title, $cloned->title);
@@ -271,7 +273,7 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests creating with an invalid clone id.
*/
public function test_create_withclone_invalid() {
public function test_create_withclone_invalid(): void {
$this->set_parameters([
'--start=60',
'--clone=-1',
@@ -284,10 +286,9 @@ class create_test extends auth_outage_cli_testcase {
/**
* Tests creating with the block flag.
*/
public function test_create_withblock() {
public function test_create_withblock(): void {
// Not an extensive test in the blocking API, cliwaitforit tests should cover them deeper.
$this->set_parameters([
'--autostart=N',
'--block',
'--warn=60',
'--start=0',
@@ -299,33 +300,16 @@ class create_test extends auth_outage_cli_testcase {
$cli = new create();
$cli->set_referencetime($now);
$text = $this->execute($cli);
self::assertContains('created', $text);
self::assertContains('started', $text);
self::assertStringContainsString('created', $text);
self::assertStringContainsString('started', $text);
}
/**
* Tests providing an invalid option as default.
*/
public function test_setdefaults_extra() {
public function test_setdefaults_extra(): void {
$cli = new create([]);
$this->set_expected_exception('coding_exception');
$cli->set_defaults(['aninvalidparameter' => 'value']);
}
/**
* Tests with an invalud autostart bool value.
*/
public function test_invalid_bool() {
$this->set_parameters([
'--autostart=maybe',
'--warn=60',
'--start=0',
'--duration=600',
'--title=Title',
'--description=Description',
]);
$cli = new create();
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_INVALID);
$cli->execute();
}
}

View File

@@ -23,13 +23,13 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\dml\outagedb;
use auth_outage\local\cli\cli_exception;
use auth_outage\local\cli\finish;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/cli_testcase.php');
require_once(__DIR__ . '/cli_testcase.php');
/**
* finish_test test class.
@@ -38,12 +38,13 @@ require_once(__DIR__.'/cli_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\cli\finish
*/
class finish_test extends auth_outage_cli_testcase {
final class finish_test extends cli_testcase {
/**
* Tests the constructor.
*/
public function test_constructor() {
public function test_constructor(): void {
$cli = new finish();
self::assertNotNull($cli);
}
@@ -51,7 +52,7 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests the generated options and shortcuts.
*/
public function test_options() {
public function test_options(): void {
$cli = new finish();
$options = $cli->generate_options();
@@ -68,18 +69,18 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests the help.
*/
public function test_help() {
public function test_help(): void {
$this->set_parameters(['--help']);
$cli = new finish();
$text = $this->execute($cli);
self::assertContains('Finishes', $text);
self::assertContains('--help', $text);
self::assertStringContainsString('Finishes', $text);
self::assertStringContainsString('--help', $text);
}
/**
* Tests if running without arguments.
*/
public function test_noarguments() {
public function test_noarguments(): void {
$cli = new finish();
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_MISSING);
$this->execute($cli);
@@ -88,18 +89,17 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests finishing an already ended outage.
*/
public function test_endedoutage() {
public function test_endedoutage(): void {
self::setAdminUser();
$now = time();
$id = outagedb::save(new outage([
'autostart' => false,
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now - 50,
'title' => 'Title',
'description' => 'Description',
]));
$this->set_parameters(['-id='.$id]);
$this->set_parameters(['-id=' . $id]);
$cli = new finish();
$cli->set_referencetime($now);
$this->set_expected_cli_exception(cli_exception::ERROR_OUTAGE_INVALID);
@@ -109,18 +109,17 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests finishing an outage.
*/
public function test_finish() {
public function test_finish(): void {
self::setAdminUser();
$now = time();
$id = outagedb::save(new outage([
'autostart' => false,
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now + 100,
'title' => 'Title',
'description' => 'Description',
]));
$this->set_parameters(['-id='.$id]);
$this->set_parameters(['-id=' . $id]);
$cli = new finish();
$cli->set_referencetime($now);
$this->execute($cli);
@@ -129,7 +128,7 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests finishing an active outage when it does not exists.
*/
public function test_activenotfound() {
public function test_activenotfound(): void {
self::setAdminUser();
$this->set_parameters(['-a']);
$cli = new finish();
@@ -140,7 +139,7 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests providing an invalid outage id.
*/
public function test_invalidid() {
public function test_invalidid(): void {
self::setAdminUser();
$this->set_parameters(['-id=theid']);
$cli = new finish();
@@ -151,7 +150,7 @@ class finish_test extends auth_outage_cli_testcase {
/**
* Tests when the outage is not found.
*/
public function test_idnotfound() {
public function test_idnotfound(): void {
self::setAdminUser();
$this->set_parameters(['-id=99999']);
$cli = new finish();

View File

@@ -23,13 +23,13 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\cli;
use auth_outage\dml\outagedb;
use auth_outage\local\cli\cli_exception;
use auth_outage\local\cli\waitforit;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/cli_testcase.php');
require_once(__DIR__ . '/cli_testcase.php');
/**
* waitforit_test test class.
@@ -38,12 +38,13 @@ require_once(__DIR__.'/cli_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\cli\waitforit
*/
class waitforit_test extends auth_outage_cli_testcase {
final class waitforit_test extends cli_testcase {
/**
* Tests the constructor.
*/
public function test_constructor() {
public function test_constructor(): void {
$cli = new waitforit();
self::assertNotNull($cli);
}
@@ -51,7 +52,7 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests the generated options.
*/
public function test_generateoptions() {
public function test_generateoptions(): void {
$cli = new waitforit();
$options = $cli->generate_options();
foreach (array_keys($options) as $k) {
@@ -62,7 +63,7 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests the generated shortcut options.
*/
public function test_generateshortcuts() {
public function test_generateshortcuts(): void {
$cli = new waitforit();
$options = $cli->generate_options();
$shorts = $cli->generate_shortcuts();
@@ -74,18 +75,18 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests if help works.
*/
public function test_help() {
public function test_help(): void {
$this->set_parameters(['--help']);
$cli = new waitforit();
$text = $this->execute($cli);
self::assertContains('Waits', $text);
self::assertContains('--help', $text);
self::assertStringContainsString('Waits', $text);
self::assertStringContainsString('--help', $text);
}
/**
* Checks if providing an outageid and active parameter.
*/
public function test_bothparams() {
public function test_bothparams(): void {
$this->set_parameters(['--outageid=1', '--active']);
$cli = new waitforit();
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_INVALID);
@@ -95,7 +96,7 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests with an invalid outage id
*/
public function test_invalidoutageid() {
public function test_invalidoutageid(): void {
$this->set_parameters(['-id=-1']);
$cli = new waitforit();
$this->set_expected_cli_exception(cli_exception::ERROR_PARAMETER_INVALID);
@@ -105,7 +106,7 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests with an active outage when it does not exists.
*/
public function test_outagenotfound() {
public function test_outagenotfound(): void {
$this->set_parameters(['-a']);
$cli = new waitforit();
$this->set_expected_cli_exception(cli_exception::ERROR_OUTAGE_NOT_FOUND);
@@ -115,18 +116,17 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests with an outage that already ended.
*/
public function test_endedoutage() {
public function test_endedoutage(): void {
self::setAdminUser();
$now = time();
$id = outagedb::save(new outage([
'autostart' => false,
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now - 50,
'title' => 'Title',
'description' => 'Description',
]));
$this->set_parameters(['-id='.$id]);
$this->set_parameters(['-id=' . $id]);
$cli = new waitforit();
$cli->set_referencetime($now);
$this->set_expected_cli_exception(cli_exception::ERROR_OUTAGE_INVALID);
@@ -136,11 +136,10 @@ class waitforit_test extends auth_outage_cli_testcase {
/**
* Tests waiting for an existing active outage, verbose mode.
*/
public function test_activeverbose() {
public function test_activeverbose(): void {
self::setAdminUser();
$now = time();
outagedb::save(new outage([
'autostart' => false,
'warntime' => $now - 10,
'starttime' => $now + 1,
'stoptime' => $now + 10,
@@ -151,9 +150,9 @@ class waitforit_test extends auth_outage_cli_testcase {
$cli = new waitforit();
$cli->set_referencetime($now);
$output = $this->execute($cli);
self::assertContains('Verbose mode', $output);
self::assertContains('starting in 1 sec', $output);
self::assertContains('started', $output);
self::assertStringContainsString('Verbose mode', $output);
self::assertStringContainsString('starting in 1 sec', $output);
self::assertStringContainsString('started', $output);
}
/**
@@ -163,7 +162,6 @@ class waitforit_test extends auth_outage_cli_testcase {
self::setAdminUser();
$now = time();
outagedb::save(new outage([
'autostart' => false,
'warntime' => $now,
'starttime' => $now + 45,
'stoptime' => $now + (60 * 60),
@@ -178,11 +176,11 @@ class waitforit_test extends auth_outage_cli_testcase {
return $now;
});
$output = $this->execute($cli);
self::assertContains("starting in 45", $output);
self::assertContains("sleep 30 second", $output);
self::assertContains("starting in 15", $output);
self::assertContains("sleep 15 second", $output);
self::assertContains("started!", $output);
self::assertStringContainsString("starting in 45", $output);
self::assertStringContainsString("sleep 30 second", $output);
self::assertStringContainsString("starting in 15", $output);
self::assertStringContainsString("sleep 15 second", $output);
self::assertStringContainsString("started!", $output);
}
/**
@@ -192,7 +190,6 @@ class waitforit_test extends auth_outage_cli_testcase {
self::setAdminUser();
$now = time();
$id = outagedb::save(new outage([
'autostart' => false,
'warntime' => $now,
'starttime' => $now + (2 * 60 * 60),
'stoptime' => $now + (60 * 60),

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,7 @@
a {
font-size: 200%;
}
div {
background-image: url('/moodle/auth/outage/tests/local/controllers/fixtures/catalyst.png');
}

View File

@@ -0,0 +1,7 @@
a {
font-size: 200%;
}
div {
background-image: url(/moodle/auth/outage/tests/local/controllers/fixtures/catalyst.png);
}

View File

@@ -0,0 +1,168 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests performed on infopage controller class and update_static_page task class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\controllers;
use auth_outage\local\outage;
use context_system;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../base_testcase.php');
/**
* Tests performed on infopage controller class and update_static_page task class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\controllers\infopage
*/
final class infopage_test extends \auth_outage\base_testcase {
/**
* Tests the constructor.
*/
public function test_constructor(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
new infopage();
}
/**
* Tests the constructor with given parameters.
*/
public function test_constructor_withparams(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
$_GET = ['id' => 1, 'static' => 'true'];
new infopage();
}
/**
* Tests the constructor with different id and outage id.
*/
public function test_constructor_idmismatch(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
$outage = $this->get_dummy_outage();
$this->set_expected_exception('coding_exception', 'Provided id and outage->id do not match. (2/1)');
new infopage(['id' => 2, 'outage' => $outage]);
}
/**
* Tests the constructor with an invalid outage.
*/
public function test_constructor_invalidoutage(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
$this->set_expected_exception('coding_exception', 'Provided outage is not a valid outage object. (My outage)');
new infopage(['outage' => 'My outage']);
}
/**
* Checks the output of the info page.
*/
public function test_output(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
$outage = $this->get_dummy_outage();
$info = new infopage(['outage' => $outage]);
$output = $info->get_output();
self::assertStringContainsString('auth_outage_info', $output);
}
/**
* Checks the output of the info page.
*/
public function test_output_without_permission(): void {
$this->revoke_info_page_permissions();
$this->assertFalse(has_capability('auth/outage:viewinfo', context_system::instance()));
$outage = $this->get_dummy_outage();
$info = new infopage(['outage' => $outage]);
$this->set_expected_exception('moodle_exception', 'Unsupported redirect detected, script execution terminated');
$output = $info->get_output();
}
/**
* Checks the output of the info page.
*/
public function test_output_without_permission_but_static(): void {
$this->revoke_info_page_permissions();
$this->assertFalse(has_capability('auth/outage:viewinfo', context_system::instance()));
$outage = $this->get_dummy_outage();
$info = new infopage(['outage' => $outage, 'static' => true]);
$output = $info->get_output();
self::assertStringContainsString('auth_outage_info', $output);
}
/**
* Checks the output of the info page.
*/
public function test_output_with_forcelogin(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
set_config('forcelogin', true);
$outage = $this->get_dummy_outage();
$info = new infopage(['outage' => $outage]);
$this->set_expected_exception('moodle_exception', 'Unsupported redirect detected, script execution terminated');
$info->get_output();
}
/**
* Checks the output of the info page.
*/
public function test_output_with_forcelogin_if_static(): void {
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
set_config('forcelogin', true);
$outage = $this->get_dummy_outage();
$info = new infopage(['outage' => $outage, 'static' => true]);
$output = $info->get_output();
self::assertStringContainsString('auth_outage_info', $output);
}
/**
* Tests the constructor enables SVG support.
*/
public function test_svgicons_is_true(): void {
global $CFG;
$this->assertTrue(has_capability('auth/outage:viewinfo', context_system::instance()));
$CFG->svgicons = false;
new infopage();
self::assertTrue($CFG->svgicons);
}
}

View File

@@ -0,0 +1,641 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* maintenance_static_page_test task class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace auth_outage\local\controllers;
use auth_outage\task\update_static_page;
use DOMDocument;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../base_testcase.php');
/**
* maintenance_static_page_test class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\controllers\maintenance_static_page_generator
*/
final class maintenance_static_page_test extends \auth_outage\base_testcase {
/**
* Test template file.
*/
public function test_templatefile(): void {
global $CFG;
$page = maintenance_static_page::create_from_html('<html></html>');
self::assertSame($CFG->dataroot . '/climaintenance.template.html', $page->get_io()->get_template_file());
$page->get_io()->set_preview(true);
self::assertSame(
$CFG->dataroot . '/auth_outage/climaintenance/preview/climaintenance.html',
$page->get_io()->get_template_file()
);
}
/**
* Test resources folder.
*/
public function test_resourcesfolder(): void {
global $CFG;
$page = maintenance_static_page::create_from_html('<html></html>');
self::assertSame($CFG->dataroot . '/auth_outage/climaintenance', $page->get_io()->get_resources_folder());
$page->get_io()->set_preview(true);
self::assertSame($CFG->dataroot . '/auth_outage/climaintenance/preview', $page->get_io()->get_resources_folder());
}
/**
* Test create from outage.
*/
public function test_createfromoutage(): void {
// How to fetch a page from PHPUnit environment?
}
/**
* Test create from HTML.
*/
public function test_createfromhtml(): void {
$html = "<!DOCTYPE html>\n<html><head><title>Title</title></head><body>Content</body></html>";
$expected = "<!DOCTYPE html>\n<html><head><title>Title</title><meta http-equiv=\"refresh\" content=\"300\">" .
"</head><body>Content</body></html>";
self::assertSame($expected, $this->generated_page_html($html));
}
/**
* Test remove script tags.
*/
public function test_removescripttags(): void {
$html = "<!DOCTYPE html>\n" .
'<html><head><script type="text/javascript" src="http://xyz"></script><title>Title</title></head>' .
'<body>Content<script> a < 5; x > 3</script></body></html>';
maintenance_static_page::create_from_html($html)->generate();
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString('<script', $generated);
}
/**
* Test remove script tags.
*/
public function test_updatelinkstylesheet(): void {
$localcsslink = $this->get_fixture_path_location('simple.css');
$externalcsslink = 'http://google.com/coolstuff.css';
$html = "<!DOCTYPE html>\n" .
'<html><head><link href="' . $localcsslink . '" rel="stylesheet" /><title>Title</title></head>' .
'<body>Content<link rel="stylesheet" href="' . $externalcsslink . '"></body></html>';
$generated = $this->generated_page_html($html);
self::assertStringContainsString('www.example.com/moodle/auth/outage/file.php?file=', $generated);
self::assertStringNotContainsString($localcsslink, $generated);
self::assertStringContainsString($externalcsslink, $generated);
}
/**
* Test update link style sheet urls.
*/
public function test_updatelinkstylesheet_urls(): void {
$localcsslink = $this->get_fixture_path_location('withurls.css');
$html = "<!DOCTYPE html>\n" .
'<html><head><link href="' . $localcsslink . '" rel="stylesheet" /><title>Title</title></head>' .
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// Check for css file.
self::assertFileExists($page->get_io()->get_resources_folder() .
'/53365950336b070c0b26ca50e7d0dad962c364e6.dGV4dC9wbGFpbg');
// Check for catalyst.png file referenced in url(..) of css.
self::assertFileExists($page->get_io()->get_resources_folder() .
'/ff7f7f87a26a908fc72930eaefb6b57306361d16.aW1hZ2UvcG5n');
}
/**
* Test update link style sheet urls quoted.
*/
public function test_updatelinkstylesheet_urls_quoted(): void {
$localcsslink = $this->get_fixture_path_location('withurls-quoted.css');
$html = "<!DOCTYPE html>\n" .
'<html><head><link href="' . $localcsslink . '" rel="stylesheet" /><title>Title</title></head>' .
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// Check for css file.
self::assertFileExists($page->get_io()->get_resources_folder()
. '/e0b34925c1f939c247a4b50d6bf08c76088def39.dGV4dC9wbGFpbg');
// Check for catalyst.png file referenced in url(..) of css.
self::assertFileExists($page->get_io()->get_resources_folder()
. '/ff7f7f87a26a908fc72930eaefb6b57306361d16.aW1hZ2UvcG5n');
}
/**
* Test update link style sheet urls with sub dir.
*/
public function test_updatelinkstylesheet_urls_subdir(): void {
$localcsslink = $this->get_fixture_path_location('subdir/withurls-subdir.css');
$html = "<!DOCTYPE html>\n" .
'<html><head><link href="' . $localcsslink . '" rel="stylesheet" /><title>Title</title></head>' .
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// Check for css file.
self::assertFileExists($page->get_io()->get_resources_folder() .
'/beb44281e23b9d872056bf0230cea34535e8cdea.dGV4dC9wbGFpbg');
// Check for file referenced in url(..) of css.
self::assertFileExists($page->get_io()->get_resources_folder() .
'/a02a8a442fa82d5205ffb24722d9df7f35161f56.dGV4dC9wbGFpbg');
}
/**
* Test update images to file.php style link.
*/
public function test_updateimages(): void {
$localimglink = $this->get_fixture_path_location('catalyst.png');
$externalimglink = 'http://google.com/coolstyle.css';
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body><img src="' . $localimglink . '">Content<img src="' . $externalimglink . '" /></body></html>';
$generated = $this->generated_page_html($html);
self::assertStringContainsString('www.example.com/moodle/auth/outage/file.php?file=', $generated);
self::assertStringNotContainsString($localimglink, $generated);
self::assertStringContainsString($externalimglink, $generated);
}
/**
* Test update favicon to file.php style link.
*/
public function test_updatelinkfavicon(): void {
$link = $this->get_fixture_path_location('catalyst.png');
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title><link rel="shortcut icon" href="' . $link . '""></head>' .
'<body>Content</body></html>';
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString($link, $generated);
self::assertStringContainsString('www.example.com/moodle/auth/outage/file.php?file=', $generated);
}
/**
* Data provider for test_update_inline_background_images
* @return array
*/
public static function update_inline_background_images_provider(): array {
return [
// Empty string.
["", false],
// URLs that should be retrieved.
["color: #FF00FF; background: lightblue url(/pluginfile.php/1/theme_custom/banner/251298630/0001.png) no-repeat", true],
["background: lightblue
url(https://www.example.com/moodle/pluginfile.php/1/theme_custom/banner/251298630/0001.png) no-repeat", true],
["background:url('https://www.example.com/moodle/pluginfile.php/1/theme_custom/banner/251298630/0001.png')", true],
["background-image : url( /pix/help.png);", true],
["background-image: url ('/pix/help.png')", true],
// URLs that should not be retrieved.
["background-image:url(data:image/gif;base64,R0lGODlhYADIAP=)", false],
["background-image:url('data:image/gif;base64,R0lGODlhYADIAP=')", false],
];
}
/**
* Tests update_inline_background_images() method to update the background images.
*
* @dataProvider update_inline_background_images_provider
* @param string $stylecontent Content of the style to test
* @param bool $rewrite Flag if URL should be rewritten
* @throws coding_exception
*/
public function test_update_inline_background_images($stylecontent, $rewrite): void {
global $CFG;
$this->resetAfterTest(true);
$generator = new maintenance_static_page_generator(new DOMDocument(), new maintenance_static_page_io());
$html = '<!DOCTYPE html>\n' .
'<html><head><title>Title</title></head>' .
'<body><div style="' . $stylecontent . '">Content</div></body></html>';
// Temporarily disable debugging to prevent errors because file does not exist.
$debuglevel = $CFG->debug;
$CFG->debug = '';
$generated = $this->generated_page_html($html);
// Restore debugging level.
$CFG->debug = $debuglevel;
$matches = $generator->get_url_from_inline_style($stylecontent);
if ($rewrite) {
self::assertStringNotContainsString($matches[1], $generated);
self::assertStringContainsString('www.example.com/moodle/auth/outage/file.php?file=', $generated);
self::assertIsArray($matches);
} else {
self::assertStringContainsString($stylecontent, $generated);
}
}
/**
* Test update preview path to file.php style link.
*/
public function test_previewpath(): void {
$link = $this->get_fixture_path_location('catalyst.png');
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title><link rel="shortcut icon" href="' . $link . '""></head>' .
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->get_io()->set_preview(true);
$page->generate();
$generated = trim(file_get_contents($page->get_io()->get_template_file()));
self::assertStringNotContainsString($link, $generated);
self::assertStringContainsString('www.example.com/moodle/auth/outage/file.php?file=preview%2F', $generated);
}
/**
* Generates the maintenance page (not using preview mode).
*
* @param string $html Input HTML.
*
* @return string Output HTML.
*/
private function generated_page_html($html) {
$page = maintenance_static_page::create_from_html($html);
$page->generate();
$generated = trim(file_get_contents($page->get_io()->get_template_file()));
return $generated;
}
/**
* Checks if we can create and execute a task to update outage pages.
*/
public function test_tasks(): void {
$this->resetAfterTest(true);
$task = new update_static_page();
self::assertNotEmpty($task->get_name());
$task->execute();
}
/**
* Tests updating the static page when there is no outage but the file existed before.
*/
public function test_updatestaticpage_hasfile(): void {
global $CFG;
$file = $CFG->dataroot . '/climaintenance.template.html';
touch($file);
self::assertFileExists($file);
maintenance_static_page::create_from_outage(null)->generate();
// Backwards compatibility with older PHPUnit - use old assertFile method.
if (method_exists($this, 'assertFileDoesNotExist')) {
self::assertFileDoesNotExist($file);
} else {
self::assertFileNotExists($file);
}
}
/**
* Tests created file.
*/
public function test_createdfile(): void {
global $CFG;
$link = $this->get_fixture_path_location('catalyst.png');
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>Content<img src="' . $link . '" /></body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// This checks if content is correct and mime type is correct from the encoded name.
$file = $page->get_io()->get_resources_folder() . '/ff7f7f87a26a908fc72930eaefb6b57306361d16.aW1hZ2UvcG5n';
self::assertFileExists($file);
// We can still assert the contents really match, not just the hash.
$found = file_get_contents($file);
$expected = file_get_contents(__DIR__ . '/fixtures/catalyst.png');
self::assertSame($found, $expected);
}
/**
* Gets a fixture file for this test case.
*
* @param string $file file name
*
* @return string
*/
private function get_fixture_path_location($file) {
return (string)new \moodle_url('/auth/outage/tests/local/controllers/fixtures/' . $file);
}
/**
* Test saving empty string for template file.
*/
public function test_invalid_string_saving_template_empty(): void {
$io = new maintenance_static_page_io();
$this->set_expected_exception('coding_exception');
$io->save_template_file('');
}
/**
* Test saving non string for template file.
*/
public function test_invalid_string_saving_template_nostring(): void {
$io = new maintenance_static_page_io();
$this->set_expected_exception('coding_exception');
$io->save_template_file(50);
}
/**
* Test get url for file.
*/
public function test_get_url_for_file(): void {
$io = new maintenance_static_page_io();
self::assertStringContainsString(
'www.example.com/moodle/auth/outage/file.php?file=img.png',
$io->get_url_for_file('img.png')
);
}
/**
* Return array of url data provider and true or false.
*/
public static function is_url_dataprovider(): array {
return [
[true, 'http://catalyst.net.nz'],
[true, 'https://www.catalyst-au.net/'],
[false, '/homepage'],
[false, 'file://homepage'],
[true, '//catalyst-au.net/img/test.jpg'],
[false, '://www.catalyst-au.net/img/test.jpg'],
];
}
/**
* Test if it is url
* @dataProvider is_url_dataprovider
* @param string $result expected result
* @param string $url url to be checked
*/
public function test_is_url($result, $url): void {
self::assertEquals($result, maintenance_static_page_io::is_url($url));
}
/**
* Test file get_data.
*/
public function test_file_get_data(): void {
$file = __DIR__ . '/fixtures/catalyst.png';
$found = maintenance_static_page_io::file_get_data($file);
self::assertSame(file_get_contents($file), $found['contents']);
self::assertSame('image/png', $found['mime']);
}
/**
* Test invalid file get_data.
*/
public function test_file_get_data_invalidfile(): void {
$found = maintenance_static_page_io::file_get_data(__DIR__ . '/fixtures/invalidfile');
self::assertSame('', $found['contents']);
self::assertSame('unknown', $found['mime']);
self::assertCount(1, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**
* Test invalid file get_data.
*/
public function test_file_get_data_invalidfilename(): void {
$this->set_expected_exception('coding_exception');
maintenance_static_page_io::file_get_data(200);
}
/**
* Test file_get_data with curlsecurityblockedhosts.
* We will use an external URL to test passing ignoresecurity inside of file_get_data works,
* ideally in real code we should only be calling file_get_data with internal URLs.
*/
public function test_file_get_data_curlsecurityblockedhosts(): void {
global $CFG, $USER;
$testhtml = $this->getExternalTestFileUrl('/test.html');
$url = new \moodle_url($testhtml);
$host = $url->get_host();
set_config('curlsecurityblockedhosts', $host); // Blocks $host.
// Test a regular curl with the default security enabled does in fact get blocked.
$curl = new \curl();
$contents = $curl->get($testhtml);
$expected = $curl->get_security()->get_blocked_url_string();
self::assertSame($expected, $contents);
self::assertSame(0, $curl->get_errno());
if ($CFG->branch >= 403) {
self::assertDebuggingCalled(
"Blocked $testhtml: The URL is blocked. [user {$USER->id}]",
DEBUG_NONE
);
}
// Test file_get_data does return the page and isn't blocked by security.
$found = maintenance_static_page_io::file_get_data($url->out());
$expected = '47250a973d1b88d9445f94db4ef2c97a';
self::assertSame($expected, md5($found['contents']));
self::assertSame('text/html', $found['mime']);
}
/**
* Test remove css selector.
*/
public function test_remove_css_selector(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>Content<b class="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '.removeme', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString('removeme', $generated);
self::assertStringNotContainsString('Goodbye cruel world', $generated);
}
/**
* Test remove css selector id.
*/
public function test_remove_css_selector_id(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#removeme', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString('removeme', $generated);
self::assertStringNotContainsString('Goodbye cruel world', $generated);
}
/**
* Test remove css selector with multi lines.
*/
public function test_remove_css_selector_with_multiline(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>' .
'<b class="deleteme">Goodbye cruel world.</b>' .
'<b class="removeme">Goodbye cruel world.</b>' .
'</body></html>';
set_config('remove_selectors', ".removeme\n.deleteme", 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString('removeme', $generated);
self::assertStringNotContainsString('deleteme', $generated);
self::assertStringNotContainsString('Goodbye cruel world', $generated);
}
/**
* Test remove css selector needs trim.
*/
public function test_remove_css_selector_needing_trim(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>' .
'<b class="deleteme">Goodbye cruel world.</b>' .
'<b class="removeme">Goodbye cruel world.</b>' .
'</body></html>';
set_config('remove_selectors', " .removeme \n .deleteme ", 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString('removeme', $generated);
self::assertStringNotContainsString('deleteme', $generated);
self::assertStringNotContainsString('Goodbye cruel world', $generated);
}
/**
* Test remove css selector with empty line.
*/
public function test_remove_css_selector_with_empty_line(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>' .
'<b class="deleteme">Goodbye cruel world.</b>' .
'<b class="removeme">Goodbye cruel world.</b>' .
'</body></html>';
set_config('remove_selectors', "\n\n.removeme\n\n\n\n.deleteme\n\n", 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringNotContainsString('removeme', $generated);
self::assertStringNotContainsString('deleteme', $generated);
self::assertStringNotContainsString('Goodbye cruel world', $generated);
}
/**
* Test remove css selector with invalid id.
*/
public function test_remove_css_selector_with_invalid_id(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#invalidid', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringContainsString('removeme', $generated);
self::assertStringContainsString('Goodbye cruel world', $generated);
}
/**
* Test meta refresh 5 minutes.
*/
public function test_meta_refresh_5minutes(): void {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#invalidid', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertStringContainsString('<meta http-equiv="refresh" content="300">', $generated);
}
/**
* Test meta refresh maximum 5 minutes.
*/
public function test_meta_refresh_maximum_5seconds() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n" .
'<html><head><title>Title</title></head>' .
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#invalidid', 'auth_outage');
$page = maintenance_static_page::create_from_html($html);
$page->set_max_refresh_time(5);
$page->generate();
$generated = trim(file_get_contents($page->get_io()->get_template_file()));
return $generated;
self::assertStringContainsString('<meta http-equiv="refresh" content="5">', $generated);
}
/**
* Data provider for test_get_urls_from_stylesheet
* @return array
*/
public static function get_urls_from_stylesheet_provider(): array {
return [
// Empty string.
["", 0],
// URLs that should be retrieved.
["background:url(/theme/image.php/_s/boost/core/1581292565/t/expanded)", 1],
["background:url('/theme/image.php/_s/boost/core/1581292565/t/expanded')", 1],
["src:url(\"/theme/font.php/boost/core/1581292565/fontawesome-webfont.eot?#iefix&v=4.7.0\")", 1],
["background-image:url(pix/vline-rtl.gif)", 1],
// URLs that should not be retrieved.
["background-image:url(data:image/gif;base64,R0lGODlhYADIAP=)", 0],
["background-image:url('data:image/gif;base64,R0lGODlhYADIAP=')", 0],
["background-image:url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\'\")", 0],
// Combination of URLs used above.
["background-image:url(pix/vline-rtl.gif) background:url(/theme/image.php/_s/boost/core/158/t/expanded)", 2],
["background-image:url(data:image/gif;base64,R0lG=)src:url(\"/theme/font.php/fontawesome-webfont.eot\")", 1],
];
}
/**
* Tests get_urls_from_stylesheet() method to get all appropriate URLS from the file.
*
* @dataProvider get_urls_from_stylesheet_provider
* @param string $filecontent Content of the file
* @param int $count Expected quantity of found URLs
* @throws coding_exception
*/
public function test_get_urls_from_stylesheet($filecontent, $count): void {
$this->resetAfterTest(true);
$generator = new maintenance_static_page_generator(new DOMDocument(), new maintenance_static_page_io());
$matches = $generator->get_urls_from_stylesheet($filecontent);
self::assertIsArray($matches);
self::assertCount(2, $matches);
self::assertCount($count, $matches[1]);
}
}

View File

@@ -14,19 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outage_test test class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\local\outage;
namespace auth_outage\local;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../base_testcase.php');
require_once(__DIR__ . '/../base_testcase.php');
/**
* outage_test test class.
@@ -35,12 +26,13 @@ require_once(__DIR__.'/../base_testcase.php');
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\outage
*/
class outage_test extends auth_outage_base_testcase {
final class outage_test extends \auth_outage\base_testcase {
/**
* Tests the constructor.
*/
public function test_constructor() {
public function test_constructor(): void {
$outage = new outage();
// Very important, this should never change.
self::assertNull($outage->id, 'New empty outage can never have an id set.');
@@ -53,10 +45,9 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests the constructor, giving data as an object.
*/
public function test_constructor_object() {
$obj = new stdClass();
public function test_constructor_object(): void {
$obj = new \stdClass();
$obj->id = 1;
$obj->autostart = true;
$obj->warntime = 2;
$obj->starttime = 3;
$obj->finished = 4;
@@ -65,7 +56,6 @@ class outage_test extends auth_outage_base_testcase {
$obj->description = 'Description';
$outage = new outage($obj);
self::assertSame($obj->id, $outage->id);
self::assertSame($obj->autostart, $outage->autostart);
self::assertSame($obj->warntime, $outage->warntime);
self::assertSame($obj->starttime, $outage->starttime);
self::assertSame($obj->finished, $outage->finished);
@@ -77,7 +67,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests the constructor with invalid data.
*/
public function test_constructor_invalid() {
public function test_constructor_invalid(): void {
$this->set_expected_exception('coding_exception');
new outage('My outage');
}
@@ -85,7 +75,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests getting the stage considering the current time (now).
*/
public function test_getstage_now() {
public function test_getstage_now(): void {
$now = time();
// Make sure it is in the past.
$outage = new outage([
@@ -101,7 +91,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests getting the stage providing an invalid time reference.
*/
public function test_getstage_invalidtime() {
public function test_getstage_invalidtime(): void {
$outage = new outage();
$this->set_expected_exception('coding_exception');
$outage->get_stage(-1);
@@ -110,7 +100,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests is_ongoing() with different outage stages.
*/
public function test_isongoing() {
public function test_isongoing(): void {
$now = time();
// In the past.
@@ -147,7 +137,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests is_active() with different outage stages.
*/
public function test_isactive() {
public function test_isactive(): void {
$now = time();
// In the past.
@@ -194,7 +184,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests different outage stages.
*/
public function test_stages() {
public function test_stages(): void {
$now = time();
$outage = new outage([
@@ -269,7 +259,7 @@ class outage_test extends auth_outage_base_testcase {
/**
* Tests if getting title and description replaces the placeholders.
*/
public function test_gettitle_getdescription() {
public function test_gettitle_getdescription(): void {
$now = time();
$outage = new outage([
'warntime' => $now - 50,
@@ -280,17 +270,17 @@ class outage_test extends auth_outage_base_testcase {
'description' => 'Description {{start}} {{stop}} {{duration}}',
]);
$title = $outage->get_title();
self::assertNotContains('{', $title);
self::assertNotContains('}', $title);
self::assertStringNotContainsString('{', $title);
self::assertStringNotContainsString('}', $title);
$description = $outage->get_description();
self::assertNotContains('{', $description);
self::assertNotContains('}', $description);
self::assertStringNotContainsString('{', $description);
self::assertStringNotContainsString('}', $description);
}
/**
* Tests getting the durations.
*/
public function test_getdurations() {
public function test_getdurations(): void {
$outage = new outage(['starttime' => 1000]);
self::assertNull($outage->get_duration_actual());

View File

@@ -0,0 +1,720 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace auth_outage\local;
use auth_outage\dml\outagedb;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/adminlib.php');
require_once(__DIR__ . '/../base_testcase.php');
/**
* outagelib_test test class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \auth_outage\local\outagelib
*/
final class outagelib_test extends \auth_outage\base_testcase {
/**
* Check outagelib::inject() works as expected.
*/
public function test_inject(): void {
global $OUTPUT;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
ob_start();
$outage->id = outagedb::save($outage);
$text = trim(ob_get_contents());
ob_end_clean();
outagelib::reset_injectcalled();
// Get full header to avoid interactions with other single inject plugins.
$header1 = $OUTPUT->standard_top_of_body_html();
self::assertStringContainsString('<style>', $header1);
self::assertStringContainsString('<script>', $header1);
// Should not inject more than once.
$size = strlen($OUTPUT->standard_top_of_body_html());
self::assertSame($size, strlen($OUTPUT->standard_top_of_body_html()));
// Check styles aren't reinjected.
self::assertStringNotContainsString('<style>', $OUTPUT->standard_top_of_body_html());
}
/**
* Check outagelib::inject() will not break the page if something goes wrong.
*/
public function test_inject_broken(): void {
$_GET = ['auth_outage_break_code' => '1'];
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertCount(2, $this->getDebuggingMessages());
$this->resetDebugging();
}
/**
* Check if injection works with preview.
*/
public function test_inject_preview(): void {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
ob_start();
$outage->id = outagedb::save($outage);
$text = trim(ob_get_contents());
ob_end_clean();
$_GET = ['auth_outage_preview' => (string)$outage->id];
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertStringContainsString('<style>', $header);
self::assertStringContainsString('<script>', $header);
}
/**
* Check if injection works with invalid preview without stopping the page.
*/
public function test_inject_preview_notfound(): void {
global $CFG;
$_GET = ['auth_outage_preview' => '1'];
// Should not throw exception or halt anything, silently ignore it.
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertEmpty($header);
}
/**
* Test injection with preview and delta.
*/
public function test_inject_preview_withdelta(): void {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
ob_start();
$outage->id = outagedb::save($outage);
$text = trim(ob_get_contents());
ob_end_clean();
$_GET = ['auth_outage_preview' => (string)$outage->id, 'auth_outage_delta' => '500'];
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
// Still empty, delta is too high (outage ended).
self::assertEmpty($header);
}
/**
* Test injection without active outage.
*/
public function test_inject_noactive(): void {
outagelib::reset_injectcalled();
outagelib::get_inject_code();
}
/**
* Check if get config works without getting defaults.
*/
public function test_get_config(): void {
$this->resetAfterTest(true);
$keys = [
'css',
'default_description',
'default_duration',
'default_title',
'default_warning_duration',
'allowedips',
'remove_selectors',
];
// Set config with values.
foreach ($keys as $k) {
set_config($k, $k . '_value', 'auth_outage');
}
// Ensure it is not using any defaults.
$config = outagelib::get_config();
foreach ($keys as $k) {
self::assertSame($config->$k, $k . '_value', 'auth_outage');
}
set_config('allowedips_forced', 'allowedips_forced_value', 'auth_outage');
$config = outagelib::get_config();
self::assertSame($config->allowedips, "allowedips_value\nallowedips_forced_value", 'auth_outage');
}
/**
* Check that config has key.
*/
public function test_config_keys(): void {
$this->resetAfterTest(true);
$keys = [
'allowedips',
'css',
'default_description',
'default_duration',
'default_title',
'default_warning_duration',
'remove_selectors',
];
$defaults = outagelib::get_config_defaults();
foreach ($keys as $k) {
self::assertArrayHasKey($k, $defaults);
}
}
/**
* Check if get config works getting defaults when needed.
*/
public function test_get_config_invalid(): void {
$this->resetAfterTest(true);
// Set config with invalid values.
set_config('allowedips', " \n", 'auth_outage');
set_config('css', " \n", 'auth_outage');
set_config('default_description', " \n", 'auth_outage');
set_config('default_duration', " \n", 'auth_outage');
set_config('default_title', " \n", 'auth_outage');
set_config('default_warning_duration', " \n", 'auth_outage');
// Get defaults.
$defaults = outagelib::get_config_defaults();
$config = outagelib::get_config();
// Ensure it is using all defaults.
foreach ($defaults as $k => $v) {
self::assertSame($v, $config->$k);
}
}
/**
* Check if outagelib::inject() does not inject on admin/settings.php?section=additionalhtml
*/
public function test_inject_settings(): void {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
ob_start();
$outage->id = outagedb::save($outage);
$text = trim(ob_get_contents());
ob_end_clean();
// Pretend we are there...
$_SERVER['SCRIPT_FILENAME'] = '/var/www/alternativepath/admin/settings.php'; // Issue #88 regression test.
$_SERVER['SCRIPT_NAME'] = '/admin/settings.php';
$_GET['section'] = 'additionalhtml';
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertEmpty($header);
}
/**
* Test create maintenance php code
*/
public function test_createmaintenancephpcode(): void {
global $CFG;
$CFG->cookiehttponly = false;
$expected = <<<'EOT'
<?php
if ((time() >= 123) && (time() < 456)) {
if (!defined('MOODLE_INTERNAL')) {
define('MOODLE_INTERNAL', true);
}
require_once($CFG->dirroot.'/lib/moodlelib.php');
if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) {
require_once($CFG->dirroot.'/lib/classes/ip_utils.php');
}
// Put access key as a cookie if given. This stops the need to put it as a url param on every request.
$urlaccesskey = optional_param('accesskey', null, PARAM_TEXT);
$isphpunit = defined('PHPUNIT_TEST');
if (!empty($urlaccesskey) && !$isphpunit) {
setcookie('auth_outage_accesskey', $urlaccesskey, time() + 86400, '/', '', true, false);
}
// Use url access key if given, else the cookie, else null.
$useraccesskey = $urlaccesskey ?: $_COOKIE['auth_outage_accesskey'] ?? null;
$ipblocked = !remoteip_in_list('hey\'\"you
a.b.c.d
e.e.e.e/20');
$accesskeyblocked = $useraccesskey != '12345';
$allowed = (true && !$accesskeyblocked) || (true && !$ipblocked);
if (!$allowed) {
if (!$isphpunit) {
header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance');
header('Status: 503 Moodle under maintenance');
header('Retry-After: 300');
header('Content-type: text/html; charset=utf-8');
header('X-UA-Compatible: IE=edge');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Accept-Ranges: none');
header('X-Moodle-Maintenance: manager');
if (!empty('testmeta')) {
header('X-Outage-Metadata: ' . 'testmeta');
}
header('X-Outage-StartTime: ' . '123');
header('X-Outage-EndTime: ' . '456');
}
if (!$isphpunit && ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER))) {
exit(0);
}
if (true && $ipblocked) {
echo '<!-- auth_outage blocked your ip: '.getremoteaddr('n/a').' -->';
}
if (true && $accesskeyblocked) {
echo '<!-- auth_outage blocked by missing or incorrect access key, access key given: '. $useraccesskey .' -->';
}
if (!$isphpunit) {
if (file_exists($CFG->dataroot.'/climaintenance.template.html')) {
require($CFG->dataroot.'/climaintenance.template.html');
exit(0);
}
// The file above should always exist, but just in case...
die('We are currently under maintentance, please try again later.');
}
}
}
EOT;
$found = outagelib::create_climaintenancephp_code(123, 456, "hey'\"you\na.b.c.d\ne.e.e.e/20", '12345', 'testmeta');
self::assertSame($expected, $found);
}
/**
* Test create maintenance php code without age
*
* @param string $configkey The key of the config.
* @dataProvider createmaintenancephpcode_withoutage_provider
*/
public function test_createmaintenancephpcode_withoutage($configkey): void {
global $CFG;
$this->resetAfterTest(true);
$CFG->cookiehttponly = false;
$expected = <<<'EOT'
<?php
if ((time() >= 123) && (time() < 456)) {
if (!defined('MOODLE_INTERNAL')) {
define('MOODLE_INTERNAL', true);
}
require_once($CFG->dirroot.'/lib/moodlelib.php');
if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) {
require_once($CFG->dirroot.'/lib/classes/ip_utils.php');
}
// Put access key as a cookie if given. This stops the need to put it as a url param on every request.
$urlaccesskey = optional_param('accesskey', null, PARAM_TEXT);
$isphpunit = defined('PHPUNIT_TEST');
if (!empty($urlaccesskey) && !$isphpunit) {
setcookie('auth_outage_accesskey', $urlaccesskey, time() + 86400, '/', '', true, false);
}
// Use url access key if given, else the cookie, else null.
$useraccesskey = $urlaccesskey ?: $_COOKIE['auth_outage_accesskey'] ?? null;
$ipblocked = !remoteip_in_list('127.0.0.1');
$accesskeyblocked = $useraccesskey != '5678';
$allowed = (true && !$accesskeyblocked) || (true && !$ipblocked);
if (!$allowed) {
if (!$isphpunit) {
header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance');
header('Status: 503 Moodle under maintenance');
header('Retry-After: 300');
header('Content-type: text/html; charset=utf-8');
header('X-UA-Compatible: IE=edge');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Accept-Ranges: none');
header('X-Moodle-Maintenance: manager');
if (!empty(NULL)) {
header('X-Outage-Metadata: ' . NULL);
}
header('X-Outage-StartTime: ' . '123');
header('X-Outage-EndTime: ' . '456');
}
if (!$isphpunit && ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER))) {
exit(0);
}
if (true && $ipblocked) {
echo '<!-- auth_outage blocked your ip: '.getremoteaddr('n/a').' -->';
}
if (true && $accesskeyblocked) {
echo '<!-- auth_outage blocked by missing or incorrect access key, access key given: '. $useraccesskey .' -->';
}
if (!$isphpunit) {
if (file_exists($CFG->dataroot.'/climaintenance.template.html')) {
require($CFG->dataroot.'/climaintenance.template.html');
exit(0);
}
// The file above should always exist, but just in case...
die('We are currently under maintentance, please try again later.');
}
}
}
EOT;
$outage = new outage([
'starttime' => 123,
'stoptime' => 456,
'accesskey' => '5678',
]);
$file = $CFG->dataroot . '/climaintenance.php';
set_config($configkey, '127.0.0.1', 'auth_outage');
outagelib::update_climaintenance_code($outage);
self::assertFileExists($file);
$found = file_get_contents($file);
self::assertSame($found, $expected);
}
/**
* Provides values to test_createmaintenancephpcode_withoutage
* @return array
*/
public static function createmaintenancephpcode_withoutage_provider(): array {
return [['allowedips'], ['allowedips_forced']];
}
/**
* Test create maintenance php code without IPs or accesskey
*/
public function test_createmaintenancephpcode_withoutips_or_accesskey(): void {
global $CFG;
$this->resetAfterTest(true);
$outage = new outage([
'starttime' => 123,
'stoptime' => 456,
'accesskey' => null,
]);
$file = $CFG->dataroot . '/climaintenance.php';
set_config('allowedips', '', 'auth_outage');
set_config('allowedips_forced', '', 'auth_outage');
touch($file);
outagelib::update_climaintenance_code($outage);
// Backwards compatibility with older PHPUnit - use old assertFile method.
if (method_exists($this, 'assertFileDoesNotExist')) {
self::assertFileDoesNotExist($file);
} else {
self::assertFileNotExists($file);
}
}
/**
* Test create maintenance php code without outage
*/
public function test_createmaintenancephpcode_withoutoutage(): void {
global $CFG;
$file = $CFG->dataroot . '/climaintenance.php';
touch($file);
outagelib::update_climaintenance_code(null);
// Backwards compatibility with older PHPUnit - use old assertFile method.
if (method_exists($this, 'assertFileDoesNotExist')) {
self::assertFileDoesNotExist($file);
} else {
self::assertFileNotExists($file);
}
}
/**
* Regression Test - Issue #82: When changing the IP address list it should recreate the maintenance files.
*/
public function test_when_we_change_allowed_ips_in_settings_it_updates_the_templates(): void {
global $CFG;
$this->create_outage();
// Change settings.
set_config('s_auth_outage_allowedips', '127', 'auth_outage');
// The method outagelib::prepare_next_outage() should have been called from admin_write_settings().
foreach ([$CFG->dataroot . '/climaintenance.template.html', $CFG->dataroot . '/climaintenance.php'] as $file) {
self::assertFileExists($file);
unlink($file);
}
}
/**
* Problem detected while solving Issue #82.
*/
public function test_when_we_change_remove_selectors_in_settings_it_updates_the_templates(): void {
global $CFG;
$this->create_outage();
// Change settings.
set_config('s_auth_outage_remove_selectors', '.something', 'auth_outage');
// The method outagelib::prepare_next_outage() should have been called from admin_write_settings().
foreach ([$CFG->dataroot . '/climaintenance.template.html', $CFG->dataroot . '/climaintenance.php'] as $file) {
self::assertFileExists($file);
unlink($file);
}
}
/**
* Regression test for issue #85.
*/
public function test_it_can_inject_in_settings_if_not_additional_html(): void {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
ob_start();
$outage->id = outagedb::save($outage);
$text = trim(ob_get_contents());
ob_end_clean();
// Pretend we are there...
$_SERVER['SCRIPT_FILENAME'] = '/var/www/alternativepath/admin/settings.php'; // Issue #88 regression test.
$_SERVER['SCRIPT_NAME'] = '/admin/settings.php';
$_GET['section'] = 'notadditionalhtml';
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertNotEmpty($header);
}
/**
* Creates outage for tests.
*/
private function create_outage() {
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
set_config('allowedips', '127.0.0.1', 'auth_outage');
outagedb::save($outage);
// Enable outage plugin so settings can be changed.
set_config('auth', 'outage');
\core\session\manager::gc(); // Remove stale sessions.
\core_plugin_manager::reset_caches();
}
/**
* Provides values to test_evaluation_maintenancepage
* @return array
*/
public static function evaluation_maintenancepage_provider(): array {
$blockedipout = '<!-- auth_outage blocked your ip:';
$blockedaccesskeyout = '<!-- auth_outage blocked by missing or incorrect access key, access key given:';
return [
// IP set up, access key not set up.
'ip allowed, no access key setup' => [
'allowedips' => '127.0.0.1',
'iptouse' => '127.0.0.1',
'accesskey' => null,
'accesskeytouse' => null,
'expectedoutputs' => [],
],
'ip not allowed, no access key setup' => [
'allowedips' => '5.5.5.5',
'iptouse' => '127.0.0.1',
'accesskey' => null,
'accesskeytouse' => null,
'expectedoutputs' => [$blockedipout],
],
// IP not set up, access key set up.
'access key incorrect, no ip setup' => [
'allowedips' => null,
'iptouse' => null,
'accesskey' => '12345',
'accesskeytouse' => 'wrong',
'expectedoutputs' => [$blockedaccesskeyout],
],
'access key correct, no ip setup' => [
'allowedips' => null,
'iptouse' => null,
'accesskey' => '12345',
'accesskeytouse' => '12345',
'expectedoutputs' => [],
],
// Both IP and access key set up.
'access key incorrect, ip incorrect' => [
'allowedips' => '127.0.0.1',
'iptouse' => '5.5.5.5',
'accesskey' => '12345',
'accesskeytouse' => 'wrong',
'expectedoutputs' => [$blockedipout, $blockedaccesskeyout],
],
'access key correct, ip incorrect' => [
'allowedips' => '127.0.0.1',
'iptouse' => '5.5.5.5',
'accesskey' => '12345',
'accesskeytouse' => '12345',
'expectedoutputs' => [],
],
'access key incorrect, ip correct' => [
'allowedips' => '127.0.0.1',
'iptouse' => '127.0.0.1',
'accesskey' => '12345',
'accesskeytouse' => 'wrong',
'expectedoutputs' => [],
],
'access key correct, ip correct' => [
'allowedips' => '127.0.0.1',
'iptouse' => '127.0.0.1',
'accesskey' => '12345',
'accesskeytouse' => '12345',
'expectedoutputs' => [],
],
];
}
/**
* Tests the evaluation logic of the generated maintenance page.
*
* @param string|null $allowedips config to set as allowed ips - null to not set
* @param string|null $iptouse ip to 'fake' as the remote ip, or null to not set.
* @param string|null $accesskey config to set as the access key in the outage - null to not set
* @param string|null $accesskeytouse access key to pass in as fake url params - null to not set
* @param array $expectedoutputs expected output strings, if empty will test that the output was also empty.
*
* @dataProvider evaluation_maintenancepage_provider
*
* We need this because we modify the request headers,
* see https://github.com/sebastianbergmann/phpunit/issues/720#issuecomment-10421092
* @runInSeparateProcess
*/
public function test_evaluation_maintenancepage(
?string $allowedips,
?string $iptouse,
?string $accesskey,
?string $accesskeytouse,
array $expectedoutputs
): void {
global $CFG, $_SERVER, $_GET;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
'accesskey' => $accesskey,
]);
if (!is_null($allowedips)) {
set_config('allowedips', $allowedips, 'auth_outage');
}
// Ensure if the file exists we clean it (e.g. from a previous test run).
$file = $CFG->dataroot . '/climaintenance.php';
if (file_exists($file)) {
unlink($file);
}
// This basically sets the output of getremoteaddr().
if (!is_null($iptouse)) {
$_SERVER['REMOTE_ADDR'] = $iptouse;
}
// This sets the output of optional_param().
if (!is_null($accesskeytouse)) {
$_GET['accesskey'] = $accesskeytouse;
}
outagelib::update_climaintenance_code($outage);
self::assertFileExists($file);
// Require the file to execute it.
// Normally this would die, but we have baked some goodies in there
// that stop it die'ing during a unit test.
ob_start();
require($file);
$contents = ob_get_clean();
// Check each output is as expected.
foreach ($expectedoutputs as $expectedoutput) {
$this->assertStringContainsString($expectedoutput, $contents);
}
// Ensure if nothing was expected, that it is empty.
if (empty($expectedoutputs)) {
$this->assertEmpty($contents);
}
}
}

View File

@@ -1,7 +0,0 @@
a {
font-size: 200%;
}
div {
background-image: url('/moodle/auth/outage/tests/phpunit/local/controllers/fixtures/catalyst.png');
}

View File

@@ -1,7 +0,0 @@
a {
font-size: 200%;
}
div {
background-image: url(/moodle/auth/outage/tests/phpunit/local/controllers/fixtures/catalyst.png);
}

View File

@@ -1,118 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests performed on infopage controller class and update_static_page task class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\local\controllers\infopage;
use auth_outage\local\outage;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../../base_testcase.php');
/**
* Tests performed on infopage controller class and update_static_page task class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class infopagecontroller_test extends auth_outage_base_testcase {
/**
* Tests the constructor.
*/
public function test_constructor() {
new infopage();
}
/**
* Tests the constructor with given parameters.
*/
public function test_constructor_withparams() {
$_GET = ['id' => 1, 'static' => 'true'];
new infopage();
}
/**
* Tests the constructor with different id and outage id.
*/
public function test_constructor_idmismatch() {
$outage = new outage([
'id' => 1,
'autostart' => false,
'warntime' => time() - 60,
'starttime' => time(),
'stoptime' => time() + 60,
'title' => 'Title',
'description' => 'Description',
]);
$this->set_expected_exception('coding_exception');
new infopage(['id' => 2, 'outage' => $outage]);
}
/**
* Tests the constructor with an invalid outage.
*/
public function test_constructor_invalidoutage() {
$this->set_expected_exception('coding_exception');
new infopage(['outage' => 'My outage']);
}
/**
* We should have an exception because CLI cannot redirect.
*/
public function test_output_nonstatic_nooutage() {
$info = new infopage(['static' => false]);
$this->set_expected_exception('moodle_exception');
$info->output();
}
/**
* Checks the output of the info page.
*/
public function test_output() {
$now = time();
$outage = new outage([
'id' => 1,
'autostart' => false,
'warntime' => $now - 100,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
$info = new infopage(['outage' => $outage]);
$output = $info->get_output();
self::assertContains('auth_outage_info', $output);
}
/**
* Tests the constructor enables SVG support.
*/
public function test_svgicons_is_true() {
global $CFG;
$CFG->svgicons = false;
new infopage();
self::assertTrue($CFG->svgicons);
}
}

View File

@@ -1,536 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* maintenance_static_page_test task class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\local\controllers\maintenance_static_page;
use auth_outage\local\controllers\maintenance_static_page_io;
use auth_outage\local\controllers\maintenance_static_page_generator;
use auth_outage\task\update_static_page;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../../base_testcase.php');
/**
* maintenance_static_page_test class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class maintenance_static_page_test extends auth_outage_base_testcase {
/**
* Test template file.
*/
public function test_templatefile() {
global $CFG;
$page = maintenance_static_page::create_from_html('<html></html>');
self::assertSame($CFG->dataroot.'/climaintenance.template.html', $page->get_io()->get_template_file());
$page->get_io()->set_preview(true);
self::assertSame($CFG->dataroot.'/auth_outage/climaintenance/preview/climaintenance.html',
$page->get_io()->get_template_file());
}
/**
* Test resources folder.
*/
public function test_resourcesfolder() {
global $CFG;
$page = maintenance_static_page::create_from_html('<html></html>');
self::assertSame($CFG->dataroot.'/auth_outage/climaintenance', $page->get_io()->get_resources_folder());
$page->get_io()->set_preview(true);
self::assertSame($CFG->dataroot.'/auth_outage/climaintenance/preview', $page->get_io()->get_resources_folder());
}
/**
* Test create from outage.
*/
public function test_createfromoutage() {
// How to fetch a page from PHPUnit environment?
}
/**
* Test create from HTML.
*/
public function test_createfromhtml() {
$html = "<!DOCTYPE html>\n<html><head><title>Title</title></head><body>Content</body></html>";
$expected = "<!DOCTYPE html>\n<html><head><title>Title</title><meta http-equiv=\"refresh\" content=\"300\">".
"</head><body>Content</body></html>";
self::assertSame($expected, $this->generated_page_html($html));
}
/**
* Test remove script tags.
*/
public function test_removescripttags() {
$html = "<!DOCTYPE html>\n".
'<html><head><script type="text/javascript" src="http://xyz"></script><title>Title</title></head>'.
'<body>Content<script> a < 5; x > 3</script></body></html>';
maintenance_static_page::create_from_html($html)->generate();
$generated = $this->generated_page_html($html);
self::assertNotContains('<script', $generated);
}
/**
* Test remove script tags.
*/
public function test_updatelinkstylesheet() {
$localcsslink = $this->get_fixture_path('simple.css');
$externalcsslink = 'http://google.com/coolstuff.css';
$html = "<!DOCTYPE html>\n".
'<html><head><link href="'.$localcsslink.'" rel="stylesheet" /><title>Title</title></head>'.
'<body>Content<link rel="stylesheet" href="'.$externalcsslink.'"></body></html>';
$generated = $this->generated_page_html($html);
self::assertContains('www.example.com/moodle/auth/outage/file.php?file=', $generated);
self::assertNotContains($localcsslink, $generated);
self::assertContains($externalcsslink, $generated);
}
/**
* Test update link style sheet urls.
*/
public function test_updatelinkstylesheet_urls() {
$localcsslink = $this->get_fixture_path('withurls.css');
$html = "<!DOCTYPE html>\n".
'<html><head><link href="'.$localcsslink.'" rel="stylesheet" /><title>Title</title></head>'.
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// Check for css file.
self::assertFileExists($page->get_io()->get_resources_folder().'/d8643101d96b093e642b15544e4d1f7815b5ba55.dGV4dC9wbGFpbg');
// Check for catalyst.png file referenced in url(..) of css.
self::assertFileExists($page->get_io()->get_resources_folder().'/ff7f7f87a26a908fc72930eaefb6b57306361d16.aW1hZ2UvcG5n');
}
/**
* Test update link style sheet urls quoted.
*/
public function test_updatelinkstylesheet_urls_quoted() {
$localcsslink = $this->get_fixture_path('withurls-quoted.css');
$html = "<!DOCTYPE html>\n".
'<html><head><link href="'.$localcsslink.'" rel="stylesheet" /><title>Title</title></head>'.
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// Check for css file.
self::assertFileExists($page->get_io()->get_resources_folder().'/9fe2374b03953e1949d54ab750be2d8706891c03.dGV4dC9wbGFpbg');
// Check for catalyst.png file referenced in url(..) of css.
self::assertFileExists($page->get_io()->get_resources_folder().'/ff7f7f87a26a908fc72930eaefb6b57306361d16.aW1hZ2UvcG5n');
}
/**
* Test update link style sheet urls with sub dir.
*/
public function test_updatelinkstylesheet_urls_subdir() {
$localcsslink = $this->get_fixture_path('subdir/withurls-subdir.css');
$html = "<!DOCTYPE html>\n".
'<html><head><link href="'.$localcsslink.'" rel="stylesheet" /><title>Title</title></head>'.
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// Check for css file.
self::assertFileExists($page->get_io()->get_resources_folder().'/beb44281e23b9d872056bf0230cea34535e8cdea.dGV4dC9wbGFpbg');
// Check for file referenced in url(..) of css.
self::assertFileExists($page->get_io()->get_resources_folder().'/a02a8a442fa82d5205ffb24722d9df7f35161f56.dGV4dC9wbGFpbg');
}
/**
* Test update images to file.php style link.
*/
public function test_updateimages() {
$localimglink = $this->get_fixture_path('catalyst.png');
$externalimglink = 'http://google.com/coolstyle.css';
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body><img src="'.$localimglink.'">Content<img src="'.$externalimglink.'" /></body></html>';
$generated = $this->generated_page_html($html);
self::assertContains('www.example.com/moodle/auth/outage/file.php?file=', $generated);
self::assertNotContains($localimglink, $generated);
self::assertContains($externalimglink, $generated);
}
/**
* Test update favicon to file.php style link.
*/
public function test_updatelinkfavicon() {
$link = $this->get_fixture_path('catalyst.png');
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title><link rel="shortcut icon" href="'.$link.'""></head>'.
'<body>Content</body></html>';
$generated = $this->generated_page_html($html);
self::assertNotContains($link, $generated);
self::assertContains('www.example.com/moodle/auth/outage/file.php?file=', $generated);
}
/**
* Test update preview path to file.php style link.
*/
public function test_previewpath() {
$link = $this->get_fixture_path('catalyst.png');
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title><link rel="shortcut icon" href="'.$link.'""></head>'.
'<body>Content</body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->get_io()->set_preview(true);
$page->generate();
$generated = trim(file_get_contents($page->get_io()->get_template_file()));
self::assertNotContains($link, $generated);
self::assertContains('www.example.com/moodle/auth/outage/file.php?file=preview%2F', $generated);
}
/**
* Generates the maintenance page (not using preview mode).
*
* @param string $html Input HTML.
*
* @return string Output HTML.
*/
private function generated_page_html($html) {
$page = maintenance_static_page::create_from_html($html);
$page->generate();
$generated = trim(file_get_contents($page->get_io()->get_template_file()));
return $generated;
}
/**
* Checks if we can create and execute a task to update outage pages.
*/
public function test_tasks() {
$this->resetAfterTest(true);
$task = new update_static_page();
self::assertNotEmpty($task->get_name());
$task->execute();
}
/**
* Tests updating the static page when there is no outage but the file existed before.
*/
public function test_updatestaticpage_hasfile() {
global $CFG;
$file = $CFG->dataroot.'/climaintenance.template.html';
touch($file);
self::assertFileExists($file);
maintenance_static_page::create_from_outage(null)->generate();
self::assertFileNotExists($file);
}
/**
* Tests created file.
*/
public function test_createdfile() {
global $CFG;
$link = $this->get_fixture_path('catalyst.png');
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>Content<img src="'.$link.'" /></body></html>';
$page = maintenance_static_page::create_from_html($html);
$page->generate();
// This checks if content is correct and mime type is correct from the encoded name.
$file = $page->get_io()->get_resources_folder().'/ff7f7f87a26a908fc72930eaefb6b57306361d16.aW1hZ2UvcG5n';
self::assertFileExists($file);
// We can still assert the contents really match, not just the hash.
$found = file_get_contents($file);
$expected = file_get_contents($CFG->dirroot.'/auth/outage/tests/phpunit/local/controllers/fixtures/catalyst.png');
self::assertSame($found, $expected);
}
/**
* Gets a fixture file for this test case.
*
* @param string $file file name
*
* @return string
*/
private function get_fixture_path($file) {
return (string)new moodle_url('/auth/outage/tests/phpunit/local/controllers/fixtures/'.$file);
}
/**
* Test saving empty string for template file.
*/
public function test_invalid_string_saving_template_empty() {
$io = new maintenance_static_page_io();
$this->set_expected_exception('coding_exception');
$io->save_template_file('');
}
/**
* Test saving non string for template file.
*/
public function test_invalid_string_saving_template_nostring() {
$io = new maintenance_static_page_io();
$this->set_expected_exception('coding_exception');
$io->save_template_file(50);
}
/**
* Test get url for file.
*/
public function test_get_url_for_file() {
$io = new maintenance_static_page_io();
self::assertContains('www.example.com/moodle/auth/outage/file.php?file=img.png', $io->get_url_for_file('img.png'));
}
/**
* Return array of url data provider and true or false.
*/
public function is_url_dataprovider() {
return [
[true, 'http://catalyst.net.nz'],
[true, 'https://www.catalyst-au.net/'],
[false, '/homepage'],
[false, 'file://homepage'],
[true, '//catalyst-au.net/img/test.jpg'],
[false, '://www.catalyst-au.net/img/test.jpg']
];
}
/**
* Test if it is url
* @dataProvider is_url_dataprovider
* @param string $result expected result
* @param string $url url to be checked
*/
public function test_is_url($result, $url) {
self::assertEquals($result, maintenance_static_page_io::is_url($url));
}
/**
* Test file get_data.
*/
public function test_file_get_data() {
$file = __DIR__.'/fixtures/catalyst.png';
$found = maintenance_static_page_io::file_get_data($file);
self::assertSame(file_get_contents($file), $found['contents']);
self::assertSame('image/png', $found['mime']);
}
/**
* Test invalid file get_data.
*/
public function test_file_get_data_invalidfile() {
$found = maintenance_static_page_io::file_get_data(__DIR__.'/fixtures/invalidfile');
self::assertSame('', $found['contents']);
self::assertSame('unknown', $found['mime']);
self::assertCount(1, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
}
/**
* Test invalid file get_data.
*/
public function test_file_get_data_invalidfilename() {
$this->set_expected_exception('coding_exception');
maintenance_static_page_io::file_get_data(200);
}
/**
* Test remove css selector.
*/
public function test_remove_css_selector() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>Content<b class="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '.removeme', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertNotContains('removeme', $generated);
self::assertNotContains('Goodbye cruel world', $generated);
}
/**
* Test remove css selector id.
*/
public function test_remove_css_selector_id() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#removeme', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertNotContains('removeme', $generated);
self::assertNotContains('Goodbye cruel world', $generated);
}
/**
* Test remove css selector with multi lines.
*/
public function test_remove_css_selector_with_multiline() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>'.
'<b class="deleteme">Goodbye cruel world.</b>'.
'<b class="removeme">Goodbye cruel world.</b>'.
'</body></html>';
set_config('remove_selectors', ".removeme\n.deleteme", 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertNotContains('removeme', $generated);
self::assertNotContains('deleteme', $generated);
self::assertNotContains('Goodbye cruel world', $generated);
}
/**
* Test remove css selector needs trim.
*/
public function test_remove_css_selector_needing_trim() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>'.
'<b class="deleteme">Goodbye cruel world.</b>'.
'<b class="removeme">Goodbye cruel world.</b>'.
'</body></html>';
set_config('remove_selectors', " .removeme \n .deleteme ", 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertNotContains('removeme', $generated);
self::assertNotContains('deleteme', $generated);
self::assertNotContains('Goodbye cruel world', $generated);
}
/**
* Test remove css selector with empty line.
*/
public function test_remove_css_selector_with_empty_line() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>'.
'<b class="deleteme">Goodbye cruel world.</b>'.
'<b class="removeme">Goodbye cruel world.</b>'.
'</body></html>';
set_config('remove_selectors', "\n\n.removeme\n\n\n\n.deleteme\n\n", 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertNotContains('removeme', $generated);
self::assertNotContains('deleteme', $generated);
self::assertNotContains('Goodbye cruel world', $generated);
}
/**
* Test remove css selector with invalid id.
*/
public function test_remove_css_selector_with_invalid_id() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#invalidid', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertContains('removeme', $generated);
self::assertContains('Goodbye cruel world', $generated);
}
/**
* Test meta refresh 5 minutes.
*/
public function test_meta_refresh_5minutes() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#invalidid', 'auth_outage');
$generated = $this->generated_page_html($html);
self::assertContains('<meta http-equiv="refresh" content="300">', $generated);
}
/**
* Test meta refresh maximum 5 minutes.
*/
public function test_meta_refresh_maximum_5seconds() {
$this->resetAfterTest(true);
$html = "<!DOCTYPE html>\n".
'<html><head><title>Title</title></head>'.
'<body>Content<b id="removeme">Goodbye cruel world.</b></body></html>';
set_config('remove_selectors', '#invalidid', 'auth_outage');
$page = maintenance_static_page::create_from_html($html);
$page->set_max_refresh_time(5);
$page->generate();
$generated = trim(file_get_contents($page->get_io()->get_template_file()));
return $generated;
self::assertContains('<meta http-equiv="refresh" content="5">', $generated);
}
/**
* Data provider for test_get_urls_from_stylesheet
* @return array
*/
public function test_get_urls_from_stylesheet_provider() {
return [
// Empty string.
["", 0],
// URLs that should be retrieved.
["background:url(/theme/image.php/_s/boost/core/1581292565/t/expanded)", 1],
["background:url('/theme/image.php/_s/boost/core/1581292565/t/expanded')", 1],
["src:url(\"/theme/font.php/boost/core/1581292565/fontawesome-webfont.eot?#iefix&v=4.7.0\")", 1],
["background-image:url(pix/vline-rtl.gif)", 1],
// URLs that should not be retrieved.
["background-image:url(data:image/gif;base64,R0lGODlhYADIAP=)", 0],
["background-image:url('data:image/gif;base64,R0lGODlhYADIAP=')", 0],
["background-image:url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\'\")", 0],
// Combination of URLs used above.
["background-image:url(pix/vline-rtl.gif) background:url(/theme/image.php/_s/boost/core/158/t/expanded)", 2],
["background-image:url(data:image/gif;base64,R0lG=)src:url(\"/theme/font.php/fontawesome-webfont.eot\")", 1],
];
}
/**
* Tests get_urls_from_stylesheet() method to get all appropriate URLS from the file.
*
* @dataProvider test_get_urls_from_stylesheet_provider
* @param string $filecontent Content of the file
* @param int $count Expected quantity of found URLs
* @throws coding_exception
*/
public function test_get_urls_from_stylesheet($filecontent, $count) {
$this->resetAfterTest(true);
$generator = new maintenance_static_page_generator(new DOMDocument(), new maintenance_static_page_io());
$matches = $generator->get_urls_from_stylesheet($filecontent);
self::assertInternalType('array', $matches);
self::assertCount(2, $matches);
self::assertCount($count, $matches[1]);
}
}

View File

@@ -1,554 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* outagelib_test test class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use auth_outage\dml\outagedb;
use auth_outage\local\outage;
use auth_outage\local\outagelib;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir.'/adminlib.php');
require_once(__DIR__.'/../base_testcase.php');
/**
* outagelib_test test class.
*
* @package auth_outage
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class outagelib_test extends auth_outage_base_testcase {
/**
* Check if maintenance message is disabled as needed.
*/
public function test_maintenancemessage() {
$this->resetAfterTest(true);
static::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => true,
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
set_config('maintenance_message', 'A message.');
outagedb::save($outage);
self::assertFalse((bool)get_config('moodle', 'maintenance_message'));
self::assertCount(2, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
}
/**
* Check if maintenance later is removed if no outage set.
*/
public function test_maintenancelater_nonext() {
$this->resetAfterTest(true);
set_config('maintenance_later', time() + (60 * 60 * 24 * 7)); // In 1 week.
self::assertNotEmpty(get_config('moodle', 'maintenance_later'));
outagelib::prepare_next_outage();
self::assertEmpty(get_config('moodle', 'maintenance_later'));
}
/**
* Check outagelib::inject() works as expected.
*/
public function test_inject() {
global $OUTPUT;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => true,
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
$outage->id = outagedb::save($outage);
outagelib::reset_injectcalled();
// Get full header to avoid interactions with other single inject plugins.
$header1 = $OUTPUT->standard_top_of_body_html();
self::assertContains('<style>', $header1);
self::assertContains('<script>', $header1);
// Should not inject more than once.
$size = strlen($OUTPUT->standard_top_of_body_html());
self::assertSame($size, strlen($OUTPUT->standard_top_of_body_html()));
// Check styles aren't reinjected.
self::assertNotContains('<style>', $OUTPUT->standard_top_of_body_html());
}
/**
* Check outagelib::inject() will not break the page if something goes wrong.
*/
public function test_inject_broken() {
$_GET = ['auth_outage_break_code' => '1'];
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertCount(2, phpunit_util::get_debugging_messages());
phpunit_util::reset_debugging();
}
/**
* Check if injection works with preview.
*/
public function test_inject_preview() {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => true,
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
$outage->id = outagedb::save($outage);
$_GET = ['auth_outage_preview' => (string)$outage->id];
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertContains('<style>', $header);
self::assertContains('<script>', $header);
}
/**
* Check if injection works with invalid preview without stopping the page.
*/
public function test_inject_preview_notfound() {
global $CFG;
$_GET = ['auth_outage_preview' => '1'];
// Should not throw exception or halt anything, silently ignore it.
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertEmpty($header);
}
/**
* Test injection with preview and delta.
*/
public function test_inject_preview_withdelta() {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => true,
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
$outage->id = outagedb::save($outage);
$_GET = ['auth_outage_preview' => (string)$outage->id, 'auth_outage_delta' => '500'];
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
// Still empty, delta is too high (outage ended).
self::assertEmpty($header);
}
/**
* Test injection without active outage.
*/
public function test_inject_noactive() {
outagelib::reset_injectcalled();
outagelib::get_inject_code();
}
/**
* Check if get config works without getting defaults.
*/
public function test_get_config() {
$this->resetAfterTest(true);
$keys = [
'css',
'default_autostart',
'default_description',
'default_duration',
'default_title',
'default_warning_duration',
'allowedips',
'remove_selectors',
];
// Set config with values.
foreach ($keys as $k) {
set_config($k, $k.'_value', 'auth_outage');
}
// Ensure it is not using any defaults.
$config = outagelib::get_config();
foreach ($keys as $k) {
self::assertSame($config->$k, $k.'_value', 'auth_outage');
}
}
/**
* Check that config has key.
*/
public function test_config_keys() {
$this->resetAfterTest(true);
$keys = [
'allowedips',
'css',
'default_autostart',
'default_description',
'default_duration',
'default_title',
'default_warning_duration',
'remove_selectors',
];
$defaults = outagelib::get_config_defaults();
foreach ($keys as $k) {
self::assertArrayHasKey($k, $defaults);
}
}
/**
* Check if get config works getting defaults when needed.
*/
public function test_get_config_invalid() {
$this->resetAfterTest(true);
// Set config with invalid values.
set_config('allowedips', " \n", 'auth_outage');
set_config('css', " \n", 'auth_outage');
set_config('default_autostart', " \n", 'auth_outage');
set_config('default_description', " \n", 'auth_outage');
set_config('default_duration', " \n", 'auth_outage');
set_config('default_title', " \n", 'auth_outage');
set_config('default_warning_duration', " \n", 'auth_outage');
// Get defaults.
$defaults = outagelib::get_config_defaults();
$config = outagelib::get_config();
// Ensure it is using all defaults.
foreach ($defaults as $k => $v) {
self::assertSame($v, $config->$k);
}
}
/**
* Check if outagelib::inject() does not inject on admin/settings.php?section=additionalhtml
*/
public function test_inject_settings() {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => true,
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
$outage->id = outagedb::save($outage);
// Pretend we are there...
$_SERVER['SCRIPT_FILENAME'] = '/var/www/alternativepath/admin/settings.php'; // Issue #88 regression test.
$_SERVER['SCRIPT_NAME'] = '/admin/settings.php';
$_GET['section'] = 'additionalhtml';
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertEmpty($header);
}
/**
* Test create maintenance php code
*/
public function test_createmaintenancephpcode() {
$expected = <<<'EOT'
<?php
if ((time() >= 123) && (time() < 456)) {
define('MOODLE_INTERNAL', true);
require_once($CFG->dirroot.'/lib/moodlelib.php');
if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) {
require_once($CFG->dirroot.'/lib/classes/ip_utils.php');
}
if (!remoteip_in_list('hey\'\"you
a.b.c.d
e.e.e.e/20')) {
header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance');
header('Status: 503 Moodle under maintenance');
header('Retry-After: 300');
header('Content-type: text/html; charset=utf-8');
header('X-UA-Compatible: IE=edge');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Accept-Ranges: none');
header('X-Moodle-Maintenance: manager');
if ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER)) {
exit(0);
}
echo '<!-- Blocked by ip, your ip: '.getremoteaddr('n/a').' -->';
if (file_exists($CFG->dataroot.'/climaintenance.template.html')) {
require($CFG->dataroot.'/climaintenance.template.html');
exit(0);
}
// The file above should always exist, but just in case...
die('We are currently under maintentance, please try again later.');
}
}
EOT;
$found = outagelib::create_climaintenancephp_code(123, 456, "hey'\"you\na.b.c.d\ne.e.e.e/20");
self::assertSame($expected, $found);
}
/**
* Test create maintenance php code without age
*/
public function test_createmaintenancephpcode_withoutage() {
global $CFG;
$this->resetAfterTest(true);
$expected = <<<'EOT'
<?php
if ((time() >= 123) && (time() < 456)) {
define('MOODLE_INTERNAL', true);
require_once($CFG->dirroot.'/lib/moodlelib.php');
if (file_exists($CFG->dirroot.'/lib/classes/ip_utils.php')) {
require_once($CFG->dirroot.'/lib/classes/ip_utils.php');
}
if (!remoteip_in_list('127.0.0.1')) {
header($_SERVER['SERVER_PROTOCOL'] . ' 503 Moodle under maintenance');
header('Status: 503 Moodle under maintenance');
header('Retry-After: 300');
header('Content-type: text/html; charset=utf-8');
header('X-UA-Compatible: IE=edge');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Accept-Ranges: none');
header('X-Moodle-Maintenance: manager');
if ((defined('AJAX_SCRIPT') && AJAX_SCRIPT) || (defined('WS_SERVER') && WS_SERVER)) {
exit(0);
}
echo '<!-- Blocked by ip, your ip: '.getremoteaddr('n/a').' -->';
if (file_exists($CFG->dataroot.'/climaintenance.template.html')) {
require($CFG->dataroot.'/climaintenance.template.html');
exit(0);
}
// The file above should always exist, but just in case...
die('We are currently under maintentance, please try again later.');
}
}
EOT;
$outage = new outage([
'starttime' => 123,
'stoptime' => 456,
]);
$file = $CFG->dataroot.'/climaintenance.php';
set_config('allowedips', '127.0.0.1', 'auth_outage');
outagelib::update_climaintenance_code($outage);
self::assertFileExists($file);
$found = file_get_contents($file);
self::assertSame($found, $expected);
}
/**
* Test create maintenance php code without IPs
*/
public function test_createmaintenancephpcode_withoutips() {
global $CFG;
$this->resetAfterTest(true);
$outage = new outage([
'starttime' => 123,
'stoptime' => 456,
]);
$file = $CFG->dataroot.'/climaintenance.php';
set_config('allowedips', '', 'auth_outage');
touch($file);
outagelib::update_climaintenance_code($outage);
self::assertFileNotExists($file);
}
/**
* Test create maintenance php code without outage
*/
public function test_createmaintenancephpcode_withoutoutage() {
global $CFG;
$file = $CFG->dataroot.'/climaintenance.php';
touch($file);
outagelib::update_climaintenance_code(null);
self::assertFileNotExists($file);
}
/**
* Related to Issue #70: Creating ongoing outage does not trigger maintenance file creation.
*/
public function test_preparenextoutage_notautostart() {
global $CFG;
$this->create_outage();
// The method outagelib::prepare_next_outage() should have been called by save().
foreach ([$CFG->dataroot.'/climaintenance.template.html', $CFG->dataroot.'/climaintenance.php'] as $file) {
self::assertFileExists($file);
unlink($file);
}
}
/**
* Regression Test - Issue #82: When changing the IP address list it should recreate the maintenance files.
*/
public function test_when_we_change_allowed_ips_in_settings_it_updates_the_templates() {
global $CFG;
$this->create_outage();
// Change settings.
admin_write_settings((object)[
's_auth_outage_allowedips' => '127',
]);
// The method outagelib::prepare_next_outage() should have been called from admin_write_settings().
foreach ([$CFG->dataroot.'/climaintenance.template.html', $CFG->dataroot.'/climaintenance.php'] as $file) {
self::assertFileExists($file);
unlink($file);
}
}
/**
* Problem detected while solving Issue #82.
*/
public function test_when_we_change_remove_selectors_in_settings_it_updates_the_templates() {
global $CFG;
$this->create_outage();
// Change settings.
admin_write_settings((object)[
's_auth_outage_remove_selectors' => '.something',
]);
// The method outagelib::prepare_next_outage() should have been called from admin_write_settings().
foreach ([$CFG->dataroot.'/climaintenance.template.html', $CFG->dataroot.'/climaintenance.php'] as $file) {
self::assertFileExists($file);
unlink($file);
}
}
/**
* Related to Issue #72: IP Block still triggers cli maintenance mode even without autostart.
*/
public function test_preparenextoutage_noautostarttrigger() {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => false,
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
outagedb::save($outage);
// The method outagelib::prepare_next_outage() should have been called by save().
self::assertFalse(get_config('moodle', 'maintenance_later'));
// This file should not exist even if the statement above fails as Moodle does not create it immediately but test anyway.
self::assertFileNotExists($CFG->dataroot.'/climaintenance.html');
}
/**
* Regression test for issue #85.
*/
public function test_it_can_inject_in_settings_if_not_additional_html() {
global $CFG;
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => true,
'warntime' => $now,
'starttime' => $now + 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
$outage->id = outagedb::save($outage);
// Pretend we are there...
$_SERVER['SCRIPT_FILENAME'] = '/var/www/alternativepath/admin/settings.php'; // Issue #88 regression test.
$_SERVER['SCRIPT_NAME'] = '/admin/settings.php';
$_GET['section'] = 'notadditionalhtml';
outagelib::reset_injectcalled();
$header = outagelib::get_inject_code();
self::assertNotEmpty($header);
}
/**
* Creates outage for tests.
*/
private function create_outage() {
$this->resetAfterTest(true);
self::setAdminUser();
$now = time();
$outage = new outage([
'autostart' => false,
'warntime' => $now - 200,
'starttime' => $now - 100,
'stoptime' => $now + 200,
'title' => 'Title',
'description' => 'Description',
]);
set_config('allowedips', '127.0.0.1', 'auth_outage');
outagedb::save($outage);
// Enable outage plugin so settings can be changed.
set_config('auth', 'outage');
\core\session\manager::gc(); // Remove stale sessions.
core_plugin_manager::reset_caches();
}
}

View File

@@ -28,7 +28,9 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = "auth_outage";
$plugin->version = 2021032500; // The current plugin version (Date: YYYYMMDDXX).
$plugin->release = 2021032500; // Human-readable release information.
$plugin->requires = 2017051500; // Requires 3.3 and higher.
$plugin->version = 2024081903; // The current plugin version (Date: YYYYMMDDXX).
$plugin->release = 2024081903; // Human-readable release information.
$plugin->requires = 2017111309; // 2017111309 = T13, but this really requires 3.9 and higher.
$plugin->maturity = MATURITY_STABLE; // Suitable for PRODUCTION environments!
$plugin->supported = [39, 405]; // A range of branch numbers of supported moodle versions.
$plugin->incompatible = 501;

View File

@@ -24,6 +24,7 @@
*
* @var array $viewbag
*/
// phpcs:disable moodle.Commenting.MissingDocblock.File
defined('MOODLE_INTERNAL') || die();
?>
@@ -40,15 +41,17 @@ defined('MOODLE_INTERNAL') || die();
</div>
<div class="auth_outage_info_description"><?php echo $viewbag['outage']->get_description(); ?></div>
<?php if ($viewbag['admin']): ?>
<?php if ($viewbag['admin']) : ?>
<?php
$adminlinks = [];
foreach ([
foreach (
[
'startofwarning' => -$viewbag['outage']->get_warning_duration(),
'15secondsbefore' => -15,
'start' => 0,
'endofoutage' => $viewbag['outage']->get_duration_planned() - 1,
] as $title => $delta) {
] as $title => $delta
) {
$adminlinks[] = html_writer::link(
new moodle_url(
'/auth/outage/info.php',
@@ -58,7 +61,7 @@ defined('MOODLE_INTERNAL') || die();
'auth_outage_delta' => $delta,
]
),
get_string('info'.$title, 'auth_outage')
get_string('info' . $title, 'auth_outage')
);
}
$adminlinks[] = html_writer::link(

View File

@@ -22,6 +22,7 @@
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// phpcs:disable moodle.Commenting.MissingDocblock.File
use auth_outage\output\manage\history_table;
use auth_outage\output\manage\planned_table;
@@ -40,11 +41,11 @@ echo $viewbag['warning'];
<section id="section_planned_outages">
<?php echo $output->rendersubtitle('outageslistfuture'); ?>
<?php if (empty($viewbag['unended'])): ?>
<?php if (empty($viewbag['unended'])) : ?>
<p>
<small><?php echo get_string('notfound', 'auth_outage'); ?></small>
</p>
<?php else: ?>
<?php else : ?>
<?php
$table = new planned_table();
$table->show_data($viewbag['unended']);
@@ -64,7 +65,7 @@ echo $viewbag['warning'];
$urlnew->param('starttime', $next);
echo $output->single_button($urlnew, get_string('outagecreate', 'auth_outage'));
if ($default) {
echo ' ' . userdate( $next, get_string('datetimeformat', 'auth_outage'));
echo ' ' . userdate($next, get_string('datetimeformat', 'auth_outage'));
}
}
endif; ?>
@@ -72,11 +73,11 @@ echo $viewbag['warning'];
<section id="section_outage_history">
<?php echo $output->rendersubtitle('outageslistpast'); ?>
<?php if (empty($viewbag['ended'])): ?>
<?php if (empty($viewbag['ended'])) : ?>
<p>
<small><?php echo get_string('notfound', 'auth_outage'); ?></small>
</p>
<?php else: ?>
<?php else : ?>
<?php
$table = new history_table();
$table->show_data($viewbag['ended']);

View File

@@ -12,12 +12,21 @@ If you need to make changes here, remember to update your settings inside Moodle
padding: 0;
position: fixed;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
top: 0;
transition: background 3s ease-out;
width: 100%;
z-index: 9999;
}
#auth_outage_warningbar_box .auth_outage_warningbar_center {
/* fix to prevent existing theme hacks to fix issue */
margin-top: 0;
position: static;
}
#auth_outage_warningbar_box.auth_outage_warning_period {
background:
repeating-linear-gradient(
@@ -66,16 +75,10 @@ If you need to make changes here, remember to update your settings inside Moodle
background-color: #090;
}
.auth_outage_warningbar_center {
margin-top: -35px;
position: relative;
top: 50%;
}
#auth_outage_warningbar_message {
font-size: 200%;
font-weight: bold;
margin: 10px 0;
margin: 0;
}
a.auth_outage_warningbar_box_title {
@@ -87,6 +90,7 @@ a.auth_outage_warningbar_box_finish {
border: 1px solid black;
border-radius: 5px;
color: darkgray;
white-space: nowrap;
font-weight: bold;
margin-left: 10px;
padding-left: 5px;
@@ -126,3 +130,9 @@ body.auth_outage .layout.fullscreen {
body.auth_outage .modal-dialog {
margin: calc(150px + 1.75rem) auto;
}
@media (max-width: 767px) {
#auth_outage_warningbar_message {
font-size: 120%;
}
}

View File

@@ -56,6 +56,8 @@ var authOutageWarningBar = {
},
tickOngoing: function() {
const MINSECS = 60;
if (this.finished) {
return;
}
@@ -78,13 +80,8 @@ var authOutageWarningBar = {
xmlhttp.open("GET", this.checkfinishedurl, true);
xmlhttp.send();
var estimatedServerTime = this.servertime + (Date.now() - this.clienttime);
var sleepSeconds = this.stops - estimatedServerTime; // How long to sleep until it stops.
if (sleepSeconds <= 0) {
sleepSeconds = 5; // It should be back, keep checking every 5 seconds.
} else {
sleepSeconds = Math.min(sleepSeconds, (5 * 60)); // Check at least every 5 minutes.
}
// Checking if the site is back online every 4-6 minutes.
var sleepSeconds = 4 * MINSECS + (2 * MINSECS * Math.random());
setTimeout(function() {
$this.tickOngoing();

View File

@@ -22,6 +22,7 @@
* @copyright 2016 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// phpcs:disable moodle.Commenting.MissingDocblock.File
use auth_outage\local\outagelib;
@@ -46,23 +47,27 @@ if (!$viewbag['static']) {
['target' => '_blank', 'class' => 'auth_outage_warningbar_box_title']
);
if (is_siteadmin()) {
$url = new moodle_url('/auth/outage/finish.php', ['id' => $viewbag['outage']->id]);
$text = html_writer::empty_tag('img', [
'src' => $OUTPUT->image_url('t/check'),
'alt' => get_string('finish', 'auth_outage'),
'class' => 'iconsmall',
]).' '.get_string('finish', 'auth_outage');
$attr = [
'title' => get_string('finish', 'auth_outage'),
'class' => 'auth_outage_warningbar_box_finish',
];
$title .= ' '.html_writer::span(html_writer::link($url, $text, $attr), '', ['id' => 'auth_outage_warningbar_button']);
$link = html_writer::link(
new moodle_url('/auth/outage/finish.php', ['id' => $viewbag['outage']->id]),
$OUTPUT->pix_icon(
't/check',
get_string('finish', 'auth_outage'),
'moodle',
['class' => 'iconsmall']
)
. get_string('finish', 'auth_outage'),
[
'title' => get_string('finish', 'auth_outage'),
'class' => 'auth_outage_warningbar_box_finish',
]
);
$title .= ' ' . html_writer::span($link, '', ['id' => 'auth_outage_warningbar_button']);
}
}
?>
<style>
<?php
readfile($CFG->dirroot.'/auth/outage/views/warningbar/warningbar.css');
readfile($CFG->dirroot . '/auth/outage/views/warningbar/warningbar.css');
echo outagelib::get_config()->css;
?>
</style>
@@ -74,11 +79,11 @@ if (!$viewbag['static']) {
</div>
</div>
<?php if (!$viewbag['static']): ?>
<?php if (!$viewbag['static']) : ?>
<script>
document.body.className += ' auth_outage';
<?php
require(__DIR__.'/warningbar.js');
require(__DIR__ . '/warningbar.js');
$json = json_encode([
'countdown' => $countdown,
'ongoing' => $ongoing,
@@ -90,7 +95,7 @@ if (!$viewbag['static']) {
'preview' => $viewbag['preview'],
'checkfinishedurl' => (string)(new moodle_url('/auth/outage/checkfinished.php')),
]);
echo 'authOutageWarningBar.init('.$json.');';
echo 'authOutageWarningBar.init(' . $json . ');';
?>
</script>
<?php endif;