1. when download zip file, push down the download in chunks;
[acontent.git] / docs / include / classes / FileUtility.class.php
1 <?php
2 /************************************************************************/
3 /* AContent                                                             */
4 /************************************************************************/
5 /* Copyright (c) 2010                                                   */
6 /* Inclusive Design Institute                                           */
7 /*                                                                      */
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
13 /**
14 * File utility functions 
15 * @access       public
16 * @author       Cindy Qi Li
17 */
18
19 if (!defined('TR_INCLUDE_PATH')) exit;
20
21 class FileUtility {
22
23         /**
24         * Allows the copying of entire directories.
25         * @access  public
26         * @param   string $source               the source directory
27         * @param   string $dest                 the destination directory
28         * @return  boolean                              whether the copy was successful or not
29         * @link    http://www.php.net/copy
30         * @author  www at w8c dot com
31         */
32         public static function copys($source,$dest)
33         {
34                 if (!is_dir($source)) {
35                         return false;
36                 }
37                 if (!is_dir($dest))     {
38                         mkdir($dest);
39                 }
40                 
41                 $h=@dir($source);
42                 while (@($entry=$h->read()) !== false) {
43                         if (($entry == '.') || ($entry == '..')) {
44                                 continue;
45                         }
46         
47                         if (is_dir("$source/$entry") && $dest!=="$source/$entry") {
48                                 copys("$source/$entry", "$dest/$entry");
49                         } else {
50                                 @copy("$source/$entry", "$dest/$entry");
51                         }
52                 }
53                 $h->close();
54                 return true;
55         } 
56         
57         /**
58         * Enables deletion of directory if not empty
59         * @access  public
60         * @param   string $dir          the directory to delete
61         * @return  boolean                      whether the deletion was successful
62         * @author  Joel Kronenberg
63         */
64         public static function clr_dir($dir) {
65                 if(!$opendir = @opendir($dir)) {
66                         return false;
67                 }
68                 
69                 while(($readdir=readdir($opendir)) !== false) {
70                         if (($readdir !== '..') && ($readdir !== '.')) {
71                                 $readdir = trim($readdir);
72         
73                                 clearstatcache(); /* especially needed for Windows machines: */
74         
75                                 if (is_file($dir.'/'.$readdir)) {
76                                         if(!@unlink($dir.'/'.$readdir)) {
77                                                 return false;
78                                         }
79                                 } else if (is_dir($dir.'/'.$readdir)) {
80                                         /* calls itself to clear subdirectories */
81                                         if(!FileUtility::clr_dir($dir.'/'.$readdir)) {
82                                                 return false;
83                                         }
84                                 }
85                         }
86                 } /* end while */
87         
88                 @closedir($opendir);
89                 
90                 if(!@rmdir($dir)) {
91                         return false;
92                 }
93                 return true;
94         }
95         
96         /**
97         * Calculate the size in Bytes of a directory recursively.
98         * @access  public
99         * @param   string $dir          the directory to traverse
100         * @return  int                          the total size in Bytes of the directory
101         * @author  Joel Kronenberg
102         */
103         public static function dirsize($dir) {
104                 if (is_dir($dir)) {
105                         $dh = @opendir($dir);
106                 }
107                 if (!$dh) {
108                         return -1;
109                 }
110                 
111                 $size = 0;
112                 while (($file = readdir($dh)) !== false) {
113             
114                         if ($file != '.' && $file != '..') {
115                                 $path = $dir.$file;
116                                 if (is_dir($path)) {
117                                         $size += FileUtility::dirsize($path.'/');
118                                 } elseif (is_file($path)) {
119                                         $size += filesize($path);
120                                 }
121                         }
122                          
123                 }
124                 closedir($dh);
125                 return $size;
126         }
127         
128         /* prints the <options> out of $cats which is an array of course categories where */
129         /* $cats[parent_cat_id][] = $row */
130         public static function print_course_cats($parent_cat_id, &$cats, $cat_row, $depth=0) {
131                 $my_cats = $cats[$parent_cat_id];
132                 if (!is_array($my_cats)) {
133                         return;
134                 }
135                 foreach ($my_cats as $cat) {
136         
137                         echo '<option value="'.$cat['cat_id'].'"';
138                         if($cat['cat_id'] == $cat_row){
139                                 echo  ' selected="selected"';
140                         }
141                         echo '>';
142                         echo str_pad('', $depth, '-');
143                         echo $cat['cat_name'].'</option>'."\n";
144         
145                         print_course_cats($cat['cat_id'], $cats,  $cat_row, $depth+1);
146                 }
147         }
148         
149         // returns the most appropriate representation of Bytes in MB, KB, or B
150         public static function get_human_size($num_bytes) {
151                 $abs_num_bytes = abs($num_bytes);
152         
153                 if ($abs_num_bytes >= TR_KBYTE_SIZE * TR_KBYTE_SIZE) {
154                         return round(FileUtility::bytes_to_megabytes($num_bytes), 2) .' '. _AT('mb');
155                 } else if ($abs_num_bytes >= TR_KBYTE_SIZE) {
156                         return round(FileUtility::bytes_to_kilobytes($num_bytes), 2) .' '._AT('kb') ;
157                 }
158                 // else:
159         
160                 return $num_bytes . ' '._AT('bt');
161         }
162         
163         /**
164         * Returns the MB representation of inputed bytes
165         * @access  public
166         * @param   int $num_bytes       the input bytes to convert
167         * @return  int                          MB representation of $num_bytes
168         * @author  Heidi Hazelton
169         */
170         public static function bytes_to_megabytes($num_bytes) {
171                 return $num_bytes/TR_KBYTE_SIZE/TR_KBYTE_SIZE;
172         }
173         
174         /**
175         * Returns the Byte representation of inputed MB
176         * @access  public
177         * @param   int $num_bytes       the input MB to convert
178         * @return  int                          the Bytes representation of $num_bytes
179         * @author  Heidi Hazelton
180         */
181         public static function megabytes_to_bytes($num_bytes) {
182                 return $num_bytes*TR_KBYTE_SIZE*TR_KBYTE_SIZE;
183         }
184         
185         /**
186         * Returns the KB representation of inputed Bytes
187         * @access  public
188         * @param   int $num_bytes       the input Bytes to convert
189         * @return  int                          the KB representation of $num_bytes
190         * @author  Heidi Hazelton
191         */
192         public static function bytes_to_kilobytes($num_bytes) {
193                 return $num_bytes/TR_KBYTE_SIZE;
194         }
195         
196         /**
197         * Returns the Bytes representation of inputed KBytes
198         * @access  public
199         * @param   int $num_bytes       the input KBytes to convert
200         * @return  int                          the KBytes representation of $num_bytes
201         * @author  Heidi Hazelton
202         */
203         public static function kilobytes_to_bytes($num_bytes) {
204                 return $num_bytes*TR_KBYTE_SIZE;
205         }
206         
207         /**
208         * Outputs the directories associated with a course in the form of <option> elements.
209         * @access public
210         * @param  string $cur_dir  the current directory to include in the options.
211         * @author Norma Thompson
212         */
213         public static function output_dirs($current_path,$cur_dir,$indent) {
214                 // open the cur_dir
215                 if ($dir = opendir($current_path.$cur_dir)) {
216         
217                         // recursively call output_dirs() for all directories in this directory
218                         while (false !== ($file = readdir($dir)) ) {
219         
220                                 //if the name is not a directory 
221                                 if( ($file == '.') || ($file == '..') ) {
222                                         continue;
223                                 }
224         
225                                 // if it is a directory call function
226                                 if(is_dir($current_path.$cur_dir.$file)) {
227                                         $ldir = explode('/',$cur_dir.$file);
228                                         $count = count($ldir);
229                                         $label = $ldir[$count-1];
230                                         
231                                         $dir_option .= '<option value="'.$cur_dir.$file.'/" >'.$indent.$label.'</option>';
232         
233                                         $dir_option .= output_dirs($current_path,$cur_dir.$file.'/',$indent.'--');
234                                 }
235                                 
236                         } // end while  
237                         
238                         closedir($dir); 
239                 }
240                 return $dir_option;
241         }
242         
243         public static function display_tree($current_path, $cur_dir, $pathext, $ignore_children = false) {
244                 // open the cur_dir
245                 static $list_array;
246                 if (!isset($list_array)) {
247                         $list_array = explode(',', $_GET['list']);
248                 }
249                 if ($dir = opendir($current_path . $cur_dir)) {
250         
251                         // recursively call output_dirs() for all directories in this directory
252                         while (false !== ($file = readdir($dir)) ) {
253         
254                                 //if the name is not a directory 
255                                 if( ($file == '.') || ($file == '..') ) {
256                                         continue;
257                                 }
258         
259                                 // if it is a directory call function
260                                 if (is_dir($current_path . $cur_dir . $file)) {
261         
262                                         //$ldir = explode('/',$cur_dir.$file);
263                                         //$count = count($ldir);
264                                         //$label = $ldir[$count-1];
265         
266                                         $check = '';
267                                         $here  = '';
268                                         if ($cur_dir . $file == substr($pathext, 0, -1)) {
269                                                 $check = 'checked="checked"';
270                                                 $here = ' ' . _AT('current_location');
271                                         } else if (($cur_dir == $pathext) && in_array($file, $list_array)) {
272                                                 $ignore_children = true;
273                                         }
274         
275                                         if ($ignore_children) {
276                                                 $check = 'disabled="disabled"';
277                                                 $class = ' disabled';
278                                         }
279         
280                                         $dir_option .= '<ul><li class="folders'.$class.'">';
281                                         $dir_option .= '<label><input type="radio" name="dir_name" value="'.$cur_dir.$file.'" '.$check. '/>'. $file . $here. '</label>';
282                                         $dir_option .= ''.FileUtility::display_tree($current_path,$cur_dir.$file.'/', $pathext, $ignore_children).'';
283                                         $dir_option .= '</li></ul>';
284         
285                                         if (($cur_dir == $pathext) && in_array($file, $list_array)) {
286                                                 $ignore_children = false;
287                                                 $class = '';
288                                         }
289                                 }
290         
291                                 
292                         } // end while  
293                         
294                         closedir($dir); 
295                 }
296                 return $dir_option;
297         }
298         
299         public static function course_realpath($file) {
300                 global $_course_id;
301                 
302                 if (!$_course_id) return FALSE;
303                 
304                 $course_path = TR_CONTENT_DIR . $_course_id;
305                 
306                 $path_parts = pathinfo($file);
307                 
308                 $dir_name   = $path_parts['dirname'];
309                 $file_name  = $path_parts['basename'];
310                 $ext_name   = $path_parts['extension'];
311         
312                 //1. determine the real path of the file/directory
313                 if (is_dir($dir_name.DIRECTORY_SEPARATOR.$file_name) && $ext_name == '') {
314                         //if directory ws passed through (moving file to diff directory)
315                         $real = realpath($dir_name . DIRECTORY_SEPARATOR . $file_name);
316                 } else {
317                         //if file was passed through or no existant direcotry was passed through (rename/creating dir)
318                         $real = realpath($dir_name);
319                 }
320         
321                 //2. and whether its in the course content directory
322                 if (substr($real, 0, strlen($course_path)) != $course_path) {
323                         return FALSE;
324                 }
325         
326                 //3. check if extensions are legal
327         
328                 //4. Otherwise return the real path of the file
329                 return $real;
330         }
331         
332         /**
333         * Returns canonicalized absolute pathname to a file/directory in the content directory
334         * @access public
335         * @param  string $file the relative path to the file or directory
336         * @return  string       the full path to the file or directory, FALSE if it does not exist in our content directory.
337         */
338         public static function course_realpath_NEW_VERSION($file) {
339                 if (!$_SESSION['course_id']) {
340                         return FALSE;
341                 }
342                 
343                 $course_path = TR_CONTENT_DIR . $_SESSION['course_id'];
344         
345                 // determine the real path of the file/directory
346                 $real = realpath($course_path . DIRECTORY_SEPARATOR . $file);
347                 
348                 if (!file_exists($real)) {
349                         // the file or directory does not exist
350                         return FALSE;
351         
352                 } else if (substr($real, 0, strlen($course_path)) != $course_path) {
353                         // the file or directory is not in the content path
354                         return FALSE;
355         
356                 } else {
357                         // Otherwise return the real path of the file
358                         return $real;
359                 }
360         }
361         
362         /**
363         * Returns the name of the readme file in the given directory
364         * @access public
365         * @param  string $dir_name the name of the directory
366         * @return  string       the name of the readme file
367         */
368         public static function get_readme($dir)
369         {
370                 if (!is_dir($dir)) return '';
371                 
372                 $dh = opendir($dir);
373                 
374                 while (($file = readdir($dh)) !== false) {
375                         if (stristr($file, 'readme') && substr($file, -4) <> '.php')
376                                 return $file;
377                 }
378                 
379                 closedir($dh);
380                 return '';
381         }
382
383         /**
384          * This function is mainly used to download big zip files that are created by ATutor.
385          * For example, download common cartridge, content package, backup.
386          * Note that the file is only in binary, as fopen($filename, "rb"). 
387          * Don't use it to read open text/html files
388          * 
389          * When downloading large files exceeding 1M, instead of use readfile() to read
390          * the whole file into memory once at a time, push down 1M at a time. This way can
391          * download whatever size of the file regardless of php memory limit.
392          */  
393         function readfile_in_chunks($filename) {
394                 $filesize = intval(sprintf("%u", filesize($filename)));
395                 $chunk_size = 1024 * 1024;
396                 
397                 if ($filesize > $chunk_size) {
398                         $fp = fopen($filename, "rb");
399                         while (!feof($fp))
400                         {
401                                 echo fread($fp, $chunk_size);
402                                 ob_flush();
403                                 flush();
404                         }
405                         fclose($fp);
406                 } else {
407                         readfile($filename);
408                 }
409         }
410 }
411 ?>