mirror of
https://github.com/catalyst/moodle-auth_outage.git
synced 2026-05-17 05:48:43 +02:00
Merge branch 'issue9-outagebar'
This commit is contained in:
@@ -16,6 +16,8 @@ This is a Moodle plugin which makes the student experience of planned outages ni
|
||||
|
||||
The main idea is that instead of an outage being a very booleon on/off situation, this plugin creates the concept of graduated outages where at predefined times before 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.
|
||||
|
||||

|
||||
|
||||
|
||||
Why it is an auth plugin?
|
||||
-------------------------
|
||||
|
||||
7
auth.php
7
auth.php
@@ -49,4 +49,11 @@ class auth_plugin_outage extends auth_plugin_base
|
||||
public function user_login($username, $password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login page hook.
|
||||
*/
|
||||
public function loginpage_hook() {
|
||||
\auth_outage\outagelib::inject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace auth_outage\models;
|
||||
|
||||
use auth_outage\outagelib;
|
||||
|
||||
class outage
|
||||
{
|
||||
class outage {
|
||||
/**
|
||||
* @var int Outage ID (auto generated by the DB).
|
||||
*/
|
||||
@@ -85,9 +84,30 @@ class outage
|
||||
|
||||
if (is_object($data) || is_array($data)) {
|
||||
outagelib::data2object($data, $this);
|
||||
|
||||
// FIXME types are wrong. Is this behaving as expected?
|
||||
$fields = ['createdby', 'id', 'lastmodified', 'modifiedby', 'starttime', 'stoptime', 'warningduration'];
|
||||
foreach ($fields as $f) {
|
||||
$this->$f = ($this->$f === null) ? null : (int)$this->$f;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('$data must be null (default), an array or an object.');
|
||||
}
|
||||
|
||||
public function is_ongoing($time = null) {
|
||||
if ($time === null) {
|
||||
$time = time();
|
||||
}
|
||||
if (!is_int($time) || ($time <= 0)) {
|
||||
throw new \InvalidArgumentException('$time must be an positive int.');
|
||||
}
|
||||
if (is_null($this->starttime) || is_null($this->stoptime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (($this->starttime <= $time) && ($time < $this->stoptime));
|
||||
}
|
||||
}
|
||||
@@ -135,4 +135,39 @@ final class outagedb {
|
||||
|
||||
$DB->delete_records('auth_outage', ['id' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most important active outage, considering importance as:
|
||||
* - Ongoing outages more important than outages in warning period.
|
||||
* - Outages that start earlier are more important.
|
||||
* - Outages that stop later are more important.
|
||||
* @param int|null $time Timestamp considered to check for outages, null for current date/time.
|
||||
* @return outage|null The outage or null if no active outages were found.
|
||||
*/
|
||||
public static function getactive($time = null) {
|
||||
global $DB;
|
||||
|
||||
if ($time === null) {
|
||||
$time = time();
|
||||
}
|
||||
if (!is_int($time)) {
|
||||
throw new \InvalidArgumentException('$time must be null or an int.');
|
||||
}
|
||||
|
||||
// TODO Is there a way to use Moodle API instead of writing SQL (conditions not equals)?
|
||||
// TODO Query not fully using indexes (starttime + 90)
|
||||
// Gets any active outage (already started or during warning period).
|
||||
// Gets only one record if available, the one that starts(ed) first and that stops last.
|
||||
$data = $DB->get_record_sql('
|
||||
SELECT *
|
||||
FROM {auth_outage}
|
||||
WHERE (starttime - warningduration <= :datetime1 AND stoptime >= :datetime2)
|
||||
ORDER BY starttime ASC, stoptime DESC, title ASC
|
||||
LIMIT 1
|
||||
',
|
||||
['datetime1' => $time, 'datetime2' => $time]
|
||||
);
|
||||
|
||||
return ($data === false) ? null : new \auth_outage\models\outage($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,9 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
* @copyright Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class outagelib
|
||||
{
|
||||
class outagelib {
|
||||
private static $initialized = false;
|
||||
|
||||
/**
|
||||
* Initializes admin pages for outage.
|
||||
*
|
||||
@@ -39,9 +40,41 @@ class outagelib
|
||||
global $PAGE;
|
||||
admin_externalpage_setup('auth_outage_manage');
|
||||
$PAGE->set_url(new \moodle_url('/auth/outage/list.php'));
|
||||
return self::get_renderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the outage renderer.
|
||||
* @return \renderer_base
|
||||
*/
|
||||
public static function get_renderer() {
|
||||
global $PAGE;
|
||||
return $PAGE->get_renderer('auth_outage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Will check for ongoing or warning outages and will attach the message bar as required.
|
||||
*/
|
||||
public static function inject() {
|
||||
global $CFG;
|
||||
global $PAGE;
|
||||
|
||||
// Many hooks can call it, execute only once.
|
||||
if (self::$initialized) {
|
||||
return;
|
||||
}
|
||||
self::$initialized = true;
|
||||
|
||||
if (($active = outagedb::getactive()) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME Code below is raising error at http://moodle.test/my/ for example.
|
||||
// $PAGE->add_body_class('auth_outage_active');
|
||||
$CFG->additionalhtmltopofbody = self::get_renderer()->renderbar($active)
|
||||
. $CFG->additionalhtmltopofbody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads data from an object or array into another object. It ensures no new fields are created in the $obj.
|
||||
*
|
||||
|
||||
BIN
docs/2016-09-06_screenshot.png
Normal file
BIN
docs/2016-09-06_screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
@@ -32,6 +32,8 @@ $string['defaultwarningtimedescription'] = 'Default warning time (in minutes) fo
|
||||
$string['description'] = 'Public description';
|
||||
$string['menudefaults'] = 'Default Settings';
|
||||
$string['menumanage'] = 'Manage';
|
||||
$string['messageoutageongoing'] = 'Our system will be under maintenance until {$a->stop}.';
|
||||
$string['messageoutagewarning'] = 'There is an scheduled downtime from {$a->start} until {$a->stop}.';
|
||||
$string['modify'] = 'Modify';
|
||||
$string['modifyoutage'] = 'Modify Outage';
|
||||
$string['outagecreate'] = 'Create Outage';
|
||||
|
||||
10
lib.php
10
lib.php
@@ -23,3 +23,13 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
// FIXME hook not installing in courses/index.php page.
|
||||
|
||||
function auth_outage_extend_navigation_user() {
|
||||
\auth_outage\outagelib::inject();
|
||||
}
|
||||
|
||||
function auth_outage_extend_navigation_frontpage() {
|
||||
\auth_outage\outagelib::inject();
|
||||
}
|
||||
|
||||
30
renderer.php
30
renderer.php
@@ -29,8 +29,7 @@ if (!defined('MOODLE_INTERNAL')) {
|
||||
* @copyright Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class auth_outage_renderer extends plugin_renderer_base
|
||||
{
|
||||
class auth_outage_renderer extends plugin_renderer_base {
|
||||
public function rendersubtitle($subtitlekey) {
|
||||
if (!is_string($subtitlekey)) {
|
||||
throw new \InvalidArgumentException('$subtitle is not a string.');
|
||||
@@ -125,4 +124,31 @@ class auth_outage_renderer extends plugin_renderer_base
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function renderbar($outage) {
|
||||
global $PAGE;
|
||||
|
||||
$PAGE->requires->css(new moodle_url('/auth/outage/res/outage.css'));
|
||||
|
||||
$start = userdate($outage->starttime, get_string('strftimedatetimeshort'));
|
||||
$stop = userdate($outage->stoptime, get_string('strftimedatetimeshort'));
|
||||
|
||||
$message = get_string(
|
||||
$outage->is_ongoing() ? 'messageoutageongoing' : 'messageoutagewarning',
|
||||
'auth_outage',
|
||||
['start' => $start, 'stop' => $stop]
|
||||
);
|
||||
|
||||
return
|
||||
html_writer::div(
|
||||
html_writer::div(
|
||||
html_writer::div($outage->title, 'auth_outage_warningbar_box_title')
|
||||
. html_writer::div($message, 'auth_outage_warningbar_box_message'),
|
||||
'auth_outage_warningbar_box'
|
||||
),
|
||||
'auth_outage_warningbar'
|
||||
)
|
||||
.
|
||||
html_writer::div(' ', 'auth_outage_warningbar_spacer');
|
||||
}
|
||||
}
|
||||
|
||||
35
res/outage.css
Normal file
35
res/outage.css
Normal file
@@ -0,0 +1,35 @@
|
||||
.auth_outage_warningbar {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 90px;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.auth_outage_warningbar_box {
|
||||
border-top: 2px dashed #a00000;
|
||||
border-bottom: 2px dashed #a00000;
|
||||
background-color: #ffcccc;
|
||||
color: #a00000;
|
||||
}
|
||||
|
||||
.auth_outage_warningbar_box_title {
|
||||
font-size: 200%;
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.auth_outage_warningbar_box_message {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.navbar.navbar-fixed-top {
|
||||
top: 90px;
|
||||
}
|
||||
|
||||
.auth_outage_warningbar_spacer {
|
||||
height: 80px;
|
||||
}
|
||||
@@ -25,7 +25,6 @@
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
// FIXME If plugin not installed, it is still generating the category Outage under Auth Plugins.
|
||||
|
||||
if ($hassiteconfig) {
|
||||
// Configure default settings page.
|
||||
$settings->visiblename = get_string('menudefaults', 'auth_outage');
|
||||
|
||||
@@ -28,8 +28,7 @@ use auth_outage\models\outage;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
class outage_test extends basic_testcase
|
||||
{
|
||||
class outage_test extends basic_testcase {
|
||||
public function test_constructor() {
|
||||
$outage = new outage();
|
||||
// Very important, this should never change.
|
||||
@@ -39,4 +38,38 @@ class outage_test extends basic_testcase
|
||||
self::assertNull($v);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_isongoing() {
|
||||
$now = time();
|
||||
|
||||
// In the past.
|
||||
$outage = new outage([
|
||||
'starttime' => $now + (-3 * 60 * 60),
|
||||
'stoptime' => $now + (-2 * 60 * 60),
|
||||
'warningduration' => 2 * 60 * 60,
|
||||
'title' => '',
|
||||
'description' => ''
|
||||
]);
|
||||
self::assertFalse($outage->is_ongoing($now));
|
||||
|
||||
// In the present (ongoing).
|
||||
$outage = new outage([
|
||||
'starttime' => $now + (-1 * 60 * 60),
|
||||
'stoptime' => $now + (1 * 60 * 60),
|
||||
'warningduration' => 2 * 60 * 60,
|
||||
'title' => '',
|
||||
'description' => ''
|
||||
]);
|
||||
self::assertTrue($outage->is_ongoing($now));
|
||||
|
||||
// In the future.
|
||||
$outage = new outage([
|
||||
'starttime' => $now + (1 * 60 * 60),
|
||||
'stoptime' => $now + (2 * 60 * 60),
|
||||
'warningduration' => 2 * 60 * 60,
|
||||
'title' => '',
|
||||
'description' => ''
|
||||
]);
|
||||
self::assertFalse($outage->is_ongoing($now));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ use auth_outage\outagedb;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
class outagedb_test extends advanced_testcase
|
||||
{
|
||||
class outagedb_test extends advanced_testcase {
|
||||
/**
|
||||
* Make sure we can save and update.
|
||||
*/
|
||||
@@ -95,7 +94,6 @@ class outagedb_test extends advanced_testcase
|
||||
* Perform some tests on the data itself, checking values after inserted and updated.
|
||||
*/
|
||||
public function test_basiccrud() {
|
||||
return;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create some outages.
|
||||
@@ -134,6 +132,64 @@ class outagedb_test extends advanced_testcase
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getactive() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Have a consistent time for now (no seconds variation), helps debugging.
|
||||
$now = time();
|
||||
|
||||
// Should never fail.
|
||||
self::assertEquals([], outagedb::getall(), 'Ensure there are no other outages that can affect the test.');
|
||||
self::assertNull(outagedb::getactive($now), 'There should be no active outage at this point.');
|
||||
|
||||
// An outage that starts in the future and is not in warning period.
|
||||
self::saveoutage($now, 2, 3, 1);
|
||||
self::assertNull(outagedb::getactive($now), 'No active outages yet.');
|
||||
|
||||
// An outage that is already in the past.
|
||||
self::saveoutage($now, -3, -2, 1);
|
||||
self::assertNull(outagedb::getactive($now), 'No active outages yet.');
|
||||
|
||||
// An outage in warning period.
|
||||
$activeid = self::saveoutage($now, 1, 2, 2);
|
||||
self::assertSame($activeid, outagedb::getactive($now)->id, 'Wrong active outage picked.');
|
||||
|
||||
// Another outage in warning period, but ignored as it starts after the previous one.
|
||||
self::saveoutage($now, 2, 3, 3);
|
||||
self::assertSame($activeid, outagedb::getactive($now)->id, 'Wrong active outage picked.');
|
||||
|
||||
// An ongoing outage.
|
||||
$activeid = self::saveoutage($now, -2, 2, 1);
|
||||
self::assertSame($activeid, outagedb::getactive($now)->id, 'Wrong active outage picked.');
|
||||
|
||||
// Another ongoing outage but ignored because it started after the previous one.
|
||||
self::saveoutage($now, -1, 2, 1);
|
||||
self::assertSame($activeid, outagedb::getactive($now)->id, 'Wrong active outage picked.');
|
||||
|
||||
// Another ongoing outage starting at the same time, but ignored as it stops before the previous one.
|
||||
self::saveoutage($now, -2, 1, 1);
|
||||
self::assertSame($activeid, outagedb::getactive($now)->id, 'Wrong active outage picked.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an outage then save it to the database.
|
||||
*
|
||||
* @param $now int Timestamp for now, such as time().
|
||||
* @param $start int In how many hours this outage starts. Can be negative.
|
||||
* @param $stop int In how many hours this outage finishes. Can be negative.
|
||||
* @param $warning int Warning duration in hours.
|
||||
* @return int Id the of created outage.
|
||||
*/
|
||||
private static function saveoutage($now, $start, $stop, $warning) {
|
||||
return outagedb::save(new outage([
|
||||
'starttime' => $now + ($start * 60 * 60),
|
||||
'stoptime' => $now + ($stop * 60 * 60),
|
||||
'warningduration' => ($warning * 60 * 60),
|
||||
'title' => 'Test Outage',
|
||||
'description' => 'Test Outage Description.'
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an outage for tests.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user