Merge branch 'MDL-63337-master' of git://github.com/bmbrands/moodle
authorJun Pataleta <jun@moodle.com>
Mon, 15 Oct 2018 13:03:16 +0000 (21:03 +0800)
committerJun Pataleta <jun@moodle.com>
Mon, 15 Oct 2018 13:03:16 +0000 (21:03 +0800)
1  2 
course/externallib.php
course/tests/externallib_test.php
theme/boost/style/moodle.css

diff --combined course/externallib.php
@@@ -58,8 -58,6 +58,8 @@@ class core_course_external extends exte
                                                  'The expected keys (value format) are:
                                                  excludemodules (bool) Do not return modules, return only the sections structure
                                                  excludecontents (bool) Do not return module contents (i.e: files inside a resource)
 +                                                includestealthmodules (bool) Return stealth modules for students in a special
 +                                                    section (with id -1)
                                                  sectionid (int) Return only this section
                                                  sectionnumber (int) Return only this section with number (order)
                                                  cmid (int) Return only this module information (among the whole sections structure)
                      switch ($name) {
                          case 'excludemodules':
                          case 'excludecontents':
 +                        case 'includestealthmodules':
                              $value = clean_param($option['value'], PARAM_BOOL);
                              $filters[$name] = $value;
                              break;
              $modinfo = get_fast_modinfo($course);
              $sections = $modinfo->get_section_info_all();
              $coursenumsections = course_get_format($course)->get_last_section_number();
 +            $stealthmodules = array();   // Array to keep all the modules available but not visible in a course section/topic.
  
              //for each sections (first displayed to last displayed)
              $modinfosections = $modinfo->get_sections();
              foreach ($sections as $key => $section) {
  
 -                // Show the section if the user is permitted to access it, OR if it's not available
 -                // but there is some available info text which explains the reason & should display.
 -                $showsection = $section->uservisible ||
 -                    ($section->visible && !$section->available &&
 -                    !empty($section->availableinfo));
 -
 -                if (!$showsection) {
 -                    continue;
 -                }
 -
                  // This becomes true when we are filtering and we found the value to filter with.
                  $sectionfound = false;
  
  
                  $sectioncontents = array();
  
 -                // For each module of the section (if it is visible).
 -                if ($section->uservisible and empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
 +                // For each module of the section.
 +                if (empty($filters['excludemodules']) and !empty($modinfosections[$section->section])) {
                      foreach ($modinfosections[$section->section] as $cmid) {
                          $cm = $modinfo->cms[$cmid];
  
                              }
                          }
  
 -                        //assign result to $sectioncontents
 -                        $sectioncontents[] = $module;
 +                        // Assign result to $sectioncontents, there is an exception,
 +                        // stealth activities in non-visible sections for students go to a special section.
 +                        if (!empty($filters['includestealthmodules']) && !$section->uservisible && $cm->is_stealth()) {
 +                            $stealthmodules[] = $module;
 +                        } else {
 +                            $sectioncontents[] = $module;
 +                        }
  
                          // If we just did a filtering, break the loop.
                          if ($modfound) {
                  $sectionvalues['modules'] = $sectioncontents;
  
                  // assign result to $coursecontents
 -                $coursecontents[] = $sectionvalues;
 +                $coursecontents[$key] = $sectionvalues;
  
                  // Break the loop if we are filtering.
                  if ($sectionfound) {
                      break;
                  }
              }
 +
 +            // Now that we have iterated over all the sections and activities, check the visibility.
 +            // We didn't this before to be able to retrieve stealth activities.
 +            foreach ($coursecontents as $sectionnumber => $sectioncontents) {
 +                $section = $sections[$sectionnumber];
 +                // Show the section if the user is permitted to access it, OR if it's not available
 +                // but there is some available info text which explains the reason & should display.
 +                $showsection = $section->uservisible ||
 +                    ($section->visible && !$section->available &&
 +                    !empty($section->availableinfo));
 +
 +                if (!$showsection) {
 +                    unset($coursecontents[$sectionnumber]);
 +                    continue;
 +                }
 +
 +                // Remove modules information if the section is not visible for the user.
 +                if (!$section->uservisible) {
 +                    $coursecontents[$sectionnumber]['modules'] = array();
 +                }
 +            }
 +
 +            // Include stealth modules in special section (without any info).
 +            if (!empty($stealthmodules)) {
 +                $coursecontents[] = array(
 +                    'id' => -1,
 +                    'name' => '',
 +                    'summary' => '',
 +                    'summaryformat' => FORMAT_MOODLE,
 +                    'modules' => $stealthmodules
 +                );
 +            }
 +
          }
          return $coursecontents;
      }
          $sort = $params['sort'];
  
          switch($classification) {
+             case COURSE_TIMELINE_ALL:
+                 break;
              case COURSE_TIMELINE_PAST:
                  break;
              case COURSE_TIMELINE_INPROGRESS:
@@@ -806,11 -806,8 +806,11 @@@ class core_course_externallib_testcase 
       * @return array A list with the course object and course modules objects
       */
      private function prepare_get_course_contents_test() {
 -        global $DB;
 -        $course  = self::getDataGenerator()->create_course(['numsections' => 3]);
 +        global $DB, $CFG;
 +
 +        $CFG->allowstealth = 1; // Allow stealth activities.
 +
 +        $course  = self::getDataGenerator()->create_course(['numsections' => 4]);
          $forumdescription = 'This is the forum description';
          $forum = $this->getDataGenerator()->create_module('forum',
              array('course' => $course->id, 'intro' => $forumdescription),
          $datacm = get_coursemodule_from_instance('page', $data->id);
          $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
          $pagecm = get_coursemodule_from_instance('page', $page->id);
 +        // This is an stealth page (set by visibleoncoursepage).
 +        $pagestealth = $this->getDataGenerator()->create_module('page', array('course' => $course->id, 'visibleoncoursepage' => 0));
          $labeldescription = 'This is a very long label to test if more than 50 characters are returned.
                  So bla bla bla bla <b>bold bold bold</b> bla bla bla bla.';
          $label = $this->getDataGenerator()->create_module('label', array('course' => $course->id,
          $conditions = array('course' => $course->id, 'section' => 2);
          $DB->set_field('course_sections', 'summary', 'Text with iframe <iframe src="https://moodle.org"></iframe>', $conditions);
  
 -        // Add date availability condition not met for last section.
 +        // Add date availability condition not met for section 3.
          $availability = '{"op":"&","c":[{"type":"date","d":">=","t":' . $tomorrow . '}],"showc":[true]}';
          $DB->set_field('course_sections', 'availability', $availability,
                  array('course' => $course->id, 'section' => 3));
 +
 +        // Create resource for last section.
 +        $pageinhiddensection = $this->getDataGenerator()->create_module('page',
 +            array('course' => $course->id, 'name' => 'Page in hidden section', 'section' => 4));
 +        // Set not visible last section.
 +        $DB->set_field('course_sections', 'visible', 0,
 +                array('course' => $course->id, 'section' => 4));
 +
          rebuild_course_cache($course->id, true);
  
          return array($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm);
  
          list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
  
 +        // We first run the test as admin.
 +        $this->setAdminUser();
          $sections = core_course_external::get_course_contents($course->id, array());
          // We need to execute the return values cleaning process to simulate the web service server.
          $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
          $this->assertEquals(2, $testexecuted);
          $this->assertEquals(0, $sections[0]['section']);
  
 -        // Check that the only return section has the 5 created modules.
 -        $this->assertCount(4, $sections[0]['modules']);
 +        $this->assertCount(5, $sections[0]['modules']);
          $this->assertCount(1, $sections[1]['modules']);
          $this->assertCount(1, $sections[2]['modules']);
 -        $this->assertCount(0, $sections[3]['modules']); // No modules for the section with availability restrictions.
 +        $this->assertCount(1, $sections[3]['modules']); // One module for the section with availability restrictions.
 +        $this->assertCount(1, $sections[4]['modules']); // One module for the hidden section with a visible activity.
          $this->assertNotEmpty($sections[3]['availabilityinfo']);
          $this->assertEquals(1, $sections[1]['section']);
          $this->assertEquals(2, $sections[2]['section']);
          $this->assertEquals(3, $sections[3]['section']);
 +        $this->assertEquals(4, $sections[4]['section']);
          $this->assertContains('<iframe', $sections[2]['summary']);
          $this->assertContains('</iframe>', $sections[2]['summary']);
 -        // The module with the availability restriction met is returning contents.
 -        $this->assertNotEmpty($sections[1]['modules'][0]['contents']);
 -        // The module with the availability restriction not met is not returning contents.
 -        $this->assertArrayNotHasKey('contents', $sections[2]['modules'][0]);
          $this->assertNotEmpty($sections[2]['modules'][0]['availabilityinfo']);
          try {
              $sections = core_course_external::get_course_contents($course->id,
      }
  
  
 +    /**
 +     * Test get_course_contents as student
 +     */
 +    public function test_get_course_contents_student() {
 +        global $DB;
 +        $this->resetAfterTest(true);
 +
 +        list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test();
 +
 +        $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
 +        $user = self::getDataGenerator()->create_user();
 +        self::getDataGenerator()->enrol_user($user->id, $course->id, $studentroleid);
 +        $this->setUser($user);
 +
 +        $sections = core_course_external::get_course_contents($course->id, array());
 +        // We need to execute the return values cleaning process to simulate the web service server.
 +        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
 +
 +        $this->assertCount(4, $sections); // Nothing for the not visible section.
 +        $this->assertCount(5, $sections[0]['modules']);
 +        $this->assertCount(1, $sections[1]['modules']);
 +        $this->assertCount(1, $sections[2]['modules']);
 +        $this->assertCount(0, $sections[3]['modules']); // No modules for the section with availability restrictions.
 +
 +        $this->assertNotEmpty($sections[3]['availabilityinfo']);
 +        $this->assertEquals(1, $sections[1]['section']);
 +        $this->assertEquals(2, $sections[2]['section']);
 +        $this->assertEquals(3, $sections[3]['section']);
 +        // The module with the availability restriction met is returning contents.
 +        $this->assertNotEmpty($sections[1]['modules'][0]['contents']);
 +        // The module with the availability restriction not met is not returning contents.
 +        $this->assertArrayNotHasKey('contents', $sections[2]['modules'][0]);
 +
 +        // Now include flag for returning stealth information (fake section).
 +        $sections = core_course_external::get_course_contents($course->id,
 +            array(array("name" => "includestealthmodules", "value" => 1)));
 +        // We need to execute the return values cleaning process to simulate the web service server.
 +        $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
 +
 +        $this->assertCount(5, $sections); // Include fake section with stealth activities.
 +        $this->assertCount(5, $sections[0]['modules']);
 +        $this->assertCount(1, $sections[1]['modules']);
 +        $this->assertCount(1, $sections[2]['modules']);
 +        $this->assertCount(0, $sections[3]['modules']); // No modules for the section with availability restrictions.
 +        $this->assertCount(1, $sections[4]['modules']); // One stealh module.
 +        $this->assertEquals(-1, $sections[4]['id']);
 +    }
 +
      /**
       * Test get_course_contents excluding modules
       */
          $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections);
  
          $this->assertCount(1, $sections);
 -        $this->assertCount(4, $sections[0]['modules']);
 +        $this->assertCount(5, $sections[0]['modules']);
      }
  
      /**
                  'expectedcourses' => ['cfuture', 'dfuture', 'efuture'],
                  'expectednextoffset' => 15
              ],
+             'all no limit or offset' => [
+                 'coursedata' => $coursedata,
+                 'classification' => 'all',
+                 'limit' => 0,
+                 'offset' => 0,
+                 'expectedcourses' => [
+                     'afuture',
+                     'ainprogress',
+                     'apast',
+                     'bfuture',
+                     'binprogress',
+                     'bpast',
+                     'cfuture',
+                     'cinprogress',
+                     'cpast',
+                     'dfuture',
+                     'dinprogress',
+                     'dpast',
+                     'efuture',
+                     'einprogress',
+                     'epast'
+                 ],
+                 'expectednextoffset' => 15
+             ],
+             'all limit no offset' => [
+                 'coursedata' => $coursedata,
+                 'classification' => 'all',
+                 'limit' => 5,
+                 'offset' => 0,
+                 'expectedcourses' => [
+                     'afuture',
+                     'ainprogress',
+                     'apast',
+                     'bfuture',
+                     'binprogress'
+                 ],
+                 'expectednextoffset' => 5
+             ],
+             'all limit and offset' => [
+                 'coursedata' => $coursedata,
+                 'classification' => 'all',
+                 'limit' => 5,
+                 'offset' => 5,
+                 'expectedcourses' => [
+                     'bpast',
+                     'cfuture',
+                     'cinprogress',
+                     'cpast',
+                     'dfuture'
+                 ],
+                 'expectednextoffset' => 10
+             ],
+             'all offset past result set' => [
+                 'coursedata' => $coursedata,
+                 'classification' => 'all',
+                 'limit' => 5,
+                 'offset' => 50,
+                 'expectedcourses' => [],
+                 'expectednextoffset' => 50
+             ],
          ];
      }
  
@@@ -5576,25 -5576,25 +5576,25 @@@ tbody.collapse.show 
    display: flex;
    flex-direction: column; }
    .card-deck .card, .card-deck #page-enrol-users #filterform, #page-enrol-users .card-deck #filterform, .card-deck .que .history, .que .card-deck .history, .card-deck .userprofile .profile_tree section, .userprofile .profile_tree .card-deck section, .card-deck .groupinfobox, .card-deck .well {
-     margin-bottom: 15px; }
+     margin-bottom: 0.25rem; }
    @media (min-width: 576px) {
      .card-deck {
        flex-flow: row wrap;
-       margin-right: -15px;
-       margin-left: -15px; }
+       margin-right: -0.25rem;
+       margin-left: -0.25rem; }
        .card-deck .card, .card-deck #page-enrol-users #filterform, #page-enrol-users .card-deck #filterform, .card-deck .que .history, .que .card-deck .history, .card-deck .userprofile .profile_tree section, .userprofile .profile_tree .card-deck section, .card-deck .groupinfobox, .card-deck .well {
          display: flex;
          flex: 1 0 0%;
          flex-direction: column;
-         margin-right: 15px;
+         margin-right: 0.25rem;
          margin-bottom: 0;
-         margin-left: 15px; } }
+         margin-left: 0.25rem; } }
  
  .card-group {
    display: flex;
    flex-direction: column; }
    .card-group > .card, #page-enrol-users .card-group > #filterform, .que .card-group > .history, .userprofile .profile_tree .card-group > section, .card-group > .groupinfobox, .card-group > .well {
-     margin-bottom: 15px; }
+     margin-bottom: 0.25rem; }
    @media (min-width: 576px) {
      .card-group {
        flex-flow: row wrap; }
@@@ -8882,12 -8882,6 +8882,12 @@@ a.autolink.glossary:hover 
  .pagelayout-mydashboard.jsenabled .collapsibleregioncaption {
    cursor: pointer; }
  
 +.pagelayout-mydashboard #region-main {
 +  border: 0;
 +  padding: 0;
 +  background-color: transparent;
 +  margin-top: -1px; }
 +
  .collapsibleregioncaption img {
    vertical-align: middle; }
  
@@@ -11131,278 -11125,73 +11131,73 @@@ div.editor_atto_toolbar button .icon 
    [data-region="blocks-column"] {
      width: 100%; } }
  
- .progress-chart-container {
-   height: 70px;
-   width: 70px; }
-   .progress-chart-container .progress-doughnut {
-     position: relative;
-     height: 70px;
-     width: 70px;
-     background-clip: padding-box;
-     border: 15px solid #dee2e6;
-     border-radius: 50%;
-     box-sizing: border-box; }
-     .progress-chart-container .progress-doughnut .progress-text {
-       position: absolute;
-       top: 50%;
-       /*rtl:ignore*/
-       left: 50%;
-       transform: translate(-50%, -50%);
-       color: #dee2e6; }
-       .progress-chart-container .progress-doughnut .progress-text.has-percent {
-         color: #ff7518; }
-     .progress-chart-container .progress-doughnut .progress-indicator {
-       position: absolute;
-       top: -15px;
-       left: -15px;
-       height: 70px;
-       width: 70px; }
-       .progress-chart-container .progress-doughnut .progress-indicator svg {
-         position: relative;
-         height: 100%;
-         width: 100%; }
-         .progress-chart-container .progress-doughnut .progress-indicator svg .circle {
-           stroke-width: 15px;
-           stroke: #ff7518;
-           fill: none;
-           stroke-dasharray: 173;
-           stroke-dashoffset: 173;
-           transform: rotate(-90deg);
-           transform-origin: center center; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-1 {
-             stroke-dashoffset: 171.27; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-2 {
-             stroke-dashoffset: 169.54; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-3 {
-             stroke-dashoffset: 167.81; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-4 {
-             stroke-dashoffset: 166.08; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-5 {
-             stroke-dashoffset: 164.35; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-6 {
-             stroke-dashoffset: 162.62; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-7 {
-             stroke-dashoffset: 160.89; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-8 {
-             stroke-dashoffset: 159.16; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-9 {
-             stroke-dashoffset: 157.43; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-10 {
-             stroke-dashoffset: 155.7; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-11 {
-             stroke-dashoffset: 153.97; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-12 {
-             stroke-dashoffset: 152.24; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-13 {
-             stroke-dashoffset: 150.51; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-14 {
-             stroke-dashoffset: 148.78; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-15 {
-             stroke-dashoffset: 147.05; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-16 {
-             stroke-dashoffset: 145.32; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-17 {
-             stroke-dashoffset: 143.59; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-18 {
-             stroke-dashoffset: 141.86; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-19 {
-             stroke-dashoffset: 140.13; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-20 {
-             stroke-dashoffset: 138.4; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-21 {
-             stroke-dashoffset: 136.67; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-22 {
-             stroke-dashoffset: 134.94; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-23 {
-             stroke-dashoffset: 133.21; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-24 {
-             stroke-dashoffset: 131.48; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-25 {
-             stroke-dashoffset: 129.75; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-26 {
-             stroke-dashoffset: 128.02; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-27 {
-             stroke-dashoffset: 126.29; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-28 {
-             stroke-dashoffset: 124.56; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-29 {
-             stroke-dashoffset: 122.83; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-30 {
-             stroke-dashoffset: 121.1; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-31 {
-             stroke-dashoffset: 119.37; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-32 {
-             stroke-dashoffset: 117.64; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-33 {
-             stroke-dashoffset: 115.91; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-34 {
-             stroke-dashoffset: 114.18; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-35 {
-             stroke-dashoffset: 112.45; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-36 {
-             stroke-dashoffset: 110.72; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-37 {
-             stroke-dashoffset: 108.99; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-38 {
-             stroke-dashoffset: 107.26; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-39 {
-             stroke-dashoffset: 105.53; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-40 {
-             stroke-dashoffset: 103.8; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-41 {
-             stroke-dashoffset: 102.07; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-42 {
-             stroke-dashoffset: 100.34; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-43 {
-             stroke-dashoffset: 98.61; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-44 {
-             stroke-dashoffset: 96.88; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-45 {
-             stroke-dashoffset: 95.15; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-46 {
-             stroke-dashoffset: 93.42; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-47 {
-             stroke-dashoffset: 91.69; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-48 {
-             stroke-dashoffset: 89.96; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-49 {
-             stroke-dashoffset: 88.23; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-50 {
-             stroke-dashoffset: 86.5; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-51 {
-             stroke-dashoffset: 84.77; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-52 {
-             stroke-dashoffset: 83.04; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-53 {
-             stroke-dashoffset: 81.31; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-54 {
-             stroke-dashoffset: 79.58; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-55 {
-             stroke-dashoffset: 77.85; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-56 {
-             stroke-dashoffset: 76.12; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-57 {
-             stroke-dashoffset: 74.39; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-58 {
-             stroke-dashoffset: 72.66; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-59 {
-             stroke-dashoffset: 70.93; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-60 {
-             stroke-dashoffset: 69.2; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-61 {
-             stroke-dashoffset: 67.47; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-62 {
-             stroke-dashoffset: 65.74; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-63 {
-             stroke-dashoffset: 64.01; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-64 {
-             stroke-dashoffset: 62.28; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-65 {
-             stroke-dashoffset: 60.55; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-66 {
-             stroke-dashoffset: 58.82; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-67 {
-             stroke-dashoffset: 57.09; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-68 {
-             stroke-dashoffset: 55.36; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-69 {
-             stroke-dashoffset: 53.63; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-70 {
-             stroke-dashoffset: 51.9; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-71 {
-             stroke-dashoffset: 50.17; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-72 {
-             stroke-dashoffset: 48.44; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-73 {
-             stroke-dashoffset: 46.71; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-74 {
-             stroke-dashoffset: 44.98; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-75 {
-             stroke-dashoffset: 43.25; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-76 {
-             stroke-dashoffset: 41.52; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-77 {
-             stroke-dashoffset: 39.79; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-78 {
-             stroke-dashoffset: 38.06; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-79 {
-             stroke-dashoffset: 36.33; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-80 {
-             stroke-dashoffset: 34.6; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-81 {
-             stroke-dashoffset: 32.87; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-82 {
-             stroke-dashoffset: 31.14; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-83 {
-             stroke-dashoffset: 29.41; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-84 {
-             stroke-dashoffset: 27.68; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-85 {
-             stroke-dashoffset: 25.95; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-86 {
-             stroke-dashoffset: 24.22; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-87 {
-             stroke-dashoffset: 22.49; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-88 {
-             stroke-dashoffset: 20.76; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-89 {
-             stroke-dashoffset: 19.03; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-90 {
-             stroke-dashoffset: 17.3; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-91 {
-             stroke-dashoffset: 15.57; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-92 {
-             stroke-dashoffset: 13.84; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-93 {
-             stroke-dashoffset: 12.11; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-94 {
-             stroke-dashoffset: 10.38; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-95 {
-             stroke-dashoffset: 8.65; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-96 {
-             stroke-dashoffset: 6.92; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-97 {
-             stroke-dashoffset: 5.19; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-98 {
-             stroke-dashoffset: 3.46; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-99 {
-             stroke-dashoffset: 1.73; }
-           .progress-chart-container .progress-doughnut .progress-indicator svg .circle.percent-100 {
-             stroke-dashoffset: 0; }
-   .progress-chart-container .no-progress {
-     height: 70px;
-     width: 70px;
-     background-color: #dee2e6;
-     border-radius: 50%;
-     position: relative; }
-     .progress-chart-container .no-progress .icon {
-       position: absolute;
-       top: 50%;
-       /*rtl:ignore*/
-       left: 50%;
-       margin: 0;
-       transform: translate(-63%, -50%);
-       color: #fff;
-       height: 45px;
-       width: 45px;
-       font-size: 45px; }
- .block_myoverview .empty-placeholder-image-sm {
-   height: 50px; }
  .block_myoverview .empty-placeholder-image-lg {
    height: 125px; }
  
- .card-deck .card, .card-deck #page-enrol-users #filterform, #page-enrol-users .card-deck #filterform, .card-deck .que .history, .que .card-deck .history, .card-deck .userprofile .profile_tree section, .userprofile .profile_tree .card-deck section, .card-deck .groupinfobox, .card-deck .well {
+ .block_myoverview .course-card {
+   margin-bottom: 0.5rem;
+   flex-basis: 100%; }
+ .block_myoverview .course-info-container {
+   padding: 0.8rem; }
+ .block_myoverview .course-card-footer {
+   padding: 0.8rem; }
+ .block_myoverview .progress {
+   height: 0.5rem; }
+ .block_myoverview .myoverviewimg {
+   height: 7rem;
+   background-position: center;
+   background-size: cover; }
+ .block_myoverview .course-summaryitem {
+   border: 1px solid #dee2e6;
+   background-color: #fff; }
+ .block_myoverview .summary img {
+   max-width: 100%; }
+ @media (max-width: 767.98px) {
+   .block_myoverview .summaryimage {
+     max-height: 7rem; } }
+ #region-main .block_myoverview .course-card {
    flex-grow: 0;
    flex-shrink: 0;
-   flex-basis: calc(50% - 30px); }
+   flex-basis: calc(50% - 0.5rem); }
+ @media (min-width: 576px) {
+   #region-main .block_myoverview .course-card {
+     flex-basis: calc(50% - 0.5rem); } }
+ @media (min-width: 768px) {
+   #region-main .block_myoverview .course-card {
+     flex-basis: calc(33.33% - 0.5rem); } }
+ @media (min-width: 992px) {
+   #region-main .block_myoverview .course-card {
+     flex-basis: calc(25% - 0.5rem); } }
  
- .card-deck .myoverviewimg {
-   height: 150px; }
-   .card-deck .myoverviewimg.courseimage {
-     background-position: center;
-     background-size: cover; }
+ @media (min-width: 1200px) {
+   #region-main .block_myoverview .course-card {
+     flex-basis: calc(20% - 0.5rem); } }
+ @media (min-width: 992px) {
+   #region-main.has-blocks .block_myoverview .course-card {
+     flex-basis: calc(33.33% - 0.5rem); } }
+ @media (min-width: 1200px) {
+   #region-main.has-blocks .block_myoverview .course-card {
+     flex-basis: calc(25% - 0.5rem); } }
+ body.drawer-open-left #region-main.has-blocks .block_myoverview .course-card {
+   flex-basis: calc(50% - 0.5rem); }
+ @media (min-width: 992px) {
+   body.drawer-open-left #region-main.has-blocks .block_myoverview .course-card {
+     flex-basis: calc(33.33% - 0.5rem); } }
  
  .block_settings .block_tree [aria-expanded="true"],
  .block_settings .block_tree [aria-expanded="true"].emptybranch,