How to implement Availability API (Restrict Access) for Site Announcement posts in Moodle?

63 Views Asked by At

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:

enter image description here

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:

enter image description here

But when I enter an invalid page ID it shows an error: enter image description here

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:

enter image description here

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?

0

There are 0 best solutions below