PageRenderTime 74ms CodeModel.GetById 31ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/filelib.php

https://bitbucket.org/bmbrands/swets
PHP | 3172 lines | 2034 code | 281 blank | 857 comment | 473 complexity | 41bd443411c2a5d5e7b10f98593b76a2 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3// This file is part of Moodle - http://moodle.org/
   4//
   5// Moodle is free software: you can redistribute it and/or modify
   6// it under the terms of the GNU General Public License as published by
   7// the Free Software Foundation, either version 3 of the License, or
   8// (at your option) any later version.
   9//
  10// Moodle is distributed in the hope that it will be useful,
  11// but WITHOUT ANY WARRANTY; without even the implied warranty of
  12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13// GNU General Public License for more details.
  14//
  15// You should have received a copy of the GNU General Public License
  16// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17
  18/**
  19 * Functions for file handling.
  20 *
  21 * @package    core
  22 * @subpackage file
  23 * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
  24 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25 */
  26
  27defined('MOODLE_INTERNAL') || die();
  28
  29/** @var string unique string constant. */
  30define('BYTESERVING_BOUNDARY', 's1k2o3d4a5k6s7');
  31
  32require_once("$CFG->libdir/filestorage/file_exceptions.php");
  33require_once("$CFG->libdir/filestorage/file_storage.php");
  34require_once("$CFG->libdir/filestorage/zip_packer.php");
  35require_once("$CFG->libdir/filebrowser/file_browser.php");
  36
  37/**
  38 * Encodes file serving url
  39 *
  40 * @deprecated use moodle_url factory methods instead
  41 *
  42 * @global object
  43 * @param string $urlbase
  44 * @param string $path /filearea/itemid/dir/dir/file.exe
  45 * @param bool $forcedownload
  46 * @param bool $https https url required
  47 * @return string encoded file url
  48 */
  49function file_encode_url($urlbase, $path, $forcedownload=false, $https=false) {
  50    global $CFG;
  51
  52//TODO: deprecate this
  53
  54    if ($CFG->slasharguments) {
  55        $parts = explode('/', $path);
  56        $parts = array_map('rawurlencode', $parts);
  57        $path  = implode('/', $parts);
  58        $return = $urlbase.$path;
  59        if ($forcedownload) {
  60            $return .= '?forcedownload=1';
  61        }
  62    } else {
  63        $path = rawurlencode($path);
  64        $return = $urlbase.'?file='.$path;
  65        if ($forcedownload) {
  66            $return .= '&amp;forcedownload=1';
  67        }
  68    }
  69
  70    if ($https) {
  71        $return = str_replace('http://', 'https://', $return);
  72    }
  73
  74    return $return;
  75}
  76
  77/**
  78 * Prepares 'editor' formslib element from data in database
  79 *
  80 * The passed $data record must contain field foobar, foobarformat and optionally foobartrust. This
  81 * function then copies the embedded files into draft area (assigning itemids automatically),
  82 * creates the form element foobar_editor and rewrites the URLs so the embedded images can be
  83 * displayed.
  84 * In your mform definition, you must have an 'editor' element called foobar_editor. Then you call
  85 * your mform's set_data() supplying the object returned by this function.
  86 *
  87 * @param object $data database field that holds the html text with embedded media
  88 * @param string $field the name of the database field that holds the html text with embedded media
  89 * @param array $options editor options (like maxifiles, maxbytes etc.)
  90 * @param object $context context of the editor
  91 * @param string $component
  92 * @param string $filearea file area name
  93 * @param int $itemid item id, required if item exists
  94 * @return object modified data object
  95 */
  96function file_prepare_standard_editor($data, $field, array $options, $context=null, $component=null, $filearea=null, $itemid=null) {
  97    $options = (array)$options;
  98    if (!isset($options['trusttext'])) {
  99        $options['trusttext'] = false;
 100    }
 101    if (!isset($options['forcehttps'])) {
 102        $options['forcehttps'] = false;
 103    }
 104    if (!isset($options['subdirs'])) {
 105        $options['subdirs'] = false;
 106    }
 107    if (!isset($options['maxfiles'])) {
 108        $options['maxfiles'] = 0; // no files by default
 109    }
 110    if (!isset($options['noclean'])) {
 111        $options['noclean'] = false;
 112    }
 113
 114    //sanity check for passed context. This function doesn't expect $option['context'] to be set
 115    //But this function is called before creating editor hence, this is one of the best places to check
 116    //if context is used properly. This check notify developer that they missed passing context to editor.
 117    if (isset($context) && !isset($options['context'])) {
 118        //if $context is not null then make sure $option['context'] is also set.
 119        debugging('Context for editor is not set in editoroptions. Hence editor will not respect editor filters', DEBUG_DEVELOPER);
 120    } else if (isset($options['context']) && isset($context)) {
 121        //If both are passed then they should be equal.
 122        if ($options['context']->id != $context->id) {
 123            $exceptionmsg = 'Editor context ['.$options['context']->id.'] is not equal to passed context ['.$context->id.']';
 124            throw new coding_exception($exceptionmsg);
 125        }
 126    }
 127
 128    if (is_null($itemid) or is_null($context)) {
 129        $contextid = null;
 130        $itemid = null;
 131        if (!isset($data->{$field})) {
 132            $data->{$field} = '';
 133        }
 134        if (!isset($data->{$field.'format'})) {
 135            $data->{$field.'format'} = editors_get_preferred_format();
 136        }
 137        if (!$options['noclean']) {
 138            $data->{$field} = clean_text($data->{$field}, $data->{$field.'format'});
 139        }
 140
 141    } else {
 142        if ($options['trusttext']) {
 143            // noclean ignored if trusttext enabled
 144            if (!isset($data->{$field.'trust'})) {
 145                $data->{$field.'trust'} = 0;
 146            }
 147            $data = trusttext_pre_edit($data, $field, $context);
 148        } else {
 149            if (!$options['noclean']) {
 150                $data->{$field} = clean_text($data->{$field}, $data->{$field.'format'});
 151            }
 152        }
 153        $contextid = $context->id;
 154    }
 155
 156    if ($options['maxfiles'] != 0) {
 157        $draftid_editor = file_get_submitted_draft_itemid($field);
 158        $currenttext = file_prepare_draft_area($draftid_editor, $contextid, $component, $filearea, $itemid, $options, $data->{$field});
 159        $data->{$field.'_editor'} = array('text'=>$currenttext, 'format'=>$data->{$field.'format'}, 'itemid'=>$draftid_editor);
 160    } else {
 161        $data->{$field.'_editor'} = array('text'=>$data->{$field}, 'format'=>$data->{$field.'format'}, 'itemid'=>0);
 162    }
 163
 164    return $data;
 165}
 166
 167/**
 168 * Prepares the content of the 'editor' form element with embedded media files to be saved in database
 169 *
 170 * This function moves files from draft area to the destination area and
 171 * encodes URLs to the draft files so they can be safely saved into DB. The
 172 * form has to contain the 'editor' element named foobar_editor, where 'foobar'
 173 * is the name of the database field to hold the wysiwyg editor content. The
 174 * editor data comes as an array with text, format and itemid properties. This
 175 * function automatically adds $data properties foobar, foobarformat and
 176 * foobartrust, where foobar has URL to embedded files encoded.
 177 *
 178 * @param object $data raw data submitted by the form
 179 * @param string $field name of the database field containing the html with embedded media files
 180 * @param array $options editor options (trusttext, subdirs, maxfiles, maxbytes etc.)
 181 * @param object $context context, required for existing data
 182 * @param string component
 183 * @param string $filearea file area name
 184 * @param int $itemid item id, required if item exists
 185 * @return object modified data object
 186 */
 187function file_postupdate_standard_editor($data, $field, array $options, $context, $component=null, $filearea=null, $itemid=null) {
 188    $options = (array)$options;
 189    if (!isset($options['trusttext'])) {
 190        $options['trusttext'] = false;
 191    }
 192    if (!isset($options['forcehttps'])) {
 193        $options['forcehttps'] = false;
 194    }
 195    if (!isset($options['subdirs'])) {
 196        $options['subdirs'] = false;
 197    }
 198    if (!isset($options['maxfiles'])) {
 199        $options['maxfiles'] = 0; // no files by default
 200    }
 201    if (!isset($options['maxbytes'])) {
 202        $options['maxbytes'] = 0; // unlimited
 203    }
 204
 205    if ($options['trusttext']) {
 206        $data->{$field.'trust'} = trusttext_trusted($context);
 207    } else {
 208        $data->{$field.'trust'} = 0;
 209    }
 210
 211    $editor = $data->{$field.'_editor'};
 212
 213    if ($options['maxfiles'] == 0 or is_null($filearea) or is_null($itemid) or empty($editor['itemid'])) {
 214        $data->{$field} = $editor['text'];
 215    } else {
 216        $data->{$field} = file_save_draft_area_files($editor['itemid'], $context->id, $component, $filearea, $itemid, $options, $editor['text'], $options['forcehttps']);
 217    }
 218    $data->{$field.'format'} = $editor['format'];
 219
 220    return $data;
 221}
 222
 223/**
 224 * Saves text and files modified by Editor formslib element
 225 *
 226 * @param object $data $database entry field
 227 * @param string $field name of data field
 228 * @param array $options various options
 229 * @param object $context context - must already exist
 230 * @param string $component
 231 * @param string $filearea file area name
 232 * @param int $itemid must already exist, usually means data is in db
 233 * @return object modified data obejct
 234 */
 235function file_prepare_standard_filemanager($data, $field, array $options, $context=null, $component=null, $filearea=null, $itemid=null) {
 236    $options = (array)$options;
 237    if (!isset($options['subdirs'])) {
 238        $options['subdirs'] = false;
 239    }
 240    if (is_null($itemid) or is_null($context)) {
 241        $itemid = null;
 242        $contextid = null;
 243    } else {
 244        $contextid = $context->id;
 245    }
 246
 247    $draftid_editor = file_get_submitted_draft_itemid($field.'_filemanager');
 248    file_prepare_draft_area($draftid_editor, $contextid, $component, $filearea, $itemid, $options);
 249    $data->{$field.'_filemanager'} = $draftid_editor;
 250
 251    return $data;
 252}
 253
 254/**
 255 * Saves files modified by File manager formslib element
 256 *
 257 * @param object $data $database entry field
 258 * @param string $field name of data field
 259 * @param array $options various options
 260 * @param object $context context - must already exist
 261 * @param string $component
 262 * @param string $filearea file area name
 263 * @param int $itemid must already exist, usually means data is in db
 264 * @return object modified data obejct
 265 */
 266function file_postupdate_standard_filemanager($data, $field, array $options, $context, $component, $filearea, $itemid) {
 267    $options = (array)$options;
 268    if (!isset($options['subdirs'])) {
 269        $options['subdirs'] = false;
 270    }
 271    if (!isset($options['maxfiles'])) {
 272        $options['maxfiles'] = -1; // unlimited
 273    }
 274    if (!isset($options['maxbytes'])) {
 275        $options['maxbytes'] = 0; // unlimited
 276    }
 277
 278    if (empty($data->{$field.'_filemanager'})) {
 279        $data->$field = '';
 280
 281    } else {
 282        file_save_draft_area_files($data->{$field.'_filemanager'}, $context->id, $component, $filearea, $itemid, $options);
 283        $fs = get_file_storage();
 284
 285        if ($fs->get_area_files($context->id, $component, $filearea, $itemid)) {
 286            $data->$field = '1'; // TODO: this is an ugly hack (skodak)
 287        } else {
 288            $data->$field = '';
 289        }
 290    }
 291
 292    return $data;
 293}
 294
 295/**
 296 *
 297 * @global object
 298 * @global object
 299 * @return int a random but available draft itemid that can be used to create a new draft
 300 * file area.
 301 */
 302function file_get_unused_draft_itemid() {
 303    global $DB, $USER;
 304
 305    if (isguestuser() or !isloggedin()) {
 306        // guests and not-logged-in users can not be allowed to upload anything!!!!!!
 307        print_error('noguest');
 308    }
 309
 310    $contextid = get_context_instance(CONTEXT_USER, $USER->id)->id;
 311
 312    $fs = get_file_storage();
 313    $draftitemid = rand(1, 999999999);
 314    while ($files = $fs->get_area_files($contextid, 'user', 'draft', $draftitemid)) {
 315        $draftitemid = rand(1, 999999999);
 316    }
 317
 318    return $draftitemid;
 319}
 320
 321/**
 322 * Initialise a draft file area from a real one by copying the files. A draft
 323 * area will be created if one does not already exist. Normally you should
 324 * get $draftitemid by calling file_get_submitted_draft_itemid('elementname');
 325 *
 326 * @global object
 327 * @global object
 328 * @param int &$draftitemid the id of the draft area to use, or 0 to create a new one, in which case this parameter is updated.
 329 * @param integer $contextid This parameter and the next two identify the file area to copy files from.
 330 * @param string $component
 331 * @param string $filearea helps indentify the file area.
 332 * @param integer $itemid helps identify the file area. Can be null if there are no files yet.
 333 * @param array $options text and file options ('subdirs'=>false, 'forcehttps'=>false)
 334 * @param string $text some html content that needs to have embedded links rewritten to point to the draft area.
 335 * @return string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
 336 */
 337function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null) {
 338    global $CFG, $USER, $CFG;
 339
 340    $options = (array)$options;
 341    if (!isset($options['subdirs'])) {
 342        $options['subdirs'] = false;
 343    }
 344    if (!isset($options['forcehttps'])) {
 345        $options['forcehttps'] = false;
 346    }
 347
 348    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 349    $fs = get_file_storage();
 350
 351    if (empty($draftitemid)) {
 352        // create a new area and copy existing files into
 353        $draftitemid = file_get_unused_draft_itemid();
 354        $file_record = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft', 'itemid'=>$draftitemid);
 355        if (!is_null($itemid) and $files = $fs->get_area_files($contextid, $component, $filearea, $itemid)) {
 356            foreach ($files as $file) {
 357                if ($file->is_directory() and $file->get_filepath() === '/') {
 358                    // we need a way to mark the age of each draft area,
 359                    // by not copying the root dir we force it to be created automatically with current timestamp
 360                    continue;
 361                }
 362                if (!$options['subdirs'] and ($file->is_directory() or $file->get_filepath() !== '/')) {
 363                    continue;
 364                }
 365                $fs->create_file_from_storedfile($file_record, $file);
 366            }
 367        }
 368        if (!is_null($text)) {
 369            // at this point there should not be any draftfile links yet,
 370            // because this is a new text from database that should still contain the @@pluginfile@@ links
 371            // this happens when developers forget to post process the text
 372            $text = str_replace("\"$CFG->httpswwwroot/draftfile.php", "\"$CFG->httpswwwroot/brokenfile.php#", $text);
 373        }
 374    } else {
 375        // nothing to do
 376    }
 377
 378    if (is_null($text)) {
 379        return null;
 380    }
 381
 382    // relink embedded files - editor can not handle @@PLUGINFILE@@ !
 383    return file_rewrite_pluginfile_urls($text, 'draftfile.php', $usercontext->id, 'user', 'draft', $draftitemid, $options);
 384}
 385
 386/**
 387 * Convert encoded URLs in $text from the @@PLUGINFILE@@/... form to an actual URL.
 388 *
 389 * @global object
 390 * @param string $text The content that may contain ULRs in need of rewriting.
 391 * @param string $file The script that should be used to serve these files. pluginfile.php, draftfile.php, etc.
 392 * @param integer $contextid This parameter and the next two identify the file area to use.
 393 * @param string $component
 394 * @param string $filearea helps identify the file area.
 395 * @param integer $itemid helps identify the file area.
 396 * @param array $options text and file options ('forcehttps'=>false)
 397 * @return string the processed text.
 398 */
 399function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $filearea, $itemid, array $options=null) {
 400    global $CFG;
 401
 402    $options = (array)$options;
 403    if (!isset($options['forcehttps'])) {
 404        $options['forcehttps'] = false;
 405    }
 406
 407    if (!$CFG->slasharguments) {
 408        $file = $file . '?file=';
 409    }
 410
 411    $baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
 412
 413    if ($itemid !== null) {
 414        $baseurl .= "$itemid/";
 415    }
 416
 417    if ($options['forcehttps']) {
 418        $baseurl = str_replace('http://', 'https://', $baseurl);
 419    }
 420
 421    return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
 422}
 423
 424/**
 425 * Returns information about files in a draft area.
 426 *
 427 * @global object
 428 * @global object
 429 * @param integer $draftitemid the draft area item id.
 430 * @return array with the following entries:
 431 *      'filecount' => number of files in the draft area.
 432 * (more information will be added as needed).
 433 */
 434function file_get_draft_area_info($draftitemid) {
 435    global $CFG, $USER;
 436
 437    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 438    $fs = get_file_storage();
 439
 440    $results = array();
 441
 442    // The number of files
 443    $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id', false);
 444    $results['filecount'] = count($draftfiles);
 445    $results['filesize'] = 0;
 446    foreach ($draftfiles as $file) {
 447        $results['filesize'] += $file->get_filesize();
 448    }
 449
 450    return $results;
 451}
 452
 453/**
 454 * Get used space of files
 455 * @return int total bytes
 456 */
 457function file_get_user_used_space() {
 458    global $DB, $USER;
 459
 460    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 461    $sql = "SELECT SUM(files1.filesize) AS totalbytes FROM {files} files1
 462            JOIN (SELECT contenthash, filename, MAX(id) AS id
 463            FROM {files}
 464            WHERE contextid = ? AND component = ? AND filearea != ?
 465            GROUP BY contenthash, filename) files2 ON files1.id = files2.id";
 466    $params = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft');
 467    $record = $DB->get_record_sql($sql, $params);
 468    return (int)$record->totalbytes;
 469}
 470
 471/**
 472 * Convert any string to a valid filepath
 473 * @param string $str
 474 * @return string path
 475 */
 476function file_correct_filepath($str) { //TODO: what is this? (skodak)
 477    if ($str == '/' or empty($str)) {
 478        return '/';
 479    } else {
 480        return '/'.trim($str, './@#$ ').'/';
 481    }
 482}
 483
 484/**
 485 * Generate a folder tree of draft area of current USER recursively
 486 * @param int $itemid
 487 * @param string $filepath
 488 * @param mixed $data //TODO: use normal return value instead, this does not fit the rest of api here (skodak)
 489 */
 490function file_get_drafarea_folders($draftitemid, $filepath, &$data) {
 491    global $USER, $OUTPUT, $CFG;
 492    $data->children = array();
 493    $context = get_context_instance(CONTEXT_USER, $USER->id);
 494    $fs = get_file_storage();
 495    if ($files = $fs->get_directory_files($context->id, 'user', 'draft', $draftitemid, $filepath, false)) {
 496        foreach ($files as $file) {
 497            if ($file->is_directory()) {
 498                $item = new stdClass();
 499                $item->sortorder = $file->get_sortorder();
 500                $item->filepath = $file->get_filepath();
 501
 502                $foldername = explode('/', trim($item->filepath, '/'));
 503                $item->fullname = trim(array_pop($foldername), '/');
 504
 505                $item->id = uniqid();
 506                file_get_drafarea_folders($draftitemid, $item->filepath, $item);
 507                $data->children[] = $item;
 508            } else {
 509                continue;
 510            }
 511        }
 512    }
 513}
 514
 515/**
 516 * Listing all files (including folders) in current path (draft area)
 517 * used by file manager
 518 * @param int $draftitemid
 519 * @param string $filepath
 520 * @return mixed
 521 */
 522function file_get_drafarea_files($draftitemid, $filepath = '/') {
 523    global $USER, $OUTPUT, $CFG;
 524
 525    $context = get_context_instance(CONTEXT_USER, $USER->id);
 526    $fs = get_file_storage();
 527
 528    $data = new stdClass();
 529    $data->path = array();
 530    $data->path[] = array('name'=>get_string('files'), 'path'=>'/');
 531
 532    // will be used to build breadcrumb
 533    $trail = '';
 534    if ($filepath !== '/') {
 535        $filepath = file_correct_filepath($filepath);
 536        $parts = explode('/', $filepath);
 537        foreach ($parts as $part) {
 538            if ($part != '' && $part != null) {
 539                $trail .= ('/'.$part.'/');
 540                $data->path[] = array('name'=>$part, 'path'=>$trail);
 541            }
 542        }
 543    }
 544
 545    $list = array();
 546    $maxlength = 12;
 547    if ($files = $fs->get_directory_files($context->id, 'user', 'draft', $draftitemid, $filepath, false)) {
 548        foreach ($files as $file) {
 549            $item = new stdClass();
 550            $item->filename = $file->get_filename();
 551            $item->filepath = $file->get_filepath();
 552            $item->fullname = trim($item->filename, '/');
 553            $filesize = $file->get_filesize();
 554            $item->filesize = $filesize ? display_size($filesize) : '';
 555
 556            $icon = mimeinfo_from_type('icon', $file->get_mimetype());
 557            $item->icon = $OUTPUT->pix_url('f/' . $icon)->out();
 558            $item->sortorder = $file->get_sortorder();
 559
 560            if ($icon == 'zip') {
 561                $item->type = 'zip';
 562            } else {
 563                $item->type = 'file';
 564            }
 565
 566            if ($file->is_directory()) {
 567                $item->filesize = 0;
 568                $item->icon = $OUTPUT->pix_url('f/folder')->out();
 569                $item->type = 'folder';
 570                $foldername = explode('/', trim($item->filepath, '/'));
 571                $item->fullname = trim(array_pop($foldername), '/');
 572            } else {
 573                // do NOT use file browser here!
 574                $item->url = moodle_url::make_draftfile_url($draftitemid, $item->filepath, $item->filename)->out();
 575            }
 576            $list[] = $item;
 577        }
 578    }
 579    $data->itemid = $draftitemid;
 580    $data->list = $list;
 581    return $data;
 582}
 583
 584/**
 585 * Returns draft area itemid for a given element.
 586 *
 587 * @param string $elname name of formlib editor element, or a hidden form field that stores the draft area item id, etc.
 588 * @return integer the itemid, or 0 if there is not one yet.
 589 */
 590function file_get_submitted_draft_itemid($elname) {
 591    $param = optional_param($elname, 0, PARAM_INT);
 592    if ($param) {
 593        require_sesskey();
 594    }
 595    if (is_array($param)) {
 596        if (!empty($param['itemid'])) {
 597            $param = $param['itemid'];
 598        } else {
 599            debugging('Missing itemid, maybe caused by unset maxfiles option', DEBUG_DEVELOPER);
 600            return false;
 601        }
 602    }
 603    return $param;
 604}
 605
 606/**
 607 * Saves files from a draft file area to a real one (merging the list of files).
 608 * Can rewrite URLs in some content at the same time if desired.
 609 *
 610 * @global object
 611 * @global object
 612 * @param integer $draftitemid the id of the draft area to use. Normally obtained
 613 *      from file_get_submitted_draft_itemid('elementname') or similar.
 614 * @param integer $contextid This parameter and the next two identify the file area to save to.
 615 * @param string $component
 616 * @param string $filearea indentifies the file area.
 617 * @param integer $itemid helps identifies the file area.
 618 * @param array $options area options (subdirs=>false, maxfiles=-1, maxbytes=0)
 619 * @param string $text some html content that needs to have embedded links rewritten
 620 *      to the @@PLUGINFILE@@ form for saving in the database.
 621 * @param boolean $forcehttps force https urls.
 622 * @return string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
 623 */
 624function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null, $forcehttps=false) {
 625    global $USER;
 626
 627    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 628    $fs = get_file_storage();
 629
 630    $options = (array)$options;
 631    if (!isset($options['subdirs'])) {
 632        $options['subdirs'] = false;
 633    }
 634    if (!isset($options['maxfiles'])) {
 635        $options['maxfiles'] = -1; // unlimited
 636    }
 637    if (!isset($options['maxbytes'])) {
 638        $options['maxbytes'] = 0; // unlimited
 639    }
 640
 641    $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id');
 642    $oldfiles   = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id');
 643
 644    if (count($draftfiles) < 2) {
 645        // means there are no files - one file means root dir only ;-)
 646        $fs->delete_area_files($contextid, $component, $filearea, $itemid);
 647
 648    } else if (count($oldfiles) < 2) {
 649        $filecount = 0;
 650        // there were no files before - one file means root dir only ;-)
 651        $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
 652        foreach ($draftfiles as $file) {
 653            if (!$options['subdirs']) {
 654                if ($file->get_filepath() !== '/' or $file->is_directory()) {
 655                    continue;
 656                }
 657            }
 658            if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) {
 659                // oversized file - should not get here at all
 660                continue;
 661            }
 662            if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) {
 663                // more files - should not get here at all
 664                break;
 665            }
 666            if (!$file->is_directory()) {
 667                $filecount++;
 668            }
 669            $fs->create_file_from_storedfile($file_record, $file);
 670        }
 671
 672    } else {
 673        // we have to merge old and new files - we want to keep file ids for files that were not changed
 674        // we change time modified for all new and changed files, we keep time created as is
 675        $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
 676
 677        $newhashes = array();
 678        foreach ($draftfiles as $file) {
 679            $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename());
 680            $newhashes[$newhash] = $file;
 681        }
 682        $filecount = 0;
 683        foreach ($oldfiles as $oldfile) {
 684            $oldhash = $oldfile->get_pathnamehash();
 685            if (!isset($newhashes[$oldhash])) {
 686                // delete files not needed any more - deleted by user
 687                $oldfile->delete();
 688                continue;
 689            }
 690            $newfile = $newhashes[$oldhash];
 691            if ($oldfile->get_contenthash() != $newfile->get_contenthash() or $oldfile->get_sortorder() != $newfile->get_sortorder()
 692                or $oldfile->get_status() != $newfile->get_status() or $oldfile->get_license() != $newfile->get_license()
 693                or $oldfile->get_author() != $newfile->get_author() or $oldfile->get_source() != $newfile->get_source()) {
 694                // file was changed, use updated with new timemodified data
 695                $oldfile->delete();
 696                continue;
 697            }
 698            // unchanged file or directory - we keep it as is
 699            unset($newhashes[$oldhash]);
 700            if (!$oldfile->is_directory()) {
 701                $filecount++;
 702            }
 703        }
 704
 705        // now add new/changed files
 706        // the size and subdirectory tests are extra safety only, the UI should prevent it
 707        foreach ($newhashes as $file) {
 708            if (!$options['subdirs']) {
 709                if ($file->get_filepath() !== '/' or $file->is_directory()) {
 710                    continue;
 711                }
 712            }
 713            if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) {
 714                // oversized file - should not get here at all
 715                continue;
 716            }
 717            if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) {
 718                // more files - should not get here at all
 719                break;
 720            }
 721            if (!$file->is_directory()) {
 722                $filecount++;
 723            }
 724            $fs->create_file_from_storedfile($file_record, $file);
 725        }
 726    }
 727
 728    // note: do not purge the draft area - we clean up areas later in cron,
 729    //       the reason is that user might press submit twice and they would loose the files,
 730    //       also sometimes we might want to use hacks that save files into two different areas
 731
 732    if (is_null($text)) {
 733        return null;
 734    } else {
 735        return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps);
 736    }
 737}
 738
 739/**
 740 * Convert the draft file area URLs in some content to @@PLUGINFILE@@ tokens
 741 * ready to be saved in the database. Normally, this is done automatically by
 742 * {@link file_save_draft_area_files()}.
 743 * @param string $text the content to process.
 744 * @param int $draftitemid the draft file area the content was using.
 745 * @param bool $forcehttps whether the content contains https URLs. Default false.
 746 * @return string the processed content.
 747 */
 748function file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps = false) {
 749    global $CFG, $USER;
 750
 751    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 752
 753    $wwwroot = $CFG->wwwroot;
 754    if ($forcehttps) {
 755        $wwwroot = str_replace('http://', 'https://', $wwwroot);
 756    }
 757
 758    // relink embedded files if text submitted - no absolute links allowed in database!
 759    $text = str_ireplace("$wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
 760
 761    if (strpos($text, 'draftfile.php?file=') !== false) {
 762        $matches = array();
 763        preg_match_all("!$wwwroot/draftfile.php\?file=%2F{$usercontext->id}%2Fuser%2Fdraft%2F{$draftitemid}%2F[^'\",&<>|`\s:\\\\]+!iu", $text, $matches);
 764        if ($matches) {
 765            foreach ($matches[0] as $match) {
 766                $replace = str_ireplace('%2F', '/', $match);
 767                $text = str_replace($match, $replace, $text);
 768            }
 769        }
 770        $text = str_ireplace("$wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
 771    }
 772
 773    return $text;
 774}
 775
 776/**
 777 * Set file sort order
 778 * @global object $DB
 779 * @param integer $contextid the context id
 780 * @param string $component
 781 * @param string $filearea file area.
 782 * @param integer $itemid itemid.
 783 * @param string $filepath file path.
 784 * @param string $filename file name.
 785 * @param integer $sortorer the sort order of file.
 786 * @return boolean
 787 */
 788function file_set_sortorder($contextid, $component, $filearea, $itemid, $filepath, $filename, $sortorder) {
 789    global $DB;
 790    $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'filename'=>$filename);
 791    if ($file_record = $DB->get_record('files', $conditions)) {
 792        $sortorder = (int)$sortorder;
 793        $file_record->sortorder = $sortorder;
 794        $DB->update_record('files', $file_record);
 795        return true;
 796    }
 797    return false;
 798}
 799
 800/**
 801 * reset file sort order number to 0
 802 * @global object $DB
 803 * @param integer $contextid the context id
 804 * @param string $component
 805 * @param string $filearea file area.
 806 * @param integer $itemid itemid.
 807 * @return boolean
 808 */
 809function file_reset_sortorder($contextid, $component, $filearea, $itemid=false) {
 810    global $DB;
 811
 812    $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea);
 813    if ($itemid !== false) {
 814        $conditions['itemid'] = $itemid;
 815    }
 816
 817    $file_records = $DB->get_records('files', $conditions);
 818    foreach ($file_records as $file_record) {
 819        $file_record->sortorder = 0;
 820        $DB->update_record('files', $file_record);
 821    }
 822    return true;
 823}
 824
 825/**
 826 * Returns description of upload error
 827 *
 828 * @param int $errorcode found in $_FILES['filename.ext']['error']
 829 * @return string error description string, '' if ok
 830 */
 831function file_get_upload_error($errorcode) {
 832
 833    switch ($errorcode) {
 834    case 0: // UPLOAD_ERR_OK - no error
 835        $errmessage = '';
 836        break;
 837
 838    case 1: // UPLOAD_ERR_INI_SIZE
 839        $errmessage = get_string('uploadserverlimit');
 840        break;
 841
 842    case 2: // UPLOAD_ERR_FORM_SIZE
 843        $errmessage = get_string('uploadformlimit');
 844        break;
 845
 846    case 3: // UPLOAD_ERR_PARTIAL
 847        $errmessage = get_string('uploadpartialfile');
 848        break;
 849
 850    case 4: // UPLOAD_ERR_NO_FILE
 851        $errmessage = get_string('uploadnofilefound');
 852        break;
 853
 854    // Note: there is no error with a value of 5
 855
 856    case 6: // UPLOAD_ERR_NO_TMP_DIR
 857        $errmessage = get_string('uploadnotempdir');
 858        break;
 859
 860    case 7: // UPLOAD_ERR_CANT_WRITE
 861        $errmessage = get_string('uploadcantwrite');
 862        break;
 863
 864    case 8: // UPLOAD_ERR_EXTENSION
 865        $errmessage = get_string('uploadextension');
 866        break;
 867
 868    default:
 869        $errmessage = get_string('uploadproblem');
 870    }
 871
 872    return $errmessage;
 873}
 874
 875/**
 876 * Recursive function formating an array in POST parameter
 877 * @param array $arraydata - the array that we are going to format and add into &$data array
 878 * @param string $currentdata - a row of the final postdata array at instant T
 879 *                when finish, it's assign to $data under this format: name[keyname][][]...[]='value'
 880 * @param array $data - the final data array containing all POST parameters : 1 row = 1 parameter
 881 */
 882function format_array_postdata_for_curlcall($arraydata, $currentdata, &$data) {
 883        foreach ($arraydata as $k=>$v) {
 884            $newcurrentdata = $currentdata;
 885            if (is_array($v)) { //the value is an array, call the function recursively
 886                $newcurrentdata = $newcurrentdata.'['.urlencode($k).']';
 887                format_array_postdata_for_curlcall($v, $newcurrentdata, $data);
 888            }  else { //add the POST parameter to the $data array
 889                $data[] = $newcurrentdata.'['.urlencode($k).']='.urlencode($v);
 890            }
 891        }
 892}
 893
 894/**
 895 * Transform a PHP array into POST parameter
 896 * (see the recursive function format_array_postdata_for_curlcall)
 897 * @param array $postdata
 898 * @return array containing all POST parameters  (1 row = 1 POST parameter)
 899 */
 900function format_postdata_for_curlcall($postdata) {
 901        $data = array();
 902        foreach ($postdata as $k=>$v) {
 903            if (is_array($v)) {
 904                $currentdata = urlencode($k);
 905                format_array_postdata_for_curlcall($v, $currentdata, $data);
 906            }  else {
 907                $data[] = urlencode($k).'='.urlencode($v);
 908            }
 909        }
 910        $convertedpostdata = implode('&', $data);
 911        return $convertedpostdata;
 912}
 913
 914
 915
 916
 917/**
 918 * Fetches content of file from Internet (using proxy if defined). Uses cURL extension if present.
 919 * Due to security concerns only downloads from http(s) sources are supported.
 920 *
 921 * @param string $url file url starting with http(s)://
 922 * @param array $headers http headers, null if none. If set, should be an
 923 *   associative array of header name => value pairs.
 924 * @param array $postdata array means use POST request with given parameters
 925 * @param bool $fullresponse return headers, responses, etc in a similar way snoopy does
 926 *   (if false, just returns content)
 927 * @param int $timeout timeout for complete download process including all file transfer
 928 *   (default 5 minutes)
 929 * @param int $connecttimeout timeout for connection to server; this is the timeout that
 930 *   usually happens if the remote server is completely down (default 20 seconds);
 931 *   may not work when using proxy
 932 * @param bool $skipcertverify If true, the peer's SSL certificate will not be checked. 
 933 *   Only use this when already in a trusted location.
 934 * @param string $tofile store the downloaded content to file instead of returning it.
 935 * @param bool $calctimeout false by default, true enables an extra head request to try and determine 
 936 *   filesize and appropriately larger timeout based on $CFG->curltimeoutkbitrate
 937 * @return mixed false if request failed or content of the file as string if ok. True if file downloaded into $tofile successfully.
 938 */
 939function download_file_content($url, $headers=null, $postdata=null, $fullresponse=false, $timeout=300, $connecttimeout=20, $skipcertverify=false, $tofile=NULL, $calctimeout=false) {
 940    global $CFG;
 941
 942    // some extra security
 943    $newlines = array("\r", "\n");
 944    if (is_array($headers) ) {
 945        foreach ($headers as $key => $value) {
 946            $headers[$key] = str_replace($newlines, '', $value);
 947        }
 948    }
 949    $url = str_replace($newlines, '', $url);
 950    if (!preg_match('|^https?://|i', $url)) {
 951        if ($fullresponse) {
 952            $response = new stdClass();
 953            $response->status        = 0;
 954            $response->headers       = array();
 955            $response->response_code = 'Invalid protocol specified in url';
 956            $response->results       = '';
 957            $response->error         = 'Invalid protocol specified in url';
 958            return $response;
 959        } else {
 960            return false;
 961        }
 962    }
 963
 964    // check if proxy (if used) should be bypassed for this url
 965    $proxybypass = is_proxybypass($url);
 966
 967    if (!$ch = curl_init($url)) {
 968        debugging('Can not init curl.');
 969        return false;
 970    }
 971
 972    // set extra headers
 973    if (is_array($headers) ) {
 974        $headers2 = array();
 975        foreach ($headers as $key => $value) {
 976            $headers2[] = "$key: $value";
 977        }
 978        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers2);
 979    }
 980
 981    if ($skipcertverify) {
 982        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 983    }
 984
 985    // use POST if requested
 986    if (is_array($postdata)) {
 987        $postdata = format_postdata_for_curlcall($postdata);
 988        curl_setopt($ch, CURLOPT_POST, true);
 989        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
 990    }
 991
 992    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 993    curl_setopt($ch, CURLOPT_HEADER, false);
 994    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connecttimeout);
 995
 996    if (!ini_get('open_basedir') and !ini_get('safe_mode')) {
 997        // TODO: add version test for '7.10.5'
 998        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 999        curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
1000    }
1001
1002    if (!empty($CFG->proxyhost) and !$proxybypass) {
1003        // SOCKS supported in PHP5 only
1004        if (!empty($CFG->proxytype) and ($CFG->proxytype == 'SOCKS5')) {
1005            if (defined('CURLPROXY_SOCKS5')) {
1006                curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
1007            } else {
1008                curl_close($ch);
1009                if ($fullresponse) {
1010                    $response = new stdClass();
1011                    $response->status        = '0';
1012                    $response->headers       = array();
1013                    $response->response_code = 'SOCKS5 proxy is not supported in PHP4';
1014                    $response->results       = '';
1015                    $response->error         = 'SOCKS5 proxy is not supported in PHP4';
1016                    return $response;
1017                } else {
1018                    debugging("SOCKS5 proxy is not supported in PHP4.", DEBUG_ALL);
1019                    return false;
1020                }
1021            }
1022        }
1023
1024        curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
1025
1026        if (empty($CFG->proxyport)) {
1027            curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost);
1028        } else {
1029            curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost.':'.$CFG->proxyport);
1030        }
1031
1032        if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
1033            curl_setopt($ch, CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
1034            if (defined('CURLOPT_PROXYAUTH')) {
1035                // any proxy authentication if PHP 5.1
1036                curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
1037            }
1038        }
1039    }
1040
1041    // set up header and content handlers
1042    $received = new stdClass();
1043    $received->headers = array(); // received headers array
1044    $received->tofile  = $tofile;
1045    $received->fh      = null;
1046    curl_setopt($ch, CURLOPT_HEADERFUNCTION, partial('download_file_content_header_handler', $received));
1047    if ($tofile) {
1048        curl_setopt($ch, CURLOPT_WRITEFUNCTION, partial('download_file_content_write_handler', $received));
1049    }
1050
1051    if (!isset($CFG->curltimeoutkbitrate)) {
1052        //use very slow rate of 56kbps as a timeout speed when not set
1053        $bitrate = 56;
1054    } else {
1055        $bitrate = $CFG->curltimeoutkbitrate;
1056    }
1057
1058    // try to calculate the proper amount for timeout from remote file size.
1059    // if disabled or zero, we won't do any checks nor head requests.
1060    if ($calctimeout && $bitrate > 0) {
1061        //setup header request only options
1062        curl_setopt_array ($ch, array(
1063            CURLOPT_RETURNTRANSFER => false,
1064            CURLOPT_NOBODY         => true)
1065        );
1066
1067        curl_exec($ch);
1068        $info = curl_getinfo($ch);
1069        $err = curl_error($ch);
1070
1071        if ($err === '' && $info['download_content_length'] > 0) { //no curl errors
1072            $timeout = max($timeout, ceil($info['download_content_length'] * 8 / ($bitrate * 1024))); //adjust for large files only - take max timeout.
1073        }
1074        //reinstate affected curl options
1075        curl_setopt_array ($ch, array(
1076            CURLOPT_RETURNTRANSFER => true,
1077            CURLOPT_NOBODY         => false)
1078        );
1079    }
1080
1081    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1082    $result = curl_exec($ch);
1083
1084    // try to detect encoding problems
1085    if ((curl_errno($ch) == 23 or curl_errno($ch) == 61) and defined('CURLOPT_ENCODING')) {
1086        curl_setopt($ch, CURLOPT_ENCODING, 'none');
1087        $result = curl_exec($ch);
1088    }
1089
1090    if ($received->fh) {
1091        fclose($received->fh);
1092    }
1093
1094    if (curl_errno($ch)) {
1095        $error    = curl_error($ch);
1096        $error_no = curl_errno($ch);
1097        curl_close($ch);
1098
1099        if ($fullresponse) {
1100            $response = new stdClass();
1101            if ($error_no == 28) {
1102                $response->status    = '-100'; // mimic snoopy
1103            } else {
1104                $response->status    = '0';
1105            }
1106            $response->headers       = array();
1107            $response->response_code = $error;
1108            $response->results       = false;
1109            $response->error         = $error;
1110            return $response;
1111        } else {
1112            debugging("cURL request for \"$url\" failed with: $error ($error_no)", DEBUG_ALL);
1113            return false;
1114        }
1115
1116    } else {
1117        $info = curl_getinfo($ch);
1118        curl_close($ch);
1119
1120        if (empty($info['http_code'])) {
1121            // for security reasons we support only true http connections (Location: file:// exploit prevention)
1122            $response = new stdClass();
1123            $response->status        = '0';
1124            $response->headers       = array();
1125            $response->response_code = 'Unknown cURL error';
1126            $response->results       = false; // do NOT change this, we really want to ignore the result!
1127            $response->error         = 'Unknown cURL error';
1128
1129        } else {
1130            $response = new stdClass();;
1131            $response->status        = (string)$info['http_code'];
1132            $response->headers       = $received->headers;
1133            $response->response_code = $received->headers[0];
1134            $response->results       = $result;
1135            $response->error         = '';
1136        }
1137
1138        if ($fullresponse) {
1139            return $response;
1140        } else if ($info['http_code'] != 200) {
1141            debugging("cURL request for \"$url\" failed, HTTP response code: ".$response->response_code, DEBUG_ALL);
1142            return false;
1143        } else {
1144            return $response->results;
1145        }
1146    }
1147}
1148
1149/**
1150 * internal implementation
1151 */
1152function download_file_content_header_handler($received, $ch, $header) {
1153    $received->headers[] = $header;
1154    return strlen($header);
1155}
1156
1157/**
1158 * internal implementation
1159 */
1160function download_file_content_write_handler($received, $ch, $data) {
1161    if (!$received->fh) {
1162        $received->fh = fopen($received->tofile, 'w');
1163        if ($received->fh === false) {
1164            // bad luck, file creation or overriding failed
1165            return 0;
1166        }
1167    }
1168    if (fwrite($received->fh, $data) === false) {
1169        // bad luck, write failed, let's abort completely
1170        return 0;
1171    }
1172    return strlen($data);
1173}
1174
1175/**
1176 * @return array List of information about file types based on extensions.
1177 *   Associative array of extension (lower-case) to associative array
1178 *   from 'element name' to data. Current element names are 'type' and 'icon'.
1179 *   Unknown types should use the 'xxx' entry which includes defaults.
1180 */
1181function get_mimetypes_array() {
1182    static $mimearray = array (
1183        'xxx'  => array ('type'=>'document/unknown', 'icon'=>'unknown'),
1184        '3gp'  => array ('type'=>'video/quicktime', 'icon'=>'video'),
1185        'aac'  => array ('type'=>'audio/aac', 'icon'=>'audio'),
1186        'ai'   => array ('type'=>'application/postscript', 'icon'=>'image'),
1187        'aif'  => array ('type'=>'audio/x-aiff', 'icon'=>'audio'),
1188        'aiff' => array ('type'=>'audio/x-aiff', 'icon'=>'audio'),
1189        'aifc' => array ('type'=>'audio/x-aiff', 'icon'=>'audio'),
1190        'applescript'  => array ('type'=>'text/plain', 'icon'=>'text'),
1191        'asc'  => array ('type'=>'text/plain', 'icon'=>'text'),
1192        'asm'  => array ('type'=>'text/plain', 'icon'=>'text'),
1193        'au'   => array ('type'=>'audio/au', 'icon'=>'audio'),
1194        'avi'  => array ('type'=>'video/x-ms-wm', 'icon'=>'avi'),
1195        'bmp'  => array ('type'=>'image/bmp', 'icon'=>'image'),
1196        'c'    => array ('type'=>'text/plain', 'icon'=>'text'),
1197        'cct'  => array ('type'=>'shockwave/director', 'icon'=>'flash'),
1198        'cpp'  => array ('type'=>'text/plain', 'icon'=>'text'),
1199        'cs'   => array ('type'=>'application/x-csh', 'icon'=>'text'),
1200        'css'  => array ('type'=>'text/css', 'icon'=>'text'),
1201        'csv'  => array ('type'=>'text/csv', 'icon'=>'excel'),
1202        'dv'   => array ('type'=>'video/x-dv', 'icon'=>'video'),
1203        'dmg'  => array ('type'=>'application/octet-stream', 'icon'=>'dmg'),
1204
1205        'doc'  => array ('type'=>'application/msword', 'icon'=>'word'),
1206        'docx' => array ('type'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'icon'=>'docx'),
1207        'docm' => array ('type'=>'application/vnd.ms-word.document.macroEnabled.12', 'icon'=>'docm'),
1208        'dotx' => array ('type'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'icon'=>'dotx'),
1209        'dotm' => array ('type'=>'application/vnd.ms-word.template.macroEnabled.12', 'icon'=>'dotm'),
1210
1211        'dcr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
1212        'dif'  => array ('type'=>'video/x-dv', 'icon'=>'video'),
1213        'dir'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
1214        'dxr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
1215        'eps'  => array ('type'=>'application/postscript', 'icon'=>'pdf'),
1216        'fdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
1217        'flv'  => array ('type'=>'video/x-flv', 'icon'=>'video'),
1218        'f4v'  => array ('type'=>'video/mp4', 'icon'=>'video'),
1219        'gif'  => array ('type'=>'image/gif', 'icon'=>'image'),
1220        'gtar' => array ('type'=>'application/x-gtar', 'icon'=>'zip'),
1221        'tgz'  => array ('type'=>'application/g-zip', 'icon'=>'zip'),
1222        'gz'   => array ('type'=>'application/g-zip', 'icon'=>'zip'),
1223        'gzip' => array ('type'=>'application/g-zip', 'icon'=>'zip'),
1224        'h'    => array ('type'=>'text/plain', 'icon'=>'text'),
1225        'hpp'  => array ('type'=>'text/plain', 'icon'=>'text'),
1226        'hqx'  => array ('type'=>'application/mac-binhex40', 'icon'=>'zip'),
1227        'htc'  => array ('type'=>'text/x-component', 'icon'=>'text'),
1228        'html' => array ('type'=>'text/html', 'icon'=>'html'),
1229        'xhtml'=> array ('type'=>'application/xhtml+xml', 'icon'=>'html'),
1230        'htm'  => array ('type'=>'text/html', 'icon'=>'html'),
1231        'ico'  => array ('type'=>'image/vnd.microsoft.icon', 'icon'=>'image'),
1232        'ics'  => array ('type'=>'text/calendar', 'icon'=>'text'),
1233        'isf'  => array ('type'=>'application/inspiration', 'icon'=>'isf'),
1234        'ist'  => array ('type'=>'application/inspiration.template', 'icon'=>'isf'),
1235        'java' => array ('type'=>'text/plain', 'icon'=>'text'),
1236        'jcb'  => array ('type'=>'text/xml', 'icon'=>'jcb'),
1237        'jcl'  => array ('type'=>'text/xml', 'icon'=>'jcl'),
1238        'jcw'  => array ('type'=>'text/xml', 'icon'=>'jcw'),
1239        'jmt'  => array ('type'=>'text/xml', 'icon'=>'jmt'),
1240        'jmx'  => array ('type'=>'text/xml', 'icon'=>'jmx'),
1241        'jpe'  => array ('type'=>'image/jpeg', 'icon'=>'image'),
1242        'jpeg' => array ('type'=>'image/jpeg', 'icon'=>'image'),
1243        'jpg'  => array ('type'=>'image/jpeg', 'icon'=>'image'),
1244        'jqz'  => array ('type'=>'text/xml', 'icon'=>'jqz'),
1245        'js'   => array ('type'=>'application/x-javascript', 'icon'=>'text'),
1246        'latex'=> array ('type'=>'application/x-latex', 'icon'=>'text'),
1247        'm'    => array ('type'=>'text/plain', 'icon'=>'text'),
1248        'mbz'  => array ('type'=>'application/vnd.moodle.backup', 'icon'=>'moodle'),
1249        'mov'  => array ('type'=>'video/quicktime', 'icon'=>'video'),
1250        'movie'=> array ('type'=>'video/x-sgi-movie', 'icon'=>'video'),
1251        'm3u'  => array ('type'=>'audio/x-mpegurl', 'icon'=>'audio'),
1252        'mp3'  => array ('type'=>'audio/mp3', 'icon'=>'audio'),
1253        'mp4'  => array ('type'=>'video/mp4', 'icon'=>'video'),
1254        'm4v'  => array ('type'=>'video/mp4', 'icon'=>'video'),
1255        'm4a'  => array ('type'=>'audio/mp4', 'icon'=>'audio'),
1256        'mpeg' => array ('type'=>'video/mpeg', 'icon'=>'video'),
1257        'mpe'  => array ('type'=>'video/mpeg', 'icon'=>'video'),
1258        'mpg'  => array ('type'=>'video/mpeg', 'icon'=>'video'),
1259
1260        'odt'  => array ('type'=>'application/vnd.oasis.opendocument.text', 'icon'=>'odt'),
1261        'ott'  => array ('type'=>'application/vnd.oasis.opendocument.text-template', 'icon'=>'odt'),
1262        'oth'  => array ('type'=>'application/vnd.oasis.opendocument.text-web', 'icon'=>'odt'),
1263        'odm'  => array ('type'=>'application/vnd.oasis.opendocument.text-master', 'i…

Large files files are truncated, but you can click here to view the full file