MDL-31901 filepicker interface changes:
[moodle.git] / files / renderer.php
CommitLineData
64f93798
PS
1<?php
2///////////////////////////////////////////////////////////////////////////
3// //
4// This file is part of Moodle - http://moodle.org/ //
5// Moodle - Modular Object-Oriented Dynamic Learning Environment //
6// //
7// Moodle is free software: you can redistribute it and/or modify //
8// it under the terms of the GNU General Public License as published by //
9// the Free Software Foundation, either version 3 of the License, or //
10// (at your option) any later version. //
11// //
12// Moodle is distributed in the hope that it will be useful, //
13// but WITHOUT ANY WARRANTY; without even the implied warranty of //
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
15// GNU General Public License for more details. //
16// //
17// You should have received a copy of the GNU General Public License //
18// along with Moodle. If not, see <http://www.gnu.org/licenses/>. //
19// //
20///////////////////////////////////////////////////////////////////////////
21
22defined('MOODLE_INTERNAL') || die();
23
24/**
25 * Rendering of files viewer related widgets.
26 * @package core
27 * @subpackage file
28 * @copyright 2010 Dongsheng Cai
29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30 * @since Moodle 2.0
31 */
32
33/**
e921afa8 34 * File browser render
64f93798
PS
35 *
36 * @copyright 2010 Dongsheng Cai
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 * @since Moodle 2.0
39 */
40class core_files_renderer extends plugin_renderer_base {
41
42 public function files_tree_viewer(file_info $file_info, array $options = null) {
43 $tree = new files_tree_viewer($file_info, $options);
44 return $this->render($tree);
45 }
46
47 public function render_files_tree_viewer(files_tree_viewer $tree) {
e0873f13 48 $html = $this->output->heading_with_help(get_string('coursefiles'), 'courselegacyfiles', 'moodle');
0b259d91
DC
49
50 $html .= $this->output->container_start('coursefilesbreadcrumb');
64f93798
PS
51 foreach($tree->path as $path) {
52 $html .= $path;
53 $html .= ' / ';
54 }
0b259d91 55 $html .= $this->output->container_end();
64f93798 56
0b259d91
DC
57 $html .= $this->output->box_start();
58 $table = new html_table();
276943d4
DC
59 $table->head = array(get_string('filename', 'backup'), get_string('size'), get_string('modified'));
60 $table->align = array('left', 'right', 'right');
0b259d91
DC
61 $table->width = '100%';
62 $table->data = array();
63
64 foreach ($tree->tree as $file) {
65 if (!empty($file['isdir'])) {
66 $table->data[] = array(
67 html_writer::link($file['url'], $this->output->pix_icon('f/folder', 'icon') . ' ' . $file['filename']),
0b259d91 68 '',
276943d4 69 $file['filedate'],
0b259d91
DC
70 );
71 } else {
72 $table->data[] = array(
73 html_writer::link($file['url'], $this->output->pix_icon('f/'.mimeinfo('icon', $file['filename']), get_string('icon')) . ' ' . $file['filename']),
276943d4 74 $file['filesize'],
0b259d91 75 $file['filedate'],
0b259d91 76 );
64f93798 77 }
64f93798 78 }
0b259d91
DC
79
80 $html .= html_writer::table($table);
e921afa8 81 $html .= $this->output->single_button(new moodle_url('/files/coursefilesedit.php', array('contextid'=>$tree->context->id)), get_string('coursefilesedit'), 'get');
0b259d91 82 $html .= $this->output->box_end();
64f93798
PS
83 return $html;
84 }
906e7d89
MG
85
86 /**
87 * Prints the file manager and initializes all necessary libraries
88 *
89 * <pre>
90 * $fm = new form_filemanager($options);
91 * $output = get_renderer('core', 'files');
92 * echo $output->render($fm);
93 * </pre>
94 *
95 * @param form_filemanager $fm File manager to render
96 * @return string HTML fragment
97 */
d1d18691 98 public function render_form_filemanager($fm) {
906e7d89 99 static $filemanagertemplateloaded;
d1d18691 100 $html = $this->fm_print_generallayout($fm);
906e7d89
MG
101 $module = array(
102 'name'=>'form_filemanager',
103 'fullpath'=>'/lib/form/filemanager.js',
4325db53
MG
104 'requires' => array('core_filepicker', 'base', 'io-base', 'node', 'json', 'core_dndupload', 'panel', 'resize-plugin', 'dd-plugin'),
105 'strings' => array(
106 array('error', 'moodle'), array('info', 'moodle'), array('confirmdeletefile', 'repository'),
107 array('draftareanofiles', 'repository'), array('entername', 'repository'), array('enternewname', 'repository'),
108 array('invalidjson', 'repository'), array('popupblockeddownload', 'repository')
906e7d89
MG
109 )
110 );
111 if (empty($filemanagertemplateloaded)) {
112 $filemanagertemplateloaded = true;
113 $this->page->requires->js_init_call('M.form_filemanager.set_templates',
e709ddd2 114 array($this->filemanager_js_templates()), true, $module);
906e7d89
MG
115 }
116 $this->page->requires->js_init_call('M.form_filemanager.init', array($fm->options), true, $module);
117
118 // non javascript file manager
119 $html .= '<noscript>';
120 $html .= "<div><object type='text/html' data='".$fm->get_nonjsurl()."' height='160' width='600' style='border:1px solid #000'></object></div>";
121 $html .= '</noscript>';
122
123
124 return $html;
125 }
126
127 /**
128 * Returns html for displaying one file manager
129 *
130 * The main element in HTML must have id="filemanager-{$client_id}" and
131 * class="filemanager fm-loading";
132 * After all necessary code on the page (both html and javascript) is loaded,
133 * the class fm-loading will be removed and added class fm-loaded;
134 * The main element (class=filemanager) will be assigned the following classes:
135 * 'fm-maxfiles' - when filemanager has maximum allowed number of files;
136 * 'fm-nofiles' - when filemanager has no files at all (although there might be folders);
137 * 'fm-noitems' - when current view (folder) has no items - neither files nor folders;
138 * 'fm-updating' - when current view is being updated (usually means that loading icon is to be displayed);
139 * 'fm-nomkdir' - when 'Make folder' action is unavailable (empty($fm->options->subdirs) == true)
140 *
141 * Element with class 'filemanager-container' will be holding evens for dnd upload (dragover, etc.).
142 * It will have class:
143 * 'dndupload-ready' - when a file is being dragged over the browser
144 * 'dndupload-over' - when file is being dragged over this filepicker (additional to 'dndupload-ready')
145 * 'dndupload-uploading' - during the upload process (note that after dnd upload process is
146 * over, the file manager will refresh the files list and therefore will have for a while class
147 * fm-updating. Both waiting processes should look similar so the images don't jump for user)
148 *
149 * If browser supports Drag-and-drop, the body element will have class 'dndsupported',
150 * otherwise - 'dndnotsupported';
151 *
e709ddd2
MG
152 * Element with class 'fp-content' will be populated with files list;
153 * Element with class 'fp-btn-add' will hold onclick event for adding a file (opening filepicker);
154 * Element with class 'fp-btn-mkdir' will hold onclick event for adding new folder;
155 * Element with class 'fp-btn-download' will hold onclick event for download action;
156 *
157 * Element with class 'fp-path-folder' is a template for one folder in path toolbar.
158 * It will hold mouse click event and will be assigned classes first/last/even/odd respectfully.
159 * Parent element will receive class 'empty' when there are no folders to be displayed;
160 * The content of subelement with class 'fp-path-folder-name' will be substituted with folder name;
161 *
162 * Element with class 'fp-viewbar' will have the class 'enabled' or 'disabled' when view mode
163 * can be changed or not;
164 * Inside element with class 'fp-viewbar' there are expected elements with classes
165 * 'fp-vb-icons', 'fp-vb-tree' and 'fp-vb-details'. They will handle onclick events to switch
166 * between the view modes, the last clicked element will have the class 'checked';
906e7d89
MG
167 *
168 * @param form_filemanager $fm
169 * @return string
170 */
d1d18691 171 private function fm_print_generallayout($fm) {
906e7d89
MG
172 global $OUTPUT;
173 $options = $fm->options;
174 $client_id = $options->client_id;
175 $straddfile = get_string('addfile', 'repository');
176 $strmakedir = get_string('makeafolder', 'moodle');
177 $strdownload = get_string('downloadfolder', 'repository');
178 $strloading = get_string('loading', 'repository');
179 $strnofilesattached = get_string('nofilesattached', 'repository');
180 $strdroptoupload = get_string('droptoupload', 'moodle');
181 $icon_progress = $OUTPUT->pix_icon('i/loading_small', $strloading).'';
d1d18691 182 $restrictions = $this->fm_print_restrictions($fm);
906e7d89
MG
183 $strdndenabled = get_string('dndenabled_insentence', 'moodle').$OUTPUT->help_icon('dndenabled');
184 $strdndenabledinbox = get_string('dndenabled_inbox', 'moodle');
185 $loading = get_string('loading', 'repository');
186
e709ddd2
MG
187 $html = '
188<div id="filemanager-'.$client_id.'" class="filemanager fm-loading">
189 <div class="filemanager-loading mdl-align">'.$icon_progress.'</div>
190 <div class="fp-pathbar">
191 <span class="{!}fp-path-folder"><a class="{!}fp-path-folder-name"></a><span>/</span></span>
192 </div>
906e7d89 193 <div class="filemanager-toolbar">
e709ddd2
MG
194 <input type="button" class="{!}fp-btn-add" value="'.$straddfile.'" />
195 <input type="button" class="{!}fp-btn-mkdir" value="'.$strmakedir.'" />
196 <input type="button" class="{!}fp-btn-download" value="'.$strdownload.'" />
197 '.$restrictions.'
198 <span class="dndupload-message"> - '.$strdndenabled.' </span>
199 <div class="{!}fp-viewbar" style="float:none;">
200 <span class=""><button class="{!}fp-vb-icons">'.get_string('iconview', 'repository').'</button></span>
201 <span class=""><button class="{!}fp-vb-tree">'.get_string('listview', 'repository').'</button></span>
202 <span class=""><button class="{!}fp-vb-details">'.get_string('detailview', 'repository').'</button></span>
203 </div>
906e7d89
MG
204 </div>
205 <div class="filemanager-container" >
e709ddd2
MG
206 <div class="fm-content-wrapper">
207 <ul class="fp-content"></ul>
208 <div class="fm-empty-container mdl-align">'.$strnofilesattached.'
209 <span class="dndupload-message">'.$strdndenabledinbox.'</span>
210 </div>
211 <div class="dndupload-target">'.$strdroptoupload.'</div>
212 <div class="dndupload-uploadinprogress">'.$icon_progress.'</div>
906e7d89 213 </div>
e709ddd2 214 <div class="filemanager-updating">'.$icon_progress.'</div>
906e7d89
MG
215 </div>
216</div>
217<div class="clearer"></div>
e709ddd2
MG
218';
219 return preg_replace('/\{\!\}/', '', $html);
220 }
221
222 /**
223 * FileManager JS template for displaying one file in 'icon view' mode.
224 *
23b83009
MG
225 * Except for elements described in fp_js_template_iconfilename, this template may also
226 * contain element with class 'fp-contextmenu'. If context menu is available for this
227 * file, the top element will receive the additional class 'fp-hascontextmenu' and
228 * the element with class 'fp-contextmenu' will hold onclick event for displaying
229 * the context menu.
230 *
e709ddd2
MG
231 * @see fp_js_template_iconfilename()
232 * @return string
233 */
234 private function fm_js_template_iconfilename() {
23b83009
MG
235 $rv = '<div class="fp-file" style="position:relative">
236 <a href="#">
237 <div class="{!}fp-thumbnail"></div>
238 <div class="{!}fp-filename"></div></a>
239 <a class="{!}fp-contextmenu" href="#">'.$this->pix_icon('i/menu', '▶').'</a>
240</div>';
241 return preg_replace('/\{\!\}/', '', $rv);
e709ddd2
MG
242 }
243
244 /**
245 * FileManager JS template for displaying file name in 'table view' and 'tree view' modes.
246 *
23b83009
MG
247 * Except for elements described in fp_js_template_listfilename, this template may also
248 * contain element with class 'fp-contextmenu'. If context menu is available for this
249 * file, the top element will receive the additional class 'fp-hascontextmenu' and
250 * the element with class 'fp-contextmenu' will hold onclick event for displaying
251 * the context menu.
252 *
253 * @todo MDL-32736 remove onclick="return false;"
e709ddd2
MG
254 * @see fp_js_template_listfilename()
255 * @return string
256 */
257 private function fm_js_template_listfilename() {
23b83009
MG
258 $rv = '<span><span class="{!}fp-icon"></span> <span class="{!}fp-filename"></span>
259 <a class="{!}fp-contextmenu" href="#" onclick="return false;">'.$this->pix_icon('i/menu', '▶').'</a></span>';
260 return preg_replace('/\{\!\}/', '', $rv);
e709ddd2
MG
261 }
262
694beb54
MG
263 /**
264 * FileManager JS template for displaying 'Make new folder' dialog.
265 *
266 * Must be wrapped in an element, CSS for this element must define width and height of the window;
267 *
268 * Must have one input element with type="text" (for users to enter the new folder name);
269 *
270 * content of element with class 'fp-dlg-curpath' will be replaced with current path where
271 * new folder is about to be created;
272 * elements with classes 'fp-dlg-butcreate' and 'fp-dlg-butcancel' will hold onclick events;
273 *
274 * @return string
275 */
276 private function fm_js_template_mkdir() {
277 $rv = '<div class="fp-mkdir-dlg">
278 <span class="{!}fp-dlg-curpath"></span>
279 <input type="text">
280 <button class="{!}fp-dlg-butcreate">'.get_string('create').'</button><button class="{!}fp-dlg-butcancel">'.get_string('cancel').'</button>
281</div>';
282 return preg_replace('/\{\!\}/', '', $rv);
283 }
284
285 /**
286 * FileManager JS template for error/info message displayed as a separate popup window.
287 *
288 * @see fp_js_template_message()
289 * @return string
290 */
291 private function fm_js_template_message() {
292 return $this->fp_js_template_message();
293 }
294
e709ddd2
MG
295 /**
296 * FileManager JS template for window with file information/actions.
297 *
55089a9d
MG
298 * All content must be enclosed in one element, CSS for this class must define width and
299 * height of the window;
e709ddd2
MG
300 *
301 * Thumbnail image will be added as content to the element with class 'fp-thumbnail';
302 *
303 * Inside the window the elements with the following classnames must be present:
304 * 'fp-saveas', 'fp-author', 'fp-license', 'fp-path'. Inside each of them must be
305 * one input element (or select in case of fp-license and fp-path). They may also have labels.
306 * The elements will be assign with class 'uneditable' and input/select element will become
307 * disabled if they are not applicable for the particular file;
308 *
e0ff0867 309 * There may be present elements with classes 'fp-original', 'fp-datemodified', 'fp-datecreated',
e709ddd2
MG
310 * 'fp-size', 'fp-dimensions'. They will receive additional class 'fp-unknown' if information
311 * is unavailable. If there is information available, the content of embedded element
312 * with class 'fp-value' will be substituted with the value;
313 *
314 * Elements with classes 'fp-file-update', 'fp-file-download', 'fp-file-delete', 'fp-file-zip',
315 * 'fp-file-unzip', 'fp-file-setmain' and 'fp-file-cancel' will hold corresponding onclick
316 * events (there may be several elements with class 'fp-file-cancel');
317 *
318 * When confirm button is pressed and file is being selected, the top element receives
319 * additional class 'loading'. It is removed when response from server is received.
320 *
321 * When any of the input fields is changed, the top element receives class 'fp-changed';
322 * When current file can be set as main - top element receives class 'fp-cansetmain';
323 * When current file is folder/zip/file - top element receives respectfully class
324 * 'fp-folder'/'fp-zip'/'fp-file';
325 *
326 * @return string
327 */
328 private function fm_js_template_fileselectlayout() {
55089a9d 329 $rv = '<div class="filemanager fp-select">
e709ddd2
MG
330<div class="fp-select-loading">
331<img src="'.$this->pix_url('i/loading').'" />
332<p>'.get_string('loading', 'repository').'</p>
333</div>
334<form>
335<p class="{!}fp-thumbnail"></p>
336<table width="100%">
337<tr class="{!}fp-saveas"><td class="mdl-right"><label>'.get_string('name', 'moodle').'</label>:</td>
338<td class="mdl-left"><input type="text"/></td></tr>
339<tr class="{!}fp-author"><td class="mdl-right"><label>'.get_string('author', 'repository').'</label>:</td>
340<td class="mdl-left"><input type="text" /></td></tr>
341<tr class="{!}fp-license"><td class="mdl-right"><label>'.get_string('chooselicense', 'repository').'</label>:</td>
342<td class="mdl-left"><select></select></td></tr>
343<tr class="{!}fp-path"><td class="mdl-right"><label>'.get_string('path', 'moodle').'</label>:</td>
344<td class="mdl-left"><select></select></td></tr>
e0ff0867 345<tr class="{!}fp-original"><td class="mdl-right"><label>'.get_string('original', 'repository').'</label>:</td>
e709ddd2
MG
346<td class="mdl-left"><span class="fp-value"/></td></tr>
347</table>
348<p><button class="{!}fp-file-update" >'.get_string('update', 'moodle').'</button>
349<button class="{!}fp-file-download" >'.get_string('download').'</button>
350<button class="{!}fp-file-delete" >'.get_string('delete').'</button>
351<button class="{!}fp-file-zip" >'.get_string('zip', 'editor').'</button>
352<button class="{!}fp-file-unzip" >'.get_string('unzip').'</button>
353<button class="{!}fp-file-setmain" >'.get_string('setmainfile', 'repository').'</button>
354<button class="{!}fp-file-cancel" >'.get_string('cancel').'</button>
355</p>
356</form>
357<div class="{!}fp-datemodified">'.get_string('lastmodified', 'moodle').': <span class="fp-value"/></div>
358<div class="{!}fp-datecreated">'.get_string('datecreated', 'repository').': <span class="fp-value"/></div>
359<div class="{!}fp-size">'.get_string('size', 'repository').': <span class="fp-value"/></div>
360<div class="{!}fp-dimensions">'.get_string('dimensions', 'repository').': <span class="fp-value"/></div>
361</div>';
362 return preg_replace('/\{\!\}/', '', $rv);
363 }
364
365 /**
366 * Returns all FileManager JavaScript templates as an array.
367 *
368 * @return array
369 */
370 public function filemanager_js_templates() {
371 $class_methods = get_class_methods($this);
372 $templates = array();
373 foreach ($class_methods as $method_name) {
374 if (preg_match('/^fm_js_template_(.*)$/', $method_name, $matches))
375 $templates[$matches[1]] = $this->$method_name();
376 }
377 return $templates;
906e7d89
MG
378 }
379
380 /**
381 * Displays restrictions for the file manager
382 *
383 * @param form_filemanager $fm
384 * @return string
385 */
d1d18691 386 private function fm_print_restrictions($fm) {
906e7d89
MG
387 $maxbytes = display_size($fm->options->maxbytes);
388 if (empty($options->maxfiles) || $options->maxfiles == -1) {
389 $maxsize = get_string('maxfilesize', 'moodle', $maxbytes);
390 //$string['maxfilesize'] = 'Maximum size for new files: {$a}';
391 } else {
392 $strparam = (object)array('size' => $maxbytes, 'attachments' => $options->maxfiles);
393 $maxsize = get_string('maxsizeandattachments', 'moodle', $strparam);
394 //$string['maxsizeandattachments'] = 'Maximum size for new files: {$a->size}, maximum attachments: {$a->attachments}';
395 }
396 // TODO MDL-32020 also should say about 'File types accepted'
397 return '<span>'. $maxsize. '</span>';
398 }
64f93798 399
d1d18691
MG
400 /**
401 * Template for FilePicker with general layout (not QuickUpload).
402 *
403 * Must have one top element containing everything else (recommended <div class="file-picker">),
404 * CSS for this element must define width and height of the filepicker window. Or CSS must
405 * define min-width, max-width, min-height and max-height and in this case the filepicker
406 * window will be resizeable;
407 *
408 * Element with class 'fp-viewbar' will have the class 'enabled' or 'disabled' when view mode
409 * can be changed or not;
410 * Inside element with class 'fp-viewbar' there are expected elements with classes
411 * 'fp-vb-icons', 'fp-vb-tree' and 'fp-vb-details'. They will handle onclick events to switch
412 * between the view modes, the last clicked element will have the class 'checked';
413 *
414 * Element with class 'fp-repo' is a template for displaying one repository. Other repositories
415 * will be attached as siblings (classes first/last/even/odd will be added respectfully).
416 * The currently selected repostory will have class 'active'. Contents of element with class
417 * 'fp-repo-name' will be replaced with repository name, source of image with class
418 * 'fp-repo-icon' will be replaced with repository icon;
419 *
420 * Element with class 'fp-content' is obligatory and will hold the current contents;
421 *
422 * Element with class 'fp-paging' will contain page navigation (will be deprecated soon);
423 *
e709ddd2 424 * Element with class 'fp-path-folder' is a template for one folder in path toolbar.
d1d18691 425 * It will hold mouse click event and will be assigned classes first/last/even/odd respectfully.
d1d18691 426 * Parent element will receive class 'empty' when there are no folders to be displayed;
e709ddd2 427 * The content of subelement with class 'fp-path-folder-name' will be substituted with folder name;
d1d18691
MG
428 *
429 * Element with class 'fp-toolbar' will have class 'empty' if all 'Back', 'Search', 'Refresh',
430 * 'Logout', 'Manage' and 'Help' are unavailable for this repo;
431 *
432 * Inside fp-toolbar there are expected elements with classes fp-tb-back, fp-tb-search,
433 * fp-tb-refresh, fp-tb-logout, fp-tb-manage and fp-tb-help. Each of them will have
434 * class 'enabled' or 'disabled' if particular repository has this functionality.
435 * Element with class 'fp-tb-search' must contain empty form inside, it's contents will
436 * be substituted with the search form returned by repository (in the most cases it
437 * is generated with template core_repository_renderer::repository_default_searchform);
438 * Other elements must have either <a> or <button> element inside, it will hold onclick
439 * event for corresponding action; labels for fp-tb-back and fp-tb-logout may be
440 * replaced with those specified by repository;
441 *
442 * @return string
443 */
444 private function fp_js_template_generallayout() {
445 $rv = '
446<div class="file-picker fp-generallayout">
e88d5641
BR
447 <div class="fp-repo-area">
448 <ul class="fp-list">
449 <li class="{!}fp-repo"><img class="{!}fp-repo-icon" width="16" height="16" />&nbsp;<a class="{!}fp-repo-name" href="#"></a></li>
450 </ul>
d1d18691 451 </div>
e88d5641
BR
452 <div class="fp-repo-items">
453 <div class="fp-navbar" >
454 <div class="{!}fp-viewbar">
455 <a class="{!}fp-vb-icons" href="#"></a>
456 <a class="{!}fp-vb-details" href="#"></a>
457 <a class="{!}fp-vb-tree" href="#"></a>
458 </div>
459 <div class="{!}fp-toolbar">
460 <div class="{!}fp-tb-back"><a href="#">'.get_string('back', 'repository').'</a></div>
461 <div class="{!}fp-tb-search fp-search"><form/></div>
462 <div class="{!}fp-tb-refresh"><a href="#"><img src="'.$this->pix_url('a/refresh').'" /></a></div>
463 <div class="{!}fp-tb-logout"><img src="'.$this->pix_url('a/logout').'" /><a href="#"></a></div>
464 <div class="{!}fp-tb-manage"><a href="#"><img src="'.$this->pix_url('a/setting').'" /> '.get_string('manageurl', 'repository').'</a></div>
465 <div class="{!}fp-tb-help"><a href="#"><img src="'.$this->pix_url('a/help').'" /> '.get_string('help').'</a></div>
466 </div>
467 <div class="fp-pathbar">
468 <span class="{!}fp-path-folder"><a class="{!}fp-path-folder-name" href="#"></a></span>
469 </div>
d1d18691 470 </div>
e88d5641 471 <div class="{!}fp-content"></div>
d1d18691 472 </div>
d1d18691
MG
473</div>';
474 return preg_replace('/\{\!\}/', '', $rv);
475 }
476
477 /**
e709ddd2 478 * FilePicker JS template for displaying one file in 'icon view' mode.
d1d18691
MG
479 *
480 * the element with class 'fp-thumbnail' will be resized to the repository thumbnail size
481 * (both width and height, unless min-width and/or min-height is set in CSS) and the content of
482 * an element will be replaced with an appropriate img;
483 *
484 * the width of element with class 'fp-filename' will be set to the repository thumbnail width
485 * (unless min-width is set in css) and the content of an element will be replaced with filename
486 * supplied by repository;
487 *
e709ddd2
MG
488 * top element(s) will have class fp-folder if the element is a folder;
489 *
490 * List of files will have parent <div> element with class 'fp-iconview'
491 *
d1d18691
MG
492 * @return string
493 */
e709ddd2 494 private function fp_js_template_iconfilename() {
23b83009 495 $rv = '<a class="fp-file" href="#" >
d1d18691
MG
496 <div class="{!}fp-thumbnail"></div>
497 <div class="{!}fp-filename"></div>
e88d5641 498</a>';
d1d18691
MG
499 return preg_replace('/\{\!\}/', '', $rv);
500 }
501
502 /**
503 * FilePicker JS template for displaying file name in 'table view' and 'tree view' modes.
504 *
505 * content of the element with class 'fp-icon' will be replaced with an appropriate img;
506 *
507 * content of element with class 'fp-filename' will be replaced with filename supplied by
508 * repository;
509 *
e709ddd2
MG
510 * top element(s) will have class fp-folder if the element is a folder;
511 *
d1d18691
MG
512 * Note that tree view and table view are the YUI widgets and therefore there are no
513 * other templates. The widgets will be wrapped in <div> with class fp-treeview or
514 * fp-tableview (respectfully).
515 *
516 * @return string
517 */
518 private function fp_js_template_listfilename() {
e709ddd2 519 $rv = '<span><span class="{!}fp-icon"></span> <span class="{!}fp-filename"></span></span>';
d1d18691
MG
520 return preg_replace('/\{\!\}/', '', $rv);
521 }
522
523 /**
524 * FilePicker JS template for displaying link/loading progress for fetching of the next page
525 *
526 * This text is added to .fp-content AFTER .fp-iconview/.fp-treeview/.fp-tableview
527 *
528 * Must have one parent element with class 'fp-nextpage'. It will be assigned additional
529 * class 'loading' during loading of the next page (it is recommended that in this case the link
530 * becomes unavailable). Also must contain one element <a> or <button> that will hold
531 * onclick event for displaying of the next page. The event will be triggered automatically
532 * when user scrolls to this link.
533 *
534 * @return string
535 */
536 private function fp_js_template_nextpage() {
537 $rv = '<div class="{!}fp-nextpage">
538 <div class="fp-nextpage-link"><a href="#">'.get_string('more').'</a></div>
539 <div class="fp-nextpage-loading">
540 <img src="'.$this->pix_url('i/loading').'" />
541 <p>'.get_string('loading', 'repository').'</p>
542 </div>
543</div>';
544 return preg_replace('/\{\!\}/', '', $rv);
545 }
546
547 /**
e709ddd2 548 * FilePicker JS template for window appearing to select a file.
d1d18691 549 *
55089a9d
MG
550 * All content must be enclosed in one element, CSS for this class must define width and
551 * height of the window;
d1d18691
MG
552 *
553 * Thumbnail image will be added as content to the element with class 'fp-thumbnail';
554 *
555 * Inside the window the elements with the following classnames must be present:
55089a9d
MG
556 * 'fp-saveas', 'fp-linktype-2', 'fp-linktype-1', 'fp-linktype-4', 'fp-setauthor',
557 * 'fp-setlicense'. Inside each of them must have one input element (or select in case of
558 * fp-setlicense). They may also have labels.
d1d18691
MG
559 * The elements will be assign with class 'uneditable' and input/select element will become
560 * disabled if they are not applicable for the particular file;
561 *
562 * There may be present elements with classes 'fp-datemodified', 'fp-datecreated', 'fp-size',
e709ddd2
MG
563 * 'fp-license', 'fp-author', 'fp-dimensions'. They will receive additional class 'fp-unknown'
564 * if information is unavailable. If there is information available, the content of embedded
565 * element with class 'fp-value' will be substituted with the value;
d1d18691
MG
566 *
567 * Elements with classes 'fp-select-confirm' and 'fp-select-cancel' will hold corresponding
568 * onclick events;
569 *
570 * When confirm button is pressed and file is being selected, the top element receives
571 * additional class 'loading'. It is removed when response from server is received.
572 *
573 * @return string
574 */
575 private function fp_js_template_selectlayout() {
55089a9d 576 $rv = '<div class="file-picker fp-select">
d1d18691
MG
577<div class="fp-select-loading">
578<img src="'.$this->pix_url('i/loading').'" />
579<p>'.get_string('loading', 'repository').'</p>
580</div>
581<form>
e88d5641
BR
582<p><a class="{!}fp-select-confirm" href="#">'.get_string('getfile', 'repository').'</a>
583<a class="{!}fp-select-cancel" href="#">'.get_string('cancel').'</a></p>
584<p class="fp-hr">&nbsp;</p>
d1d18691
MG
585<table width="100%">
586<tr class="{!}fp-saveas"><td class="mdl-right"><label>'.get_string('saveas', 'repository').'</label>:</td>
587<td class="mdl-left"><input type="text"/></td></tr>
5cf44c1f
MG
588<tr class="{!}fp-linktype-2"><td></td>
589<td class="mdl-left"><input type="radio"/><label>'.get_string('makefileinternal', 'repository').'</label></td></tr>
590<tr class="{!}fp-linktype-1"><td></td>
591<td class="mdl-left"><input type="radio"/><label>'.get_string('makefilelink', 'repository').'</label></td></tr>
592<tr class="{!}fp-linktype-4"><td></td>
593<td class="mdl-left"><input type="radio"/><label>'.get_string('makefilereference', 'repository').'</label></td></tr>
d1d18691
MG
594<tr class="{!}fp-setauthor"><td class="mdl-right"><label>'.get_string('author', 'repository').'</label>:</td>
595<td class="mdl-left"><input type="text" /></td></tr>
596<tr class="{!}fp-setlicense"><td class="mdl-right"><label>'.get_string('chooselicense', 'repository').'</label>:</td>
597<td class="mdl-left"><select></select></td></tr>
598</table>
d1d18691 599</form>
e88d5641
BR
600<p class="{!}fp-thumbnail"></p>
601<div class="fp-fileinfo">
d1d18691
MG
602<div class="{!}fp-datemodified">'.get_string('lastmodified', 'moodle').': <span class="fp-value"/></div>
603<div class="{!}fp-datecreated">'.get_string('datecreated', 'repository').': <span class="fp-value"/></div>
604<div class="{!}fp-size">'.get_string('size', 'repository').': <span class="fp-value"/></div>
605<div class="{!}fp-license">'.get_string('license', 'moodle').': <span class="fp-value"/></div>
606<div class="{!}fp-author">'.get_string('author', 'repository').': <span class="fp-value"/></div>
607<div class="{!}fp-dimensions">'.get_string('dimensions', 'repository').': <span class="fp-value"/></div>
e88d5641 608</div>
d1d18691
MG
609</div>';
610 return preg_replace('/\{\!\}/', '', $rv);
611 }
612
613 /**
614 * FilePicker JS template for 'Upload file' repository
615 *
616 * Content to display when user chooses 'Upload file' repository (will be nested inside
617 * element with class 'fp-content').
618 *
619 * Must contain form (enctype="multipart/form-data" method="POST")
620 *
621 * The elements with the following classnames must be present:
622 * 'fp-file', 'fp-saveas', 'fp-setauthor', 'fp-setlicense'. Inside each of them must have
623 * one input element (or select in case of fp-setlicense). They may also have labels.
624 *
625 * Element with class 'fp-upload-btn' will hold onclick event for uploading the file;
626 *
627 * Please note that some fields may be hidden using CSS if this is part of quickupload form
628 *
629 * @return string
630 */
631 private function fp_js_template_uploadform() {
632 $rv = '<div class="fp-upload-form mdl-align">
e88d5641 633<div class="fp-content-center">
d1d18691 634<form enctype="multipart/form-data" method="POST">
e88d5641 635 <table >
d1d18691
MG
636 <tr class="{!}fp-file">
637 <td class="mdl-right"><label>'.get_string('attachment', 'repository').'</label>:</td>
638 <td class="mdl-left"><input type="file"/></td>
639 </tr>
640 <tr class="{!}fp-saveas">
641 <td class="mdl-right"><label>'.get_string('saveas', 'repository').'</label>:</td>
642 <td class="mdl-left"><input type="text"/></td>
643 </tr>
644 <tr class="{!}fp-setauthor">
645 <td class="mdl-right"><label>'.get_string('author', 'repository').'</label>:</td>
646 <td class="mdl-left"><input type="text"/></td>
647 </tr>
648 <tr class="{!}fp-setlicense">
649 <td class="mdl-right"><label>'.get_string('chooselicense', 'repository').'</label>:</td>
650 <td class="mdl-left"><select/></td>
651 </tr>
652 </table>
653</form>
654<div><button class="{!}fp-upload-btn">'.get_string('upload', 'repository').'</button></div>
e88d5641 655</div>
d1d18691
MG
656</div> ';
657 return preg_replace('/\{\!\}/', '', $rv);
658 }
659
660 /**
661 * FilePicker JS template to display during loading process (inside element with class 'fp-content').
662 *
663 * @return string
664 */
665 private function fp_js_template_loading() {
666 return '<div style="text-align:center">
667<img src="'.$this->pix_url('i/loading').'" />
668<p>'.get_string('loading', 'repository').'</p>
669</div>';
670 }
671
672 /**
673 * FilePicker JS template for error (inside element with class 'fp-content').
674 *
675 * must have element with class 'fp-error', its content will be replaced with error text
676 * and the error code will be assigned as additional class to this element
677 * used errors: invalidjson, nofilesavailable, norepositoriesavailable
678 *
679 * @return string
680 */
681 private function fp_js_template_error() {
e88d5641 682 $rv = '<div class="fp-content-error" ><div class="{!}fp-error" /></div>';
d1d18691
MG
683 return preg_replace('/\{\!\}/', '', $rv);
684 }
685
686 /**
687 * FilePicker JS template for error/info message displayed as a separate popup window.
688 *
689 * Must be wrapped in an element with class 'fp-msg', CSS for this element must define
690 * width and height of the window. It will be assigned with an additional class 'fp-msg-error'
691 * or 'fp-msg-info' depending on message type;
692 *
693 * content of element with class 'fp-msg-text' will be replaced with error/info text;
694 *
695 * element with class 'fp-msg-butok' will hold onclick event
696 *
697 * @return string
698 */
699 private function fp_js_template_message() {
700 $rv = '<div class="{!}fp-msg">
701 <div class="{!}fp-msg-text"></div>
702 <div><button class="{!}fp-msg-butok">'.get_string('ok').'</button></div>
703 </div>';
704 return preg_replace('/\{\!\}/', '', $rv);
705 }
706
707 /**
708 * FilePicker JS template for popup dialogue window asking for action when file with the same name already exists.
709 *
710 * Must be wrapped in an element with class 'fp-dlg', CSS for this element must define width
711 * and height of the window;
712 *
713 * content of element with class 'fp-dlg-text' will be replaced with dialog text;
714 * elements with classes 'fp-dlg-butoverwrite', 'fp-dlg-butrename' and 'fp-dlg-butcancel' will
715 * hold onclick events;
716 *
717 * content of element with class 'fp-dlg-butrename' will be substituted with appropriate string
718 * (Note that it may have long text)
719 *
720 * @return string
721 */
722 private function fp_js_template_processexistingfile() {
723 $rv = '<div class="{!}fp-dlg"><div class="{!}fp-dlg-text"></div>
724<div class="fp-dlg-but"><button class="{!}fp-dlg-butoverwrite" >'.get_string('overwrite', 'repository').'</button></div>
725<div class="fp-dlg-but"><button class="{!}fp-dlg-butrename" /></div>
726<div class="fp-dlg-but"><button class="{!}fp-dlg-butcancel" >'.get_string('cancel').'</button></div>
727</div>';
728 return preg_replace('/\{\!\}/', '', $rv);
729 }
730
731 /**
732 * FilePicker JS template for repository login form including templates for each element type
733 *
734 * Must contain one <form> element with templates for different input types inside:
735 * Elements with classes 'fp-login-popup', 'fp-login-textarea', 'fp-login-select' and
736 * 'fp-login-input' are templates for displaying respective login form elements. Inside
737 * there must be exactly one element with type <button>, <textarea>, <select> or <input>
738 * (i.e. fp-login-popup should have <button>, fp-login-textarea should have <textarea>, etc.);
739 * They may also contain the <label> element and it's content will be substituted with
740 * label;
741 *
742 * You can also define elements with classes 'fp-login-checkbox', 'fp-login-text'
743 * but if they are not found, 'fp-login-input' will be used;
744 *
745 * Element with class 'fp-login-radiogroup' will be used for group of radio inputs. Inside
746 * it should hava a template for one radio input (with class 'fp-login-radio');
747 *
748 * Element with class 'fp-login-submit' will hold on click mouse event (form submission). It
749 * will be removed if at least one popup element is present;
750 *
751 * @return string
752 */
753 private function fp_js_template_loginform() {
754 $rv = '
755<div class="fp-login-form">
e88d5641 756<div class="fp-content-center">
d1d18691 757 <form>
e88d5641 758 <table >
d1d18691
MG
759 <tr class="{!}fp-login-popup">
760 <td colspan="2">
761 <label>'.get_string('popup', 'repository').'</label>
762 <p class="fp-popup"><button class="{!}fp-login-popup-but">'.get_string('login', 'repository').'</button></p>
763 </td>
764 </tr>
765 <tr class="{!}fp-login-textarea">
766 <td colspan="2"><p><textarea></textarea></p></td>
767 </tr>
768 <tr class="{!}fp-login-select">
769 <td align="right"><label></label></td>
770 <td align="left"><select></select></td>
771 </tr>
772 <tr class="{!}fp-login-input">
e88d5641
BR
773 <td class="label"><label /></td>
774 <td class="input"><input/></td>
d1d18691
MG
775 </tr>
776 <tr class="{!}fp-login-radiogroup">
777 <td align="right" width="30%" valign="top"><label /></td>
778 <td align="left" valign="top">
779 <p class="{!}fp-login-radio"><input /> <label /></p>
780 </td>
781 </tr>
782 </table>
783 <p><button class="{!}fp-login-submit">'.get_string('submit', 'repository').'</button></p>
784 </form>
e88d5641 785</div>
d1d18691
MG
786</div>';
787 return preg_replace('/\{\!\}/', '', $rv);
788 }
789
790 /**
791 * Returns all FilePicker JavaScript templates as an array.
792 *
793 * @return array
794 */
795 public function filepicker_js_templates() {
796 $class_methods = get_class_methods($this);
797 $templates = array();
798 foreach ($class_methods as $method_name) {
799 if (preg_match('/^fp_js_template_(.*)$/', $method_name, $matches))
800 $templates[$matches[1]] = $this->$method_name();
801 }
802 return $templates;
803 }
804
805 /**
806 * Returns HTML for default repository searchform to be passed to Filepicker
807 *
808 * This will be used as contents for search form defined in generallayout template
809 * (form with id {TOOLSEARCHID}).
810 * Default contents is one text input field with name="s"
811 */
812 public function repository_default_searchform() {
e88d5641 813 $str = '<input class="search-entry" name="s" value="Search" />';
d1d18691
MG
814 return $str;
815 }
816}
64f93798
PS
817
818/**
819 * Data structure representing a general moodle file tree viewer
820 *
821 * @copyright 2010 Dongsheng Cai
822 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
823 * @since Moodle 2.0
824 */
825class files_tree_viewer implements renderable {
826 public $tree;
827 public $path;
e921afa8 828 public $context;
64f93798
PS
829
830 /**
831 * Constructor of moodle_file_tree_viewer class
832 * @param file_info $file_info
833 * @param array $options
834 */
835 public function __construct(file_info $file_info, array $options = null) {
836 global $CFG;
837
838 //note: this MUST NOT use get_file_storage() !!!!!!!!!!!!!!!!!!!!!!!!!!!!
64f93798 839 $this->options = (array)$options;
e921afa8
DC
840 $this->context = $options['context'];
841
64f93798
PS
842 $this->tree = array();
843 $children = $file_info->get_children();
0b259d91 844 $current_file_params = $file_info->get_params();
64f93798 845 $parent_info = $file_info->get_parent();
64f93798
PS
846 $level = $parent_info;
847 $this->path = array();
848 while ($level) {
849 $params = $level->get_params();
850 $context = get_context_instance_by_id($params['contextid']);
0b259d91
DC
851 // $this->context is current context
852 if ($context->id != $this->context->id or empty($params['filearea'])) {
64f93798
PS
853 break;
854 }
e921afa8
DC
855 // unset unused parameters
856 unset($params['component']);
857 unset($params['filearea']);
0b259d91 858 unset($params['filename']);
e921afa8 859 unset($params['itemid']);
64f93798 860 $url = new moodle_url('/files/index.php', $params);
e921afa8 861 $this->path[] = html_writer::link($url, $level->get_visible_name());
64f93798
PS
862 $level = $level->get_parent();
863 }
864 $this->path = array_reverse($this->path);
0b259d91
DC
865 if ($current_file_params['filepath'] != '/') {
866 $this->path[] = $file_info->get_visible_name();
867 }
64f93798
PS
868
869 foreach ($children as $child) {
870 $filedate = $child->get_timemodified();
871 $filesize = $child->get_filesize();
872 $mimetype = $child->get_mimetype();
873 $params = $child->get_params();
0b259d91
DC
874 unset($params['component']);
875 unset($params['filearea']);
876 unset($params['filename']);
877 unset($params['itemid']);
64f93798
PS
878 $fileitem = array(
879 'params' => $params,
880 'filename' => $child->get_visible_name(),
881 'filedate' => $filedate ? userdate($filedate) : '',
882 'filesize' => $filesize ? display_size($filesize) : ''
883 );
e921afa8 884 $url = new moodle_url('/files/index.php', $params);
64f93798
PS
885 if ($child->is_directory()) {
886 $fileitem['isdir'] = true;
887 $fileitem['url'] = $url->out(false);
64f93798
PS
888 } else {
889 $fileitem['url'] = $child->get_url();
890 }
891 $this->tree[] = $fileitem;
892 }
893 }
894}