<?php
///////////////////////////////////////////////////////////////////////////
// //
// This file is part of Moodle - http://moodle.org/ //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// //
// 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/>. //
// //
///////////////////////////////////////////////////////////////////////////
defined('MOODLE_INTERNAL') || die();
/**
* Rendering of files viewer related widgets.
* @package core
* @subpackage file
* @copyright 2010 Dongsheng Cai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 2.0
*/
/**
* File browser render
*
* @copyright 2010 Dongsheng Cai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 2.0
*/
class core_files_renderer extends plugin_renderer_base {
public function files_tree_viewer(file_info $file_info, array $options = null) {
$tree = new files_tree_viewer($file_info, $options);
return $this->render($tree);
}
public function render_files_tree_viewer(files_tree_viewer $tree) {
$html = $this->output->heading_with_help(get_string('coursefiles'), 'courselegacyfiles', 'moodle');
$html .= $this->output->container_start('coursefilesbreadcrumb');
foreach($tree->path as $path) {
$html .= $path;
$html .= ' / ';
}
$html .= $this->output->container_end();
$html .= $this->output->box_start();
$table = new html_table();
$table->head = array(get_string('name'), get_string('lastmodified'), get_string('size', 'repository'), get_string('type', 'repository'));
$table->align = array('left', 'left', 'left', 'left');
$table->width = '100%';
$table->data = array();
foreach ($tree->tree as $file) {
$filedate = $filesize = $filetype = '';
if ($file['filedate']) {
$filedate = userdate($file['filedate'], get_string('strftimedatetimeshort', 'langconfig'));
}
if (empty($file['isdir'])) {
if ($file['filesize']) {
$filesize = display_size($file['filesize']);
}
$fileicon = file_file_icon($file, 24);
$filetype = get_mimetype_description($file);
} else {
$fileicon = file_folder_icon(24);
}
$table->data[] = array(
html_writer::link($file['url'], $this->output->pix_icon($fileicon, get_string('icon')) . ' ' . $file['filename']),
$filedate,
$filesize,
$filetype
);
}
$html .= html_writer::table($table);
$html .= $this->output->single_button(new moodle_url('/files/coursefilesedit.php', array('contextid'=>$tree->context->id)), get_string('coursefilesedit'), 'get');
$html .= $this->output->box_end();
return $html;
}
/**
* Prints the file manager and initializes all necessary libraries
*
* <pre>
* $fm = new form_filemanager($options);
* $output = get_renderer('core', 'files');
* echo $output->render($fm);
* </pre>
*
* @param form_filemanager $fm File manager to render
* @return string HTML fragment
*/
public function render_form_filemanager($fm) {
$html = $this->fm_print_generallayout($fm);
$module = array(
'name'=>'form_filemanager',
'fullpath'=>'/lib/form/filemanager.js',
'requires' => array('moodle-core-notification-dialogue', 'core_filepicker', 'base', 'io-base', 'node', 'json', 'core_dndupload', 'panel', 'resize-plugin', 'dd-plugin'),
'strings' => array(
array('error', 'moodle'), array('info', 'moodle'), array('confirmdeletefile', 'repository'),
array('draftareanofiles', 'repository'), array('entername', 'repository'), array('enternewname', 'repository'),
array('invalidjson', 'repository'), array('popupblockeddownload', 'repository'),
array('unknownoriginal', 'repository'), array('confirmdeletefolder', 'repository'),
array('confirmdeletefilewithhref', 'repository'), array('confirmrenamefolder', 'repository'),
array('confirmrenamefile', 'repository'), array('newfolder', 'repository'), array('edit', 'moodle'),
array('originalextensionchange', 'repository'), array('originalextensionremove', 'repository'),
array('aliaseschange', 'repository'), ['nofilesselected', 'repository'],
['confirmdeleteselectedfile', 'repository'], ['selectall', 'moodle'], ['deselectall', 'moodle'],
['selectallornone', 'form'],
)
);
if ($this->page->requires->should_create_one_time_item_now('core_file_managertemplate')) {
$this->page->requires->js_init_call('M.form_filemanager.set_templates',
array($this->filemanager_js_templates()), true, $module);
}
$this->page->requires->js_call_amd('core/checkbox-toggleall', 'init');
$this->page->requires->js_init_call('M.form_filemanager.init', array($fm->options), true, $module);
// non javascript file manager
$html .= '<noscript>';
$html .= "<div><object type='text/html' data='".$fm->get_nonjsurl()."' height='160' width='600' style='border:1px solid #000'></object></div>";
$html .= '</noscript>';
return $html;
}
/**
* Returns html for displaying one file manager
*
* @param form_filemanager $fm
* @return string
*/
protected function fm_print_generallayout($fm) {
$context = [
'client_id' => $fm->options->client_id,
'helpicon' => $this->help_icon('setmainfile', 'repository'),
'restrictions' => $this->fm_print_restrictions($fm)
];
return $this->render_from_template('core/filemanager_page_generallayout', $context);
}
/**
* FileManager JS template for displaying one file in 'icon view' mode.
*
* Except for elements described in fp_js_template_iconfilename, this template may also
* contain element with class 'fp-contextmenu'. If context menu is available for this
* file, the top element will receive the additional class 'fp-hascontextmenu' and
* the element with class 'fp-contextmenu' will hold onclick event for displaying
* the context menu.
*
* @see fp_js_template_iconfilename()
* @return string
*/
protected function fm_js_template_iconfilename() {
$rv = '
<div class="fp-file">
<a href="#" class="d-block aabtn">
<div style="position:relative;">
<div class="fp-thumbnail"></div>
<div class="fp-reficons1"></div>
<div class="fp-reficons2"></div>
</div>
<div class="fp-filename-field">
<div class="fp-filename text-truncate"></div>
</div>
</a>
<a class="fp-contextmenu btn btn-icon btn-light border icon-no-margin icon-size-3" href="#">
<span>'.$this->pix_icon('i/menu', '▶').'</span></a>
</div>';
return $rv;
}
/**
* FileManager JS template for displaying file name in 'table view' and 'tree view' modes.
*
* Except for elements described in fp_js_template_listfilename, this template may also
* contain element with class 'fp-contextmenu'. If context menu is available for this
* file, the top element will receive the additional class 'fp-hascontextmenu' and
* the element with class 'fp-contextmenu' will hold onclick event for displaying
* the context menu.
*
* @todo MDL-32736 remove onclick="return false;"
* @see fp_js_template_listfilename()
* @return string
*/
protected function fm_js_template_listfilename() {
$rv = '
<span class="fp-filename-icon">
<a href="#">
<span class="fp-icon"></span>
<span class="fp-reficons1"></span>
<span class="fp-reficons2"></span>
<span class="fp-filename"></span>
</a>
<a class="fp-contextmenu" href="#" onclick="return false;">'.$this->pix_icon('i/menu', '▶').'</a>
</span>';
return $rv;
}
/**
* FileManager JS template for displaying 'Make new folder' dialog.
*
* Must be wrapped in an element, CSS for this element must define width and height of the window;
*
* Must have one input element with type="text" (for users to enter the new folder name);
*
* content of element with class 'fp-dlg-curpath' will be replaced with current path where
* new folder is about to be created;
* elements with classes 'fp-dlg-butcreate' and 'fp-dlg-butcancel' will hold onclick events;
*
* @return string
*/
protected function fm_js_template_mkdir() {
$rv = '
<div class="filemanager fp-mkdir-dlg" role="dialog" aria-live="assertive" aria-labelledby="fp-mkdir-dlg-title">
<div class="fp-mkdir-dlg-text">
<label id="fp-mkdir-dlg-title">' . get_string('newfoldername', 'repository') . '</label><br/>
<input type="text" class="form-control"/>
</div>
<button class="fp-dlg-butcreate btn-primary btn">'.get_string('makeafolder').'</button>
<button class="fp-dlg-butcancel btn-cancel btn">'.get_string('cancel').'</button>
</div>';
return $rv;
}
/**
* FileManager JS template for error/info message displayed as a separate popup window.
*
* @see fp_js_template_message()
* @return string
*/
protected function fm_js_template_message() {
return $this->fp_js_template_message();
}
/**
* FileManager JS template for window with file information/actions.
*
*/
protected function fm_js_template_fileselectlayout() {
$context = [
'helpicon' => $this->help_icon('setmainfile', 'repository'),
'licensehelpicon' => $this->create_license_help_icon_context(),
'columns' => true
];
return $this->render_from_template('core/filemanager_fileselect', $context);
}
/**
* FileManager JS template for popup confirm dialogue window.
*
* @return string
*/
protected function fm_js_template_confirmdialog() {
return $this->render_from_template('core/filemanager_confirmdialog', []);
}
/**
* Returns all FileManager JavaScript templates as an array.
*
* @return array
*/
public function filemanager_js_templates() {
$class_methods = get_class_methods($this);
$templates = array();
foreach ($class_methods as $method_name) {
if (preg_match('/^fm_js_template_(.*)$/', $method_name, $matches))
$templates[$matches[1]] = $this->$method_name();
}
return $templates;
}
/**
* Displays restrictions for the file manager
*
* @param form_filemanager $fm
* @return string
*/
protected function fm_print_restrictions($fm) {
< $maxbytes = display_size($fm->options->maxbytes);
> $maxbytes = display_size($fm->options->maxbytes, 0);
$strparam = (object) array('size' => $maxbytes, 'attachments' => $fm->options->maxfiles,
< 'areasize' => display_size($fm->options->areamaxbytes));
> 'areasize' => display_size($fm->options->areamaxbytes, 0));
$hasmaxfiles = !empty($fm->options->maxfiles) && $fm->options->maxfiles > 0;
$hasarealimit = !empty($fm->options->areamaxbytes) && $fm->options->areamaxbytes != -1;
if ($hasmaxfiles && $hasarealimit) {
$maxsize = get_string('maxsizeandattachmentsandareasize', 'moodle', $strparam);
} else if ($hasmaxfiles) {
$maxsize = get_string('maxsizeandattachments', 'moodle', $strparam);
} else if ($hasarealimit) {
$maxsize = get_string('maxsizeandareasize', 'moodle', $strparam);
} else {
$maxsize = get_string('maxfilesize', 'moodle', $maxbytes);
}
< // TODO MDL-32020 also should say about 'File types accepted'
>
return '<span>'. $maxsize . '</span>';
}
/**
* Template for FilePicker with general layout (not QuickUpload).
*
*
* @return string
*/
protected function fp_js_template_generallayout() {
return $this->render_from_template('core/filemanager_modal_generallayout', []);
}
/**
* FilePicker JS template for displaying one file in 'icon view' mode.
*
* the element with class 'fp-thumbnail' will be resized to the repository thumbnail size
* (both width and height, unless min-width and/or min-height is set in CSS) and the content of
* an element will be replaced with an appropriate img;
*
* the width of element with class 'fp-filename' will be set to the repository thumbnail width
* (unless min-width is set in css) and the content of an element will be replaced with filename
* supplied by repository;
*
* top element(s) will have class fp-folder if the element is a folder;
*
* List of files will have parent <div> element with class 'fp-iconview'
*
* @return string
*/
protected function fp_js_template_iconfilename() {
$rv = '
<a class="fp-file" href="#" >
<div style="position:relative;">
<div class="fp-thumbnail"></div>
<div class="fp-reficons1"></div>
<div class="fp-reficons2"></div>
</div>
<div class="fp-filename-field">
<p class="fp-filename text-truncate"></p>
</div>
</a>';
return $rv;
}
/**
* FilePicker JS template for displaying file name in 'table view' and 'tree view' modes.
*
* content of the element with class 'fp-icon' will be replaced with an appropriate img;
*
* content of element with class 'fp-filename' will be replaced with filename supplied by
* repository;
*
* top element(s) will have class fp-folder if the element is a folder;
*
* Note that tree view and table view are the YUI widgets and therefore there are no
* other templates. The widgets will be wrapped in <div> with class fp-treeview or
* fp-tableview (respectfully).
*
* @return string
*/
protected function fp_js_template_listfilename() {
$rv = '
<span class="fp-filename-icon">
<a href="#">
<span class="fp-icon"></span>
<span class="fp-filename"></span>
</a>
</span>';
return $rv;
}
/**
* FilePicker JS template for displaying link/loading progress for fetching of the next page
*
* This text is added to .fp-content AFTER .fp-iconview/.fp-treeview/.fp-tableview
*
* Must have one parent element with class 'fp-nextpage'. It will be assigned additional
* class 'loading' during loading of the next page (it is recommended that in this case the link
* becomes unavailable). Also must contain one element <a> or <button> that will hold
* onclick event for displaying of the next page. The event will be triggered automatically
* when user scrolls to this link.
*
* @return string
*/
protected function fp_js_template_nextpage() {
$rv = '
<div class="fp-nextpage">
<div class="fp-nextpage-link"><a href="#">'.get_string('more').'</a></div>
<div class="fp-nextpage-loading">
' . $this->pix_icon('i/loading_small', '') . '
</div>
</div>';
return $rv;
}
/**
* FilePicker JS template for window appearing to select a file.
*
* @return string
*/
protected function fp_js_template_selectlayout() {
$context = [
'licensehelpicon' => $this->create_license_help_icon_context()
];
return $this->render_from_template('core/filemanager_selectlayout', $context);
}
/**
* FilePicker JS template for 'Upload file' repository
*
* @return string
*/
protected function fp_js_template_uploadform() {
$context = [
'licensehelpicon' => $this->create_license_help_icon_context()
];
return $this->render_from_template('core/filemanager_uploadform', $context);
}
/**
* FilePicker JS template to display during loading process (inside element with class 'fp-content').
*
* @return string
*/
protected function fp_js_template_loading() {
return '
<div class="fp-content-loading">
<div class="fp-content-center">
' . $this->pix_icon('i/loading_small', '') . '
</div>
</div>';
}
/**
* FilePicker JS template for error (inside element with class 'fp-content').
*
* must have element with class 'fp-error', its content will be replaced with error text
* and the error code will be assigned as additional class to this element
* used errors: invalidjson, nofilesavailable, norepositoriesavailable
*
* @return string
*/
protected function fp_js_template_error() {
$rv = '
<div class="fp-content-error" ><div class="fp-error"></div></div>';
return $rv;
}
/**
* FilePicker JS template for error/info message displayed as a separate popup window.
*
* Must be wrapped in one element, CSS for this element must define
* width and height of the window. It will be assigned with an additional class 'fp-msg-error'
* or 'fp-msg-info' depending on message type;
*
* content of element with class 'fp-msg-text' will be replaced with error/info text;
*
* element with class 'fp-msg-butok' will hold onclick event
*
* @return string
*/
protected function fp_js_template_message() {
$rv = '
<div class="file-picker fp-msg" role="alertdialog" aria-live="assertive" aria-labelledby="fp-msg-labelledby">
<p class="fp-msg-text" id="fp-msg-labelledby"></p>
<button class="fp-msg-butok btn-primary btn">'.get_string('ok').'</button>
</div>';
return $rv;
}
/**
* FilePicker JS template for popup dialogue window asking for action when file with the same name already exists.
*
* @return string
*/
protected function fp_js_template_processexistingfile() {
return $this->render_from_template('core/filemanager_processexistingfile', []);
}
/**
* FilePicker JS template for popup dialogue window asking for action when file with the same name already exists
* (multiple-file version).
*
* @return string
*/
protected function fp_js_template_processexistingfilemultiple() {
return $this->render_from_template('core/filemanager_processexistingfilemultiple', []);
}
/**
* FilePicker JS template for repository login form including templates for each element type
*
* @return string
*/
protected function fp_js_template_loginform() {
return $this->render_from_template('core/filemanager_loginform', []);
}
/**
* Returns all FilePicker JavaScript templates as an array.
*
* @return array
*/
public function filepicker_js_templates() {
$class_methods = get_class_methods($this);
$templates = array();
foreach ($class_methods as $method_name) {
if (preg_match('/^fp_js_template_(.*)$/', $method_name, $matches))
$templates[$matches[1]] = $this->$method_name();
}
return $templates;
}
/**
* Returns HTML for default repository searchform to be passed to Filepicker
*
* This will be used as contents for search form defined in generallayout template
* (form with id {TOOLSEARCHID}).
* Default contents is one text input field with name="s"
*/
public function repository_default_searchform() {
return $this->render_from_template('core/filemanager_default_searchform', []);
}
/**
* Create the context for rendering help icon with license links displaying all licenses and sources.
*
* @return \stdClass $iconcontext the context for rendering license help info.
*/
protected function create_license_help_icon_context() : stdClass {
$licensecontext = new stdClass();
$licenses = [];
// Discard licenses without a name or source from enabled licenses.
foreach (license_manager::get_active_licenses() as $license) {
if (!empty($license->fullname) && !empty($license->source)) {
$licenses[] = $license;
}
}
$licensecontext->licenses = $licenses;
$helptext = $this->render_from_template('core/filemanager_licenselinks', $licensecontext);
$iconcontext = new stdClass();
$iconcontext->text = $helptext;
$iconcontext->alt = get_string('helpprefix2', 'moodle', get_string('chooselicense', 'repository'));
return $iconcontext;
}
}
/**
* Data structure representing a general moodle file tree viewer
*
* @copyright 2010 Dongsheng Cai
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 2.0
*/
class files_tree_viewer implements renderable {
public $tree;
public $path;
public $context;
/**
* Constructor of moodle_file_tree_viewer class
* @param file_info $file_info
* @param array $options
*/
public function __construct(file_info $file_info, array $options = null) {
global $CFG;
//note: this MUST NOT use get_file_storage() !!!!!!!!!!!!!!!!!!!!!!!!!!!!
$this->options = (array)$options;
$this->context = $options['context'];
$this->tree = array();
$children = $file_info->get_children();
$current_file_params = $file_info->get_params();
$parent_info = $file_info->get_parent();
$level = $parent_info;
$this->path = array();
while ($level) {
$params = $level->get_params();
$context = context::instance_by_id($params['contextid']);
// $this->context is current context
if ($context->id != $this->context->id or empty($params['filearea'])) {
break;
}
// unset unused parameters
unset($params['component']);
unset($params['filearea']);
unset($params['filename']);
unset($params['itemid']);
$url = new moodle_url('/files/index.php', $params);
$this->path[] = html_writer::link($url, $level->get_visible_name());
$level = $level->get_parent();
}
$this->path = array_reverse($this->path);
if ($current_file_params['filepath'] != '/') {
$this->path[] = $file_info->get_visible_name();
}
foreach ($children as $child) {
$filedate = $child->get_timemodified();
$filesize = $child->get_filesize();
$mimetype = $child->get_mimetype();
$params = $child->get_params();
unset($params['component']);
unset($params['filearea']);
unset($params['filename']);
unset($params['itemid']);
$fileitem = array(
'params' => $params,
'filename' => $child->get_visible_name(),
'mimetype' => $child->get_mimetype(),
'filedate' => $filedate ? $filedate : '',
'filesize' => $filesize ? $filesize : ''
);
$url = new moodle_url('/files/index.php', $params);
if ($child->is_directory()) {
$fileitem['isdir'] = true;
$fileitem['url'] = $url->out(false);
} else {
$fileitem['url'] = $child->get_url();
}
$this->tree[] = $fileitem;
}
}
}