Commit 33b8ca26 authored by Amaia's avatar Amaia
Browse files

MDL-67786 core_contentbank: New content bank contenttype plugin type

Co-authored by: Ferran Recio <ferran@moodle.com>
parent df0e58ad
<?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/>.
/**
* Content bank and its plugins settings.
*
* @package core
* @subpackage contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
$action = required_param('action', PARAM_ALPHANUMEXT);
$name = required_param('name', PARAM_PLUGIN);
$syscontext = context_system::instance();
$PAGE->set_url('/admin/contentbank.php');
$PAGE->set_context($syscontext);
require_admin();
require_sesskey();
$return = new moodle_url('/admin/settings.php', array('section' => 'managecontentbanktypes'));
$plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
$sortorder = array_flip(array_keys($plugins));
if (!isset($plugins[$name])) {
print_error('contenttypenotfound', 'error', $return, $name);
}
switch ($action) {
case 'disable':
if ($plugins[$name]->is_enabled()) {
set_config('disabled', 1, 'contentbank_'. $name);
}
break;
case 'enable':
if (!$plugins[$name]->is_enabled()) {
unset_config('disabled', 'contentbank_'. $name);
}
break;
case 'up':
if ($sortorder[$name]) {
$currentindex = $sortorder[$name];
$seq = array_keys($plugins);
$seq[$currentindex] = $seq[$currentindex - 1];
$seq[$currentindex - 1] = $name;
set_config('contentbank_plugins_sortorder', implode(',', $seq));
}
break;
case 'down':
if ($sortorder[$name] < count($sortorder) - 1) {
$currentindex = $sortorder[$name];
$seq = array_keys($plugins);
$seq[$currentindex] = $seq[$currentindex + 1];
$seq[$currentindex + 1] = $name;
set_config('contentbank_plugins_sortorder', implode(',', $seq));
}
break;
}
core_plugin_manager::reset_caches();
$cache = cache::make('core', 'contentbank_enabled_extensions');
$cache->purge();
$cache = cache::make('core', 'contentbank_context_extensions');
$cache->purge();
redirect($return);
......@@ -657,6 +657,19 @@ if ($hassiteconfig) {
}
}
// Content bank content types.
if ($hassiteconfig) {
$ADMIN->add('modules', new admin_category('contenbanksettings', new lang_string('contentbank')));
$temp = new admin_settingpage('managecontentbanktypes', new lang_string('managecontentbanktypes'));
$temp->add(new admin_setting_managecontentbankcontenttypes());
$ADMIN->add('contenbanksettings', $temp);
$plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
foreach ($plugins as $plugin) {
/** @var \core\plugininfo\contentbank $plugin */
$plugin->load_settings($ADMIN, 'contenbanksettings', $hassiteconfig);
}
}
/// Add all local plugins - must be always last!
if ($hassiteconfig) {
$ADMIN->add('modules', new admin_category('localplugins', new lang_string('localplugins')));
......
<?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/>.
/**
* Content manager class
*
* @package core_contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_contentbank;
use stored_file;
use stdClass;
use coding_exception;
use moodle_url;
/**
* Content manager class
*
* @package core_contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class content {
/** @var stdClass $content The content of the current instance. **/
protected $content = null;
/**
* Content bank constructor
*
* @param stdClass $content A contentbanck_content record.
* @throws coding_exception If content type is not right.
*/
public function __construct(stdClass $content) {
// Content type should exist and be linked to plugin classname.
$classname = $content->contenttype.'\\content';
if (get_class($this) != $classname) {
throw new coding_exception(get_string('contenttypenotfound', 'error', $content->contenttype));
}
$typeclass = $content->contenttype.'\\contenttype';
if (!class_exists($typeclass)) {
throw new coding_exception(get_string('contenttypenotfound', 'error', $content->contenttype));
}
// A record with the id must exist in 'contenbank_content' table.
// To improve performance, we are only checking the id is set, but no querying the database.
if (!isset($content->id)) {
throw new coding_exception(get_string('invalidcontentid', 'error'));
}
$this->content = $content;
}
/**
* Returns $this->content.
*
* @return stdClass $this->content.
*/
public function get_content(): stdClass {
return $this->content;
}
/**
* Returns $this->content->contenttype.
*
* @return string $this->content->contenttype.
*/
public function get_content_type(): string {
return $this->content->contenttype;
}
/**
* Updates content_bank table with information in $this->content.
*
* @return boolean True if the content has been succesfully updated. False otherwise.
* @throws \coding_exception if not loaded.
*/
public function update_content(): bool {
global $USER, $DB;
// A record with the id must exist in 'contenbank_content' table.
// To improve performance, we are only checking the id is set, but no querying the database.
if (!isset($this->content->id)) {
throw new coding_exception(get_string('invalidcontentid', 'error'));
}
$this->content->usermodified = $USER->id;
$this->content->timemodified = time();
return $DB->update_record('contentbank_content', $this->content);
}
/**
* Returns the name of the content.
*
* @return string The name of the content.
*/
public function get_name(): string {
return $this->content->name;
}
/**
* Returns the content ID.
*
* @return int The content ID.
*/
public function get_id(): int {
return $this->content->id;
}
/**
* Change the content instanceid value.
*
* @param int $instanceid New instanceid for this content
* @return boolean True if the instanceid has been succesfully updated. False otherwise.
*/
public function set_instanceid(int $instanceid): bool {
$this->content->instanceid = $instanceid;
return $this->update_content();
}
/**
* Returns the $instanceid of this content.
*
* @return int contentbank instanceid
*/
public function get_instanceid(): int {
return $this->content->instanceid;
}
/**
* Change the content config values.
*
* @param string $configdata New config information for this content
* @return boolean True if the configdata has been succesfully updated. False otherwise.
*/
public function set_configdata(string $configdata): bool {
$this->content->configdata = $configdata;
return $this->update_content();
}
/**
* Return the content config values.
*
* @return mixed Config information for this content (json decoded)
*/
public function get_configdata() {
return $this->content->configdata;
}
/**
* Returns the $file related to this content.
*
* @return stored_file File stored in content bank area related to the given itemid.
* @throws \coding_exception if not loaded.
*/
public function get_file(): ?stored_file {
$itemid = $this->get_id();
$fs = get_file_storage();
$files = $fs->get_area_files(
$this->content->contextid,
'contentbank',
'public',
$itemid,
'itemid, filepath, filename',
false
);
if (!empty($files)) {
$file = reset($files);
return $file;
}
return null;
}
/**
* Returns the file url related to this content.
*
* @return string URL of the file stored in content bank area related to the given itemid.
* @throws \coding_exception if not loaded.
*/
public function get_file_url(): string {
if (!$file = $this->get_file()) {
return '';
}
$fileurl = moodle_url::make_pluginfile_url(
$this->content->contextid,
'contentbank',
'public',
$file->get_itemid(),
$file->get_filepath(),
$file->get_filename()
);
return $fileurl;
}
/**
* Returns user has access permission for the content itself (based on what plugin needs).
*
* @return bool True if content could be accessed. False otherwise.
*/
public function can_view(): bool {
// There's no capability at content level to check,
// but plugins can overwrite this method in case they want to check something related to content properties.
return true;
}
}
<?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/>.
/**
* Content bank manager class
*
* @package core_contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_contentbank;
/**
* Content bank manager class
*
* @package core_contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class contentbank {
/**
* Obtains the list of core_contentbank_content objects currently active.
*
* The list does not include players which are disabled.
*
* @return string[] Array of contentbank contenttypes.
*/
private function get_enabled_content_types(): array {
$enabledtypes = \core\plugininfo\contenttype::get_enabled_plugins();
$types = [];
foreach ($enabledtypes as $name) {
$classname = "\\contenttype_$name\\contenttype";
if (class_exists($classname)) {
$types[] = $name;
}
}
return $types;
}
/**
* Obtains an array of supported extensions by active plugins.
*
* @return array The array with all the extensions supported and the supporting plugin names.
*/
public function load_all_supported_extensions(): array {
$extensionscache = \cache::make('core', 'contentbank_enabled_extensions');
$supportedextensions = $extensionscache->get('enabled_extensions');
if ($supportedextensions === false) {
// Load all enabled extensions.
$supportedextensions = [];
foreach ($this->get_enabled_content_types() as $type) {
$classname = "\\contenttype_$type\\contenttype";
if (class_exists($classname)) {
$manager = new $classname;
if ($manager->is_feature_supported($manager::CAN_UPLOAD)) {
$extensions = $manager->get_manageable_extensions();
foreach ($extensions as $extension) {
if (array_key_exists($extension, $supportedextensions)) {
$supportedextensions[$extension][] = $type;
} else {
$supportedextensions[$extension] = [$type];
}
}
}
}
}
$extensionscache->set('enabled_extensions', $supportedextensions);
}
return $supportedextensions;
}
/**
* Obtains an array of supported extensions in the given context.
*
* @param \context $context Optional context to check (default null)
* @return array The array with all the extensions supported and the supporting plugin names.
*/
public function load_context_supported_extensions(\context $context = null): array {
$extensionscache = \cache::make('core', 'contentbank_context_extensions');
$contextextensions = $extensionscache->get($context->id);
if ($contextextensions === false) {
$contextextensions = [];
$supportedextensions = $this->load_all_supported_extensions();
foreach ($supportedextensions as $extension => $types) {
foreach ($types as $type) {
$classname = "\\contenttype_$type\\contenttype";
if (class_exists($classname)) {
$manager = new $classname($context);
if ($manager->can_upload()) {
$contextextensions[$extension] = $type;
break;
}
}
}
}
$extensionscache->set($context->id, $contextextensions);
}
return $contextextensions;
}
/**
* Obtains a string with all supported extensions by active plugins.
* Mainly to use as filepicker options parameter.
*
* @param \context $context Optional context to check (default null)
* @return string A string with all the extensions supported.
*/
public function get_supported_extensions_as_string(\context $context = null) {
$supported = $this->load_context_supported_extensions($context);
$extensions = array_keys($supported);
return implode(',', $extensions);
}
/**
* Returns the file extension for a file.
*
* @param string $filename The name of the file
* @return string The extension of the file
*/
public function get_extension(string $filename) {
$dot = strrpos($filename, '.');
if ($dot === false) {
return '';
}
return strtolower(substr($filename, $dot));
}
/**
* Get the first content bank plugin supports a file extension.
*
* @param string $extension Content file extension
* @param \context $context $context Optional context to check (default null)
* @return string contenttype name supports the file extension or null if the extension is not supported by any allowed plugin.
*/
public function get_extension_supporter(string $extension, \context $context = null): ?string {
$supporters = $this->load_context_supported_extensions($context);
if (array_key_exists($extension, $supporters)) {
return $supporters[$extension];
}
return null;
}
}
<?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/>.
/**
* Content type manager class
*
* @package core_contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_contentbank;
use coding_exception;
use moodle_url;
/**
* Content type manager class
*
* @package core_contentbank
* @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class contenttype {
/** Plugin implements uploading feature */
const CAN_UPLOAD = 'upload';
/** @var context This content's context. **/
protected $context = null;
/**
* Content type constructor
*
* @param \context $context Optional context to check (default null)
*/
public function __construct(\context $context = null) {
if (empty($context)) {
$context = \context_system::instance();
}
$this->context = $context;
}
/**
* Fills content_bank table with appropiate information.
*
* @param stdClass $content An optional content record compatible object (default null)
* @return content Object with content bank information.
*/
public function create_content(\stdClass $content = null): ?content {
global $USER, $DB;
$record = new \stdClass();
$record->contenttype = $this->get_contenttype_name();
$record->contextid = $this->context->id;
$record->name = $content->name ?? '';
$record->usercreated = $content->usercreated ?? $USER->id;
$record->timecreated = time();
$record->usermodified = $record->usercreated;
$record->timemodified = $record->timecreated;
$record->configdata = $content->configdata ?? '';
$record->id = $DB->insert_record('contentbank_content', $record);
if ($record->id) {
$classname = '\\'.$record->contenttype.'\\content';
return new $classname($record);
}
return null;
}
/**
* Returns the contenttype name of this content.
*
* @return string Content type of the current instance
*/
public function get_contenttype_name(): string {
$classname = get_class($this);
$contenttype = explode('\\', $classname);
return array_shift($contenttype);
}
/**
* Returns the plugin name of the current instance.
*
* @return string Plugin name of the current instance
*/
public function get_plugin_name(): string {
$contenttype = $this->get_contenttype_name();
$plugin = explode('_', $contenttype);
return array_pop($plugin);
}
/**
* Returns the URL where the content will be visualized.
*
* @param stdClass $record Th content to be displayed.
* @return string URL where to visualize the given content.
*/
public function get_view_url(\stdClass $record): string {
return new moodle_url('/contentbank/view.php', ['id' => $record->id]);
}
/**
* Returns the HTML content to add to view.php visualizer.
*
* @param stdClass $record Th content to be displayed.
* @return string HTML code to include in view.php.
*/
public function get_view_content(\stdClass $record): string {
// Main contenttype class can visualize the content, but plugins could overwrite visualization.
return '';
}
/**
* Returns the HTML code to render the icon for content bank contents.
*
* @param string $contentname The contentname to add as alt value to the icon.
* @return string HTML code to render the icon
*/
public function get_icon(string $contentname): string {
global $OUTPUT;
return $OUTPUT->pix_icon('f/unknown-64', $contentname, 'moodle', ['class' => 'iconsize-big']);
}
/**