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/classes/preset.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/>.

/**
 * Create pulse using presets, Preset template generate and module restore method definied.
 *
 * @package   mod_pulse
 * @copyright 2021, bdecent gmbh bdecent.de
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace mod_pulse;

use context_module;
use core_plugin_manager;
use moodle_exception;
use stdclass;

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

require_once($CFG->libdir.'/formslib.php');
require_once($CFG->dirroot . '/course/modlib.php');
require_once($CFG->dirroot.'/mod/pulse/mod_form.php');

/**
 * Create pulse using the backup file of mod pulse. Defined preset form to extract the template file data.
 */
class preset extends \moodleform {

    /**
     * Preset form to replace the module form during the apply and customize method.
     *
     * @var stdclass
     */
    public $_form;

    /**
     * Pulse module context.
     *
     * @var \context_module
     */
    public $modulecontext;

    /**
     * Pulse module data record.
     *
     * @var stdclass
     */
    public $pulse;

    /**
     * Course id.
     *
     * @var int
     */
    public $courseid;

    /**
     * Module current section.
     *
     * @var int
     */
    public $section;

    /**
     * Restore controller, helps to restore the preset template as pulse module.
     *
     * @var stdclass
     */
    protected $controller;

    /**
     * Module form data.
     *
     * @var array
     */
    public $modformdata = [];

    /**
     * Pulse mod form instance.
     *
     * @var mod_pulse_mod_form
     */
    public $pulseform;

    /**
     * Preset instance data
     *
     * @var stdclass
     */
    public $preset;

    /**
     * Id of current preset.
     *
     * @var int
     */
    public $presetid;

    /**
     * Course instance data.
     *
     * @var stdclass
     */
    public $course;

    /**
     * Basic data stored as moodle form element, used to pass the preset and course data to modal.
     *
     * @return void
     */
    public function definition() {
        // Empty form content.

        $this->_form->updateAttributes(['id' => 'preset-configurable-params']);
        $this->_form->addElement('hidden', 'importmethod');
        $this->_form->setType('importmethod', PARAM_INT);

        $this->_form->addElement('hidden', 'courseid', $this->courseid);
        $this->_form->setType('courseid', PARAM_INT);

        $this->_form->addElement('hidden', 'presetid', $this->presetid);
        $this->_form->setType('presetid', PARAM_INT);

        if ($this->section) {
            $this->_form->addElement('hidden', 'section', $this->section);
        }
    }

    /**
     * Prepare and process the preset data like description and instructions.
     *
     * @param int $presetid ID of selected preset.
     * @param int $courseid ID of current course.
     * @param stdclass $coursecontext Course context.
     * @param int $section Section number.
     */
    public function __construct(int $presetid, int $courseid, $coursecontext=null, ?int $section=0) {
        global $COURSE;
        $this->courseid = $courseid;
        $this->presetid = $presetid;
        $this->course = get_course($courseid);
        $this->section = $section;
        $this->presetdata();
        $COURSE = $this->course;
        parent::__construct();
    }

    /**
     * Create class variable for the pulse form.
     *
     * @return void
     */
    public function setpulseform(): void {
        $this->pulseform = self::pulseform_instance($this->courseid);
    }

    /**
     * Create pulse mod form instance.
     *
     * @param int $courseid ID of course.
     * @param int $section Section number.
     * @return mod_pulse_mod_form mod_pulse form instance.
     */
    public static function pulseform_instance(int $courseid, int $section=1) {
        $course = get_course($courseid);
        list($module, $context, $cw, $cm, $data) = \prepare_new_moduleinfo_data($course, 'pulse', $section);
        $pulseform = new \mod_pulse_mod_form($data, $section, $cm, $course);
        return $pulseform;
    }

    /**
     * Load preview content of selected preset with available configurable form field elements in modal.
     *
     * @return string
     */
    public function output_fragment(): string {
        global $OUTPUT;
        $this->setpulseform();
        $this->load_forms();
        $presethtml = $OUTPUT->render_from_template('mod_pulse/preset', $this->preset);
        return $presethtml;
    }

    /**
     * Fetch the stored preset data,  and process the text editors content, to filter the multilang and files.
     *
     * @return void
     */
    public function presetdata(): void {
        global $DB;
        $this->preset = $DB->get_record('pulse_presets', ['id' => $this->presetid, 'status' => 1]);
        if (!empty($this->preset)) {
            $description = file_rewrite_pluginfile_urls(
                $this->preset->description, 'pluginfile.php', \context_system::instance()->id,
                'mod_pulse', 'description', $this->preset->id
            );
            $instruction = file_rewrite_pluginfile_urls(
                $this->preset->instruction, 'pluginfile.php', \context_system::instance()->id,
                'mod_pulse', 'instruction', $this->preset->id
            );

            $this->preset->title = format_text($this->preset->title);
            $this->preset->description = format_text($description, FORMAT_HTML);
            $this->preset->instruction = format_text($instruction, FORMAT_HTML);
        }
    }

    /**
     * Convert all the selected configurable parameters into moodle form element.
     * Then, the modal will display the config params for the user. It helps users to customize before applying.
     *
     * Fetch all the available form elements from module pulse.
     * Then copy the selected configurable params into preset form elements.
     * Finally, it assigns the HTML of preset form elements as config params.
     *
     * @return void
     */
    public function load_forms(): void {

        $configparams = (isset($this->preset->configparams)) ? json_decode($this->preset->configparams, true) : [];
        self::js_collection_requirement(); // End js collection.
        if (!empty($configparams)) {
            // Prevent the javascript collections due to the duplicate of preset and email placeholder js inclusion.
            $configlist = self::get_pulse_config_list($this->courseid);
            // List of available elements in module pulse form.
            foreach ($this->pulseform->_form->_elements as $key => $element) {

                $hide = ['hidden', 'html', 'submit', 'static'];
                if (in_array($element->_type, $hide)) {
                    continue;
                }
                if ($element instanceof \MoodleQuickForm_group && in_array($element->_name, $configparams, true)) {
                    $group = [];
                    $elem = $this->pulseform->_form->getElement($element->_name);
                    $elem->_label = isset($configlist[$element->_name])
                        ? $configlist[$element->_name] : $elem->_label;
                    $this->_form->addElement($elem);
                    $this->_form->addElement('hidden', $elem->_name.'_changed', false);
                    if (isset($elem->_elements) && !empty($elem->_elements)) {
                        foreach ($elem->_elements as $key => $subelem) {
                            $subname = $subelem->_attributes['name'];
                            $this->_form->addElement('hidden', $subname.'_changed', false);
                        }
                    }
                } else if (isset($element->_attributes['name']) && in_array($element->_attributes['name'], $configparams, true)) {
                    $elementname = $element->_attributes['name'];
                    $elem = $this->pulseform->_form->getElement($elementname);
                    $attributename = $elem->_attributes['name'];
                    if ($elem->_type == 'editor' || $elem->_type == 'autocomplete') {
                        // Prevent the confilict with module form editors.
                        // Using same names in editor elements in same page, not load the text editors in second elements.
                        $elem->_attributes['name'] = 'preseteditor_'.$elem->_attributes['name'];
                    }
                    $elem->_label = isset($configlist[$attributename])
                        ? $configlist[$attributename] : $elem->_label;
                    $this->_form->addElement($elem);
                    $this->_form->addElement('hidden', $attributename.'_changed', false);
                }
            }
            $this->add_action_buttons(false, 's');
        }
        // Start to collect the javascripts.
        self::js_collection_requirement(true);
        // Render all the configurable params form into html.
        $this->preset->configparams = $this->render();
    }

    /**
     * Start or end the javascript requirement collection.
     *
     * @param bool $method if true start the collection otherwise end the collection.
     * @return void
     */
    public static function js_collection_requirement(bool $method=false): void {
        global $PAGE;

        if ($method == true) {
            if (get_class($PAGE->requires) != 'fragment_requirements_manager') {
                $PAGE->start_collecting_javascript_requirements();
            }
        } else {
            if (get_class($PAGE->requires) == 'fragment_requirements_manager') {
                $PAGE->end_collecting_javascript_requirements();
            }
        }
    }


    /**
     * Generate the list of available presets based on the order.
     *
     * @param int $courseid Id of the current course.
     * @return array List of presets and manage presets page URL.
     */
    public static function generate_presets_list(int $courseid) {
        global $DB, $OUTPUT, $PAGE;
        $link = '';
        $pluginmanager = core_plugin_manager::instance()->get_installed_plugins('local');
        if (array_key_exists('pulsepro', $pluginmanager)) {
            $link = new \moodle_url('/local/pulsepro/presets.php');
        }
        if ($records = $DB->get_records('pulse_presets', ['status' => 1], 'order_no ASC')) {
            $presets = [];
            $configlist = self::get_pulse_config_list($courseid);

            foreach ($records as $presetid => $record) {
                $description = file_rewrite_pluginfile_urls(
                    $record->description, 'pluginfile.php', \context_system::instance()->id, 'mod_pulse', 'description', $record->id
                );

                $configparams = array_map(function($value) use ($configlist) {
                    return isset($configlist[$value]) ? $configlist[$value] : '';
                }, json_decode($record->configparams, true));

                $configparams = array_filter($configparams);

                $item = [
                    'id' => $record->id,
                    'title' => format_string($record->title),
                    'description' => format_text($description, FORMAT_HTML),
                    'configurableparams' => $configparams,
                ];
                if (!empty($record->icon)) {
                    $icon = explode(':', $record->icon);
                    $icon1 = isset($icon[1]) ? $icon[1] : 'core';
                    $icon0 = isset($icon[0]) ? $icon[0] : '';
                    $item['icon'] = $OUTPUT->pix_icon( $icon1, $icon0 );
                }
                $presets[] = $item;
            }
            return ['presetslist' => (!empty($presets) ? 1 : 0), 'presets' => $presets, 'managepresets' => $link];
        }
        return ['managepresets' => $link];
    }

    /**
     * List of pulse module form fields list with config label.
     *
     * @param int $courseid Id of course.
     * @return array List of available form fields.
     */
    public static function get_pulse_config_list($courseid): array {
        self::js_collection_requirement();
        $fields = array();
        $header = '';
        $pulseform = self::pulseform_instance($courseid);
        foreach ($pulseform->_form->_elements as $element) {
            $hide = ['hidden', 'html', 'submit', 'static'];
            $hide = ['hidden', 'html', 'submit', 'static'];
            if (in_array($element->_type, $hide)) {
                continue;
            }

            if ($element->_type == 'header') {
                $header = $element->_text;
            } else if ($element instanceof \MoodleQuickForm_group) {
                $label = (($element->_label) ? $element->_label : $element->_name);
                if (strpos($element->_name, 'relativedate') !== false) {
                    $label = get_string('schedule:relativedate', 'pulse');
                }
                if (strpos($element->_name, 'fixeddate') !== false) {
                    $label = get_string('schedule:fixeddate', 'pulse');
                }

                $fields[$element->_name] = $header .' > '. $label;
            } else {
                $label = (($element->_label) ? $element->_label : $element->_text);
                $fields[$element->_attributes['name']] = $header.' > '.$label;
            }
        }
        self::js_collection_requirement(true);
        // Remove session key.
        if (!empty($fields)) {
            unset($fields['sesskey']);
            unset($fields['_qf__mod_pulse_mod_form']);
        }

        return $fields;
    }

    /**
     * Basic create pulse module dataset.
     * Set the pulse module form data fetched from the current create module form.
     * Afterwards override these datas with configurable params and backup module data.
     *
     * @param array $modformdata Current create module form dataset.
     * @return void
     */
    public function set_modformdata($modformdata) {
        $this->modformdata = $modformdata;
    }

    /**
     * Extract the preset files and trigger the import method.
     *
     * Apply the selected preset to the current adding pulse module. Creates the temp directory for backup.
     * Extract the xml data files from selected preset template backup(mbz) file into the backup temp directory
     *
     * @param [type] $configdata
     * @return string
     */
    public function apply_presets($configdata) {

        if (empty($this->preset->id)) {
            return false;
        }

        $fs = get_file_storage();
        $files = $fs->get_area_files(\context_system::instance()->id, 'mod_pulse', 'preset_template', $this->preset->id, '', false);
        $files = array_values($files);

        if (!isset($files[0])) {
            throw new \moodle_exception('activitybackupnotset', 'pulse');
        }

        $file = $files[0];

        $fp = get_file_packer('application/vnd.moodle.backup');
        $backuptempdir = make_backup_temp_directory('preset' . $this->preset->id);
        $files[0]->extract_to_pathname($fp, $backuptempdir);

        return $this->import_presets('preset'.$this->preset->id, $configdata);
    }

    /**
     * Remove all the empty config params from custom config params. Otherwise it makes the backup data empty.
     *
     * @param array $config custom configurable params data.
     * @return array
     */
    public function clear_empty_data(array $config): array {

        foreach ($config as $key => $value) {

            if ($key == 'completionapprovalroles') {
                $value = (is_array($config['completionapprovalroles']))
                        ? json_encode($config['completionapprovalroles']) : '';
            }

            if (is_array($value)) {
                $config[$key] = $this->clear_empty_data($value);
                continue;
            }

            if (trim($value) !== null && trim($value) !== '') {
                // Remove the empty restrict conditions.
                if ($key == 'availabilityconditionsjson') {
                    $json = json_decode($value);
                    if (!isset($json->c) || empty($json->c)) {
                        unset($config[$key]);
                        continue;
                    }
                }
                $config[$key] = $value;
            } else {
                unset($config[$key]);
            }
        }
        return $config;
    }

    /**
     * Update the format of DB record data into moodle editor form element format.
     *
     * @param array $data Element record data.
     * @param string $name Element form name.
     * @param string $editor Element name with Editor keyword.
     * @return void
     */
    public static function format_editordata(array &$data, string $name, string $editor) {
        if (isset($data[$name])) {
            $data[$editor] = array(
                'text' => $data[$name],
                'format' => $data[$name.'format']
            );
        }
    }

    /**
     * Available pulse field to update to pulse table after the restore of preset.
     *
     * @return array List of pulse module record fields.
     */
    public static function pulsefields(): array {
        return [
            'name',
            'intro',
            'introformat',
            'pulse_subject',
            'pulse_content',
            'pulse_contentformat',
            'pulse',
            'diff_pulse',
            'completionavailable',
            'completionself',
            'completionapproval',
            'completionapprovalroles',
        ];
    }

    /**
     * Check custom config params has contains any field to update.
     *
     * @param array $configdata Custom configurable fields.
     * @param array $fields Record fields.
     * @return bool True if contains, otherwise false.
     */
    public static function hasupdatefields(array $configdata, $fields=null): bool {
        $fields = ($fields) ? $fields : self::pulsefields();
        if (array_intersect(array_keys($configdata), $fields)) {
            return true;
        }
        return false;
    }

    /**
     * Import the preset template with custom config data as a new pulse module.
     * Contains two methods, Save, Customize.
     * When the save method is triggered, it will create the module using moodle basic restore method.
     * Then apply the custom config data directly into tables.
     * On customize method, It fetches all the pulse module-related data from backup XML files.
     * Set as the default value for the pulse module.
     * Then returns the form content to replace with the current mod form.So the user can able to customize it before applying it.
     *
     * @param string $backuptempdir Backup files extracted directory root.
     * @param array $configdata Custom config params data.
     * @return string|json Redirect course url for save method or Module Form html to replace when import method is customize.
     */
    public function import_presets($backuptempdir, $configdata) {
        global $CFG, $USER, $DB;

        require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
        require_once($CFG->dirroot . '/mod/pulse/preset_restore.php');

        if (isset($configdata['pulse_content'])) {
            $configdata['pulse_contentformat'] = $configdata['pulse_contenteditor']['format'];
            $configdata['pulse_content'] = $configdata['pulse_contenteditor']['text'];
        }

        // Update completion fields.
        $configdata['completionavailable'] = isset($configdata['completionwhenavailable'])
                ? $configdata['completionwhenavailable'] : '';

        if (class_exists('\local_pulsepro\presets\preset_form')) {
            \local_pulsepro\presets\preset_form::clean_configdata($configdata);
        }

        // No need to clear basic data and pro schedules.
        $nochanged = array('courseid', 'presetid', 'section', 'importmethod', 'sesskey', 'second_schedule', 'first_schedule');
        $configdata = array_filter($configdata, function($value, $key) use ($nochanged, $configdata) {
            if (!in_array($key, $nochanged) && strpos($key, 'recipients') == false && strpos($key, 'editor') == false) {
                $value = (isset($configdata[$key.'_changed']) && empty($configdata[$key.'_changed'])) ? '' : $value;
            }
            return ($value !== null && $value !== "") ? true : false;
        }, ARRAY_FILTER_USE_BOTH);

        // Clear the empty custom element.
        $configdata = $this->clear_empty_data($configdata);

        $method = \backup::TARGET_CURRENT_ADDING;
        // Restore controller.
        $this->controller = new \restore_controller($backuptempdir, $this->courseid, \backup::INTERACTIVE_NO,
        \backup::MODE_IMPORT, $USER->id, $method);

        if ($configdata['importmethod'] == 'save') {
            $this->controller->execute_precheck();
            $this->controller->execute_plan();
            foreach ($this->controller->get_plan()->get_tasks() as $key => $task) {
                if ($task instanceof \restore_activity_task) {
                    $pulseid = $task->get_activityid();
                    $contextid = $task->get_contextid();
                    $configdata['contextid'] = $contextid;
                    if (isset($configdata['introeditor']) && !empty($configdata['introeditor']['text'])) {
                        $introeditor = $configdata['introeditor'];
                        $configdata['introformat'] = $introeditor['format'];
                        $configdata['intro'] = file_save_draft_area_files(
                            $introeditor['itemid'], $contextid, 'mod_pulse', 'intro', 0,
                            array('subdirs' => true), $introeditor['text']
                        );
                    }
                    if (isset($configdata['pulse_contenteditor']) && !empty($configdata['pulse_contenteditor']['text'])) {
                        $pulseeditor = $configdata['pulse_contenteditor'];
                        $configdata['pulse_contentformat'] = $pulseeditor['format'];
                        $configdata['pulse_content'] = file_save_draft_area_files(
                            $pulseeditor['itemid'], $contextid, 'mod_pulse', 'pulse_content', 0,
                            array('subdirs' => true), $pulseeditor['text']
                        );
                    }
                    unset($configdata['pulse_contenteditor']);
                    unset($configdata['introeditor']);
                    // Update the pro reminder contents.
                    \mod_pulse\extendpro::pulse_preset_update($pulseid, $configdata);
                    if (!empty($configdata)) {
                        $configdata['id'] = $pulseid;
                        $configdata['timemodified'] = time();
                        $DB->update_record('pulse', (object) $configdata);
                    }
                    // Update course modules.
                    if (!empty($configdata)) {
                        $configdata['id'] = $task->get_moduleid();
                        $configdata['instance'] = $pulseid;
                        $this->updatemodulesection($configdata);
                        $DB->update_record('course_modules', $configdata);
                    }
                    // Remove the Database cache.
                    purge_other_caches();
                    $DB->reset_caches();
                    break;
                }
            }
            $this->controller->destroy();
            $courseurl = new \moodle_url('/course/view.php', ['id' => $this->courseid ]);
            return json_encode(['url' => $courseurl->out(), 'pulseid' => (isset($pulseid) ? $pulseid : '') ]);

        } else if ($configdata['importmethod'] == 'customize') {
            // Fetch values from backup file.
            $backupdata = $this->fetch_pulse_data_fromxml();
            // Replace the element availability to availabilityconditionjson.
            if (isset($backupdata['availability'])) {
                $backupdata['availabilityconditionsjson'] = $backupdata['availability'];
            }

            $this->controller->destroy();
            if (isset($backupdata['intro'])) {
                $backupdata['introeditor'] = array(
                    'text' => $backupdata['intro'],
                    'format' => $backupdata['introformat']
                );
            }
            if (isset($backupdata['pulse_content'])) {
                $backupdata['pulse_content_editor'] = array(
                    'text' => $backupdata['pulse_content'],
                    'format' => $backupdata['pulse_contentformat']
                );
            }

            // Update the config params with backup data.
            $formdata = array_replace_recursive($backupdata, $configdata);

            // Replace the config data.
            $formdata['course'] = $this->courseid;
            $formdata = array_filter($formdata, function($value) {
                if (!is_array($value)) {
                    return (trim($value) !== '') ? true : false;
                }
                return true;
            });
            // Create form with values.
            $form = $this->prepare_modform($formdata);
            // Send to replace the form.
            return $form;
        }
    }

    /**
     * Replace the created pulse module section with selected section.
     *
     * @param array $configdata Custom parameters.
     * @return void
     */
    public function updatemodulesection(array &$configdata): void {
        global $DB;
        if (isset($configdata['availabilityconditionsjson']) && !empty($configdata['availabilityconditionsjson'])) {
            $configdata['availability'] = $configdata['availabilityconditionsjson'];
            unset($configdata['availabilityconditionsjson']);
        }
        try {
            $transaction = $DB->start_delegated_transaction();
            if (isset($configdata['section']) && !empty($configdata['section'])) {
                $section = $configdata['section'];
                if ($sectionid = $DB->get_field('course_modules', 'section', ['id' => $configdata['id']])) {
                    // Remove the mod id from current section sequence.
                    if ($currentsection = $DB->get_record('course_sections', ['id' => $sectionid])) {
                        $sequence = ($currentsection->sequence) ? explode(',', $currentsection->sequence) : [];
                        $currentsection->sequence = ($sequence)
                            ? implode(',', array_diff($sequence, [$configdata['id']])) : $configdata['id'];
                        $DB->update_record('course_sections', $currentsection);
                    }

                    // Add the mod id to new section sequence.
                    $params = ['course' => $configdata['courseid'], 'section' => $section];
                    if ($newsection = $DB->get_record('course_sections', $params)) {
                        $sequence = ($newsection->sequence) ? explode(',', $newsection->sequence) : [];
                        array_push($sequence, $configdata['id']);
                        $newsection->sequence = ($sequence) ? implode(',', $sequence) : $configdata['id'];
                        if ($DB->update_record('course_sections', $newsection)) {
                            $DB->set_field('course_modules', 'section', $newsection->id, ['id' => $configdata['id']]);
                        }
                    }

                }
            }
            $transaction->allow_commit();
        } catch (\Exception $e) {
            if (!empty($transaction) && !$transaction->is_disposed()) {
                $transaction->rollback($e);
            }
        }
        unset($configdata['section']);
    }

    /**
     * Prepare the pulse module form html to replace with current adding module form using fragments.
     * Creates the new pulse form with backup and custom config data to user customization.
     * Triggered only on customize preset method.
     *
     * @param array $data Mod form dataset.
     * @return string Pulse mod form html content.
     */
    public function prepare_modform($data) {
        global $CFG, $PAGE, $COURSE;
        require_once($CFG->dirroot . '/course/modlib.php');
        require_once($CFG->dirroot.'/mod/pulse/mod_form.php');
        $this->modformdata = array_replace_recursive($this->modformdata, $data);
        // Replace the config data.
        $this->modformdata = array_map(function($value) {
            return (!is_array($value)) ? trim($value) : $value;
        }, $this->modformdata);

        $COURSE = $this->course;
        $PAGE->set_url(new \moodle_url('/course/modedit.php', [
            'section' => $this->modformdata['section'],
            'add' => $this->modformdata['add'],
            'update' => $this->modformdata['update'],
            'course' => $this->course->id
        ]));

        $pulseform = new \mod_pulse_mod_form((object) $this->modformdata, $this->modformdata['section'], null, $this->course);
        $pulseform->set_data($this->modformdata);
        $this->pulseform = $pulseform; // Test the form elements.
        return $pulseform->render();
    }

    /**
     * Fetch the pulse activity data from the extracted xml file of preset template backup file.
     * Contains three seperate data sets. Course Modules, Pulse, Pulse pro data.
     *
     * Used the custom restore step class to fetch the data. Direct use of xml parsers raise the permission issue.
     *
     * @return array Set of pulse module datas from backup file.
     */
    public function fetch_pulse_data_fromxml(): array {
        $record = [];
        foreach ($this->controller->get_plan()->get_tasks() as $key => $task) {
            if ($task instanceof \restore_activity_task) {
                $te = new \preset_customize_restore('module_info', 'module.xml', $task);
                $te->execute();
                $excluedfields = ['id', 'version', 'modulename', 'sectionid', 'sectionnumber', 'idnumber', 'added'];
                $record += array_filter($te->data, function($key) use ($excluedfields) {
                    return (!in_array($key, $excluedfields));
                }, ARRAY_FILTER_USE_KEY);

                $te = new \preset_customize_restore('module_info', 'pulse.xml', $task);
                $te->execute();
                $excluedfields = ['id', 'course', 'pulseid'];
                $record += array_filter($te->data, function($key) use ($excluedfields) {
                    return (!in_array($key, $excluedfields));
                }, ARRAY_FILTER_USE_KEY);

                $te = new \preset_customize_restore('pulsepro', 'pulse.xml', $task);
                $te->execute();
                $excluedfields = ['id', 'course', 'pulseid'];
                $record += array_filter($te->data, function($key) use ($excluedfields) {
                    return (!in_array($key, $excluedfields));
                }, ARRAY_FILTER_USE_KEY);

                \mod_pulse\extendpro::pulse_extend_preset('cleandata', $record);
            }
        }

        return $record;
    }

    /**
     * Create presets during the plugin installation and upgradation.
     *
     * @param array $presets List of presets with details.
     * @param boolean $pro Create template for pro version.
     * @return array List of created presets id.
     */
    public static function pulse_create_presets($presets=[], $pro=false) {
        global $DB, $CFG;
        if (!isloggedin() || isguestuser()) {
            return [];
        }
        $fs = get_file_storage();
        if (empty($presets)) {
            $presets = self::pulse_free_presets();
        }
        foreach ($presets as $key => $preset) {
            $sql = "SELECT id FROM {pulse_presets} WHERE ".$DB->sql_like('title', ':title');
            if ($DB->record_exists_sql($sql, ['title' => $preset['title']])) {
                continue;
            }
            $file = $preset['preset_template'];
            $preset['preset_template'] = file_get_unused_draft_itemid();
            $presetid = $DB->insert_record('pulse_presets', $preset);

            $filerecord = new stdClass();
            $filerecord->component = 'mod_pulse';
            $filerecord->contextid = \context_system::instance()->id;
            $filerecord->filearea = "preset_template";
            $filerecord->filepath = '/';
            $filerecord->itemid = $presetid;
            $filerecord->filename = $file;

            if (!$fs->file_exists($filerecord->contextid, $filerecord->component, $filerecord->filearea,
            $filerecord->itemid, $filerecord->filepath, $filerecord->filename)) {
                if ($pro) {
                    $backuppath = $CFG->dirroot . "/local/pulsepro/assets/$file";
                } else {
                    $backuppath = $CFG->dirroot . "/mod/pulse/assets/$file";
                }
                $fs->create_file_from_pathname($filerecord, $backuppath);
            }
            $created[] = $presetid;
        }
        return (isset($created)) ? $created : [];
    }


    /**
     * Demo presets data shipped with plugin by default for demo purpose.
     *
     * @return array List of demo presets.
     */
    public static function pulse_free_presets(): array {
        global $CFG;
        if (file_exists($CFG->dirroot.'/mod/pulse/assets/presets.xml')) {
            $presetsxml = simplexml_load_file($CFG->dirroot.'/mod/pulse/assets/presets.xml');
            $result = json_decode(json_encode($presetsxml), true);
            return $result;
        }
        return array();
    }
}