diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index 1a63f4f1cfa..5e13a2fc9ad 100644 --- a/admin/settings/appearance.php +++ b/admin/settings/appearance.php @@ -183,7 +183,7 @@ preferences,moodle|/user/preferences.php|preferences', new lang_string('confignavcourselimit', 'admin'), 10, PARAM_INT)); $temp->add(new admin_setting_configcheckbox('usesitenameforsitepages', new lang_string('usesitenameforsitepages', 'admin'), new lang_string('configusesitenameforsitepages', 'admin'), 0)); $temp->add(new admin_setting_configcheckbox('linkadmincategories', new lang_string('linkadmincategories', 'admin'), new lang_string('linkadmincategories_help', 'admin'), 1)); - $temp->add(new admin_setting_configcheckbox('linkcoursesections', new lang_string('linkcoursesections', 'admin'), new lang_string('linkcoursesections_help', 'admin'), 0)); + $temp->add(new admin_setting_configcheckbox('linkcoursesections', new lang_string('linkcoursesections', 'admin'), new lang_string('linkcoursesections_help', 'admin'), 1)); $temp->add(new admin_setting_configcheckbox('navshowfrontpagemods', new lang_string('navshowfrontpagemods', 'admin'), new lang_string('navshowfrontpagemods_help', 'admin'), 1)); $temp->add(new admin_setting_configcheckbox('navadduserpostslinks', new lang_string('navadduserpostslinks', 'admin'), new lang_string('navadduserpostslinks_help', 'admin'), 1)); diff --git a/course/format/topics/lib.php b/course/format/topics/lib.php index 0351be2aacc..c2a33fb42d4 100644 --- a/course/format/topics/lib.php +++ b/course/format/topics/lib.php @@ -121,6 +121,9 @@ class format_topics extends format_base { if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) { $url->param('section', $sectionno); } else { + if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) { + return null; + } $url->set_anchor('section-'.$sectionno); } } diff --git a/course/format/topics/tests/format_topics_test.php b/course/format/topics/tests/format_topics_test.php index 527c44e81ae..796996005c0 100644 --- a/course/format/topics/tests/format_topics_test.php +++ b/course/format/topics/tests/format_topics_test.php @@ -224,4 +224,41 @@ class format_topics_testcase extends advanced_testcase { $this->assertEquals($enddate, $weeksformat->get_default_course_enddate($courseform->get_quick_form())); } + + /** + * Test for get_view_url() to ensure that the url is only given for the correct cases + */ + public function test_get_view_url() { + global $CFG; + $this->resetAfterTest(); + + $linkcoursesections = $CFG->linkcoursesections; + + // Generate a course with two sections (0 and 1) and two modules. + $generator = $this->getDataGenerator(); + $course1 = $generator->create_course(array('format' => 'topics')); + course_create_sections_if_missing($course1, array(0, 1)); + + $data = (object)['id' => $course1->id]; + $format = course_get_format($course1); + $format->update_course_format_options($data); + + // In page. + $CFG->linkcoursesections = 0; + $this->assertNotEmpty($format->get_view_url(null)); + $this->assertNotEmpty($format->get_view_url(0)); + $this->assertNotEmpty($format->get_view_url(1)); + $CFG->linkcoursesections = 1; + $this->assertNotEmpty($format->get_view_url(null)); + $this->assertNotEmpty($format->get_view_url(0)); + $this->assertNotEmpty($format->get_view_url(1)); + + // Navigation. + $CFG->linkcoursesections = 0; + $this->assertNull($format->get_view_url(1, ['navigation' => 1])); + $this->assertNull($format->get_view_url(0, ['navigation' => 1])); + $CFG->linkcoursesections = 1; + $this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1])); + $this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1])); + } } diff --git a/course/format/weeks/lib.php b/course/format/weeks/lib.php index d0f0c9fa37a..cc8996c41c5 100644 --- a/course/format/weeks/lib.php +++ b/course/format/weeks/lib.php @@ -124,6 +124,9 @@ class format_weeks extends format_base { if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) { $url->param('section', $sectionno); } else { + if (empty($CFG->linkcoursesections) && !empty($options['navigation'])) { + return null; + } $url->set_anchor('section-'.$sectionno); } } diff --git a/course/format/weeks/tests/format_weeks_test.php b/course/format/weeks/tests/format_weeks_test.php index 32a25f6106b..522ff22549e 100644 --- a/course/format/weeks/tests/format_weeks_test.php +++ b/course/format/weeks/tests/format_weeks_test.php @@ -232,4 +232,41 @@ class format_weeks_testcase extends advanced_testcase { $this->assertEquals($enddate, $weeksformat->get_default_course_enddate($courseform->get_quick_form())); } + /** + * Test for get_view_url() to ensure that the url is only given for the correct cases + */ + public function test_get_view_url() { + global $CFG; + $this->resetAfterTest(); + + $linkcoursesections = $CFG->linkcoursesections; + + // Generate a course with two sections (0 and 1) and two modules. + $generator = $this->getDataGenerator(); + $course1 = $generator->create_course(array('format' => 'weeks')); + course_create_sections_if_missing($course1, array(0, 1)); + + $data = (object)['id' => $course1->id]; + $format = course_get_format($course1); + $format->update_course_format_options($data); + + // In page. + $CFG->linkcoursesections = 0; + $this->assertNotEmpty($format->get_view_url(null)); + $this->assertNotEmpty($format->get_view_url(0)); + $this->assertNotEmpty($format->get_view_url(1)); + $CFG->linkcoursesections = 1; + $this->assertNotEmpty($format->get_view_url(null)); + $this->assertNotEmpty($format->get_view_url(0)); + $this->assertNotEmpty($format->get_view_url(1)); + + // Navigation. + $CFG->linkcoursesections = 0; + $this->assertNull($format->get_view_url(1, ['navigation' => 1])); + $this->assertNull($format->get_view_url(0, ['navigation' => 1])); + $CFG->linkcoursesections = 1; + $this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1])); + $this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1])); + } + } diff --git a/course/tests/courseformat_test.php b/course/tests/courseformat_test.php index 5f37025ff83..dd7b1a94dfe 100644 --- a/course/tests/courseformat_test.php +++ b/course/tests/courseformat_test.php @@ -162,6 +162,44 @@ class core_course_courseformat_testcase extends advanced_testcase { $format = course_get_format((object)['format' => 'testlegacy']); $this->assertTrue($format->supports_news()); } + + /** + * Test for get_view_url() to ensure that the url is only given for the correct cases + */ + public function test_get_view_url() { + global $CFG; + $this->resetAfterTest(); + + $linkcoursesections = $CFG->linkcoursesections; + + // Generate a course with two sections (0 and 1) and two modules. Course format is set to 'testformat'. + // This will allow us to test the default implementation of get_view_url. + $generator = $this->getDataGenerator(); + $course1 = $generator->create_course(array('format' => 'testformat')); + course_create_sections_if_missing($course1, array(0, 1)); + + $data = (object)['id' => $course1->id]; + $format = course_get_format($course1); + $format->update_course_format_options($data); + + // In page. + $CFG->linkcoursesections = 0; + $this->assertNotEmpty($format->get_view_url(null)); + $this->assertNotEmpty($format->get_view_url(0)); + $this->assertNotEmpty($format->get_view_url(1)); + $CFG->linkcoursesections = 1; + $this->assertNotEmpty($format->get_view_url(null)); + $this->assertNotEmpty($format->get_view_url(0)); + $this->assertNotEmpty($format->get_view_url(1)); + + // Navigation. + $CFG->linkcoursesections = 0; + $this->assertNull($format->get_view_url(1, ['navigation' => 1])); + $this->assertNull($format->get_view_url(0, ['navigation' => 1])); + $CFG->linkcoursesections = 1; + $this->assertNotEmpty($format->get_view_url(1, ['navigation' => 1])); + $this->assertNotEmpty($format->get_view_url(0, ['navigation' => 1])); + } } /** diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index a693075ba37..a9abd6c6106 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2916,5 +2916,21 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2017051501.05); } + if ($oldversion < 2017051501.09) { + + // This script in included in each major version upgrade process so make sure we don't run it twice. + if (empty($CFG->linkcoursesectionsupgradescriptwasrun)) { + // Check if the site is using a boost-based theme. + // If value of 'linkcoursesections' is set to the old default value, change it to the new default. + if (upgrade_theme_is_from_family('boost', $CFG->theme)) { + set_config('linkcoursesections', 1); + } + set_config('linkcoursesectionsupgradescriptwasrun', 1); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2017051501.09); + } + return true; } diff --git a/lib/tests/upgradelib_test.php b/lib/tests/upgradelib_test.php index dd684ad5a7c..3681f74f2cf 100644 --- a/lib/tests/upgradelib_test.php +++ b/lib/tests/upgradelib_test.php @@ -976,4 +976,82 @@ class core_upgradelib_testcase extends advanced_testcase { // Assert the new format took precedence in case of conflict. $this->assertSame('val1', get_config('auth_qux', 'name1')); } + + /** + * Create a collection of test themes to test determining parent themes. + * + * @return Url to the path containing the test themes + */ + public function create_testthemes() { + global $CFG; + + $themedircontent = [ + 'testtheme' => [ + 'config.php' => 'name = "testtheme"; $THEME->parents = [""];', + ], + 'childoftesttheme' => [ + 'config.php' => 'name = "childofboost"; $THEME->parents = ["testtheme"];', + ], + 'infinite' => [ + 'config.php' => 'name = "infinite"; $THEME->parents = ["forever"];', + ], + 'forever' => [ + 'config.php' => 'name = "forever"; $THEME->parents = ["infinite", "childoftesttheme"];', + ], + 'orphantheme' => [ + 'config.php' => 'name = "orphantheme"; $THEME->parents = [];', + ], + 'loop' => [ + 'config.php' => 'name = "loop"; $THEME->parents = ["around"];', + ], + 'around' => [ + 'config.php' => 'name = "around"; $THEME->parents = ["loop"];', + ], + 'themewithbrokenparent' => [ + 'config.php' => 'name = "orphantheme"; $THEME->parents = ["nonexistent", "testtheme"];', + ], + ]; + $vthemedir = \org\bovigo\vfs\vfsStream::setup('themes', null, $themedircontent); + + return \org\bovigo\vfs\vfsStream::url('themes'); + } + + /** + * Test finding theme locations. + */ + public function test_upgrade_find_theme_location() { + global $CFG; + + $this->resetAfterTest(); + + $CFG->themedir = $this->create_testthemes(); + + $this->assertSame($CFG->dirroot . '/theme/boost', upgrade_find_theme_location('boost')); + $this->assertSame($CFG->dirroot . '/theme/clean', upgrade_find_theme_location('clean')); + $this->assertSame($CFG->dirroot . '/theme/bootstrapbase', upgrade_find_theme_location('bootstrapbase')); + + $this->assertSame($CFG->themedir . '/testtheme', upgrade_find_theme_location('testtheme')); + $this->assertSame($CFG->themedir . '/childoftesttheme', upgrade_find_theme_location('childoftesttheme')); + } + + /** + * Test figuring out if theme is or is a child of a certain theme. + */ + public function test_upgrade_theme_is_from_family() { + global $CFG; + + $this->resetAfterTest(); + + $CFG->themedir = $this->create_testthemes(); + + $this->assertTrue(upgrade_theme_is_from_family('boost', 'boost'), 'Boost is a boost theme'); + $this->assertTrue(upgrade_theme_is_from_family('bootstrapbase', 'clean'), 'Clean is a bootstrap base theme'); + $this->assertFalse(upgrade_theme_is_from_family('boost', 'clean'), 'Clean is not a boost theme'); + + $this->assertTrue(upgrade_theme_is_from_family('testtheme', 'childoftesttheme'), 'childoftesttheme is a testtheme'); + $this->assertFalse(upgrade_theme_is_from_family('testtheme', 'orphantheme'), 'ofphantheme is not a testtheme'); + $this->assertTrue(upgrade_theme_is_from_family('testtheme', 'infinite'), 'Infinite loop with testtheme parent is true'); + $this->assertFalse(upgrade_theme_is_from_family('testtheme', 'loop'), 'Infinite loop without testtheme parent is false'); + $this->assertTrue(upgrade_theme_is_from_family('testtheme', 'themewithbrokenparent'), 'No error on broken parent'); + } } diff --git a/lib/upgradelib.php b/lib/upgradelib.php index fc81446d97f..d4db126446f 100644 --- a/lib/upgradelib.php +++ b/lib/upgradelib.php @@ -2619,3 +2619,66 @@ function upgrade_fix_config_auth_plugin_defaults($plugin) { } } } + +/** + * Search for a given theme in any of the parent themes of a given theme. + * + * @param string $needle The name of the theme you want to search for + * @param string $themename The name of the theme you want to search for + * @param string $checkedthemeforparents The name of all the themes already checked + * @return bool True if found, false if not. + */ +function upgrade_theme_is_from_family($needle, $themename, $checkedthemeforparents = []) { + global $CFG; + + // Once we've started checking a theme, don't start checking it again. Prevent recursion. + if (!empty($checkedthemeforparents[$themename])) { + return false; + } + $checkedthemeforparents[$themename] = true; + + if ($themename == $needle) { + return true; + } + + if ($themedir = upgrade_find_theme_location($themename)) { + $THEME = new stdClass(); + require($themedir . '/config.php'); + $theme = $THEME; + } else { + return false; + } + + if (empty($theme->parents)) { + return false; + } + + // Recursively search through each parent theme. + foreach ($theme->parents as $parent) { + if (upgrade_theme_is_from_family($needle, $parent, $checkedthemeforparents)) { + return true; + } + } + return false; +} + +/** + * Finds the theme location and verifies the theme has all needed files. + * + * @param string $themename The name of the theme you want to search for + * @return string full dir path or null if not found + * @see \theme_config::find_theme_location() + */ +function upgrade_find_theme_location($themename) { + global $CFG; + + if (file_exists("$CFG->dirroot/theme/$themename/config.php")) { + $dir = "$CFG->dirroot/theme/$themename"; + } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$themename/config.php")) { + $dir = "$CFG->themedir/$themename"; + } else { + return null; + } + + return $dir; +} diff --git a/version.php b/version.php index 7142666019a..fb4d48b8db1 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2017051501.08; // 20170515 = branching date YYYYMMDD - do not modify! +$version = 2017051501.09; // 20170515 = branching date YYYYMMDD - do not modify! // RR = release increments - 00 in DEV branches. // .XX = incremental changes.