remove old readme
[atutor.git] / docs / mods / _standard / file_storage / file_storage.inc.php
1 <?php
2 /************************************************************************/
3 /* ATutor                                                                                                                               */
4 /************************************************************************/
5 /* Copyright (c) 2002-2010                                              */
6 /* Inclusive Design Institute                                           */
7 /* http://atutor.ca                                                     */
8 /* This program is free software. You can redistribute it and/or        */
9 /* modify it under the terms of the GNU General Public License          */
10 /* as published by the Free Software Foundation.                        */
11 /************************************************************************/
12 // $Id$
13
14 /**
15  * Additional constants are found in /include/lib/constants.inc.php (for the work spaces)
16  *
17  * The File Storage was designed to allow for unlimited workspace, although only four right now.
18  *
19  * All the functions are namespaced with fs_ (for File Storage not File System).
20  *
21  * These two variables are used throughout.
22  * $owner_type is used to define the workspace type. Also appears as $ot in _GET.
23  * $owner_id is the ID of the particular workspace type. Also appears as $oid in _GET.
24  * 
25  **/
26
27 if (!defined('AT_INCLUDE_PATH')) { exit; }
28
29 define('WORKSPACE_AUTH_NONE',  0);
30 define('WORKSPACE_AUTH_READ',  1);
31 define('WORKSPACE_AUTH_WRITE', 2); 
32 define('WORKSPACE_AUTH_RW',    3); // to save time
33
34 /**
35  * given an owner_type and owner_id
36  * returns false if user cannot read or write to this workspace
37  * returns WORKSPACE_AUTH_READ if the user can read
38  * returns WORKSPACE_AUTH_WRITE if the user can write
39  */
40 function fs_authenticate($owner_type, $owner_id) {
41         if (($owner_type == WORKSPACE_PERSONAL) && $_SESSION['member_id'] && $_SESSION['enroll'] && ($owner_id == $_SESSION['member_id'])) {
42
43                 return WORKSPACE_AUTH_RW;
44
45         } else if ($owner_type == WORKSPACE_ASSIGNMENT) {
46                 if (authenticate(AT_PRIV_ASSIGNMENTS, AT_PRIV_RETURN))
47                 { 
48                         // instructors have read only access to assignments
49                         return WORKSPACE_AUTH_READ;
50                 }
51                 else
52                 { 
53                         // students have read access to their own assignments
54                         global $db;
55                         $sql = "SELECT COUNT(*) cnt FROM ".TABLE_PREFIX."files
56                                  WHERE owner_id =".$owner_id."
57                            AND owner_type= ".WORKSPACE_ASSIGNMENT."
58                            AND member_id = ".$_SESSION['member_id'];
59                         $result = mysql_query($sql, $db);
60                         $row = mysql_fetch_assoc($result);
61                         
62                         if ($row['cnt'] > 0) RETURN WORKSPACE_AUTH_READ;
63                 }
64         
65         } else if ($owner_type == WORKSPACE_GROUP) {
66                 if (isset($_SESSION['groups'][$owner_id])) {
67                         global $db;
68                         $sql = "SELECT * FROM ".TABLE_PREFIX."file_storage_groups WHERE group_id=$owner_id";
69                         $result = mysql_query($sql, $db);
70                         if (mysql_fetch_assoc($result)) {
71                                 return WORKSPACE_AUTH_RW;
72                         }
73                 }
74
75         } else if ($owner_type == WORKSPACE_COURSE) {
76                 if (($owner_id == $_SESSION['course_id']) && authenticate(AT_PRIV_FILE_STORAGE, AT_PRIV_RETURN)) {
77                         return WORKSPACE_AUTH_RW;
78                 } else if ($owner_id == $_SESSION['course_id']) {
79                         return WORKSPACE_AUTH_READ;
80                 }
81         }
82         /* else if ($owner_type == WORKSPACE_SYSTEM) {
83                 if (admin_authenticate(AT_ADMIN_PRIV_FILE_STORAGE, TRUE)) {
84                         return WORKSPACE_AUTH_RW;
85                 } // else
86                 return WORKSPACE_AUTH_READ; // everyone can read the System File Space
87         } */
88
89         return WORKSPACE_AUTH_NONE;
90 }
91
92 /**
93  * returns the localised name of the specified workspace
94  */
95 function fs_get_workspace($owner_type, $owner_id) {
96         if ($owner_type == WORKSPACE_PERSONAL) {
97                 return _AT('my_files');
98
99         } else if ($owner_type == WORKSPACE_COURSE) {
100                 return _AT('course_files');
101
102         } else if ($owner_type == WORKSPACE_GROUP) {
103                 global $db;
104                 $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$owner_id";
105                 $result = mysql_query($sql, $db);
106                 $row    = mysql_fetch_assoc($result);
107                 return $row['title'];
108
109         } else if ($owner_type == WORKSPACE_ASSIGNMENT) {
110                 $row    = fs_get_assignment($owner_id);
111                 return ($row ? $row['title'] : false);
112         } /*
113                 else if ($owner_type == WORKSPACE_SYSTEM) {
114                 return _AT('system_files');
115      }
116         */
117 }
118
119 /**
120  * returns the assignment row specified by $assignment_id
121  * false if not found.
122  */
123 function fs_get_assignment($assignment_id) {
124         global $db;
125         $sql = "SELECT assignment_id, title, assign_to, date_due, date_cutoff, UNIX_TIMESTAMP(date_cutoff) AS u_date_cutoff, multi_submit FROM ".TABLE_PREFIX."assignments WHERE assignment_id=$assignment_id AND course_id=$_SESSION[course_id]";
126         $result = mysql_query($sql, $db);
127         $row    = mysql_fetch_assoc($result);
128         return $row;
129 }
130
131 /**
132  * retrieve folder(s) specified by $folder_id
133  * $folder_id the ID of the single folder, or array of IDs
134  * if $folder_id is an array then returns an array of folder rows
135  * if $folder_id is an int then returns the single row array
136  *
137  * This function does not authenticate the $folder_id for the assignment.
138  *
139  * Note: This function checks if the $owner_type is an Assignment.
140  *
141  */
142 function fs_get_folder_by_id($folder_id, $owner_type, $owner_id) {
143         global $db;
144
145         $rows = array();
146
147         if ($owner_type == WORKSPACE_ASSIGNMENT) {
148                 // get the folder row from the assignments table
149
150                 $sql = "SELECT assign_to FROM ".TABLE_PREFIX."assignments WHERE assignment_id=$owner_id AND course_id=$_SESSION[course_id]";
151                 $result = mysql_query($sql, $db);
152                 $row  = mysql_fetch_assoc($result);
153                 if ($row['assign_to']) {
154                         $sql = "SELECT title FROM ".TABLE_PREFIX."groups WHERE group_id=$folder_id";
155                         $result = mysql_query($sql, $db);
156                         $row = mysql_fetch_assoc($result);
157
158                         $rows = array('title' => $row['title'], 'folder_id' => $folder_id);
159                 } else {
160                         $rows = array('title' => get_display_name($folder_id), 'folder_id' => $folder_id);
161                 }
162         } else {
163                 if (is_array($folder_id)) {
164                         $folder_id_list = implode(',', $folder_id);
165
166                         $sql = "SELECT folder_id, title, parent_folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id IN ($folder_id_list) AND owner_type=$owner_type AND owner_id=$owner_id ORDER BY title";
167                         $result = mysql_query($sql, $db);
168                         while ($row = mysql_fetch_assoc($result)) {
169                                 $rows[] = $row;
170                         }
171
172                 } else {
173                         $sql = "SELECT folder_id, title, parent_folder_id FROM ".TABLE_PREFIX."folders WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id";
174                         $result = mysql_query($sql, $db);
175                         $rows = mysql_fetch_assoc($result);
176                 }
177         }
178         return $rows;
179 }
180
181 /**
182  * retrieve folder(s) specified by $parent_folder_id
183  *
184  * Note: This function checks if the $owner_type is an Assignment.
185  *
186  */
187 function fs_get_folder_by_pid($parent_folder_id, $owner_type, $owner_id) {
188         global $db;
189
190         $rows = array();
191         if ($owner_type == WORKSPACE_ASSIGNMENT) {
192                 // get the folder row from the assignments table
193                 // does not currently support sub-folders for assignments
194                 if ($parent_folder_id == 0 && authenticate(AT_PRIV_ASSIGNMENTS, AT_PRIV_RETURN)) {
195                         $sql = "SELECT assign_to FROM ".TABLE_PREFIX."assignments WHERE assignment_id=$owner_id AND course_id=$_SESSION[course_id]";
196                         $result = mysql_query($sql, $db);
197                         $row  = mysql_fetch_assoc($result);
198                         if ($row['assign_to']) {
199                                 $sql = "SELECT G.group_id AS folder_id, G.title FROM ".TABLE_PREFIX."groups G INNER JOIN ".TABLE_PREFIX."file_storage_groups FS USING (group_id) WHERE G.type_id=$row[assign_to] ORDER BY G.title";
200                         } else {
201                                 global $system_courses;
202
203                                 $sql = "SELECT E.member_id AS folder_id, M.login AS title FROM ".TABLE_PREFIX."course_enrollment E INNER JOIN ".TABLE_PREFIX."members M USING (member_id) WHERE E.course_id=$_SESSION[course_id] AND E.approved='y' AND E.privileges & ".AT_PRIV_GROUPS." = 0 AND E.member_id<>{$system_courses[$_SESSION[course_id]][member_id]} ORDER BY M.login";
204                         }
205                         $result = mysql_query($sql, $db);
206
207                         while ($row = mysql_fetch_assoc($result)) {
208                                 $rows[] = $row;
209                         }
210                 }
211         } else {
212                 $sql = "SELECT folder_id, title FROM ".TABLE_PREFIX."folders WHERE parent_folder_id=$parent_folder_id AND owner_type=$owner_type AND owner_id=$owner_id ORDER BY title";
213                 $result = mysql_query($sql, $db);
214                 while ($row = mysql_fetch_assoc($result)) {
215                         $rows[] = $row; 
216                 }
217         }
218         return $rows;
219 }
220
221 /**
222  * outputs the folders as a  list.
223  *
224  * $current_folder_id the current folder id, used for pre-selecting the radio button
225  * $parent_folder_id the folder id to display children of
226  * $folders the array of folders returned from get_folders()
227  * $disable whether or not the radio button is available
228  */
229 function fs_print_folders($current_folder_id, $parent_folder_id, &$folders, $disable = FALSE) {
230         if (!isset($folders[$parent_folder_id])) {
231                 return;
232         }
233
234         echo '<ul>';
235         foreach ($folders[$parent_folder_id] as $folder_id => $folder_info) {
236                 echo '<li class="folders">';
237                 
238                 echo '<input type="radio" name="new_folder" value="'.$folder_id.'" id="f'.$folder_id.'"';
239                 if ($_GET['folders'] && in_array($folder_id, $_GET['folders'])) {
240                         $disable = TRUE;
241                 }
242                 if ($folder_id == $current_folder_id) {
243                         echo ' checked="checked"';
244                 }
245                 if ($disable) {
246                         echo ' disabled="disabled"';
247                 }
248                 echo '/><label for="f'.$folder_id.'">'.htmlspecialchars($folder_info['title']);
249                 if ($folder_id == $current_folder_id) {
250                         echo ' '._AT('current_location');
251                 }
252                 echo '</label>';
253                 
254                 fs_print_folders($current_folder_id, $folder_id, $folders, $disable);
255                 if ($_GET['folders'] && in_array($folder_id, $_GET['folders'])) {
256                         $disable = FALSE;
257                 }
258                 echo '</li>';
259         }
260         echo '</ul>';
261 }
262
263 /**
264  * returns an array of all the revisions for the given file_id
265  *
266  * $file_id ID of a file in a revision sequence. can be any revision, does not have to be the latest.
267  * This function is recursive and uses fs_get_revisions_down_recursive() and fs_get_revisions_recurisve() below.
268  */
269 function fs_get_revisions($file_id, $owner_type, $owner_id) {
270         global $db;
271
272         $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE file_id=$file_id AND owner_type=$owner_type AND owner_id=$owner_id";
273         $result = mysql_query($sql, $db);
274         if ($row = mysql_fetch_assoc($result)) {
275                 return array_merge(array_reverse(fs_get_revisions_down_recursive($row['parent_file_id'])), array($row), fs_get_revisions_recursive($file_id));
276         }
277         return array();
278 }
279
280 /**
281  * recursively retrieves all the revisions of the file.
282  * recurses DOWN the revisions path.
283  * PRIVATE! use fs_get_revisions() above.
284  */
285 function fs_get_revisions_down_recursive($file_id) {
286         global $db;
287
288         if ($file_id == 0) {
289                 return array();
290         }
291
292         $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE file_id=$file_id";
293         $result = mysql_query($sql, $db);
294         $row = mysql_fetch_assoc($result);
295
296         if (!$row) {
297                 return array();
298         } else if (!$row['parent_file_id']) {
299                 return array($row);
300         }
301
302         return array_merge(array($row), fs_get_revisions_down_recursive($row['parent_file_id']));
303 }
304
305 /**
306  * recursively retrieves all the revisions of the file.
307  * recurses UP the revisions path.
308  * PRIVATE! use fs_get_revisions() above.
309  */
310 function fs_get_revisions_recursive($file_id) {
311         global $db;
312
313         if ($file_id == 0) {
314                 return array();
315         }
316
317         $sql = "SELECT * FROM ".TABLE_PREFIX."files WHERE parent_file_id=$file_id";
318         $result = mysql_query($sql, $db);
319         $row = mysql_fetch_assoc($result);
320
321         if (!$row) {
322                 return array();
323         }
324
325         return array_merge(array($row), fs_get_revisions_recursive($row['file_id']));
326 }
327
328 /**
329  * returns the full path based on $file_id with trailing slash.
330  *
331  * Ex. if file_id is 2345 and WORKSPACE_PATH_DEPTH is set to 3 then
332  * the path returned will be WORKSPACE_FILE_PATH.'5/4/3/'
333  *
334  * If the path does not exist within the WORKSPACE_FILE_PATH then attempts
335  * to create it.
336  */
337 function fs_get_file_path($file_id) {
338         $end_part = substr($file_id, -WORKSPACE_PATH_DEPTH);
339         $path = WORKSPACE_FILE_PATH;
340         $dirs = max(-WORKSPACE_PATH_DEPTH, -strlen($file_id));
341         $id_threshold = pow(10,WORKSPACE_PATH_DEPTH); // only check for the dir before reaching this value.
342     for ($i = -1; $i >= $dirs; $i--) {
343                 $path .= substr($file_id, $i, 1) . DIRECTORY_SEPARATOR;
344                 if ($file_id <= $id_threshold) {
345                         if (!is_dir($path)) {
346                                 @mkdir($path);
347                         }
348                 }
349         }
350
351         return $path;
352 }
353
354 /**
355  * delete a given file, its revisions, and comments.
356  *
357  * $file_id the ID of the file to delete. can be any ID within a revision sequence.
358  */
359 function fs_delete_file($file_id, $owner_type, $owner_id) {
360         global $db;
361         $revisions = fs_get_revisions($file_id, $owner_type, $owner_id);
362         foreach ($revisions as $file) {
363                 $sql = "DELETE FROM ".TABLE_PREFIX."files WHERE file_id=$file[file_id] AND owner_type=$owner_type AND owner_id=$owner_id";
364                 mysql_query($sql, $db);
365
366                 if (mysql_affected_rows($db) == 1) {
367                         $sql = "DELETE FROM ".TABLE_PREFIX."files_comments WHERE file_id=$file[file_id]";
368                         mysql_query($sql, $db);
369
370                         $path = fs_get_file_path($file['file_id']);
371                         if (file_exists($path . $file['file_id'])) {
372                                 @unlink($path . $file['file_id']);
373                         }
374                 }
375         }
376 }
377
378 /**
379  * returns only the extension part of the specified file name
380  *
381  * $file_name the full name of the file.
382  */
383 function fs_get_file_extension($file_name) {
384         $ext = pathinfo($file_name);
385         return $ext['extension'];
386 }
387
388 /**
389  * returns the image name (w/o the ".gif" ending) of the icon to use
390  * for the given file name.
391  * if no icon is specified (by mime.inc.php) then returns "generic"
392  */
393 function fs_get_file_type_icon($file_name) {
394         global $mime;
395         if (!isset($mime)) {
396                 require(AT_INCLUDE_PATH.'lib/mime.inc.php');
397         }
398         $ext = fs_get_file_extension($file_name);
399
400         if (isset($mime[$ext]) && $mime[$ext][1]) {
401                 return $mime[$ext][1];
402         }
403         return 'generic';
404 }
405
406 /**
407  * deletes the folder, its sub-folders and associated files.
408  *
409  * $folder_id the ID of the folder to delete, recursively, with content.
410  */
411 function fs_delete_folder($folder_id, $owner_type, $owner_id) {
412         if (!$folder_id) { return; }
413
414         global $db;
415
416         $rows = fs_get_folder_by_pid($folder_id, $owner_type, $owner_id);
417         foreach ($rows as $row) {
418                 fs_delete_folder($row['folder_id'], $owner_type, $owner_id);
419         }
420
421         $sql = "DELETE FROM ".TABLE_PREFIX."folders WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id";
422         mysql_query($sql, $db);
423
424         // delete this file's folders (we only select the latest versions because
425         // the delete_file() function takes care of the revisions for us
426         $sql = "SELECT file_id FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND parent_file_id=0 AND owner_type=$owner_type AND owner_id=$owner_id";
427         $result = mysql_query($sql, $db);
428         while ($row = mysql_fetch_assoc($result)) {
429                 fs_delete_file($row['file_id'], $owner_type, $owner_id);
430         }
431 }
432
433 /**
434  * archives a folder into a specified zip handler.
435  *
436  * $folder_id the ID of the folder to archive recursively, with content.
437  * $zipfile reference to the zipFile object.
438  * $path the absolute path to the current folder.
439  */
440 function fs_download_folder($folder_id, &$zipfile, $owner_type, $owner_id, $path = '') {
441         global $db;
442
443         $parent_row = fs_get_folder_by_id($folder_id, $owner_type, $owner_id);
444         //$sql = "SELECT title FROM ".TABLE_PREFIX."folders WHERE folder_id=$folder_id AND owner_type=$owner_type AND owner_id=$owner_id";
445         //$result = mysql_query($sql, $db);
446         if ($parent_row) {
447                 $zipfile->create_dir($path . $parent_row['title']);
448         }
449
450         $sql = "SELECT file_id, file_name, UNIX_TIMESTAMP(date) AS date FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND parent_file_id=0 AND owner_type=$owner_type AND owner_id=$owner_id";
451         $result = mysql_query($sql, $db);
452         while ($row = mysql_fetch_assoc($result)) {
453                 $file_path = fs_get_file_path($row['file_id']) . $row['file_id'];
454
455                 $zipfile->add_file(file_get_contents($file_path), $path . $parent_row['title'] .'/' . $row['file_name'], $row['date']);
456         }
457
458         $rows = fs_get_folder_by_pid($folder_id, $owner_type, $owner_id);
459         foreach ($rows as $row) {
460                 fs_download_folder($row['folder_id'], $zipfile, $owner_type, $owner_id, $path . $parent_row['title'] . '/');
461         }
462 }
463
464 /**
465  * returns the full path to the current folder
466  *
467  * $folder_id the current folder
468  * $workspace the owner_type of this folder
469  * $owner_id the ID of the owner.
470  */
471 function fs_get_folder_path($folder_id, $owner_type, $owner_id) {
472         $folder_path = fs_get_folder_path_recursive($folder_id, $owner_type, $owner_id);
473
474         return array_reverse($folder_path);
475 }
476
477 /**
478  * recursively return the path to the current folder
479  * PRIVATE! do not call directly, use get_folder_path() above.
480  */
481 function fs_get_folder_path_recursive($folder_id, $owner_type, $owner_id) {
482         global $db;
483
484         if ($folder_id == 0) {
485                 return array();
486         }
487
488         $row = fs_get_folder_by_id($folder_id, $owner_type, $owner_id);
489
490         return array_merge(array($row), fs_get_folder_path_recursive($row['parent_folder_id'], $owner_type, $owner_id));
491 }
492
493 /**
494  * deletes all the files, folders, comments, revisions, etc.. in the specified workspace.
495  */
496 function fs_delete_workspace($owner_type, $owner_id) {
497         global $db;
498
499         $sql = "SELECT folder_id, owner_type, owner_id FROM ".TABLE_PREFIX."folders WHERE owner_type=$owner_type AND owner_id=$owner_id AND parent_folder_id=0";
500         $result = mysql_query($sql, $db);
501         while ($row = mysql_fetch_assoc($result)) {
502                 fs_delete_folder($row['folder_id'], $row['owner_type'], $row['owner_id']);
503         }
504
505         $sql = "SELECT file_id, owner_type, owner_id FROM ".TABLE_PREFIX."files WHERE owner_type=$owner_type AND owner_id=$owner_id";
506         $result = mysql_query($sql, $db);
507         while ($row = mysql_fetch_assoc($result)) {
508                 fs_delete_file($row['file_id'], $row['owner_type'], $row['owner_id']);
509         }
510 }
511
512 /**
513  * copies a file to another workspace.
514  * currently only used for submitting assignments.
515  **/
516 function fs_copy_file($file_id, $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $dest_folder_id) {
517         global $db;
518
519         $sql = "SELECT file_name, file_size, description FROM ".TABLE_PREFIX."files WHERE file_id=$file_id AND owner_type=$src_owner_type AND owner_id=$src_owner_id";
520         $result = mysql_query($sql, $db);
521         if (!$row = mysql_fetch_assoc($result)) {
522                 return false;
523         }
524         $sql = "INSERT INTO ".TABLE_PREFIX."files VALUES (NULL, $dest_owner_type, $dest_owner_id, $_SESSION[member_id], $dest_folder_id, 0, NOW(), 0, 0, '$row[file_name]', '$row[file_size]', '$row[description]')";
525         $result = mysql_query($sql, $db);
526
527         $id = mysql_insert_id($db);
528
529         $src_file  = fs_get_file_path($file_id) . $file_id;
530         $dest_file = fs_get_file_path($id) . $id;
531         copy($src_file, $dest_file);
532 }
533
534 /**
535  * used with usort() to sort the revisions array returned from fs_get_revisions()
536  * $col is a valid array key to sort by
537  * $order is either 'asc' or 'desc'
538  */
539 function fs_revisions_sort_compare($a, $b) {
540         global $col, $order;
541
542         if ($order == 'asc') {
543                 return strcasecmp($a[$col], $b[$col]);
544         }
545         return strcasecmp($b[$col], $a[$col]);
546 }
547
548 /**
549  * copies a directory to another workspace.
550  * not currently used anywhere.
551  */
552 /***
553 function fs_copy_folder($folder_id, $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $dest_parent_folder_id) {
554         global $db;
555
556         $folder = fs_get_folder_by_id($folder_id, $src_owner_type, $src_owner_id);
557         if (!$folder) {
558                 return false;
559         }
560
561         $sql = "INSERT INTO ".TABLE_PREFIX."folders VALUES (0, $dest_parent_folder_id, $dest_owner_type, $dest_owner_id, '$folder[title]')";
562         $result = mysql_query($sql, $db);
563         $id = mysql_insert_id($db);
564
565         $sql = "SELECT file_id FROM ".TABLE_PREFIX."files WHERE folder_id=$folder_id AND owner_type=$src_owner_type AND owner_id=$src_owner_id";
566         $result = mysql_query($sql, $db);
567         while ($row = mysql_fetch_assoc($result)) {
568                 fs_copy_file($row['file_id'], $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $id);
569         }
570
571         $folders = fs_get_folder_by_pid($folder_id, $src_owner_type, $src_owner_id);
572         foreach ($folders as $folder) {
573                 fs_copy_folder($folder['folder_id'], $src_owner_type, $src_owner_id, $dest_owner_type, $dest_owner_id, $id);
574         }
575 }
576 */
577 ?>