diff --git a/classes/local/controllers/maintenance_static_page.php b/classes/local/controllers/maintenance_static_page.php index 01c431f..690538f 100644 --- a/classes/local/controllers/maintenance_static_page.php +++ b/classes/local/controllers/maintenance_static_page.php @@ -42,32 +42,10 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class maintenance_static_page { - /** - * Gets the cli maintenance template file location. - * @return string - */ - public static function get_template_file() { - global $CFG; - return $CFG->dataroot.'/climaintenance.template.html'; - } - - /** - * Gets the resources folder in dataroot. - * - * Warning: this folder will be deleted every time the page is regenerated. - * - * @return string - */ - public static function get_resources_folder() { - global $CFG; - // If you change the path, also change file auth/outage/maintenance.php as it does not use this reference. - return $CFG->dataroot.'/auth_outage/climaintenance'; - } - public static function create_from_outage(outage $outage) { global $CFG; $html = file_get_contents($CFG->wwwroot.'/auth/outage/info.php?auth_outage_hide_warning=1&id='.$outage->id); - self::create_from_html($html); + return self::create_from_html($html); } public static function create_from_html($html) { @@ -82,20 +60,69 @@ class maintenance_static_page { $dom->loadHTML($html); libxml_clear_errors(); - self::generate($dom); + return new maintenance_static_page($dom); } - private static function generate(DOMDocument $dom) { + /** @var DOMDocument */ + protected $dom; + + /** @var bool */ + protected $preview = false; + + public function __construct(DOMDocument $dom) { + $this->dom = $dom; + } + + /** + * Gets the cli maintenance template file location. + * @return string + */ + public function get_template_file() { + global $CFG; + if ($this->preview) { + return $this->get_resources_folder().'/climaintenance.html'; + } else { + return $CFG->dataroot.'/climaintenance.template.html'; + } + } + + /** + * Gets the resources folder in dataroot. + * + * Warning: this folder will be deleted every time the page is regenerated. + * + * @return string + */ + public function get_resources_folder() { + global $CFG; + // If you change the path, also change file auth/outage/maintenance.php as it does not use this reference. + if ($this->preview) { + return $CFG->dataroot.'/auth_outage/climaintenance/preview'; + } else { + return $CFG->dataroot.'/auth_outage/climaintenance'; + } + } + + public function generate() { self::prepare_dataroot(); - self::remove_script_tags($dom); - self::update_link_stylesheet($dom); - self::update_link_favicon($dom); - self::update_images($dom); - file_put_contents(self::get_template_file(), $dom->saveHTML()); + 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()); } - private static function remove_script_tags(DOMDocument $dom) { - $scripts = $dom->getElementsByTagName('script'); + /** + * @param boolean $preview + * @return maintenance_static_page + */ + public function set_preview($preview) { + $this->preview = $preview; + return $this; + } + + private function remove_script_tags() { + $scripts = $this->dom->getElementsByTagName('script'); // List items to remove without changing the DOM. $remove = []; foreach ($scripts as $node) { @@ -107,7 +134,7 @@ class maintenance_static_page { } } - private static function prepare_dataroot() { + private function prepare_dataroot() { $dir = self::get_resources_folder(); if (is_dir($dir)) { self::delete_directory_recursively($dir); @@ -115,7 +142,7 @@ class maintenance_static_page { mkdir($dir, 0775, true); } - private static function delete_directory_recursively($dir) { + private function delete_directory_recursively($dir) { // It should never come from user, but protect against possible attacks anyway. $dir = realpath($dir); $safedir = self::get_resources_folder(); @@ -145,8 +172,8 @@ class maintenance_static_page { rmdir($dir); } - private static function update_link_stylesheet(DOMDocument $dom) { - $links = $dom->getElementsByTagName('link'); + private function update_link_stylesheet() { + $links = $this->dom->getElementsByTagName('link'); foreach ($links as $link) { $rel = $link->getAttribute("rel"); @@ -158,8 +185,8 @@ class maintenance_static_page { } } - private static function update_link_favicon(DOMDocument $dom) { - $links = $dom->getElementsByTagName('link'); + private function update_link_favicon() { + $links = $this->dom->getElementsByTagName('link'); foreach ($links as $link) { $rel = $link->getAttribute("rel"); @@ -171,8 +198,8 @@ class maintenance_static_page { } } - private static function update_images(DOMDocument $dom) { - $links = $dom->getElementsByTagName('img'); + private function update_images() { + $links = $this->dom->getElementsByTagName('img'); foreach ($links as $link) { $src = $link->getAttribute("src"); @@ -183,7 +210,7 @@ class maintenance_static_page { } } - private static function prepare_url($url, $type) { + private function prepare_url($url, $type) { global $CFG; if (!preg_match('#^http(s)?://#', $url)) { @@ -205,7 +232,10 @@ class maintenance_static_page { $filepath = self::get_resources_folder().'/'.$filename; file_put_contents($filepath, $contents); - $url = (string)new moodle_url('/auth/outage/maintenance.php?file='.$filename); + if ($this->preview) { + $filename = 'preview/'.$filename; + } + $url = (string)new moodle_url('/auth/outage/maintenance.php', ['file' => $filename]); return $url; } } diff --git a/lang/en/auth_outage.php b/lang/en/auth_outage.php index a999efc..a451778 100644 --- a/lang/en/auth_outage.php +++ b/lang/en/auth_outage.php @@ -84,6 +84,7 @@ $string['infohidewarning'] = 'no warning bar'; $string['infountil'] = 'Until:'; $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 ({$a->ip}) and block all other IPs.'; $string['allowedipshasmyip'] = 'Your IP ({$a->ip}) is in the list and you will not be blocked out during an Outage.'; diff --git a/maintenance.php b/maintenance.php index 902b193..cb9187b 100644 --- a/maintenance.php +++ b/maintenance.php @@ -36,9 +36,11 @@ if (isset($_GET['file'])) { $resourcedir = $CFG->dataroot.'/auth_outage/climaintenance'; // Protect against path traversal attacks. - $file = $resourcedir.'/'.basename($_GET['file']); + $file = $resourcedir.'/'.$_GET['file']; if (realpath($file) !== $file) { + // @codingStandardsIgnoreStart error_log('Invalid file: '.$_GET['file']); + // @codingStandardsIgnoreEnd http_response_code(404); die('Not found.'); } @@ -59,9 +61,17 @@ if (isset($_GET['file'])) { if (isset($_GET['debug'])) { // Use auth/outage/maintenance.php?debug to preview how it will render without triggering maintenance mode. + require_once(__DIR__.'/../../config.php'); - $outage = outagedb::get_next_starting(); - maintenance_static_page::create_from_outage($outage); - readfile(maintenance_static_page::get_template_file()); + $id = optional_param('id', null, PARAM_INT); + $outage = is_null($id) ? outagedb::get_next_starting() : outagedb::get_by_id($id); + if (is_null($outage)) { + throw new invalid_parameter_exception('Outage not found.'); + } + + $page = maintenance_static_page::create_from_outage($outage); + $page->set_preview(true); + $page->generate(); + readfile($page->get_template_file()); return; } \ No newline at end of file diff --git a/tests/phpunit/local/controllers/maintenance_static_page_test.php b/tests/phpunit/local/controllers/maintenance_static_page_test.php index 815cc0a..38b0537 100644 --- a/tests/phpunit/local/controllers/maintenance_static_page_test.php +++ b/tests/phpunit/local/controllers/maintenance_static_page_test.php @@ -38,23 +38,20 @@ require_once(__DIR__.'/../../base_testcase.php'); * @SuppressWarnings(public) Allow as many methods as needed. */ class maintenance_static_page_test extends auth_outage_base_testcase { - /** - * Ensures the template file does not exist when starting a test. - */ - public function setUp() { - $file = maintenance_static_page::get_template_file(); - if (file_exists($file)) { - if (is_file($file)) { - unlink($file); - } else { - self::fail('Invalid temp file: '.$file); - } - } - } - public function test_templatefile() { global $CFG; - self::assertSame($CFG->dataroot.'/climaintenance.template.html', maintenance_static_page::get_template_file()); + $page = maintenance_static_page::create_from_html(''); + self::assertSame($CFG->dataroot.'/climaintenance.template.html', $page->get_template_file()); + $page->set_preview(true); + self::assertSame($CFG->dataroot.'/auth_outage/climaintenance/preview/climaintenance.html', $page->get_template_file()); + } + + public function test_resourcesfolder() { + global $CFG; + $page = maintenance_static_page::create_from_html(''); + self::assertSame($CFG->dataroot.'/auth_outage/climaintenance', $page->get_resources_folder()); + $page->set_preview(true); + self::assertSame($CFG->dataroot.'/auth_outage/climaintenance/preview', $page->get_resources_folder()); } public function test_createfromoutage() { @@ -63,18 +60,16 @@ class maintenance_static_page_test extends auth_outage_base_testcase { public function test_createfromhtml() { $html = "\nTitleContent"; - maintenance_static_page::create_from_html($html); - $generated = trim(file_get_contents(maintenance_static_page::get_template_file())); - self::assertSame($html, $generated); + self::assertSame($html, $this->generated_page_html($html)); } public function test_removescripttags() { $html = "\n". 'Title'. 'Content'; - maintenance_static_page::create_from_html($html); + maintenance_static_page::create_from_html($html)->generate(); - $generated = file_get_contents(maintenance_static_page::get_template_file()); + $generated = $this->generated_page_html($html); self::assertNotContains('Title'. 'Content'; - maintenance_static_page::create_from_html($html); - $generated = file_get_contents(maintenance_static_page::get_template_file()); + $generated = $this->generated_page_html($html); self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=', $generated); self::assertNotContains($link1, $generated); @@ -101,8 +95,7 @@ class maintenance_static_page_test extends auth_outage_base_testcase { $html = "\n". 'an imageTitle'. 'Content'; - maintenance_static_page::create_from_html($html); - $generated = file_get_contents(maintenance_static_page::get_template_file()); + $generated = $this->generated_page_html($html); self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=', $generated); self::assertNotContains($link1, $generated); @@ -115,10 +108,30 @@ class maintenance_static_page_test extends auth_outage_base_testcase { $html = "\n". 'Title'. 'Content'; - maintenance_static_page::create_from_html($html); - $generated = file_get_contents(maintenance_static_page::get_template_file()); + $generated = $this->generated_page_html($html); self::assertNotContains($link, $generated); self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=', $generated); } + + public function test_previewpath() { + $link = (string)new moodle_url('/favicon.jpg'); + $html = "\n". + 'Title'. + 'Content'; + $page = maintenance_static_page::create_from_html($html); + $page->set_preview(true); + $page->generate(); + $generated = trim(file_get_contents($page->get_template_file())); + + self::assertNotContains($link, $generated); + self::assertContains('http://www.example.com/moodle/auth/outage/maintenance.php?file=preview%2F', $generated); + } + + private function generated_page_html($html) { + $page = maintenance_static_page::create_from_html($html); + $page->generate(); + $generated = trim(file_get_contents($page->get_template_file())); + return $generated; + } } diff --git a/views/info/content.php b/views/info/content.php index 080f807..542d01f 100644 --- a/views/info/content.php +++ b/views/info/content.php @@ -57,6 +57,10 @@ if ($viewbag['admin']) { ), get_string('infohidewarning', 'auth_outage') ); + $adminlinks[] = html_writer::link( + new moodle_url('/auth/outage/maintenance.php', ['debug' => '', 'id' => $viewbag['outage']->id]), + get_string('infostaticpage', 'auth_outage') + ); $admineditlink = html_writer::link( new moodle_url('/auth/outage/edit.php', ['edit' => $viewbag['outage']->id]),