From 6ddc9278ff8b818f65a66c8d4fcf9ed034ae0123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20S=CC=8Ckoda?= Date: Tue, 9 Apr 2013 20:43:28 +0200 Subject: [PATCH] MDL-35434 new stored file config setting --- lib/adminlib.php | 206 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 200 insertions(+), 6 deletions(-) diff --git a/lib/adminlib.php b/lib/adminlib.php index 3156374c2df..e9d654762a8 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -1688,6 +1688,24 @@ abstract class admin_setting { $this->updatedcallback = $functionname; } + /** + * Execute postupdatecallback if necessary. + * @param mixed $original original value before write_setting() + * @return bool true if changed, false if not. + */ + public function post_write_settings($original) { + // Comparison must work for arrays too. + if (serialize($original) === serialize($this->get_setting())) { + return false; + } + + $callbackfunction = $this->updatedcallback; + if (!empty($callbackfunction) and function_exists($callbackfunction)) { + $callbackfunction($this->get_full_name()); + } + return true; + } + /** * Is setting related to query text - used when searching * @param string $query @@ -6422,7 +6440,8 @@ function admin_write_settings($formdata) { $count = 0; foreach ($settings as $fullname=>$setting) { - $original = serialize($setting->get_setting()); // comparison must work for arrays too + /** @var $setting admin_setting */ + $original = $setting->get_setting(); $error = $setting->write_setting($data[$fullname]); if ($error !== '') { $adminroot->errors[$fullname] = new stdClass(); @@ -6430,12 +6449,8 @@ function admin_write_settings($formdata) { $adminroot->errors[$fullname]->id = $setting->get_id(); $adminroot->errors[$fullname]->error = $error; } - if ($original !== serialize($setting->get_setting())) { + if ($setting->post_write_settings($original)) { $count++; - $callbackfunction = $setting->updatedcallback; - if (function_exists($callbackfunction)) { - $callbackfunction($fullname); - } } } @@ -8118,6 +8133,185 @@ class admin_setting_configcolourpicker extends admin_setting { } } + +/** + * Class used for uploading of one file into file storage, + * the file name is stored in config table. + * + * Please note you need to implement your own '_pluginfile' callback function, + * this setting only stores the file, it does not deal with file serving. + * + * @copyright 2013 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_setting_configstoredfile extends admin_setting { + /** @var array file area options - should be one file only */ + protected $options; + /** @var string name of the file area */ + protected $filearea; + /** @var int intemid */ + protected $itemid; + /** @var string used for detection of changes */ + protected $oldhashes; + + /** + * Create new stored file setting. + * + * @param string $name low level setting name + * @param string $visiblename human readable setting name + * @param string $description description of setting + * @param mixed $filearea file area for file storage + * @param int $itemid itemid for file storage + * @param array $options file area options + */ + public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) { + parent::__construct($name, $visiblename, $description, ''); + $this->filearea = $filearea; + $this->itemid = $itemid; + $this->options = (array)$options; + } + + /** + * Applies defaults and returns all options. + * @return array + */ + protected function get_options() { + global $CFG; + + require_once("$CFG->libdir/filelib.php"); + require_once("$CFG->dirroot/repository/lib.php"); + $defaults = array( + 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1, + 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED, + 'context' => context_system::instance()); + foreach($this->options as $k => $v) { + $defaults[$k] = $v; + } + + return $defaults; + } + + public function get_setting() { + return $this->config_read($this->name); + } + + public function write_setting($data) { + global $USER; + + // Let's not deal with validation here, this is for admins only. + $current = $this->get_setting(); + if (empty($data)) { + // Most probably applying default settings. + if ($current === null) { + return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin')); + } + return ''; + } else if (!is_number($data)) { + // Draft item id is expected here! + return get_string('errorsetting', 'admin'); + } + + $options = $this->get_options(); + $fs = get_file_storage(); + $component = is_null($this->plugin) ? 'core' : $this->plugin; + + $this->oldhashes = null; + if ($current) { + $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current); + if ($file = $fs->get_file_by_hash($hash)) { + $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash(); + } + unset($file); + } + + if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) { + // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime. + $usercontext = context_user::instance($USER->id); + if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.')) { + return get_string('errorsetting', 'admin'); + } + } + + file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options); + $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false); + + $filepath = ''; + if ($files) { + /** @var stored_file $file */ + $file = reset($files); + $filepath = $file->get_filepath().$file->get_filename(); + } + + return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin')); + } + + public function post_write_settings($original) { + $options = $this->get_options(); + $fs = get_file_storage(); + $component = is_null($this->plugin) ? 'core' : $this->plugin; + + $current = $this->get_setting(); + $newhashes = null; + if ($current) { + $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current); + if ($file = $fs->get_file_by_hash($hash)) { + $newhashes = $file->get_contenthash().$file->get_pathnamehash(); + } + unset($file); + } + + if ($this->oldhashes === $newhashes) { + $this->oldhashes = null; + return false; + } + $this->oldhashes = null; + + $callbackfunction = $this->updatedcallback; + if (!empty($callbackfunction) and function_exists($callbackfunction)) { + $callbackfunction($this->get_full_name()); + } + return true; + } + + public function output_html($data, $query = '') { + global $PAGE, $CFG; + + $options = $this->get_options(); + $id = $this->get_id(); + $elname = $this->get_full_name(); + $draftitemid = file_get_submitted_draft_itemid($elname); + $component = is_null($this->plugin) ? 'core' : $this->plugin; + file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options); + + // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it... + require_once("$CFG->dirroot/lib/form/filemanager.php"); + + $fmoptions = new stdClass(); + $fmoptions->mainfile = $options['mainfile']; + $fmoptions->maxbytes = $options['maxbytes']; + $fmoptions->maxfiles = $options['maxfiles']; + $fmoptions->client_id = uniqid(); + $fmoptions->itemid = $draftitemid; + $fmoptions->subdirs = $options['subdirs']; + $fmoptions->target = $id; + $fmoptions->accepted_types = $options['accepted_types']; + $fmoptions->return_types = $options['return_types']; + $fmoptions->context = $options['context']; + $fmoptions->areamaxbytes = $options['areamaxbytes']; + + $fm = new form_filemanager($fmoptions); + $output = $PAGE->get_renderer('core', 'files'); + $html = $output->render($fm); + + $html .= ''; + $html .= ''; + + return format_admin_setting($this, $this->visiblename, + '
'.$html.'
', $this->description, true, '', '', $query); + } +} + + /** * Administration interface for user specified regular expressions for device detection. *