lib.php 125 KB
Newer Older
1 2 3 4 5
<?php
/**
 *
 * @package    mahara
 * @subpackage artefact-internal
6
 * @author     Catalyst IT Ltd
7 8
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
9 10 11 12 13
 *
 */

defined('INTERNAL') || die();

14 15
require_once('file.php');

16 17
class PluginArtefactFile extends PluginArtefact {

18 19 20 21
    public static function is_active() {
        return get_field('artefact_installed', 'active', 'name', 'file');
    }

22
    public static function get_artefact_types() {
23 24 25 26
        return array(
            'file',
            'folder',
            'image',
27
            'profileicon',
28
            'archive',
29 30
            'video',
            'audio',
31
        );
32
    }
Aaron Wells's avatar
Aaron Wells committed
33

34
    public static function get_block_types() {
35
        return array('image');
36
    }
37 38 39 40 41 42

    public static function get_plugin_name() {
        return 'file';
    }

    public static function menu_items() {
Richard Mansfield's avatar
Richard Mansfield committed
43
        return array(
44 45
            'create/files' => array(
                'path' => 'create/files',
46
                'url' => 'artefact/file/index.php',
47
                'title' => get_string('Files', 'artefact.file'),
48
                'weight' => 20,
49
            ),
50 51 52 53 54 55 56
        );
    }

    public static function right_nav_menu_items() {
        return array(
            'profileicons' => array(
                'path' => 'profileicons',
57 58
                'url' => 'artefact/file/profileicons.php',
                'title' => get_string('profileicons', 'artefact.file'),
59
                'weight' => 15,
60
                'iconclass' => 'regular icon-portrait',
61
            ),
Richard Mansfield's avatar
Richard Mansfield committed
62
        );
63
    }
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78
    public static function group_tabs($groupid, $role) {
        if ($role) {
            return array(
                'files' => array(
                    'path' => 'groups/files',
                    'url' => 'artefact/file/groupfiles.php?group='.$groupid,
                    'title' => get_string('Files', 'artefact.file'),
                    'weight' => 80,
                ),
            );
        }
        else {
            return array();
        }
79 80
    }

81 82 83 84 85 86 87
    public static function get_event_subscriptions() {
        $subscriptions = array(
            (object)array(
                'plugin'       => 'file',
                'event'        => 'createuser',
                'callfunction' => 'newuser',
            ),
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
            (object)array(
                'plugin'        => 'file',
                'event'         => 'saveartefact',
                'callfunction'  => 'eventlistener_savedeleteartefact',
            ),
            (object)array(
                'plugin'        => 'file',
                'event'         => 'deleteartefact',
                'callfunction'  => 'eventlistener_savedeleteartefact',
            ),
            (object)array(
                'plugin'        => 'file',
                'event'         => 'deleteartefacts',
                'callfunction'  => 'eventlistener_savedeleteartefact',
            ),
            (object)array(
                'plugin'        => 'file',
                'event'         => 'updateuser',
                'callfunction'  => 'eventlistener_savedeleteartefact',
            ),
108 109 110 111 112
        );

        return $subscriptions;
    }

113 114
    public static function postinst($prevversion) {
        if ($prevversion == 0) {
Hugh Davenport's avatar
Hugh Davenport committed
115
            // Set default quotas to 50MB
116
            set_config_plugin('artefact', 'file', 'defaultquota', 52428800);
Hugh Davenport's avatar
Hugh Davenport committed
117
            set_config_plugin('artefact', 'file', 'defaultgroupquota', 52428800);
118 119
            set_config_plugin('artefact', 'file', 'folderdownloadzip', 1);
            set_config_plugin('artefact', 'file', 'folderdownloadkeepzipfor', 3600);
120
            self::set_quota_triggers();
121
        }
122
        set_config_plugin('artefact', 'file', 'commentsallowedimage', 1);
123
        self::resync_filetype_list();
124 125
    }

126 127 128 129 130 131
    public static function set_quota_triggers() {
        set_config_plugin('artefact', 'file', 'quotanotifylimit', 80);
        set_config_plugin('artefact', 'file', 'quotanotifyadmin', false);

        // Create triggers to reset the quota notification flag
        if (is_postgres()) {
132 133
            $sql = "DROP FUNCTION IF EXISTS {unmark_quota_exeed_upd_set}() CASCADE;";
            execute_sql($sql);
134 135

            db_create_trigger(
136
                'unmark_quota_exceed_upd_usr_set',
137 138 139 140 141 142 143 144 145 146 147
                'AFTER', 'UPDATE', 'usr', "
                UPDATE {usr_account_preference}
                SET value = 0 FROM {artefact_config}
                WHERE {usr_account_preference}.field = 'quota_exceeded_notified'
                AND {usr_account_preference}.usr = NEW.id
                AND {artefact_config}.plugin = 'file'
                AND {artefact_config}.field = 'quotanotifylimit'
                AND CAST(NEW.quotaused AS float)/CAST(NEW.quota AS float) < CAST({artefact_config}.value AS float)/100;"
            );
        }
        else {
148 149 150
            $sql = "DROP TRIGGER IF EXISTS {unmark_quota_exceed_upd_set}";
            execute_sql($sql);

151
            db_create_trigger(
152
                'unmark_quota_exceed_upd_usr_set',
153 154 155 156 157 158 159 160 161 162 163 164
                'AFTER', 'UPDATE', 'usr', "
                UPDATE {usr_account_preference}, {artefact_config}
                SET {usr_account_preference}.value = 0
                WHERE {usr_account_preference}.field = 'quota_exceeded_notified'
                AND {usr_account_preference}.usr = NEW.id
                AND {artefact_config}.plugin = 'file'
                AND {artefact_config}.field = 'quotanotifylimit'
                AND NEW.quotaused/NEW.quota < {artefact_config}.value/100;"
            );
        }
    }

165
    public static function newuser($event, $user) {
166
        $user = is_object($user) ? (array)$user : $user;
167
        if (empty($user['quota'])) {
168 169
            update_record('usr', array('quota' => get_config_plugin('artefact', 'file', 'defaultquota')), array('id' => $user['id']));
        }
170
    }
Aaron Wells's avatar
Aaron Wells committed
171

Richard Mansfield's avatar
Richard Mansfield committed
172

173 174 175 176 177 178 179 180 181
    public static function sort_child_data($a, $b) {
        if ($a->container && !$b->container) {
            return -1;
        }
        else if (!$a->container && $b->container) {
            return 1;
        }
        return strnatcasecmp($a->text, $b->text);
    }
Richard Mansfield's avatar
Richard Mansfield committed
182

183 184
    public static function jsstrings($type) {
        static $jsstrings = array(
185 186 187
            'filebrowser' => array(
                'mahara' => array(
                    'remove',
188
                    'cancel',
189
                    'defaulthint',
190 191
                ),
                'artefact.file' => array(
192 193 194
                    'confirmdeletefile',
                    'confirmdeletefolder',
                    'confirmdeletefolderandcontents',
195
                    'createfoldersuccess',
196
                    'defaultprofileicon',
197 198
                    'editfile',
                    'editfolder',
199
                    'fileappearsinviews',
200
                    'fileappearsinposts',
201
                    'fileattachedtoportfolioitems',
202
                    'fileappearsinskins',
203
                    'filewithnameexists',
204
                    'folderappearsinviews',
205
                    'foldercontainsprofileicons',
206
                    'foldernamerequired',
207
                    'foldernotempty',
208
                    'maxuploadsize',
209
                    'fileisfolder',
210 211
                    'nametoolong',
                    'namefieldisrequired',
212
                    'upload',
213 214
                    'uploadingfiletofolder',
                    'youmustagreetothecopyrightnotice',
215
                    'moveto',
216 217
                ),
            ),
218 219 220
        );
        return $jsstrings[$type];
    }
221

222 223
    public static function jshelp($type) {
        static $jshelp = array(
224
            'filebrowser' => array(
225 226 227 228
                'artefact.file' => array(
                    'notice',
                    'quota_message',
                    'uploadfile',
229
                    'tags',
230 231 232 233 234 235
                ),
            ),
        );
        return $jshelp[$type];
    }

236 237 238 239 240 241 242

    /**
     * Resyncs the allowed filetypes list with the XML configuration file.
     *
     * This can be called on install (and is, in the postinst method above),
     * and every time an upgrade is made that changes the file.
     */
243
    public static function resync_filetype_list() {
244 245 246
        require_once('xmlize.php');
        db_begin();

247 248
        delete_records('artefact_file_mime_types');

249
        $newlist     = xmlize(file_get_contents(get_config('docroot') . 'artefact/file/filetypes.xml'));
250
        $filetypes   = $newlist['filetypes']['#']['filetype'];
251

252
        foreach ($filetypes as $filetype) {
253 254 255
            $description = $filetype['#']['description'][0]['#'];
            foreach ($filetype['#']['mimetypes'][0]['#']['mimetype'] as $type) {
                $mimetype = $type['#'];
256
                execute_sql("INSERT INTO {artefact_file_mime_types} (mimetype, description) VALUES (?,?)", array($mimetype, $description));
257 258
            }
        }
Aaron Wells's avatar
Aaron Wells committed
259

260
        db_commit();
261 262

        log_info('Synced filetype list with filetypes.xml');
263 264
    }

265 266 267 268
    public static function get_mimetypes_from_description($description=null, $getrecords=false) {
        static $allmimetypes = null;

        if (is_null($allmimetypes)) {
269
            $allmimetypes = get_records_array('artefact_file_mime_types');
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
        }

        if (is_string($description)) {
            $description = array($description);
        }

        $mimetypes = array();

        foreach ($allmimetypes as $r) {
            if (is_null($description) || in_array($r->description, $description)) {
                if ($getrecords) {
                    $mimetypes[$r->mimetype] = $r;
                }
                else {
                    $mimetypes[] = $r->mimetype;
                }
            }
287
        }
288 289

        return $mimetypes;
290 291
    }

292 293 294
    public static function can_be_disabled() {
        return false;
    }
295 296 297 298

    public static function get_artefact_type_content_types() {
        return array(
            'file'        => array('file'),
299 300
            'image'       => array('image'),
            'profileicon' => array('image'),
301
            'archive'     => array('file'),
302 303
            'video'       => array('file'),
            'audio'       => array('file'),
304 305
        );
    }
306 307

    public static function get_attachment_types() {
308 309 310 311 312 313 314
        return array(
            'file',
            'image',
            'archive',
            'video',
            'audio'
        );
315
    }
316 317 318

    public static function recalculate_quota() {
        $data = get_records_sql_assoc("
319
            SELECT a.owner, SUM(f.size) AS bytes
320
            FROM {artefact} a JOIN {artefact_file_files} f ON a.id = f.artefact
321
            WHERE a.artefacttype IN (" . join(',',  array_map('db_quote', PluginArtefactFile::get_artefact_types())) . ")
322 323 324 325
            AND a.owner IS NOT NULL
            GROUP BY a.owner", array()
        );
        if ($data) {
326
            return array_map(function($a) { return $a->bytes; }, $data);
327 328 329
        }
        return array();
    }
330 331

    public static function recalculate_group_quota() {
332

333 334 335
        $data = get_records_sql_assoc("
            SELECT a.group, SUM(f.size) AS bytes
            FROM {artefact} a JOIN {artefact_file_files} f ON a.id = f.artefact
336
            WHERE a.artefacttype IN (" . join(',',  array_map('db_quote', PluginArtefactFile::get_artefact_types())) . ")
337 338 339 340
            AND a.group IS NOT NULL
            GROUP BY a.group", array()
        );
        if ($data) {
341
            return array_map(function($a) { return $a->bytes; }, $data);
342 343 344
        }
        return array();
    }
345 346 347 348 349 350 351 352 353 354

    public static function progressbar_link($artefacttype) {
        switch ($artefacttype) {
         case 'profileicon':
            return 'artefact/file/profileicons.php';
            break;
         default:
            return 'artefact/file/index.php';
        }
    }
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373

    /**
     * The "file" artefact type should count uploads of all file types.
     * @param object $plugin
     * @return object
     */
    public static function progressbar_metaartefact_count($name) {
        global $USER;
        if ($name == 'file') {
            $meta = new stdClass();
            $meta->artefacttype = $name;
            $count = count_records_select('artefact', "owner=? and artefacttype in ('file','image','archive','video','audio')", array($USER->get('id')));
            $meta->completed = $count;
            return $meta;
        }
        else {
            return false;
        }
    }
374 375 376 377 378 379 380 381 382 383 384

    /**
     * eventlistener to respond to saveartefact, deleteartefact and
     * deleteartefacts.
     * Check if the user just passed the critical amount of his quota with a new
     * artefact or just deleted an artefact and now is below the critical percentage
     *
     * @param type $event
     * @param type $eventdata
     */
    public static function eventlistener_savedeleteartefact($event, $eventdata) {
385
        $userid = $group = null;
386 387 388 389 390
        $owner = null;
        $addsize = 0;

        safe_require('notification', 'internal');

391 392
        $filesize = 0;
        $quotatypes = array('file','audio','video','image','archive','profileicon');
393 394
        if (('saveartefact' === $event) && in_array($eventdata->get('artefacttype'), $quotatypes)) {
            $owner = array($eventdata->get('owner'));
395 396
            $group = $eventdata->get('group');
            $filesize = $eventdata->get('size');
397
        }
398
        else if (('deleteartefact' === $event) && in_array($eventdata->get('artefacttype'), $quotatypes)) {
399
            $owner = array($eventdata->get('owner'));
400 401 402
            $group = $eventdata->get('group');
            // we want to remove the size of the file from the quota check so we make it a negative integer
            $filesize = intval('-' . $eventdata->get('size'));
403 404 405
        }
        else if ('updateuser' === $event) {
            $userid = $eventdata;
406 407 408
            if (is_array($userid)) {
                $userid = reset($userid);
            }
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
        }
        else if (is_array($eventdata)) {
            foreach ($eventdata as $artefactid) {
                if (!is_int($artefactid)) {
                    continue;
                }
                $artefact = artefact_instance_from_id($artefactid);
                $owner = $artefact->get('owner');
                break;
            }
        }

        if (is_array($owner)) {
            $userid = reset($owner);
        }

425 426 427 428 429
        $quotanotifylimit = get_config_plugin('artefact', 'file', 'quotanotifylimit');
        if ($quotanotifylimit <= 0 || $quotanotifylimit >= 100) {
            $quotanotifylimit = 100;
        }

430 431
        if ($userid !== null) {
            $userdata = get_user($userid);
432
            $userdata->quotausedpercent = empty($userdata->quota) ? 0 : (($userdata->quotaused + $filesize ) / $userdata->quota) * 100;
433 434 435
            $overlimit = false;
            if ($quotanotifylimit <= $userdata->quotausedpercent) {
                $overlimit = true;
436 437 438 439
            }

            $notified = get_field('usr_account_preference', 'value', 'field', 'quota_exceeded_notified', 'usr', $userid);

440
            if ($overlimit && '1' !== $notified) {
441
                $notifyadmin = get_config_plugin('artefact', 'file', 'quotanotifyadmin');
442
                ArtefactTypeFile::notify_users_threshold_exceeded(array($userdata), $notifyadmin);
443
            }
444
            else if ($notified && !$overlimit) {
445 446 447
                set_account_preference($userid, 'quota_exceeded_notified', false);
            }
        }
448
        else if ($group !== null) {
449
            $groupdata = get_group_by_id($group, true);
450

451
            $groupdata->quotausedpercent = empty($groupdata->quota) ? 0 : (($groupdata->quotaused + $filesize ) / $groupdata->quota) * 100;
452 453 454 455 456 457 458 459 460
            $overlimit = false;
            if ($quotanotifylimit <= $groupdata->quotausedpercent) {
                $overlimit = true;
            }
            if ($overlimit) {
                require_once(get_config('docroot') . 'artefact/file/lib.php');
                ArtefactTypeFile::notify_groups_threshold_exceeded(array($groupdata));
            }
        }
461
    }
462 463
}

Martyn Smith's avatar
Martyn Smith committed
464
abstract class ArtefactTypeFileBase extends ArtefactType {
465

466 467 468 469
    public function __construct($id = 0, $data = null) {
        parent::__construct($id, $data);

        if (empty($this->id)) {
470 471
            $allowcomments = get_config_plugin('artefact', 'file', 'commentsallowed' . $this->artefacttype);
            $this->allowcomments = !is_null($allowcomments) ? $allowcomments : $this->allowcomments;
472 473 474
        }
    }

Nigel McNie's avatar
Nigel McNie committed
475
    public static function is_singular() {
Penny Leach's avatar
Penny Leach committed
476 477 478
        return false;
    }

479 480 481 482 483 484 485
    /**
     * This function checks if a artefact can be deleted
     */
    public function can_be_deleted() {
        return true;
    }

486
    public static function get_icon($options=null) {
487 488 489 490 491 492 493

    }

    public static function collapse_config() {
        return 'file';
    }

Richard Mansfield's avatar
Richard Mansfield committed
494 495 496 497 498 499
    public function move($newparentid) {
        $this->set('parent', $newparentid);
        $this->commit();
        return true;
    }

500 501 502 503 504 505 506
    /**
     * Return an instance of external filesystem class or False if not configured.
     *
     * @return external_file_system
     */
    final public static function get_external_filesystem_instance() {
        global $CFG;
507

508 509 510 511 512 513 514
        if (is_using_external_filesystem()) {
            $classname = $CFG->externalfilesystem['class'];
            return new $classname();
        }

        return false;
    }
515

Richard Mansfield's avatar
Richard Mansfield committed
516 517
    // Check if something exists in the db with a given title and parent,
    // either in adminfiles or with a specific owner.
Richard Mansfield's avatar
Richard Mansfield committed
518
    public static function file_exists($title, $owner, $folder, $institution=null, $group=null) {
519
        $filetypesql = "('" . join("','", PluginArtefactFile::get_artefact_types()) . "')";
520
        $ownersql = artefact_owner_sql($owner, $group, $institution);
521 522
        return get_field_sql('SELECT a.id FROM {artefact} a
            LEFT OUTER JOIN {artefact_file_files} f ON f.artefact = a.id
523
            WHERE a.title = ?
524
            AND a.' . $ownersql . '
525
            AND a.parent ' . (empty($folder) ? ' IS NULL' : ' = ' . (int)$folder) . '
526
            AND a.artefacttype IN ' . $filetypesql, array($title));
527 528
    }

529 530 531

    // Sort folders before files; then use nat sort order.
    public static function my_files_cmp($a, $b) {
532 533
        return strnatcasecmp((-2 * isset($a->isparent) + ($a->artefacttype != 'folder')) . 'a' . $a->title,
                             (-2 * isset($b->isparent) + ($b->artefacttype != 'folder')) . 'a' . $b->title);
534 535
    }

536 537 538 539 540
    // Sort folders before files in descending order; then use nat sort order descending.
    public static function my_files_cmp_desc($a, $b) {
        return strnatcasecmp((+2 * isset($b->isparent) + ($b->artefacttype == 'folder')) . 'a' . $b->title,
                             (+2 * isset($a->isparent) + ($a->artefacttype == 'folder')) . 'a' . $a->title);
    }
541

542 543 544 545 546 547 548 549 550 551 552
    /**
     * Gets a list of files in one folder
     *
     * @param integer $parentfolderid    Artefact id of the folder
     * @param integer $userid            Id of the owner, if the owner is a user
     * @param integer $group             Id of the owner, if the owner is a group
     * @param string  $institution       Id of the owner, if the owner is a institution
     * @param array   $filters           Filters to apply to the results. An array with keys 'artefacttype', 'filetype',
     *                                   where array values are arrays of artefacttype or mimetype strings.
     * @return array  A list of artefacts
     */
553
    public static function get_my_files_data($parentfolderid, $userid, $group=null, $institution=null, $filters=null) {
554
        global $USER;
Richard Mansfield's avatar
Richard Mansfield committed
555
        $select = '
Richard Mansfield's avatar
Richard Mansfield committed
556
            SELECT
557
                a.id, a.artefacttype, a.mtime, f.size, fi.orientation, a.title, a.description, a.license, a.licensor, a.licensorurl, a.locked, a.allowcomments, u.profileicon AS defaultprofileicon, a.author,
558
                COUNT(DISTINCT c.id) AS childcount, COUNT (DISTINCT aa.artefact) AS attachcount, COUNT(DISTINCT va.view) AS viewcount, COUNT(DISTINCT s.id) AS skincount,
559
                COUNT(DISTINCT api.id) AS profileiconcount, COUNT(DISTINCT fpa.id) AS postcount';
Richard Mansfield's avatar
Richard Mansfield committed
560
        $from = '
561 562
            FROM {artefact} a
                LEFT OUTER JOIN {artefact_file_files} f ON f.artefact = a.id
563
                LEFT OUTER JOIN {artefact_file_image} fi ON fi.artefact = a.id
564 565
                LEFT OUTER JOIN {artefact} c ON c.parent = a.id
                LEFT OUTER JOIN {artefact} api ON api.parent = a.id AND api.artefacttype = \'profileicon\'
566
                LEFT OUTER JOIN {view_artefact} va ON va.artefact = a.id
567
                LEFT OUTER JOIN {artefact_attachment} aa ON aa.attachment = a.id
568
                LEFT OUTER JOIN {skin} s ON (s.bodybgimg = a.id OR s.viewbgimg = a.id)
569
                LEFT OUTER JOIN {interaction_forum_post_attachment} fpa ON fpa.attachment = a.id
570
                LEFT OUTER JOIN {usr} u ON a.id = u.profileicon AND a.owner = u.id';
571 572

        if (!empty($filters['artefacttype'])) {
573 574 575 576
            $artefacttypes = $filters['artefacttype'];
            $artefacttypes[] = 'folder';
        }
        else {
577
            $artefacttypes = PluginArtefactFile::get_artefact_types();
578
        }
Richard Mansfield's avatar
Richard Mansfield committed
579
        $where = "
580 581 582 583 584 585
            WHERE a.artefacttype IN (" . join(',',  array_map('db_quote', $artefacttypes)) . ")";
        if (!empty($filters['filetype']) && is_array($filters['filetype'])) {
            $where .= "
            AND (a.artefacttype = 'folder' OR f.filetype IN (" . join(',',  array_map('db_quote', $filters['filetype'])) . '))';
        }

Richard Mansfield's avatar
Richard Mansfield committed
586
        $groupby = '
587
            GROUP BY
588
                a.id, a.artefacttype, a.mtime, f.size, a.title, a.description, a.license, a.licensor, a.licensorurl, a.locked, a.allowcomments,
589
                u.profileicon, fi.orientation';
Richard Mansfield's avatar
Richard Mansfield committed
590 591

        $phvals = array();
592

Richard Mansfield's avatar
Richard Mansfield committed
593
        if ($institution) {
594 595 596 597
            if ($institution == 'mahara' && !$USER->get('admin')) {
                // If non-admins are browsing site files, only let them see the public folder & its contents
                $publicfolder = ArtefactTypeFolder::admin_public_folder_id();
                $where .= '
598 599 600 601 602 603 604 605 606 607 608 609 610
                AND (a.path = ? OR a.path LIKE ?)';
                $phvals = array("/$publicfolder", "/$publicfolder/%");
            }
            else {
                $from .= '
                    LEFT OUTER JOIN {usr_institution} ui ON ui.institution = a.institution';
                $where .= ' AND a.institution = ? ';
                $phvals[] = $institution;
                // Check if user is an admin in this institution.
                if (!$USER->get('admin')) {
                    $where .= ' AND ui.admin = 1 AND ui.usr = ? ';
                    $phvals[] = $USER->get('id');
                }
611
            }
Richard Mansfield's avatar
Richard Mansfield committed
612 613 614
        }
        else if ($group) {
            $select .= ',
615
                r.can_edit, r.can_view, r.can_republish';
Richard Mansfield's avatar
Richard Mansfield committed
616 617
            $from .= '
                LEFT OUTER JOIN (
618
                    SELECT ar.artefact, ar.can_edit, ar.can_view, ar.can_republish
Richard Mansfield's avatar
Richard Mansfield committed
619 620
                    FROM {artefact_access_role} ar
                    INNER JOIN {group_member} gm ON ar.role = gm.role
621
                    WHERE gm.group = ? AND gm.member = ?
Richard Mansfield's avatar
Richard Mansfield committed
622 623 624 625
                ) r ON r.artefact = a.id';
            $phvals[] = $group;
            $phvals[] = $USER->get('id');
            $where .= '
626
            AND a.group = ? AND (r.can_view = 1 OR a.author = ?)';
Richard Mansfield's avatar
Richard Mansfield committed
627
            $phvals[] = $group;
628 629
            $phvals[] = $USER->get('id');
            $groupby .= ', r.can_edit, r.can_view, r.can_republish, a.author';
Richard Mansfield's avatar
Richard Mansfield committed
630 631 632 633 634 635 636 637 638 639 640
        }
        else {
            $where .= '
            AND a.institution IS NULL AND a.owner = ?';
            $phvals[] = $userid;
        }

        if ($parentfolderid) {
            $where .= '
            AND a.parent = ? ';
            $phvals[] = $parentfolderid;
641 642 643 644 645 646
            $parent = artefact_instance_from_id($parentfolderid);
            $can_view_parent = $USER->can_view_artefact($parent);
            if (!$can_view_parent) {
                return null;
            }
            $can_edit_parent = $USER->can_edit_artefact($parent);
Richard Mansfield's avatar
Richard Mansfield committed
647 648 649 650
        }
        else {
            $where .= '
            AND a.parent IS NULL';
651 652
            $can_edit_parent = true;
            $can_view_parent = true;
Richard Mansfield's avatar
Richard Mansfield committed
653
        }
654

Richard Mansfield's avatar
Richard Mansfield committed
655
        $filedata = get_records_sql_assoc($select . $from . $where . $groupby, $phvals);
656 657 658 659 660
        if (!$filedata) {
            $filedata = array();
        }
        else {
            foreach ($filedata as $item) {
Richard Mansfield's avatar
Richard Mansfield committed
661
                $item->mtime = format_date(strtotime($item->mtime), 'strfdaymonthyearshort');
Richard Mansfield's avatar
Richard Mansfield committed
662
                $item->tags = array();
663
                $item->allowcomments = (bool) $item->allowcomments;
664
                $item->icon = call_static_method(generate_artefact_class_name($item->artefacttype), 'get_icon', array('id' => $item->id));
665 666 667
                if ($item->size) { // Doing this here now for non-js users
                    $item->size = ArtefactTypeFile::short_size($item->size, true);
                }
668
                if ($group) {
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
                  if ( $institution == 'mahara' && $USER->get('admin')) {
                    //site files inside and outside public folder (only admin user)
                    $item->can_edit = 1;
                    $item->can_view = 1;
                    $item->can_republish = 1;
                  }
                  else if ($institution == 'mahara' && ArtefactTypeFolder::admin_public_folder_id() == $parentfolderid) {
                       // site public files, not admin user
                       $item->can_edit = 0;
                       $item->can_view = 1;
                       $item->can_republish = 1;
                  }
                  else if (!empty($item->author) && $item->author == $USER->get('id')) {
                      $item->can_edit = 1;
                      $item->can_view = 1;
                      $item->can_republish = 1;
                  }
                  else {
                      $item->can_edit = $can_edit_parent && $item->can_edit;
                      $item->can_view = $can_view_parent && $item->can_view;
                      $item->can_republish = $can_view_parent && $item->can_republish;
                  }
691
                }
692 693 694 695
                if (!empty($item->author)) {
                    if ($group && $item->author == $USER->get('id')) {
                        $item->can_edit = 1;    // This will show the delete, edit buttons in filelist, but doesn't change the actual permissions in the checkbox
                    }
696 697 698 699 700
                    if ($group || $institution || ($USER->get('id') != $item->author)) {
                        $userobj = new User();
                        $userobj->find_by_id($item->author);
                        $item->uploadedby = display_name($userobj, null, true);
                    }
701
                }
702
                if ($item->artefacttype == 'folder') {
703
                    if ($item->childcount > 0 && defined('FOLDER_SIZE')) {
704
                        $foldersize = get_record_sql("SELECT SUM(aff.size) AS size FROM {artefact} a
705 706
                                                      JOIN {artefact_file_files} aff ON aff.artefact = a.id
                                                      WHERE a.path LIKE ?", array('%/' . $item->id . '/%'));
707
                        $item->foldersize = ArtefactTypeFile::short_size($foldersize->size, true);
708 709 710 711
                    }
                    else {
                        $item->foldersize = ArtefactTypeFile::short_size(0, true);
                    }
712
                }
Richard Mansfield's avatar
Richard Mansfield committed
713
            }
714
            $tagwhere = "'" . join("','", array_keys($filedata)) . "'";
715 716 717 718 719 720 721 722 723 724 725
            $typecast = is_postgres() ? '::varchar' : '';
            $tags = get_records_sql_array("
                SELECT
                    (CASE
                        WHEN t.tag LIKE 'tagid_%' THEN CONCAT(i.displayname, ': ', t2.tag)
                        ELSE t.tag
                    END) AS tag, t.resourceid
                FROM {tag} t
                LEFT JOIN {tag} t2 ON t2.id" . $typecast . " = SUBSTRING(t.tag, 7)
                LEFT JOIN {institution} i ON i.name = t2.ownerid
                WHERE t.resourcetype = 'artefact' AND t.resourceid IN (" . $tagwhere . ")");
Richard Mansfield's avatar
Richard Mansfield committed
726
            if ($tags) {
727 728
                require_once(get_config('docroot') . 'lib/form/elements/tags.php');
                $alltags = get_all_tags_for_user();
Richard Mansfield's avatar
Richard Mansfield committed
729
                foreach ($tags as $t) {
730 731
                    $tagname = remove_prefix($t->tag);
                    $filedata[$t->resourceid]->tags[$tagname] = display_tag($tagname, $alltags['tags']);
Richard Mansfield's avatar
Richard Mansfield committed
732 733
                }
            }
734
            $where = 'artefact IN (' . join(',', array_keys($filedata)) . ')';
Richard Mansfield's avatar
Richard Mansfield committed
735 736 737 738 739 740 741 742 743 744
            if ($group) {  // Fetch permissions for each artefact
                $perms = get_records_select_array('artefact_access_role', $where);
                if ($perms) {
                    foreach ($perms as $perm) {
                        $filedata[$perm->artefact]->permissions[$perm->role] = array(
                            'view' => $perm->can_view,
                            'edit' => $perm->can_edit,
                            'republish' => $perm->can_republish
                        );
                    }
745
                }
746 747 748
            }
        }

Richard Mansfield's avatar
Richard Mansfield committed
749 750
        // Add parent folder to the list
        if (!empty($parentfolderid)) {
751 752 753
            $grandparentid = (int) get_field('artefact', 'parent', 'id', $parentfolderid);
            $filedata[$grandparentid] = (object) array(
                'title'        => get_string('parentfolder', 'artefact.file'),
Richard Mansfield's avatar
Richard Mansfield committed
754 755 756
                'artefacttype' => 'folder',
                'description'  => get_string('parentfolder', 'artefact.file'),
                'isparent'     => true,
757
                'id'           => $grandparentid
Richard Mansfield's avatar
Richard Mansfield committed
758 759 760
            );
        }

761
        uasort($filedata, array("ArtefactTypeFileBase", "my_files_cmp"));
762

763 764
        return $filedata;
    }
765

766 767 768 769

    /**
     * Creates pieforms definition for forms on the my files, group files, etc. pages.
     */
770
    public static function files_form($page='', $group=null, $institution=null, $folder=null, $highlight=null, $edit=null) {
Mike Kelly's avatar
Mike Kelly committed
771 772 773
        global $USER;
        $resizeonuploaduserdefault = $USER->get_account_preference('resizeonuploaduserdefault');

774 775 776 777 778 779 780 781 782 783 784 785
        $folder = param_integer('folder', 0);
        $edit = param_variable('edit', 0);
        if (is_array($edit)) {
            $edit = array_keys($edit);
            $edit = $edit[0];
        }
        $edit = (int) $edit;
        $highlight = null;
        if ($file = param_integer('file', 0)) {
            $highlight = array($file); // todo convert to file1=1&file2=2 etc
        }

786 787 788 789 790 791 792 793 794 795
        // Check whether the user may upload files; either the group needs to
        // be within its edit window (if one is set) or the user needs to be
        // the group admin.
        if (!empty($group)) {
            $editfilesfolders = group_within_edit_window($group);
        }
        else {
            $editfilesfolders = true;
        }

796 797 798 799
        $form = array(
            'name'               => 'files',
            'jsform'             => true,
            'newiframeonsubmit'  => true,
800 801
            'jssuccesscallback'  => 'files_callback',
            'jserrorcallback'    => 'files_callback',
802
            'renderer'           => 'div',
803 804 805
            'plugintype'         => 'artefact',
            'pluginname'         => 'file',
            'configdirs'         => array(get_config('libroot') . 'form/', get_config('docroot') . 'artefact/file/form/'),
806 807
            'group'              => $group,
            'institution'        => $institution,
808 809 810 811 812 813
            'elements'           => array(
                'filebrowser' => array(
                    'type'         => 'filebrowser',
                    'folder'       => $folder,
                    'highlight'    => $highlight,
                    'edit'         => $edit,
814
                    'page'         => $page,
815
                    'config'       => array(
816
                        'upload'          => $editfilesfolders,
817
                        'uploadagreement' => get_config_plugin('artefact', 'file', 'uploadagreement'),
Mike Kelly's avatar
Mike Kelly committed
818 819
                        'resizeonuploaduseroption' => get_config_plugin('artefact', 'file', 'resizeonuploaduseroption'),
                        'resizeonuploaduserdefault' => $resizeonuploaduserdefault,
820 821
                        'createfolder'    => $editfilesfolders,
                        'edit'            => $editfilesfolders,
822
                        'select'          => false,
823
                        'tag'             => true,
824 825 826 827 828 829
                    ),
                ),
            ),
        );
        return $form;
    }
830

831
    public static function files_js() {
832
        return "function files_callback(form, data) { files_filebrowser.callback(form, data); }";
833 834
    }

835
    public static function count_user_files($owner=null, $group=null, $institution=null) {
836
        $filetypes = PluginArtefactFile::get_artefact_types();
837 838 839 840 841 842 843
        foreach ($filetypes as $k => $v) {
            if ($v == 'folder') {
                unset($filetypes[$k]);
            }
        }
        $filetypesql = "('" . join("','", $filetypes) . "')";

844
        $ownersql = artefact_owner_sql($owner, $group, $institution);
845
        return (object) array(
846 847
            'files'   => count_records_select('artefact', "artefacttype IN $filetypesql AND $ownersql", array()),
            'folders' => count_records_select('artefact', "artefacttype = 'folder' AND $ownersql", array())
848 849 850
        );
    }

851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
    public static function artefactchooser_get_file_data($artefact) {
        $artefact->icon = call_static_method(generate_artefact_class_name($artefact->artefacttype), 'get_icon', array('id' => $artefact->id));
        if ($artefact->artefacttype == 'profileicon') {
            $artefact->hovertitle  =  $artefact->note;
            if ($artefact->title) {
                $artefact->hovertitle .= ': ' . $artefact->title;
            }
        }
        else {
            $artefact->hovertitle  =  $artefact->title;
            if ($artefact->description) {
                $artefact->hovertitle .= ': ' . $artefact->description;
            }
        }

866
        $folderdata = self::artefactchooser_folder_data($artefact);
867 868

        if ($artefact->artefacttype == 'profileicon') {
869
            $artefact->description = str_shorten_text($artefact->title, 30);
870 871 872
        }
        else {
            $path = $artefact->parent ? self::get_full_path($artefact->parent, $folderdata->data) : '';
873
            $artefact->description = str_shorten_text($folderdata->ownername . $path . $artefact->title, 30);
874 875 876 877 878
        }

        return $artefact;
    }

879
    public static function artefactchooser_folder_data(&$artefact) {
880 881 882 883 884 885 886 887
        // Grab data about all folders the artefact owner has, so we
        // can make full paths to them, and show the artefact owner if
        // it's a group or institution.
        static $folderdata = array();

        $ownerkey = $artefact->owner . '::' . $artefact->group . '::' . $artefact->institution;
        if (!isset($folderdata[$ownerkey])) {
            $ownersql = artefact_owner_sql($artefact->owner, $artefact->group, $artefact->institution);
888
            $folderdata[$ownerkey] = new stdClass();
889 890 891 892 893 894
            $folderdata[$ownerkey]->data = get_records_select_assoc('artefact', "artefacttype='folder' AND $ownersql", array(), '', 'id, title, parent');
            if ($artefact->group) {
                $folderdata[$ownerkey]->ownername = get_field('group', 'name', 'id', $artefact->group) . ':';
            }
            else if ($artefact->institution) {
                if ($artefact->institution == 'mahara') {
895
                    $folderdata[$ownerkey]->ownername = get_config('sitename') . ':';
896 897 898 899 900 901 902 903 904 905 906 907 908 909
                }
                else {
                    $folderdata[$ownerkey]->ownername = get_field('institution', 'displayname', 'name', $artefact->institution) . ':';
                }
            }
            else {
                $folderdata[$ownerkey]->ownername = '';
            }
        }

        return $folderdata[$ownerkey];
    }

    /**
Aaron Wells's avatar
Aaron Wells committed
910
     * Works out a full path to a folder, given an ID. Implemented this way so
911 912
     * only one query is made.
     */
913
    public static function get_full_path($id, &$folderdata) {
914 915 916 917 918 919 920 921
        $path = '';
        while (!empty($id)) {
            $path = $folderdata[$id]->title . '/' . $path;
            $id = $folderdata[$id]->parent;
        }
        return $path;
    }

922
    public function default_parent_for_copy(&$view, &$template, $artefactstoignore) {
923
        static $folderids;
924

925 926 927 928
        $viewid = $view->get('id');

        if (isset($folderids[$viewid])) {
            return $folderids[$viewid];
929 930 931
        }

        $viewfilesfolder = ArtefactTypeFolder::get_folder_id(get_string('viewfilesdirname', 'view'), get_string('viewfilesdirdesc', 'view'),