HEX
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/8.0.30
System: Linux multiplicar 3.10.0-1160.102.1.el7.x86_64 #1 SMP Tue Oct 17 15:42:21 UTC 2023 x86_64
User: root (0)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: /var/www/html/demo.sumar.com.py/mod/pulse/lib.php
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Pulse instance libarary file. contains pro feature extended methods
 *
 * @package   mod_pulse
 * @copyright 2021, bdecent gmbh bdecent.de
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

defined( 'MOODLE_INTERNAL') || die(' No direct access ');

define( 'MAX_PULSE_NAME_LENGTH', 50);

global $PAGE;

require_once($CFG->libdir."/completionlib.php");
require_once($CFG->dirroot.'/lib/filelib.php');

/**
 * Add pulse instance.
 *
 * @param  mixed $pulse
 * @return void
 */
function pulse_add_instance($pulse) {
    global $DB;

    $context = context_module::instance($pulse->coursemodule);

    $pulse->timemodified = time();

    if (isset($pulse->pulse_content_editor)) {
        $pulse->pulse_content = file_save_draft_area_files($pulse->pulse_content_editor['itemid'],
                                                    $context->id, 'mod_pulse', 'pulse_content', 0,
                                                    array('subdirs' => true), $pulse->pulse_content_editor['text']);
        $pulse->pulse_contentformat = $pulse->pulse_content_editor['format'];
        unset($pulse->pulse_content_editor);
    }
    // Insert the instance in DB.
    $pulseid = $DB->insert_record('pulse', $pulse);
    // Extend the pro features.
    \mod_pulse\extendpro::pulse_extend_add_instance($pulseid, $pulse);

    // Retrun new instance id.
    return $pulseid;
}

/**
 * Update pulse instance.
 *
 * @param  mixed $pulse formdata in object.
 * @return bool Update record instance result.
 */
function pulse_update_instance($pulse) {
    global $DB;

    $context = context_module::instance($pulse->coursemodule);

    $pulse->id = $pulse->instance;
    $pulse->timemodified = time();
    if (isset($pulse->pulse_content_editor)) {
        // Save pulse content areafiles.
        $pulse->pulse_content = file_save_draft_area_files($pulse->pulse_content_editor['itemid'],
                                                    $context->id, 'mod_pulse', 'pulse_content', 0,
                                                    array('subdirs' => true), $pulse->pulse_content_editor['text']);
        $pulse->pulse_contentformat = $pulse->pulse_content_editor['format'];
        unset($pulse->pulse_content_editor);
    }

    if (!isset($pulse->boxicon) && isset($pulse->displaymode)) {
        $pulse->boxicon = '';
    }

    // If module resend triggred then set the notified status to null for instance.
    if (isset($pulse->resend_pulse)) {
        $DB->set_field('pulse_users', 'status', 0, ['pulseid' => $pulse->id]);
        // Reschedule the notification if resend notification enabled.
        $message = get_string('resendnotificationdesc', 'mod_pulse');
        \core\notification::add($message, 'info');
    }
    // Update instance data.
    $updates = $DB->update_record('pulse', $pulse);
    // Extend the updated module instance pro features.
    \mod_pulse\extendpro::pulse_extend_update_instance($pulse, $context);

    return $updates;
}


/**
 * Delete Pulse instnace
 *
 * @param  mixed $pulseid
 * @return bool
 */
function pulse_delete_instance($pulseid) {
    global $DB;
    if ($DB->record_exists('pulse', ['id' => $pulseid])) {
        $cm = get_coursemodule_from_instance('pulse', $pulseid);

        if ($DB->delete_records('pulse', ['id' => $pulseid])) {
            \mod_pulse\extendpro::pulse_extend_delete_instance($cm->id, $pulseid);
            return true;
        }
    }
    return false;
}

/**
 * Features that supports by pulse module.
 *
 * @uses FEATURE_IDNUMBER
 * @uses FEATURE_GROUPS
 * @uses FEATURE_GROUPINGS
 * @uses FEATURE_MOD_INTRO
 * @uses FEATURE_COMPLETION_TRACKS_VIEWS
 * @uses FEATURE_GRADE_HAS_GRADE
 * @uses FEATURE_GRADE_OUTCOMES
 * @uses FEATURE_MOD_PURPOSE
 * @param string $feature FEATURE_xx constant for requested feature
 * @return bool|null True if module supports feature, false if not, null if doesn't know
 */
function pulse_supports($feature) {
    if (defined('FEATURE_MOD_PURPOSE') && $feature == FEATURE_MOD_PURPOSE) {
        return MOD_PURPOSE_ADMINISTRATION;
    }
    switch($feature) {
        case FEATURE_IDNUMBER:
            return true;
        case FEATURE_GROUPS:
            return true;
        case FEATURE_GROUPINGS:
            return false;
        case FEATURE_MOD_INTRO:
            return true;
        case FEATURE_COMPLETION_TRACKS_VIEWS:
            return false;
        case FEATURE_COMPLETION_HAS_RULES:
            return true;
        case FEATURE_GRADE_HAS_GRADE:
            return false;
        case FEATURE_GRADE_OUTCOMES:
            return false;
        case FEATURE_MOD_ARCHETYPE:
            return MOD_ARCHETYPE_RESOURCE;
        case FEATURE_BACKUP_MOODLE2:
            return true;
        case FEATURE_NO_VIEW_LINK:
            return false;
        default:
            return null;
    }
}

/**
 * To make the stealth enable to pulse, it must have view link. So pulse supports the view option on features.
 * On dynamic check, removed the view link. if module hidden from students then pulse should has view support.
 *
 * @param cm_info $cm
 * @return void
 */
function mod_pulse_cm_info_dynamic(cm_info &$cm) {
    if ($cm->visible) {
        $cm->set_no_view_link();
    }
}

/**
 * Serve the files from the Pulse file areas
 *
 * @param stdClass $course the course object
 * @param stdClass $cm the course module object
 * @param stdClass $context the context
 * @param string $filearea the name of the file area
 * @param array $args extra arguments (itemid, path)
 * @param bool $forcedownload whether or not force download
 * @param array $options additional options affecting the file serving
 * @return bool false if the file not found, just send the file otherwise and do not return anything
 */
function pulse_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
    // Check the contextlevel is as expected - if your plugin is a block, this becomes CONTEXT_BLOCK, etc.
    if ($context->contextlevel != CONTEXT_MODULE && $context->contextlevel != CONTEXT_SYSTEM) {
        return false;
    }
    // Get extended plugins fileareas.
    $availablefiles = \mod_pulse\extendpro::pulse_extend_filearea();
    $availablefiles += ['pulse_content', 'intro', 'notificationheader', 'notificationfooter'];
    // Make sure the filearea is one of those used by the plugin.
    if (!in_array($filearea, $availablefiles)) {
        return false;
    }

    // Item id is 0.
    $itemid = array_shift($args);

    // Extract the filename / filepath from the $args array.
    $filename = array_pop($args); // The last item in the $args array.
    if (!$args) {
        $filepath = '/'; // ...$args is empty => the path is '/'
    } else {
        $filepath = '/'.implode('/', $args).'/'; // ...$args contains elements of the filepath
    }

    // Retrieve the file from the Files API.
    $fs = get_file_storage();
    $file = $fs->get_file($context->id, 'mod_pulse', $filearea, $itemid, $filepath, $filename);
    if (!$file) {
        return false; // The file does not exist.
    }

    // We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
    send_stored_file($file, 86400, 0, $forcedownload, $options);
}


/**
 * Add a get_coursemodule_info function in case any pulse type wants to add 'extra' information
 * for the course (see resource).
 *
 * Given a course_module object, this function returns any "extra" information that may be needed
 * when printing this activity in a course listing.  See get_array_of_activities() in course/lib.php.
 *
 * @param stdClass $coursemodule The coursemodule object (record).
 * @return cached_cm_info An object on information that the courses
 *                        will know about (most noticeably, an icon).
 */
function pulse_get_coursemodule_info($coursemodule) {
    global $DB;

    $dbparams = ['id' => $coursemodule->instance];
    $fields = 'id, name, intro, introformat, completionavailable, completionself, completionapproval, completionapprovalroles';
    if (!$pulse = $DB->get_record('pulse', $dbparams, $fields)) {
        return false;
    }

    $result = new cached_cm_info();
    $result->name = $pulse->name;

    if ($coursemodule->showdescription) {
        // Convert intro to html. Do not filter cached version, filters run at display time.
        $result->content = format_module_intro('pulse', $pulse, $coursemodule->id, false);
    }

    // Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'.
    if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) {
        $result->customdata['customcompletionrules']['completionself'] = $pulse->completionself;
        $result->customdata['customcompletionrules']['completionwhenavailable'] = $pulse->completionavailable;
        $result->customdata['customcompletionrules']['completionapproval'] = $pulse->completionapproval;
    }

    // Populate some other values that can be used in calendar or on dashboard.
    if ($pulse->completionapprovalroles ) {
        $result->customdata['completionapprovalroles'] = $pulse->completionapprovalroles;

    }

    return $result;
}

/**
 * Callback which returns human-readable strings describing the active completion custom rules for the module instance.
 *
 * @param cm_info|stdClass $cm object with fields ->completion and ->customdata['customcompletionrules']
 * @return array $descriptions the array of descriptions for the custom rules.
 */
function mod_pulse_get_completion_active_rule_descriptions($cm) {
    // Values will be present in cm_info, and we assume these are up to date.
    if (empty($cm->customdata['customcompletionrules'])
        || $cm->completion != COMPLETION_TRACKING_AUTOMATIC) {
        return [];
    }

    $descriptions = [];
    foreach ($cm->customdata['customcompletionrules'] as $key => $val) {
        switch ($key) {
            case 'completionwhenavailable':
                $descriptions[] = get_string('completionwhenavailable', 'pulse');
                break;
            case 'completionself':
                $descriptions[] = get_string('completionself', 'pulse');
                break;
            case 'completionapproval':
                $descriptions[] = get_string('completionrequireapproval', 'pulse');
                break;
            default:
                break;
        }
    }
    return $descriptions;
}

/**
 * Obtains the automatic completion state for this pulse based on any conditions
 * in forum settings.
 *
 * @param  object $course Course data record
 * @param  object $cm Course-module data object
 * @param  int $userid User ID
 * @param  bool $type Type of comparison (or/and; can be used as return value if no conditions)
 * @param  mixed $pulse Pulse instance data record
 * @param  mixed $completion Completion data.
 * @param  mixed $modinfo Module info class object.
 * @return bool True if completed, false if not, $type if conditions not set.
 */
function pulse_get_completion_state($course, $cm, $userid, $type, $pulse=null, $completion=null, $modinfo=null) {
    global $CFG, $DB;

    if ($pulse == null) {
        $pulse = $DB->get_record('pulse', ['id' => $cm->instance], "*", MUST_EXIST);
    }

    if ($completion == null) {
        $completion = $DB->get_record('pulse_completion', ['userid' => $userid, 'pulseid' => $pulse->id]);
    }
    $status = $type;
    // Module availablity completion for student.
    if ($pulse->completionavailable) {
        if ($modinfo == null) {
            $modinfo = get_fast_modinfo($course->id, $userid);
            $cm = $modinfo->get_cm($cm->id);
            $isvisble = $cm->uservisible;
        } else {
            $cm = $modinfo->get_cm($cm->id);
            $info = new \core_availability\info_module($cm);
            $str = '';
            // Get section info for cm.
            // Check section is accessable by user.
            $section = $cm->get_section_info();
            $sectioninfo = new \core_availability\info_section($section);
            $isvisble = \mod_pulse\helper::pulse_mod_uservisible($cm, $userid, $sectioninfo, $modinfo, $info);
        }
        if ($isvisble) {
            $status = COMPLETION_COMPLETE;
        } else {
            return COMPLETION_INCOMPLETE;
        }
    }

    // Completion by any selected role user.
    if ($pulse->completionapproval) {
        if (!empty($completion) && $completion->approvalstatus == 1) {
            $status = COMPLETION_COMPLETE;
        } else {
            return COMPLETION_INCOMPLETE;
        }
    }
    // Self completion by own.
    if ($pulse->completionself) {
        if (!empty($completion) && $completion->selfcompletion == 1) {
            $status = COMPLETION_COMPLETE;
        } else {
            return COMPLETION_INCOMPLETE;
        }
    }

    return $status;
}


/**
 * This function receives a calendar event and returns the action associated with it, or null if there is none.
 *
 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
 * is not displayed on the block.
 *
 * @param calendar_event $event
 * @param \core_calendar\action_factory $factory
 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
 * @return \core_calendar\local\event\entities\action_interface|null
 */
function mod_pulse_core_calendar_provide_event_action(calendar_event $event,
                                                     \core_calendar\action_factory $factory,
                                                     int $userid = 0) {
    global $USER;

    if (empty($userid)) {
        $userid = $USER->id;
    }

    $cm = get_fast_modinfo($event->courseid, $userid)->instances['pulse'][$event->instance];
    if (!$cm->uservisible) {
        // The module is not visible to the user for any reason.
        return null;
    }

    $context = context_module::instance($cm->id);
    if (!has_capability('mod/pulse:notifyuser', $context, $userid)) {
        return null;
    }

    $completion = new \completion_info($cm->get_course());
    $completiondata = $completion->get_data($cm, false, $userid);
    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
        return null;
    }

    return $factory->create_instance(
        get_string('view'),
        new \moodle_url('/mod/pulse/view.php', ['id' => $cm->id]),
        1,
        true
    );
}

/**
 * Update the pulse content with bootstrap box before rendered in course page.
 *
 * @param cm_info $cm
 * @return void
 */
function mod_pulse_cm_info_view(cm_info $cm) {
    global $DB, $USER;

    $pulse = $DB->get_record('pulse', ['id' => $cm->instance]);
    $content = $cm->content;
    $course = $cm->get_course();
    $senderdata = \mod_pulse\task\sendinvitation::get_sender($course->id, $cm->context->id);
    $sender = \mod_pulse\task\sendinvitation::find_user_sender($senderdata, $USER->id);
    list($subject, $content) = \mod_pulse\helper::update_emailvars($content, '', $course,
                            $USER, $pulse, $sender);
    $cm->set_content($content);
    if (isset($pulse->cssclass) && $pulse->cssclass) {
        $cm->set_extra_classes($pulse->cssclass);
    }
    if (isset($pulse->displaymode) && $pulse->displaymode == 1) {
        $boxtype = ($pulse->boxtype) ? $pulse->boxtype : 'primary';
        $boxicon = ($pulse->boxicon) ? $pulse->boxicon : '';
        $content = $cm->content;
        $content = \mod_pulse\helper::pulse_render_content($content, $boxicon, $boxtype);
        $cm->set_content($content);
    }

    $completionbtn = \mod_pulse\helper::cm_completionbuttons($cm, $pulse);
    if (!empty($completionbtn)) {
        $content = $cm->content;
        $content .= html_writer::tag('div', $completionbtn, ['class' => 'pulse-completion-btn']);
        $cm->set_content($content);
    }
}

/**
 * Custom method to prevent the mtrace logs based on admin config.
 *
 * @param string $message Message to log on cron.
 * @param bool $detail Need to display this in log even detailedlog config disable state.
 * @return void
 */
function pulse_mtrace($message, $detail=false) {
    $showdetail = get_config('mod_pulse', 'detailedlog');
    if ($showdetail || $detail) {
        mtrace($message);
    }
}

/**
 * Fragement output to preview the selected preset. Loads all the available informations and configurable params as form elements.
 *
 * @param array $args Preset ID and Course ID with context.
 */
function mod_pulse_output_fragment_get_preset_preview(array $args) : ?string {
    global $CFG;
    $context = $args['context'];

    if ($context->contextlevel !== CONTEXT_COURSE && $context->contextlevel !== CONTEXT_MODULE) {
        return null;
    }
    $presetid = $args['presetid'];
    $courseid = $args['courseid'];
    $sectionid = $args['section'];
    $preset = new \mod_pulse\preset($presetid, $courseid, $context, $sectionid);
    return $preset->output_fragment();
}

/**
 * Fragement output to result of apply methods on selected preset.
 * Trigger the apply preset method in preset to create the pulse module using the selected preset and apply method.
 *
 * @param array $args Custom config data and Current module form data with context.
 */
function mod_pulse_output_fragment_apply_preset(array $args) : ?string {
    global $CFG;
    $context = $args['context'];

    if ($context->contextlevel !== CONTEXT_COURSE && $context->contextlevel !== CONTEXT_MODULE) {
        return null;
    }
    $formdata = $args['formdata'];
    $pageparams = $args['pageparams'];
    $external = new \mod_pulse\external();
    $result = $external->apply_presets($context->id, $formdata, $pageparams);

    return $result;
}

/**
 * Fragement output to list all the presets in the pulse module add/edit form.
 *
 * @param array $args context and Course ID with context.
 */
function mod_pulse_output_fragment_get_presetslist(array $args) {
    global $OUTPUT;
    $context = $args['context'];

    if ($context->contextlevel !== CONTEXT_COURSE && $context->contextlevel !== CONTEXT_MODULE) {
        return null;
    }
    $courseid = $args['courseid'];
    $presets = \mod_pulse\preset::generate_presets_list($courseid);
    return $OUTPUT->render_from_template('mod_pulse/presets_list', $presets);
}

/**
 * Generate approval buttons and self mark completions buttons based on user roles and availability.
 *
 * @param  array $args List of modules id available in the course page
 * @return string encoded html string.
 */
function mod_pulse_output_fragment_completionbuttons($args) {
    global $CFG, $DB, $USER;

    $modules = json_decode($args['modules']);
    list($insql, $inparams) = $DB->get_in_or_equal($modules);
    $sql = "SELECT cm.*, nf.completionapproval, nf.completionapprovalroles, nf.completionself FROM {course_modules} cm
    JOIN {modules} AS md ON md.id = cm.module
    JOIN {pulse} nf ON nf.id = cm.instance WHERE cm.id $insql AND md.name = 'pulse'";
    $records = $DB->get_records_sql($sql, $inparams);

    $html = [];

    foreach ($modules as $moduleid) {
        if (isset($records[$moduleid])) {
            $data = $records[$moduleid];
            $html[$moduleid] = '';
            $extend = true;
            // Approval button generation for selected roles.
            if ($data->completionapproval == 1) {
                $roles = $data->completionapprovalroles;
                if (\mod_pulse\helper::pulse_has_approvalrole($roles, $moduleid)) {
                    $approvelink = new moodle_url('/mod/pulse/approve.php', ['cmid' => $moduleid]);
                    $html[$moduleid] .= html_writer::tag('div',
                        html_writer::link($approvelink, get_string('approveuserbtn', 'pulse'),
                        ['class' => 'btn btn-primary pulse-approve-users']),
                        ['class' => 'approve-user-wrapper']
                    );
                } else if (\mod_pulse\helper::pulse_user_isstudent($moduleid)) {
                    if (!class_exists('core_completion\activity_custom_completion')
                        && $message = \mod_pulse\helper::pulse_user_approved($records[$moduleid]->instance, $USER->id) ) {
                        $html[$moduleid] .= $message.'<br>';
                    }
                }
            }
            // Generate self mark completion buttons for students.
            if (\mod_pulse\helper::pulse_is_uservisible($moduleid, $USER->id, $data->course)) {
                if ($data->completionself == 1 && \mod_pulse\helper::pulse_user_isstudent($moduleid)
                    && !\mod_pulse\helper::pulse_isusercontext($data->completionapprovalroles, $moduleid)) {
                    // Add self mark completed informations.
                    if (!class_exists('core_completion\activity_custom_completion')
                        && $date = \mod_pulse\helper::pulse_already_selfcomplete($records[$moduleid]->instance, $USER->id)) {
                        $selfmarked = get_string('selfmarked', 'pulse', ['date' => $date]).'<br>';
                        $html[$moduleid] .= html_writer::tag('div', $selfmarked,
                        ['class' => 'pulse-self-marked badge badge-success']);
                    } else {
                        $selfcomplete = new moodle_url('/mod/pulse/approve.php', ['cmid' => $moduleid, 'action' => 'selfcomplete']);
                        $selfmarklink = html_writer::link($selfcomplete, get_string('markcomplete', 'pulse'),
                            ['class' => 'btn btn-primary pulse-approve-users']
                        );
                        $html[$moduleid] .= html_writer::tag('div', $selfmarklink, ['class' => 'pulse-approve-users']);
                    }
                }
            } else {
                $extend = false;
            }
            // Extend the pro features if the logged in users has able to view the module.
            if ($extend) {
                $instance = new stdclass();
                $instance->pulse = $data;
                $instance->pulse->id = $data->instance;
                $instance->user = $USER;
                $html[$moduleid] .= \mod_pulse\extendpro::pulse_extend_reaction($instance, 'content');
            }
        }
    }

    return json_encode($html);
}