AutorÃa | Ultima modificación | Ver Log |
<?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/>.
/**
* unilabel type carousel.
*
* @package unilabeltype_carousel
* @author Andreas Grabs <info@grabs-edv.de>
* @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace unilabeltype_carousel;
use mod_unilabel\setting_configselect_button;
/**
* Content type definition.
* @package unilabeltype_carousel
* @author Andreas Grabs <info@grabs-edv.de>
* @copyright 2018 onwards Grabs EDV {@link https://www.grabs-edv.de}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content_type extends \mod_unilabel\content_type {
/** @var \stdClass */
private $unilabeltyperecord;
/** @var array */
private $slides;
/** @var \stdClass */
private $cm;
/** @var \context */
private $context;
/** Caption styles to be used in the instances caption setting. */
public const CAPTIONSTYLES = [
'dark',
'light',
];
/**
* Constructor.
*
* @return void
*/
public function __construct() {
$this->init_type(__NAMESPACE__);
}
/**
* Get true if the unilabeltype supports sortorder by using drag-and-drop.
*
* @return bool
*/
public function use_sortorder() {
return true;
}
/**
* Add elements to the activity settings form.
*
* @param \mod_unilabel\edit_content_form $form
* @param \context $context
* @return void
*/
public function add_form_fragment(\mod_unilabel\edit_content_form $form, \context $context) {
global $PAGE, $OUTPUT;
$unilabeltyperecord = $this->load_unilabeltype_record($form->unilabel->id);
$mform = $form->get_mform();
$prefix = $this->component . '_';
$mform->addElement('advcheckbox', $prefix . 'showintro', get_string('showunilabeltext', $this->component));
$mform->addElement('header', $prefix . 'hdr', $this->get_name());
$mform->addHelpButton($prefix . 'hdr', 'pluginname', $this->component);
$mform->addElement(
'checkbox',
$prefix . 'autorun',
get_string('autorun', 'mod_unilabel'),
''
);
$autorundefault = !empty($this->config->autorun);
$mform->setDefault($prefix . 'autorun', $autorundefault);
$numbers = array_combine(range(1, 10), range(1, 10));
$mform->addElement(
'select',
$prefix . 'carouselinterval',
get_string('carouselinterval', $this->component),
$numbers
);
$mform->hideIf($prefix . 'carouselinterval', $prefix . 'autorun', 'notchecked');
$numbers = array_combine(range(100, 600, 50), range(100, 600, 50));
$numbers = [0 => get_string('autoheight', $this->component)] + $numbers;
$mform->addElement('select', $prefix . 'height', get_string('height', $this->component), $numbers);
$mform->addHelpButton($prefix . 'height', 'height', $this->component);
$backgrounddefault = empty($unilabeltyperecord->background) ? '' : $unilabeltyperecord->background;
$this->add_colourpicker($mform,
$prefix . 'background',
get_string('background', $this->component),
$backgrounddefault);
$mform->addElement('advcheckbox', $prefix . 'usemobile', get_string('use_mobile_images', $this->component));
$mform->addHelpButton($prefix . 'usemobile', 'use_mobile_images', $this->component);
$captionoptions = $this->get_captionstyle_options(true);
$mform->addElement('select', $prefix . 'captionstyle', get_string('captionstyle', $this->component), $captionoptions);
$mform->addHelpButton($prefix . 'captionstyle', 'captionstyle', $this->component);
$mform->addElement('text', $prefix . 'captionwidth', get_string('captionwidth', $this->component));
$mform->setType($prefix . 'captionwidth', PARAM_INT);
$mform->addHelpButton($prefix . 'captionwidth', 'captionwidth', $this->component);
// Prepare the activity url picker.
$formid = $mform->getAttribute('id');
$course = $form->get_course();
$picker = new \mod_unilabel\output\component\activity_picker($course, $formid);
$inputidbase = 'id_' . $prefix . 'url_';
$pickerbutton = new \mod_unilabel\output\component\activity_picker_button($formid, $inputidbase);
$mform->addElement('html', $OUTPUT->render($picker));
$repeatarray = [];
// If we want each repeated elment in a numbered group we add a header with '{no}' in its label.
// This is replaced by the number of element.
$repeatarray[] = $mform->createElement(
'header',
'singleelementheader',
get_string('slide', $this->component) . '-{no}'
);
$repeatarray[] = $mform->createElement(
'hidden',
$prefix . 'sortorder'
);
$repeatarray[] = $mform->createElement(
'editor',
$prefix . 'caption',
get_string('caption', $this->component) . '-{no}',
['rows' => 4],
$this->editor_options($form->context)
);
$urlelement = $mform->createElement(
'text',
$prefix . 'url',
get_string('url', $this->component) . '-{no}',
['size' => 50]
);
$mform->setType($prefix . 'url', PARAM_URL);
$newwindowelement = $mform->createElement(
'checkbox',
$prefix . 'newwindow',
get_string('newwindow')
);
$repeatarray[] = $mform->createElement(
'group',
$prefix . 'urlgroup',
get_string('url', $this->component) . '-{no}',
[$urlelement, $newwindowelement],
null,
false
);
$repeatarray[] = $mform->createElement(
'static',
$prefix . 'activitypickerbutton',
'',
$OUTPUT->render($pickerbutton)
);
$repeatarray[] = $mform->createElement(
'filemanager',
$prefix . 'image',
get_string('image', $this->component) . '-{no}',
null,
$this->manager_options()
);
$repeatarray[] = $mform->createElement(
'filemanager',
$prefix . 'image_mobile',
get_string('image_mobile', $this->component) . '-{no}',
null,
$this->manager_options()
);
$repeatedoptions = [];
$repeatedoptions[$prefix . 'sortorder']['type'] = PARAM_INT;
$repeatedoptions[$prefix . 'url']['type'] = PARAM_URL;
$repeatedoptions[$prefix . 'caption']['type'] = PARAM_RAW;
$repeatedoptions[$prefix . 'image']['type'] = PARAM_FILE;
$repeatedoptions[$prefix . 'image_mobile']['type'] = PARAM_FILE;
// Adding the help buttons.
$repeatedoptions[$prefix . 'caption']['helpbutton'] = ['caption', $this->component];
$repeatedoptions[$prefix . 'urlgroup']['helpbutton'] = ['url', $this->component];
$repeatedoptions[$prefix . 'image_mobile']['helpbutton'] = ['image_mobile', $this->component];
$defaultrepeatcount = 1; // The default count for slides.
$repeatcount = count($this->slides);
$nextel = $form->repeat_elements(
$repeatarray,
$repeatcount,
$repeatedoptions,
'multiple_chosen_elements_count', // We need a fixed name here to get drag and drop work.
$prefix . 'add_more_elements_btn', // This element musst be called so to get removed when dnd is enabled.
$defaultrepeatcount, // Each time we add 3 elements.
get_string('addmoreslides', $this->component),
false
);
// This elements are needed by js to set empty hidden fields while deleting an element.
$myelements = [
'caption',
'url',
'image',
'image_mobile',
];
// Render the button to add elements.
$btn = $OUTPUT->render_from_template('mod_unilabel/load_element_button', [
'type' => $this->type,
'formid' => $formid,
'contextid' => $context->id,
'courseid' => $course->id,
'prefix' => $prefix,
]);
$mform->addElement('html', $btn);
// Add dynamic buttons like "Add item", "Delete" and "move".
$PAGE->requires->js_call_amd(
'mod_unilabel/add_dyn_formbuttons',
'init',
[
$this->type,
$formid,
$context->id,
$prefix,
$myelements,
$this->use_sortorder(), // Use drag and drop.
]
);
}
/**
* Get the default values for the settings form.
*
* @param array $data
* @param \stdClass $unilabel
* @return array
*/
public function get_form_default($data, $unilabel) {
global $DB;
$cm = get_coursemodule_from_instance('unilabel', $unilabel->id);
$context = \context_module::instance($cm->id);
$prefix = $this->component . '_';
// Set default data for the carousel in generel.
if (!$unilabeltyperecord = $this->load_unilabeltype_record($unilabel->id)) {
$data[$prefix . 'carouselinterval'] = $this->config->carouselinterval;
$data[$prefix . 'autorun'] = $this->config->autorun;
$data[$prefix . 'height'] = $this->config->height;
$data[$prefix . 'background'] = '#ffffff';
$data[$prefix . 'showintro'] = !empty($this->config->showintro);
$data[$prefix . 'usemobile'] = !empty($this->config->usemobile);
$data[$prefix . 'captionwidth'] = 0;
return $data;
}
$data[$prefix . 'carouselinterval'] = $unilabeltyperecord->carouselinterval;
$data[$prefix . 'autorun'] = (bool) (!empty($unilabeltyperecord->carouselinterval));
$data[$prefix . 'height'] = $unilabeltyperecord->height;
$data[$prefix . 'background'] = $unilabeltyperecord->background;
$data[$prefix . 'showintro'] = $unilabeltyperecord->showintro;
$data[$prefix . 'usemobile'] = $unilabeltyperecord->usemobile;
$data[$prefix . 'captionstyle'] = $unilabeltyperecord->captionstyle;
$data[$prefix . 'captionwidth'] = $unilabeltyperecord->captionwidth;
// Set default data for slides.
$slides = $DB->get_records(
'unilabeltype_carousel_slide',
[
'carouselid' => $unilabeltyperecord->id,
],
'sortorder ASC'
);
if (!$slides) {
return $data;
}
$index = 0;
foreach ($slides as $slide) {
// Prepare the url field.
$elementname = $prefix . 'url[' . $index . ']';
$data[$elementname] = $slide->url;
// Prepare the newwindow field.
$elementname = $prefix . 'newwindow[' . $index . ']';
$data[$elementname] = $slide->newwindow;
// Prepare the caption field.
$elementname = $prefix . 'caption[' . $index . ']';
$data[$elementname]['format'] = FORMAT_HTML;
$draftitemid = file_get_submitted_draft_itemid($elementname);
$data[$elementname]['text'] = file_prepare_draft_area(
$draftitemid,
$context->id,
$this->component,
'caption',
$slide->id,
null,
$slide->caption
);
$data[$elementname]['itemid'] = $draftitemid;
// Prepare the images.
// $draftitemid is set by the function file_prepare_draft_area().
$draftitemid = 0; // This is needed to create a new draftitemid.
file_prepare_draft_area($draftitemid, $context->id, $this->component, 'image', $slide->id);
$elementname = $prefix . 'image[' . $index . ']';
$data[$elementname] = $draftitemid;
// Prepare the mobile images.
// $draftitemid is set by the function file_prepare_draft_area().
$draftitemid = 0; // This is needed to create a new draftitemid.
file_prepare_draft_area($draftitemid, $context->id, $this->component, 'image_mobile', $slide->id);
$elementname = $prefix . 'image_mobile[' . $index . ']';
$data[$elementname] = $draftitemid;
// Prepare the sortorder field.
$elementname = $prefix . 'sortorder[' . $index . ']';
$data[$elementname] = $slide->sortorder ?? ($index + 1);
++$index;
}
return $data;
}
/**
* Validate all form values given in $data and returns an array with errors.
* It does the same as the validation method in moodle forms.
*
* @param array $errors
* @param array $data
* @param array $files
* @return array
*/
public function form_validation($errors, $data, $files) {
$prefix = $this->component . '_';
if (!empty($data[$prefix . 'background'])) {
if (!\mod_unilabel\configcolourpicker_validation::validate_colourdata($data[$prefix . 'background'])) {
$errors[$prefix . 'background'] = get_string('invalidvalue', 'mod_unilabel');
}
}
return $errors;
}
/**
* Get the namespace of this content type.
*
* @return string
*/
public function get_namespace() {
return __NAMESPACE__;
}
/**
* Get the html formated content for this type.
*
* @param \stdClass $unilabel
* @param \stdClass $cm
* @param \plugin_renderer_base $renderer
* @return string
*/
public function get_content($unilabel, $cm, \plugin_renderer_base $renderer) {
if (!$unilabeltyperecord = $this->load_unilabeltype_record($unilabel->id)) {
$content = [
'intro' => get_string('nocontent', $this->component),
'cmid' => $cm->id,
'hasslides' => false,
];
} else {
$intro = $this->format_intro($unilabel, $cm);
$showintro = !empty($unilabeltyperecord->showintro);
$content = [
'showintro' => $showintro,
'intro' => $showintro ? $intro : '',
'interval' => $unilabeltyperecord->carouselinterval,
'height' => $unilabeltyperecord->height,
'autoheight' => empty($unilabeltyperecord->height),
'background' => $unilabeltyperecord->background,
'hasslides' => count($this->slides) > 0,
'cmid' => $cm->id,
'plugin' => $this->component,
'captionstyle' => $unilabeltyperecord->captionstyle,
'captionwidth' => $unilabeltyperecord->captionwidth,
];
$content['slides'] = array_values(
array_map(function ($slide) {
$slide->caption = file_rewrite_pluginfile_urls(
$slide->caption,
'pluginfile.php',
$this->context->id,
$this->component,
'caption',
$slide->id
);
return $slide;
}, $this->slides)
);
if (!empty($this->config->custombutton)) {
$fontbuttons = setting_configselect_button::get_font_buttons();
$content['custombuttons'] = 1;
$content['fontawesomenext'] = $fontbuttons[$this->config->custombutton]['next'];
$content['fontawesomeprev'] = $fontbuttons[$this->config->custombutton]['prev'];
// To make sure we have clean html we have to put the carousel css into the <head> by using javascript.
$cssstring = $renderer->render_from_template('mod_unilabel/carousel_button_style', $content);
$content['cssjsonstring'] = json_encode($cssstring);
}
}
$content = $renderer->render_from_template('unilabeltype_carousel/carousel', $content);
return $content;
}
/**
* Delete the content of this type.
*
* @param int $unilabelid
* @return void
*/
public function delete_content($unilabelid) {
global $DB;
$unilabeltyperecord = $this->load_unilabeltype_record($unilabelid);
// Delete all slides.
if (!empty($unilabeltyperecord)) {
$DB->delete_records('unilabeltype_carousel_slide', ['carouselid' => $unilabeltyperecord->id]);
}
$DB->delete_records('unilabeltype_carousel', ['unilabelid' => $unilabelid]);
}
/**
* Save the content from settings page.
*
* @param \stdClass $formdata
* @param \stdClass $unilabel
* @return bool
*/
public function save_content($formdata, $unilabel) {
global $DB, $USER;
// We want to keep the slides consistent so we start a transaction here.
$transaction = $DB->start_delegated_transaction();
$prefix = $this->component . '_';
// First save the carousel record.
if (!$unilabeltyperecord = $DB->get_record('unilabeltype_carousel', ['unilabelid' => $unilabel->id])) {
$unilabeltyperecord = new \stdClass();
$unilabeltyperecord->unilabelid = $unilabel->id;
$unilabeltyperecord->id = $DB->insert_record('unilabeltype_carousel', $unilabeltyperecord);
}
if (!empty($formdata->{$prefix . 'autorun'})) {
$unilabeltyperecord->carouselinterval = $formdata->{$prefix . 'carouselinterval'};
} else {
$unilabeltyperecord->carouselinterval = 0;
}
$unilabeltyperecord->height = $formdata->{$prefix . 'height'};
$unilabeltyperecord->background = $formdata->{$prefix . 'background'};
$unilabeltyperecord->showintro = $formdata->{$prefix . 'showintro'};
$unilabeltyperecord->usemobile = $formdata->{$prefix . 'usemobile'};
$unilabeltyperecord->captionstyle = $formdata->{$prefix . 'captionstyle'};
$unilabeltyperecord->captionwidth = $formdata->{$prefix . 'captionwidth'};
$DB->update_record('unilabeltype_carousel', $unilabeltyperecord);
$fs = get_file_storage();
$context = \context_module::instance($formdata->cmid);
$usercontext = \context_user::instance($USER->id);
// First: remove old slide images.
// We use the module_context as context and this component as component.
$fs->delete_area_files($context->id, $this->component, 'caption');
$fs->delete_area_files($context->id, $this->component, 'image');
$fs->delete_area_files($context->id, $this->component, 'image_mobile');
// Second: remove old slide records.
$DB->delete_records('unilabeltype_carousel_slide', ['carouselid' => $unilabeltyperecord->id]);
// How many slides could be defined (we have an array here)?
// They may not all used so some could be left out.
$potentialslidecount = $formdata->multiple_chosen_elements_count;
for ($i = 0; $i < $potentialslidecount; ++$i) {
// Get the draftitemid to identify the submitted file.
$draftitemid = $formdata->{$prefix . 'image'}[$i];
if (!empty($unilabeltyperecord->usemobile)) {
$draftitemidmobile = $formdata->{$prefix . 'image_mobile'}[$i];
}
// Do we have an image? We get this information with file_get_draft_area_info().
$fileinfo = file_get_draft_area_info($draftitemid);
// We only create a record if we have at least a file or a caption.
$caption = $formdata->{$prefix . 'caption'}[$i]['text'] ?? '';
if ($fileinfo['filecount'] < 1 && !$this->html_has_content($caption)) {
continue;
}
// Rewrite file url in caption.
// Get the draftitemid for caption editor.
$drafitemidcaption = $formdata->{$prefix . 'caption'}[$i]['itemid'];
$caption = file_rewrite_urls_to_pluginfile($caption, $drafitemidcaption);
$sortorder = $formdata->{$prefix . 'sortorder'}[$i];
$sliderecord = new \stdClass();
$sliderecord->carouselid = $unilabeltyperecord->id;
$sliderecord->url = $formdata->{$prefix . 'url'}[$i];
$sliderecord->newwindow = !empty($formdata->{$prefix . 'newwindow'}[$i]);
$sliderecord->caption = $caption;
$sliderecord->sortorder = $sortorder;
$sliderecord->id = $DB->insert_record('unilabeltype_carousel_slide', $sliderecord);
// Now we can save our draft files.
file_save_draft_area_files(
$drafitemidcaption,
$context->id,
$this->component,
'caption',
$sliderecord->id
);
file_save_draft_area_files(
$draftitemid,
$context->id,
$this->component,
'image',
$sliderecord->id
);
if (!empty($formdata->{$prefix . 'usemobile'})) {
file_save_draft_area_files(
$draftitemidmobile,
$context->id,
$this->component,
'image_mobile',
$sliderecord->id
);
}
}
$transaction->allow_commit();
return !empty($unilabeltyperecord->id);
}
/**
* Load and cache the unilabel record.
*
* @param int $unilabelid
* @return \stdClass
*/
public function load_unilabeltype_record($unilabelid) {
global $DB;
if (empty($this->unilabeltyperecord)) {
if (!$this->unilabeltyperecord = $DB->get_record('unilabeltype_carousel', ['unilabelid' => $unilabelid])) {
$this->slides = [];
return;
}
$this->cm = get_coursemodule_from_instance('unilabel', $unilabelid);
$this->context = \context_module::instance($this->cm->id);
$slides = $DB->get_records(
'unilabeltype_carousel_slide',
[
'carouselid' => $this->unilabeltyperecord->id,
],
'sortorder ASC'
);
$index = 0;
foreach ($slides as $slide) {
$slide->imageurl = $this->get_image_for_slide($slide);
$slide->imagemobileurl = $this->get_image_mobile_for_slide($slide);
$slide->nr = $index;
$slide->captionplain = format_string($slide->caption);
++$index;
}
$this->slides = $slides;
}
return $this->unilabeltyperecord;
}
/**
* Get the image url for the given slide.
*
* @param \stdClass $slide
* @return string
*/
private function get_image_for_slide($slide) {
$fs = get_file_storage();
$files = $fs->get_area_files($this->context->id, $this->component, 'image', $slide->id, '', $includedirs = false);
if (!$file = array_shift($files)) {
return '';
}
$imageurl = \moodle_url::make_pluginfile_url(
$this->context->id,
$this->component,
'image',
$slide->id,
'/',
$file->get_filename()
);
return $imageurl;
}
/**
* Get the mobile image url.
*
* @param \stdClass $slide
* @return string
*/
private function get_image_mobile_for_slide($slide) {
$fs = get_file_storage();
$files = $fs->get_area_files(
$this->context->id,
$this->component,
'image_mobile',
$slide->id,
'',
false
);
if (!$file = array_shift($files)) {
return '';
}
$imageurl = \moodle_url::make_pluginfile_url(
$this->context->id,
$this->component,
'image_mobile',
$slide->id,
'/',
$file->get_filename()
);
return $imageurl;
}
/**
* Check whether ther is content or not.
*
* @param string $caption
* @return bool
*/
private function html_has_content($caption) {
$searches = [
'<br>',
'<br />',
'<p>',
'</p>',
];
$check = trim(str_replace($searches, '', $caption));
return !empty($check);
}
/**
* Get the options array to support files in editor.
*
* @param \context $context
* @return array
*/
public function editor_options($context) {
return [
'maxfiles' => EDITOR_UNLIMITED_FILES,
'noclean' => true,
'context' => $context,
'subdirs' => true,
];
}
/**
* Get the options array for a file manager.
*
* @return array
*/
public function manager_options() {
return [
'maxfiles' => 1,
'subdirs' => false,
'accepted_types' => ['web_image'],
];
}
/**
* Generates an array with options to define a css class.
* The structure is like:
* [
* 'light' => 'captionstyle_light',
* 'dark' => 'captionstyle_dark',
* ]
*
* @param bool $addchoose If true an additional element "Choose" is add at the beginning.
* @return array
*/
public function get_captionstyle_options($addchoose = false) {
$options = [];
foreach (static::CAPTIONSTYLES as $style) {
$options[$style] = get_string('captionstyle_' . $style, $this->component);
}
if ($addchoose) {
$options = ['' => get_string('choose')] + $options;
}
return $options;
}
/**
* Check that this plugin is activated on config settings.
*
* @return bool
*/
public function is_active() {
return !empty($this->config->active);
}
}