/**
  * If dynamic data for this course-module is not yet available, gets it.
  *
  * This function is automatically called when requesting any course_modinfo property
  * that can be modified by modules (have a set_xxx method).
  *
  * Dynamic data is data which does not come directly from the cache but is calculated at
  * runtime based on the current user. Primarily this concerns whether the user can access
  * the module or not.
  *
  * As part of this function, the module's _cm_info_dynamic function from its lib.php will
  * be called (if it exists).
  * @return void
  */
 private function obtain_dynamic_data()
 {
     global $CFG;
     $userid = $this->modinfo->get_user_id();
     if ($this->state >= self::STATE_BUILDING_DYNAMIC || $userid == -1) {
         return;
     }
     $this->state = self::STATE_BUILDING_DYNAMIC;
     if (!empty($CFG->enableavailability)) {
         require_once $CFG->libdir . '/conditionlib.php';
         // Get availability information.
         $ci = new \core_availability\info_module($this);
         // Note that the modinfo currently available only includes minimal details (basic data)
         // but we know that this function does not need anything more than basic data.
         $this->available = $ci->is_available($this->availableinfo, true, $userid, $this->modinfo);
         // Check parent section
         $parentsection = $this->modinfo->get_section_info($this->sectionnum);
         if (!$parentsection->available) {
             // Do not store info from section here, as that is already
             // presented from the section (if appropriate) - just change
             // the flag
             $this->available = false;
         }
     } else {
         $this->available = true;
     }
     // Update visible state for current user
     $this->update_user_visible();
     // Let module make dynamic changes at this point
     $this->call_mod_function('cm_info_dynamic');
     $this->state = self::STATE_DYNAMIC;
 }
 /**
  * Tests constructing and using condition.
  */
 public function test_usage()
 {
     global $CFG, $USER;
     $this->resetAfterTest();
     $CFG->enableavailability = true;
     // Erase static cache before test.
     condition::wipe_static_cache();
     // Make a test course and user.
     $generator = $this->getDataGenerator();
     $course = $generator->create_course();
     $user = $generator->create_user();
     $generator->enrol_user($user->id, $course->id);
     $info = new \core_availability\mock_info($course, $user->id);
     // Make a test grouping and group.
     $grouping = $generator->create_grouping(array('courseid' => $course->id, 'name' => 'Grouping!'));
     $group = $generator->create_group(array('courseid' => $course->id));
     groups_assign_grouping($grouping->id, $group->id);
     // Do test (not in grouping).
     $structure = (object) array('type' => 'grouping', 'id' => (int) $grouping->id);
     $cond = new condition($structure);
     // Check if available (when not available).
     $this->assertFalse($cond->is_available(false, $info, true, $user->id));
     $information = $cond->get_description(false, false, $info);
     $this->assertRegExp('~belong to a group in.*Grouping!~', $information);
     $this->assertTrue($cond->is_available(true, $info, true, $user->id));
     // Add user to grouping and refresh cache.
     groups_add_member($group, $user);
     get_fast_modinfo($course->id, 0, true);
     // Recheck.
     $this->assertTrue($cond->is_available(false, $info, true, $user->id));
     $this->assertFalse($cond->is_available(true, $info, true, $user->id));
     $information = $cond->get_description(false, true, $info);
     $this->assertRegExp('~do not belong to a group in.*Grouping!~', $information);
     // Admin user doesn't belong to the grouping, but they can access it
     // either way (positive or NOT) because of accessallgroups.
     $this->setAdminUser();
     $infoadmin = new \core_availability\mock_info($course, $USER->id);
     $this->assertTrue($cond->is_available(false, $infoadmin, true, $USER->id));
     $this->assertTrue($cond->is_available(true, $infoadmin, true, $USER->id));
     // Grouping that doesn't exist uses 'missing' text.
     $cond = new condition((object) array('id' => $grouping->id + 1000));
     $this->assertFalse($cond->is_available(false, $info, true, $user->id));
     $information = $cond->get_description(false, false, $info);
     $this->assertRegExp('~belong to a group in.*(Missing grouping)~', $information);
     // We need an actual cm object to test the 'grouping from cm' option.
     $pagegen = $generator->get_plugin_generator('mod_page');
     $page = $pagegen->create_instance(array('course' => $course->id, 'groupingid' => $grouping->id, 'availability' => '{"op":"|","show":true,"c":[{"type":"grouping","activity":true}]}'));
     rebuild_course_cache($course->id, true);
     // Check if available using the 'from course-module' grouping option.
     $modinfo = get_fast_modinfo($course, $user->id);
     $cm = $modinfo->get_cm($page->cmid);
     $info = new \core_availability\info_module($cm);
     $information = '';
     $this->assertTrue($info->is_available($information, false, $user->id));
     // Remove user from grouping again and recheck.
     groups_remove_member($group, $user);
     get_fast_modinfo($course->id, 0, true);
     $this->assertFalse($info->is_available($information, false, $user->id));
     $this->assertRegExp('~belong to a group in.*Grouping!~', $information);
 }
 /**
  * Return conditionally unavailable elements.
  * @param $course
  * @return array
  * @throws \coding_exception
  */
 public static function conditionally_unavailable_elements($course)
 {
     $cancomplete = isloggedin() && !isguestuser();
     $unavailablesections = [];
     $unavailablemods = [];
     $information = '';
     if ($cancomplete) {
         $completioninfo = new \completion_info($course);
         if ($completioninfo->is_enabled()) {
             $modinfo = get_fast_modinfo($course);
             $sections = $modinfo->get_section_info_all();
             foreach ($sections as $number => $section) {
                 $ci = new \core_availability\info_section($section);
                 if (!$ci->is_available($information, true)) {
                     $unavailablesections[] = $number;
                 }
             }
             foreach ($modinfo->get_cms() as $mod) {
                 $ci = new \core_availability\info_module($mod);
                 if (!$ci->is_available($information, true)) {
                     $unavailablemods[] = $mod->id;
                 }
             }
         }
     }
     return [$unavailablesections, $unavailablemods];
 }
 /**
  * Checks whether a user can be subscribed to the forum, regardless of
  * subscription option. Includes a variety of other checks. [These are
  * supposed to be the same as checks done when building the list of people
  * for email.]
  * @param int $userid User ID or 0 for current
  * @return bool True if user can be subscribed
  */
 private function can_be_subscribed($userid = 0)
 {
     global $USER, $CFG;
     $userid = mod_forumng_utils::get_real_userid($userid);
     $cm = $this->get_course_module();
     $course = $this->get_course();
     $context = $this->get_context();
     // Guests cannot subscribe
     if (!isloggedin() || isguestuser($userid)) {
         return false;
     }
     // Get from cache if possible
     if (!isset($this->cache->can_be_subscribed)) {
         $this->cache->can_be_subscribed = array();
     }
     if (array_key_exists($userid, $this->cache->can_be_subscribed)) {
         return $this->cache->can_be_subscribed[$userid];
     }
     // This is not a loop, just so I can use break
     do {
         // Check user can see forum
         if (!has_capability('mod/forumng:viewdiscussion', $context, $userid)) {
             $result = false;
             break;
         }
         // For current user, can take shortcut
         if ($userid == $USER->id) {
             if (empty($cm->uservisible)) {
                 $uservisible = false;
             } else {
                 $uservisible = true;
             }
             if (!$uservisible) {
                 $result = false;
                 break;
             }
         } else {
             $visible = $cm->visible;
             $info = new \core_availability\info_module($cm);
             $visible = $visible && $info->is_available($crap, false, $userid);
             if (!$visible && !has_capability('moodle/site:viewhiddenactivities', $context, $userid)) {
                 $result = false;
                 break;
             }
         }
         if ($this->get_group_mode() == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context, $userid)) {
             // Limit it to people within groups in the grouping, if one is selected.
             $groupobjs = groups_get_all_groups($course->id, $userid, $cm->groupingid, 'g.id');
             if (!$groupobjs || count($groupobjs) == 0) {
                 $result = false;
                 break;
             }
         }
         $result = true;
         break;
     } while (false);
     $this->cache->can_be_subscribed[$userid] = $result;
     return $result;
 }
 /**
  * Javascript required by both standard header layout and flexpage layout
  *
  * @return void
  */
 public static function page_requires_js()
 {
     global $CFG, $PAGE, $COURSE, $USER;
     $PAGE->requires->jquery();
     $PAGE->requires->strings_for_js(array('close', 'debugerrors', 'problemsfound', 'error:coverimageexceedsmaxbytes', 'error:coverimageresolutionlow', 'forumtopic', 'forumauthor', 'forumpicturegroup', 'forumreplies', 'forumlastpost', 'hiddencoursestoggle', 'loading', 'more', 'moving', 'movingcount', 'movehere', 'movefailed', 'movingdropsectionhelp', 'movingstartedhelp'), 'theme_snap');
     $PAGE->requires->strings_for_js(['ok', 'cancel'], 'moodle');
     $PAGE->requires->strings_for_js(['printbook'], 'booktool_print');
     // Are we viewing /course/view.php - note, this is different from just checking the page type.
     // We only ever want to load course.js when on site page or view.php - no point in loading it when on
     // course settings page, etc.
     $courseviewpage = local::current_url_path() === '/course/view.php';
     $pagehascoursecontent = $PAGE->pagetype === 'site-index' || $courseviewpage;
     $cancomplete = isloggedin() && !isguestuser();
     $unavailablesections = [];
     $unavailablemods = [];
     if ($cancomplete) {
         $completioninfo = new \completion_info($COURSE);
         if ($completioninfo->is_enabled()) {
             $modinfo = get_fast_modinfo($COURSE);
             $sections = $modinfo->get_section_info_all();
             foreach ($sections as $number => $section) {
                 $ci = new \core_availability\info_section($section);
                 $information = '';
                 if (!$ci->is_available($information, true)) {
                     $unavailablesections[] = $number;
                 }
             }
             foreach ($modinfo as $mod) {
                 $ci = new \core_availability\info_module($mod);
                 if (!$ci->is_available($information, true)) {
                     $unavailablemods[] = $mod->id;
                 }
             }
         }
     }
     list($unavailablesections, $unavailablemods) = local::conditionally_unavailable_elements($COURSE);
     $coursevars = (object) ['id' => $COURSE->id, 'shortname' => $COURSE->shortname, 'contextid' => $PAGE->context->id, 'ajaxurl' => '/course/rest.php', 'unavailablesections' => $unavailablesections, 'unavailablemods' => $unavailablemods, 'enablecompletion' => $COURSE->enablecompletion];
     $initvars = [$coursevars, $pagehascoursecontent, get_max_upload_file_size($CFG->maxbytes)];
     $PAGE->requires->js_call_amd('theme_snap/snap', 'snapInit', $initvars);
     // Does the page have editable course content?
     if ($pagehascoursecontent && $PAGE->user_allowed_editing()) {
         $canmanageacts = has_capability('moodle/course:manageactivities', context_course::instance($COURSE->id));
         if ($canmanageacts && empty($USER->editing)) {
             $modinfo = get_fast_modinfo($COURSE);
             $modnamesused = $modinfo->get_used_module_names();
             // Temporarily change edit mode to on for course ajax to be included.
             $USER->editing = true;
             self::include_course_ajax($COURSE, $modnamesused);
             $USER->editing = false;
         }
     }
 }