I am implementing the Site Announcement block in our Moodle site, and I need to restrict access for each of the announcement based on Institution field of the User Profile.
As Site Announcement block is basically a Forum in the Dashboard page, I have "Restrict access" option in the Site Announcement Forum's settings form. To restrict the access for the forum post level, I need to implement the Availability API for each items under the Forum Module, ie. in the Forum post's form:
How can I do add Availability API for each forum post?
I got a document exactly for this in Moodle Docs: Availability API for items within a module.
I saved the example in Moodle/mod/page/silly.php. And as described in the document, when I load the url with a valid page ID, I get a blank page:
But when I enter an invalid page ID it shows an error:

What am I doing wrong? Please help!
Edit: Below is the code I save at Moodle/mod/page/silly.php
<?php
require(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/formslib.php');
// Standard Moodle setup for module access.
$id = required_param('id', PARAM_INT);
$course = $DB->get_record_sql("
SELECT c.*
FROM {course_modules} cm
JOIN {course} c ON c.id = cm.course
WHERE cm.id = ?", array($id), MUST_EXIST);
$modinfo = get_fast_modinfo($course);
$cm = $modinfo->get_cm($id);
require_login($course, true, $cm);
// Set up page stuff.
$pageurl = new moodle_url('/mod/page/silly.php', array('id' => $id));
$PAGE->set_url($pageurl);
$PAGE->set_context(context_module::instance($id));
// Define a form that includes availability settings.
class mod_mymodule_silly_form extends moodleform {
public function definition() {
global $COURSE;
$mform = $this->_form;
$cm = $this->_customdata->cm;
$mform->addElement('hidden', 'id', $cm->id);
$mform->setType('id', PARAM_INT);
// Use this code to add the 'Restrict access' section.
// NOTE: Due to limitations in the JavaScript and CSS, you may only
// have one of these fields on a page! Sorry.
$mform->addElement('header', 'availabilityconditionsheader',
get_string('restrictaccess', 'availability'));
$mform->addElement('textarea', 'availabilityconditionsjson',
get_string('accessrestrictions', 'availability'));
\core_availability\frontend::include_all_javascript($COURSE, $cm);
$this->add_action_buttons(false);
}
public function validation($data, $files) {
$errors = array();
// Use this code to validate the 'Restrict access' section.
\core_availability\frontend::report_validation_errors($data, $errors);
return $errors;
}
}
// Custom availability class. You need one of these for each type of items that
// can have availability, so there's one in core for modules and one for
// sections, and you make a new one if you put availability on anything else.
class mod_mymodule_availability_info extends \core_availability\info {
protected $cm;
// You would probably define more suitable parameters here about the
// specific thing you're controlling availability for.
public function __construct(\cm_info $cm, $availability) {
// You should probably set the $visible parameter to 'true' unless
// you want to implement a separate eye icon for your thingy.
parent::__construct($cm->get_course(), true, $availability);
$this->cm = $cm;
}
protected function get_thing_name() {
// This may be used in error messages etc. You would probably use
// the name of the thing you're controlling availability for.
return 'Special thing within module';
}
protected function set_in_database($availability) {
// This function should save the availability settings back to database.
// It's needed if doing an update after restore, so you do need to
// implement it.
}
public function get_context() {
return \context_module::instance($this->cm->id);
}
// I didn't bother to implement filter_user_list, so it's using the default
// which considers only this condition. You might want to make a
// filter_user_list that takes into account the course-module's permissions
// too (like how the info_module class includes the section), if you expect
// to actually use the 'list users who can access this' APIs.
}
$mform = new mod_mymodule_silly_form('silly.php', (object)array('cm' => $cm));
if ($data = $mform->get_data()) {
// Because this is a silly test, we're not going to store the availability
// settings in the database, instead we put it in user session.
$SESSION->mod_mymodule_silly_availability = $data->availabilityconditionsjson;
redirect($pageurl);
}
echo $OUTPUT->header();
// If there is some availability data...
if (isset($SESSION->mod_mymodule_silly_availability)) {
// Set form data.
$mform->set_data(array('availabilityconditionsjson' => $SESSION->mod_mymodule_silly_availability));
// Check availability. Note that this does NOT take into account any
// capabilities the user might have - you probably want to check for
// viewhiddenactivities capability and skip availability checks in that
// case.
$info = new mod_mymodule_availability_info($cm, $SESSION->mod_mymodule_silly_availability);
$information = '';
$available = $info->is_available($information);
// Because this is a test page, we'll just display whether it's available or
// not, and the message(s) if it isn't.
echo html_writer::start_tag('ul');
echo html_writer::tag('li', $available ? 'This page IS available to you' :
'This page IS NOT available to you');
if (!$available) {
echo html_writer::tag('li', $information === '' ?
'No information displayed (hide entirely)' :
'Information displayed (show info)');
}
echo html_writer::end_tag('ul');
if (!$available) {
echo html_writer::start_div();
echo $information;
echo html_writer::end_div();
}
}
$mform->display();
echo $OUTPUT->footer();
UPDATE: So, I tried to learn how Restrict access section is added to a module. I opened the simplest module: Label's form. I found that the code for Restrict access section is defined in a function standard_coursemodule_elements() in /course/moodleform_mod.php. I copied it and pasted in the Forum post's Form's definition() inside /mod/forum/classes/post_form.php:
function definition() {
global $CFG, $OUTPUT, $COURSE;
...
if (!empty($CFG->enableavailability)) {
// Add special button to end of previous section if groups/groupings
// are enabled.
$availabilityplugins = \core\plugininfo\availability::get_enabled_plugins();
$groupavailability = $this->_features->groups && array_key_exists('group', $availabilityplugins);
$groupingavailability = $this->_features->groupings && array_key_exists('grouping', $availabilityplugins);
if ($groupavailability || $groupingavailability) {
// When creating the button, we need to set type=button to prevent it behaving as a submit.
$mform->addElement('static', 'restrictgroupbutton', '',
html_writer::tag('button', get_string('restrictbygroup', 'availability'), [
'id' => 'restrictbygroup',
'type' => 'button',
'disabled' => 'disabled',
'class' => 'btn btn-secondary',
'data-groupavailability' => $groupavailability,
'data-groupingavailability' => $groupingavailability
])
);
}
// Availability field. This is just a textarea; the user interface
// interaction is all implemented in JavaScript.
$mform->addElement('header', 'availabilityconditionsheader',
get_string('restrictaccess', 'availability'));
// Note: This field cannot be named 'availability' because that
// conflicts with fields in existing modules (such as assign).
// So it uses a long name that will not conflict.
$mform->addElement('textarea', 'availabilityconditionsjson',
get_string('accessrestrictions', 'availability'));
// The _cm variable may not be a proper cm_info, so get one from modinfo.
if ($this->_cm) {
$modinfo = get_fast_modinfo($COURSE);
$cm = $modinfo->get_cm($this->_cm->id);
} else {
$cm = null;
}
\core_availability\frontend::include_all_javascript($COURSE, $cm);
}
}
I was able to successfully add the Restrict Access section in the Forum Post's form:
Inspite of some errors about "$_cm", "$_features", and "property 'groupings' of non-object", I was able to save changes.
Where does these changes gets saved? How can I use this to Hide the Forum post in Site Announcement for users who doesn't meet the conditions set in the Restrict Access?


