diff --git a/classes/local/controllers/infopage.php b/classes/local/controllers/infopage.php index 5b30c98..11d4dd5 100644 --- a/classes/local/controllers/infopage.php +++ b/classes/local/controllers/infopage.php @@ -28,11 +28,8 @@ namespace auth_outage\local\controllers; use auth_outage\dml\outagedb; use auth_outage\local\outage; use auth_outage\local\outagelib; -use auth_outage\output\renderer; use coding_exception; use context_system; -use file_exception; -use invalid_state_exception; use moodle_url; defined('MOODLE_INTERNAL') || die(); @@ -51,11 +48,6 @@ class infopage { */ private $outage; - /** - * @var bool Flags if a static version of this page should be displayed (maintenance mode). - */ - private $static; - /** * infopage_controller constructor. * @param array $params Parameters to use or null to get from Moodle API (request). @@ -64,14 +56,12 @@ class infopage { if (is_null($params)) { $params = [ 'id' => optional_param('id', null, PARAM_INT), - 'static' => optional_param('static', false, PARAM_BOOL), 'outage' => null, ]; } else { $defaults = [ 'id' => null, 'outage' => null, - 'static' => false, ]; $params = array_merge($defaults, $params); } @@ -79,98 +69,6 @@ class infopage { $this->set_parameters($params); } - /** - * Given the HTML code for the static page, find the outage id for that page. - * @param string $html Static info page HTML. - * @return int|null Outage id or null if not found. - * @throws coding_exception - */ - public static function find_outageid_from_infopage($html) { - if (!is_string($html)) { - throw new coding_exception('$html must be a string.', gettype($html)); - } - - $output = []; - if (preg_match('/data-outage-id="(?P\d+)"/', $html, $output)) { - return (int)$output['id']; - } - return null; - } - - /** - * Saves a static info page for the given outage. - * @param outage $outage Outage to generate the info page. - * @param string $file File to save the static info page. - * @throws coding_exception - * @throws file_exception - * @throws invalid_state_exception - */ - public static function save_static_page(outage $outage, $file) { - if (!is_string($file)) { - throw new coding_exception('$file is not a string.', $file); - } - - $info = new infopage(['outage' => $outage, 'static' => true]); - $html = $info->get_output(); - - self::save_static_page_sanitycheck($html); - - $dir = dirname($file); - if (!file_exists($dir) || !is_dir($dir)) { - throw new file_exception('Directory must exists: '.$dir); - } - file_put_contents($file, $html); - } - - /** - * Updates the static info page by (re)creating or deleting it as needed. - * @param outage|null $outage Outage or null if no scheduled outage. - * @param string|null $file File to update. Null to use default. - * @throws coding_exception - * @throws file_exception - */ - public static function update_static_page($outage, $file = null) { - if (is_null($file)) { - $file = self::get_defaulttemplatefile(); - } - if (!is_string($file)) { - throw new coding_exception('$file is not a string.', $file); - } - if (!is_null($outage) && !($outage instanceof outage)) { - throw new coding_exception('$outage must be null or an outage object.'); - } - - if (is_null($outage)) { - if (file_exists($file)) { - unlink($file); - } - } else { - self::save_static_page($outage, $file); - } - } - - /** - * Gets the default template file to use for static info page. - * @return string The default template file to use for static info page. - */ - public static function get_defaulttemplatefile() { - global $CFG; - return $CFG->dataroot.'/climaintenance.template.html'; - } - - /** - * Ensures the data to create the page is valid. - * It should never be invalid, but if it is we will get a blank page while the maintenance is ongoing and the - * system administrators may become frustrated to understand why it is not working, let's not provoke them! - * @param string $html The HTML to check. - * @throws invalid_state_exception - */ - public static function save_static_page_sanitycheck($html) { - if (!is_string($html) || (trim($html) == '') || (trim(html_to_text($html)) == '')) { - throw new invalid_state_exception('Sanity check failed. Invalid contents on $html.'); - } - } - /** * Generates and returns the HTML for the info page. * @return string HTML for the info page. @@ -187,14 +85,6 @@ class infopage { return $output; } - /** - * Checks if this page should have admin options, taking in consideration it should happen if generating a static page. - * @return bool True if it should display admin options. - */ - public function has_admin_options() { - return (!$this->static && is_siteadmin()); - } - /** * Generates and outputs the HTML for the info page. * @uses redirect @@ -203,11 +93,7 @@ class infopage { global $PAGE, $CFG, $OUTPUT; if (is_null($this->outage)) { - if ($this->static) { - throw new coding_exception('Cannot render a static info page without an outage.'); - } else { - redirect(new moodle_url('/')); - } + redirect(new moodle_url('/')); } $viewbag = [ @@ -216,24 +102,19 @@ class infopage { ]; $PAGE->set_context(context_system::instance()); - if ($this->static) { - $viewbag['admin'] = false; - renderer::get()->output_view('info/static.php', $viewbag); - } else { - $PAGE->set_title($this->outage->get_title()); - $PAGE->set_heading($this->outage->get_title()); - $PAGE->set_url(new moodle_url('/auth/outage/info.php')); + $PAGE->set_title($this->outage->get_title()); + $PAGE->set_heading($this->outage->get_title()); + $PAGE->set_url(new moodle_url('/auth/outage/info.php')); - // No hooks injecting into this page, do it manually. - outagelib::inject(); + // No hooks injecting into this page, do it manually. + outagelib::inject(); - echo $OUTPUT->header(); - require($CFG->dirroot.'/auth/outage/views/info/content.php'); + echo $OUTPUT->header(); + 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)) { - echo $OUTPUT->footer(); - } + // Moodle 2.7 did not check for CLI mode, which was fixed later. + if (!($CFG->branch == '27' && CLI_SCRIPT)) { + echo $OUTPUT->footer(); } } @@ -258,6 +139,5 @@ class infopage { } $this->outage = $params['outage']; - $this->static = (bool)$params['static']; } } diff --git a/classes/local/controllers/maintenance_static_page.php b/classes/local/controllers/maintenance_static_page.php index cc0b511..28753cf 100644 --- a/classes/local/controllers/maintenance_static_page.php +++ b/classes/local/controllers/maintenance_static_page.php @@ -28,8 +28,8 @@ namespace auth_outage\local\controllers; use auth_outage\local\outage; use coding_exception; use DOMDocument; -use DOMElement; use invalid_parameter_exception; +use invalid_state_exception; use moodle_url; defined('MOODLE_INTERNAL') || die(); @@ -45,32 +45,49 @@ defined('MOODLE_INTERNAL') || die(); class maintenance_static_page { /** * Creates a page based on the given outage. - * @param outage $outage + * @param outage|null $outage * @return maintenance_static_page + * @throws coding_exception */ - public static function create_from_outage(outage $outage) { + public static function create_from_outage($outage) { global $CFG; - $html = file_get_contents($CFG->wwwroot.'/auth/outage/info.php?auth_outage_hide_warning=1&id='.$outage->id); + + if (!is_null($outage) && !($outage instanceof outage)) { + throw new coding_exception('$outage must be null or an outage object.'); + } + + if (is_null($outage)) { + $html = null; + } else if (PHPUNIT_TEST) { + $html = ''; + } else { + $html = file_get_contents($CFG->wwwroot.'/auth/outage/info.php?auth_outage_hide_warning=1&id='.$outage->id); + } + return self::create_from_html($html); } /** * Creates a page based on the given HTML. - * @param string $html + * @param string|null $html * @return maintenance_static_page * @throws coding_exception */ public static function create_from_html($html) { - if (!is_string($html)) { + if (!is_null($html) && !is_string($html)) { throw new coding_exception('$html is not valid.'); } - $dom = new DOMDocument(); + if (is_null($html)) { + $dom = null; + } else { + $dom = new DOMDocument(); - // Let's assume we have no parsing errors as we cannot rely on a badly-formed page anyway. - libxml_use_internal_errors(true); - $dom->loadHTML($html); - libxml_clear_errors(); + // Let's assume we have no parsing errors as we cannot rely on a badly-formed page anyway. + libxml_use_internal_errors(true); + $dom->loadHTML($html); + libxml_clear_errors(); + } return new maintenance_static_page($dom); } @@ -83,9 +100,13 @@ class maintenance_static_page { /** * maintenance_static_page constructor. - * @param DOMDocument $dom + * @param DOMDocument|null $dom + * @throws coding_exception */ - public function __construct(DOMDocument $dom) { + public function __construct($dom) { + if (!is_null($dom) && !($dom instanceof DOMDocument)) { + throw new coding_exception('$dom must be null or an DOMDocument object.'); + } $this->dom = $dom; } @@ -123,12 +144,21 @@ class maintenance_static_page { * Generates the page. */ public function generate() { - self::prepare_dataroot(); - self::remove_script_tags(); - self::update_link_stylesheet(); - self::update_link_favicon(); - self::update_images(); - file_put_contents(self::get_template_file(), $this->dom->saveHTML()); + self::cleanup(); + + if (!is_null($this->dom)) { + self::remove_script_tags(); + self::update_link_stylesheet(); + self::update_link_favicon(); + self::update_images(); + + $html = $this->dom->saveHTML(); + if (trim($html) == '') { + // Should never happen, but just in case... + throw new invalid_state_exception('Sanity check failed, $html is empty.'); + } + file_put_contents(self::get_template_file(), $html); + } } /** @@ -159,12 +189,20 @@ class maintenance_static_page { /** * Clean up the dataroot as needed. */ - private function prepare_dataroot() { - $dir = self::get_resources_folder(); - if (is_dir($dir)) { - self::delete_directory_recursively($dir); + private function cleanup() { + $resources = $this->get_resources_folder(); + if (is_dir($resources)) { + self::delete_directory_recursively($resources); + } + + $template = $this->get_template_file(); + if (is_file($template)) { + unlink($template); + } + + if (!is_null($this->dom)) { + mkdir($resources, 0775, true); } - mkdir($dir, 0775, true); } /** diff --git a/classes/local/outagelib.php b/classes/local/outagelib.php index 5906d21..8966744 100644 --- a/classes/local/outagelib.php +++ b/classes/local/outagelib.php @@ -26,7 +26,7 @@ namespace auth_outage\local; use auth_outage\dml\outagedb; -use auth_outage\local\controllers\infopage; +use auth_outage\local\controllers\maintenance_static_page; use auth_outage\output\renderer; use coding_exception; use Exception; @@ -156,7 +156,7 @@ class outagelib { if (is_null($outage)) { $outage = outagedb::get_next_starting(); } - infopage::update_static_page($outage); + maintenance_static_page::create_from_outage($outage)->generate(); self::update_climaintenance_code($outage); self::update_maintenance_later($outage); } diff --git a/db/uninstall.php b/db/uninstall.php index 643fad8..4da1ad7 100644 --- a/db/uninstall.php +++ b/db/uninstall.php @@ -22,6 +22,8 @@ * @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; + defined('MOODLE_INTERNAL') || die(); /** @@ -36,13 +38,8 @@ function xmldb_auth_outage_uninstall() { // Delete all outage events. $DB->delete_records('event', ['eventtype' => 'auth_outage']); - // Delete template file. - $template = auth_outage\local\controllers\infopage::get_defaulttemplatefile(); - if (file_exists($template)) { - if (!unlink($template)) { - throw new moodle_exception('Cannot remote maintenance template file: '.$template); - } - } + // Delete files. + maintenance_static_page::create_from_outage(null)->generate(); // Remove 'maintenance later' which could have been set for the next outage. unset_config('maintenance_later'); diff --git a/tests/phpunit/installation_test.php b/tests/phpunit/installation_test.php index 7d19d71..4a51d28 100644 --- a/tests/phpunit/installation_test.php +++ b/tests/phpunit/installation_test.php @@ -51,7 +51,7 @@ class installation_test extends auth_outage_base_testcase { global $CFG, $DB; $this->resetAfterTest(); - $this->setAdminUser(); + static::setAdminUser(); $dbman = $DB->get_manager(); // Create a future outage with autostart. @@ -77,7 +77,7 @@ class installation_test extends auth_outage_base_testcase { // Check ... self::assertSame(0, $DB->count_records_select('event', "eventtype = 'auth_outage'", null), 'The outage events were not removed.'); - self::assertFalse(file_exists(auth_outage\local\controllers\infopage::get_defaulttemplatefile()), + 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. diff --git a/tests/phpunit/local/controllers/infopage_test.php b/tests/phpunit/local/controllers/infopage_test.php index 09d0b18..1041175 100644 --- a/tests/phpunit/local/controllers/infopage_test.php +++ b/tests/phpunit/local/controllers/infopage_test.php @@ -23,10 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -use auth_outage\dml\outagedb; use auth_outage\local\controllers\infopage; use auth_outage\local\outage; -use auth_outage\task\update_static_page; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__.'/../../base_testcase.php'); @@ -41,20 +39,6 @@ require_once(__DIR__.'/../../base_testcase.php'); * @SuppressWarnings(public) Allow as many methods as needed. */ class infopagecontroller_test extends auth_outage_base_testcase { - /** - * Ensures the template file does not exist when starting a test. - */ - public function setUp() { - $file = infopage::get_defaulttemplatefile(); - if (file_exists($file)) { - if (is_file($file)) { - unlink($file); - } else { - self::fail('Invalid temp file: '.$file); - } - } - } - /** * Tests the constructor. */ @@ -95,226 +79,6 @@ class infopagecontroller_test extends auth_outage_base_testcase { new infopage(['outage' => 'My outage']); } - /** - * Tests the static page contents. - */ - public function test_staticpage_output() { - global $PAGE; - $this->resetAfterTest(true); - - $PAGE->set_context(context_system::instance()); - $now = time(); - $outage = new outage([ - 'id' => 1, - 'starttime' => $now + (60 * 60), - 'warntime' => $now - (60 * 60), - 'stoptime' => $now + (2 * 60 * 60), - 'title' => 'Outage Title at {{start}}', - 'description' => 'This is an important outage, starting at {{start}}.', - ]); - $info = new infopage(['static' => true, 'outage' => $outage]); - $html = $info->get_output(); - // Must find... - self::assertContains('', $html); - self::assertContains('', $html); // Issue #53. - self::assertContains('', $html); - self::assertContains($outage->get_title(), $html); - self::assertContains($outage->get_description(), $html); - // Must not find... - self::assertNotContains('id, infopage::find_outageid_from_infopage($html)); - } - - /** - * Tests the static page file contents. - */ - public function test_staticpage_file() { - $now = time(); - $outage = new outage([ - 'id' => 1, - 'warntime' => $now - 100, - 'starttime' => $now + 100, - 'stoptime' => $now + 200, - 'title' => 'Title', - 'description' => 'Description', - ]); - $file = infopage::get_defaulttemplatefile(); - infopage::save_static_page($outage, $file); - self::assertFileExists($file); - - $id = infopage::find_outageid_from_infopage(file_get_contents($file)); - self::assertSame($outage->id, $id); - - unlink($file); - } - - /** - * Checks if the default template file is a valid string with the name templage. - */ - public function test_getdefaulttemplatefile() { - $file = infopage::get_defaulttemplatefile(); - self::assertTrue(is_string($file)); - self::assertContains('template', $file); - } - - /** - * Tests infopage::find_outageid_from_infopage() with an invalid parameter. - */ - public function test_findoutageid_notstring() { - $this->set_expected_exception('coding_exception'); - infopage::find_outageid_from_infopage(new stdClass()); - } - - /** - * Tests infopage::find_outageid_from_infopage() when the id is not found. - */ - public function test_findoutageid_notfound() { - self::assertNull( - infopage::find_outageid_from_infopage( - 'Hello world!Done.' - ) - ); - } - - /** - * Tests infopage::save_static_page() with an invalid parameter. - */ - public function test_savestaticpage_filenotstring() { - $this->set_expected_exception('coding_exception'); - infopage::save_static_page(new outage(), 1); - } - - /** - * Tests infopage::save_static_page() with an invalid filename. - */ - public function test_savestaticpage_fileinvalid() { - global $CFG; - $outage = new outage([ - 'id' => 1, - 'warntime' => time() - 100, - 'starttime' => time() + 100, - 'stoptime' => time() + 200, - 'title' => 'Title', - 'description' => 'Description', - ]); - - $this->set_expected_exception('file_exception'); - infopage::save_static_page($outage, $CFG->dataroot.'/someinvalidpath/someinvalidfile'); - } - - /** - * Tests infopage::test_sanity_notstring() with invalid contents: an int. - */ - public function test_sanity_notstring() { - $this->set_expected_exception('invalid_state_exception'); - infopage::save_static_page_sanitycheck(404); - } - - /** - * Tests infopage::test_sanity_notstring() with invalid contents: an empty string. - */ - public function test_sanity_empty() { - $this->set_expected_exception('invalid_state_exception'); - infopage::save_static_page_sanitycheck(' '); - } - - /** - * Tests infopage::test_sanity_notstring() with invalid contents: an empty HTML. - */ - public function test_sanity_clearhtml() { - $this->set_expected_exception('invalid_state_exception'); - infopage::save_static_page_sanitycheck(' '); - } - - /** - * Tests updating the static page. - */ - public function test_updatestaticpage() { - $this->resetAfterTest(true); - self::setAdminUser(); - $file = infopage::get_defaulttemplatefile(); - $now = time(); - $outage = new outage([ - 'autostart' => false, - 'warntime' => $now - 100, - 'starttime' => $now + 100, - 'stoptime' => $now + 200, - 'title' => 'Title', - 'description' => 'Description', - ]); - $id = outagedb::save($outage); - // The method update_static_page should have been called by save(). - self::assertFileExists($file); - $idfound = infopage::find_outageid_from_infopage(file_get_contents($file)); - self::assertSame($id, $idfound); - unlink($file); - } - - /** - * Tests updating the static page when there is no outage. - */ - public function test_updatestaticpage_nooutage() { - infopage::update_static_page(null); - } - - /** - * Tests updating the static page when there is no outage but the file existed before. - */ - public function test_updatestaticpage_hasfile() { - $file = infopage::get_defaulttemplatefile(); - touch($file); - self::assertFileExists($file); - infopage::update_static_page(null); - self::assertFileNotExists($file); - } - - /** - * Tests updating the static page with an invalid filename. - */ - public function test_updatestaticpage_invalidfile() { - $this->set_expected_exception('coding_exception'); - infopage::update_static_page(null, 123); - } - - /** - * Tests updating the static page with an invalid outage. - */ - public function test_updatestaticpage_invalidoutage() { - $this->set_expected_exception('coding_exception'); - infopage::update_static_page("foobar"); - } - - /** - * Checks if infopage::has_admin_options() works as expected. - */ - public function test_hasadminoptions() { - $this->resetAfterTest(true); - $static = new infopage(['static' => true]); - $nonstatic = new infopage(['static' => false]); - // Now I am guest. - self::assertFalse(is_siteadmin()); - self::assertFalse($static->has_admin_options()); - self::assertFalse($nonstatic->has_admin_options()); - // Now I am admin. - self::setAdminUser(); - self::assertTrue(is_siteadmin()); - self::assertFalse($static->has_admin_options()); - self::assertTrue($nonstatic->has_admin_options()); - } - - /** - * Tries to output a static page without a defined outage. - */ - public function test_output_static_nooutage() { - $info = new infopage(['static' => true]); - - $this->set_expected_exception('coding_exception'); - $info->output(); - } - /** * We should have an exception because CLI cannot redirect. */ @@ -342,14 +106,4 @@ class infopagecontroller_test extends auth_outage_base_testcase { $output = $info->get_output(); self::assertContains('auth_outage_info', $output); } - - /** - * 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(); - } } diff --git a/tests/phpunit/local/controllers/maintenance_static_page_test.php b/tests/phpunit/local/controllers/maintenance_static_page_test.php index 86a66d3..4fa27e3 100644 --- a/tests/phpunit/local/controllers/maintenance_static_page_test.php +++ b/tests/phpunit/local/controllers/maintenance_static_page_test.php @@ -24,6 +24,7 @@ */ use auth_outage\local\controllers\maintenance_static_page; +use auth_outage\task\update_static_page; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__.'/../../base_testcase.php'); @@ -139,4 +140,26 @@ class maintenance_static_page_test extends auth_outage_base_testcase { $generated = trim(file_get_contents($page->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); + } } diff --git a/tests/phpunit/local/outagelib_test.php b/tests/phpunit/local/outagelib_test.php index 2b7a069..c39e6db 100644 --- a/tests/phpunit/local/outagelib_test.php +++ b/tests/phpunit/local/outagelib_test.php @@ -409,7 +409,7 @@ EOT; outagedb::save($outage); // The method outagelib::prepare_next_outage() should have been called by save(). - foreach ([infopage::get_defaulttemplatefile(), $CFG->dataroot.'/climaintenance.php'] as $file) { + foreach ([$CFG->dataroot.'/climaintenance.template.html', $CFG->dataroot.'/climaintenance.php'] as $file) { self::assertFileExists($file); unlink($file); } diff --git a/views/info/static.php b/views/info/static.php deleted file mode 100644 index 3b29898..0000000 --- a/views/info/static.php +++ /dev/null @@ -1,72 +0,0 @@ -. - -/** - * View included by the renderer to output the static outage information page. - * - * @package auth_outage - * @author Daniel Thee Roperto - * @copyright 2016 Catalyst IT - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -use auth_outage\output\renderer; - -defined('MOODLE_INTERNAL') || die(); - -global $SITE; - -// The Meta Refresh will ensure the page keeps refreshing every 5 minutes until outage is over. -?> - - - - <?php echo strip_tags($SITE->fullname); ?> - - - - - - - -render_warningbar($viewbag['outage'], $viewbag['outage']->starttime, true, false); -?> - -
-

fullname); ?>

-
- -
-

get_title(); ?>

- render_view('info/content.php', $viewbag); ?> -
- - - - -