1<?php 2 3/* Copyright (c) 1998-2012 ILIAS open source, Extended GPL, see docs/LICENSE */ 4 5include_once("./Services/Object/classes/class.ilObjectAccess.php"); 6require_once('./Services/WebAccessChecker/interfaces/interface.ilWACCheckingClass.php'); 7 8/** 9 * Access class for file objects. 10 * 11 * @author Alex Killing <alex.killing@gmx.de> 12 * @author Stefan Born <stefan.born@phzh.ch> 13 * @version $Id$ 14 * 15 * @ingroup ModulesFile 16 */ 17class ilObjFileAccess extends ilObjectAccess implements ilWACCheckingClass 18{ 19 20 /** 21 * @param $obj_id 22 * 23 * @return bool 24 */ 25 protected function checkAccessToObjectId($obj_id) 26 { 27 global $DIC; 28 $ilAccess = $DIC['ilAccess']; 29 /** 30 * @var $ilAccess ilAccessHandler 31 */ 32 foreach (ilObject::_getAllReferences($obj_id) as $ref_id) { 33 if ($ilAccess->checkAccess('read', '', $ref_id)) { 34 return true; 35 } 36 } 37 38 return false; 39 } 40 41 42 /** 43 * @param \ilWACPath $ilWACPath 44 * 45 * @return bool 46 */ 47 public function canBeDelivered(ilWACPath $ilWACPath) 48 { 49 switch ($ilWACPath->getSecurePathId()) { 50 case 'previews': 51 $re = '/\/previews\/[\d\/]{0,}\/preview_([\d]{0,})\//uU'; 52 break; 53 } 54 preg_match($re, $ilWACPath->getPath(), $matches); 55 56 return $this->checkAccessToObjectId($matches[1]); 57 } 58 59 60 61 // BEGIN WebDAV cache inline file extensions 62 63 64 /** 65 * Contains an array of extensions separated by space. 66 * Since this array is needed for every file object displayed on a 67 * repository page, we only create it once, and cache it here. 68 * 69 * @see function _isFileInline 70 */ 71 protected static $_inlineFileExtensionsArray; 72 // END WebDAV cache inline file extensions 73 74 protected static $preload_list_gui_data; // [array] 75 76 77 /** 78 * get commands 79 * 80 * this method returns an array of all possible commands/permission combinations 81 * 82 * example: 83 * $commands = array 84 * ( 85 * array("permission" => "read", "cmd" => "view", "lang_var" => "show"), 86 * array("permission" => "write", "cmd" => "edit", "lang_var" => "edit"), 87 * ); 88 */ 89 public static function _getCommands() 90 { 91 $commands = array(); 92 $commands[] = array( 93 "permission" => "read", 94 "cmd" => "sendfile", 95 "lang_var" => "download", 96 "default" => true, 97 ); 98 $commands[] = array( 99 "permission" => "write", 100 "cmd" => "versions", 101 "lang_var" => "versions", 102 ); 103 $commands[] = array( 104 "permission" => "write", 105 "cmd" => "edit", 106 "lang_var" => "settings", 107 ); 108 109 return $commands; 110 } 111 112 113 /** 114 * check whether goto script will succeed 115 */ 116 public static function _checkGoto($a_target) 117 { 118 global $DIC; 119 $ilAccess = $DIC['ilAccess']; 120 121 $t_arr = explode("_", $a_target); 122 123 // personal workspace context: do not force normal login 124 if (isset($t_arr[2]) && $t_arr[2] == "wsp") { 125 include_once "Services/PersonalWorkspace/classes/class.ilSharedResourceGUI.php"; 126 127 return ilSharedResourceGUI::hasAccess($t_arr[1]); 128 } 129 130 if ($t_arr[0] != "file" || ((int) $t_arr[1]) <= 0) { 131 return false; 132 } 133 134 if ($ilAccess->checkAccess("visible", "", $t_arr[1]) 135 || $ilAccess->checkAccess("read", "", $t_arr[1]) 136 ) { 137 return true; 138 } 139 140 return false; 141 } 142 143 144 /** 145 * looks up the file_data for the file object with the specified object id 146 * as an associative array. 147 */ 148 public static function _lookupFileData($a_id) 149 { 150 global $DIC; 151 $ilDB = $DIC['ilDB']; 152 153 $q = "SELECT * FROM file_data WHERE file_id = " . $ilDB->quote($a_id, 'integer'); 154 $r = $ilDB->query($q); 155 $row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC); 156 157 return $row; 158 } 159 160 161 /** 162 * lookup version 163 */ 164 public static function _lookupVersion($a_id) 165 { 166 global $DIC; 167 $ilDB = $DIC['ilDB']; 168 169 $q = "SELECT version FROM file_data WHERE file_id = " . $ilDB->quote($a_id, 'integer'); 170 $r = $ilDB->query($q); 171 $row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT); 172 173 $striped = ilUtil::stripSlashes($row->version); 174 175 return $striped > 0 ? $striped : 1; 176 } 177 178 179 /** 180 * Quickly looks up the file size from the database and returns the 181 * number of bytes. 182 */ 183 public static function _lookupFileSize($a_id) 184 { 185 global $DIC; 186 $ilDB = $DIC['ilDB']; 187 188 $q = "SELECT file_size FROM file_data WHERE file_id = " . $ilDB->quote($a_id, 'integer'); 189 $r = $ilDB->query($q); 190 $row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT); 191 192 $size = $row->file_size; 193 194 return $size; 195 } 196 197 198 /** 199 * Looks up the file size by retrieving it from the filesystem. 200 * This function runs much slower than _lookupFileSize()! Use this 201 * function only, to update the data in the database. For example, if 202 * the file size in the database has become inconsistent for some reason. 203 */ 204 public static function _lookupFileSizeFromFilesystem($a_id) 205 { 206 global $DIC; 207 $ilDB = $DIC['ilDB']; 208 209 $q = "SELECT * FROM file_data WHERE file_id = " . $ilDB->quote($a_id, 'integer'); 210 $r = $ilDB->query($q); 211 $row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT); 212 213 require_once('Modules/File/classes/class.ilFSStorageFile.php'); 214 $fss = new ilFSStorageFile($a_id); 215 $file = $fss->getAbsolutePath() . '/' . $row->file_name; 216 217 if (@!is_file($file)) { 218 $version_subdir = "/" . sprintf("%03d", ilObjFileAccess::_lookupVersion($a_id)); 219 $file = $fss->getAbsolutePath() . '/' . $version_subdir . '/' . $row->file_name; 220 } 221 222 if (is_file($file)) { 223 $size = filesize($file); 224 } else { 225 $size = 0; 226 } 227 228 return $size; 229 } 230 231 232 /** 233 * lookup suffix 234 */ 235 public static function _lookupSuffix($a_id) 236 { 237 include_once('Modules/File/classes/class.ilFSStorageFile.php'); 238 239 global $DIC; 240 $ilDB = $DIC['ilDB']; 241 242 // BEGIN WebDAV: Filename suffix is determined by file title 243 $q = "SELECT * FROM object_data WHERE obj_id = " . $ilDB->quote($a_id, 'integer'); 244 $r = $ilDB->query($q); 245 $row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT); 246 require_once 'Modules/File/classes/class.ilObjFile.php'; 247 248 return self::_getFileExtension($row->title); 249 // END WebDAV: Filename suffix is determined by file title 250 } 251 252 253 /** 254 * Returns the number of bytes used on the harddisk by the file object 255 * with the specified object id. 256 * 257 * @param int object id of a file object. 258 */ 259 public static function _lookupDiskUsage($a_id) 260 { 261 include_once('Modules/File/classes/class.ilFSStorageFile.php'); 262 $fileStorage = new ilFSStorageFile($a_id); 263 $dir = $fileStorage->getAbsolutePath(); 264 265 return ilUtil::dirsize($dir); 266 } 267 268 // BEGIN WebDAV: Get file extension, determine if file is inline, guess file type. 269 270 271 /** 272 * Returns true, if the specified file shall be displayed inline in the browser. 273 */ 274 public static function _isFileInline($a_file_name) 275 { 276 if (self::$_inlineFileExtensionsArray 277 === null 278 ) { // the === makes a huge difference, if the array is empty 279 require_once 'Services/Administration/classes/class.ilSetting.php'; 280 $settings = new ilSetting('file_access'); 281 self::$_inlineFileExtensionsArray = preg_split('/ /', $settings->get('inline_file_extensions'), -1, PREG_SPLIT_NO_EMPTY); 282 } 283 $extension = self::_getFileExtension($a_file_name); 284 285 return in_array($extension, self::$_inlineFileExtensionsArray); 286 } 287 288 289 /** 290 * Gets the file extension of the specified file name. 291 * The file name extension is converted to lower case before it is returned. 292 * 293 * For example, for the file name "HELLO.MP3", this function returns "mp3". 294 * 295 * A file name extension can have multiple parts. For the file name 296 * "hello.tar.gz", this function returns "gz". 297 * 298 * 299 * @param string $a_file_name The file name 300 */ 301 public static function _getFileExtension($a_file_name) 302 { 303 if (preg_match('/\.([a-z0-9]+)\z/i', $a_file_name, $matches) == 1) { 304 return strtolower($matches[1]); 305 } else { 306 return ''; 307 } 308 } 309 310 311 /** 312 * Returns true, if a file with the specified name, is usually hidden from 313 * the user. 314 * 315 * - Filenames starting with '.' are hidden Unix files 316 * - Filenames ending with '~' are temporary Unix files 317 * - Filenames starting with '~$' are temporary Windows files 318 * - The file "Thumbs.db" is a hidden Windows file 319 */ 320 public static function _isFileHidden($a_file_name) 321 { 322 return substr($a_file_name, 0, 1) == '.' || substr($a_file_name, -1, 1) == '~' 323 || substr($a_file_name, 0, 2) == '~$' 324 || $a_file_name == 'Thumbs.db'; 325 } 326 // END WebDAV: Get file extension, determine if file is inline, guess file type. 327 328 329 /** 330 * Appends the text " - Copy" to a filename in the language of 331 * the current user. 332 * 333 * If the provided $nth_copy parameter is greater than 1, then 334 * is appended in round brackets. If $nth_copy parameter is null, then 335 * the function determines the copy number on its own. 336 * 337 * If this function detects, that the filename already ends with " - Copy", 338 * or with "- Copy ($nth_copy), it only appends the number of the copy to 339 * the filename. 340 * 341 * This function retains the extension of the filename. 342 * 343 * Examples: 344 * - Calling ilObjFileAccess::_appendCopyToTitle('Hello.txt', 1) 345 * returns: "Hello - Copy.txt". 346 * 347 * - Calling ilObjFileAccess::_appendCopyToTitle('Hello.txt', 2) 348 * returns: "Hello - Copy (2).txt". 349 * 350 * - Calling ilObjFileAccess::_appendCopyToTitle('Hello - Copy (3).txt', 2) 351 * returns: "Hello - Copy (2).txt". 352 * 353 * - Calling ilObjFileAccess::_appendCopyToTitle('Hello - Copy (3).txt', null) 354 * returns: "Hello - Copy (4).txt". 355 */ 356 public static function _appendNumberOfCopyToFilename($a_file_name, $nth_copy = null, $a_handle_extension = false) 357 { 358 global $DIC; 359 $lng = $DIC['lng']; 360 361 $filenameWithoutExtension = $a_file_name; 362 363 $extension = null; 364 if ($a_handle_extension) { 365 // Get the extension and the filename without the extension 366 $extension = ilObjFileAccess::_getFileExtension($a_file_name); 367 if (strlen($extension) > 0) { 368 $extension = '.' . $extension; 369 $filenameWithoutExtension = substr($a_file_name, 0, -strlen($extension)); 370 } 371 } 372 373 // create a regular expression from the language text copy_n_of_suffix, so that 374 // we can match it against $filenameWithoutExtension, and retrieve the number of the copy. 375 // for example, if copy_n_of_suffix is 'Copy (%1s)', this creates the regular 376 // expression '/ Copy \\([0-9]+)\\)$/'. 377 $nthCopyRegex = preg_replace('/([\^$.\[\]|()?*+{}])/', '\\\\${1}', ' ' 378 . $lng->txt('copy_n_of_suffix')); 379 $nthCopyRegex = '/' . preg_replace('/%1\\\\\$s/', '([0-9]+)', $nthCopyRegex) . '$/'; 380 381 // Get the filename without any previously added number of copy. 382 // Determine the number of copy, if it has not been specified. 383 if (preg_match($nthCopyRegex, $filenameWithoutExtension, $matches)) { 384 // this is going to be at least the third copy of the filename 385 $filenameWithoutCopy = substr($filenameWithoutExtension, 0, -strlen($matches[0])); 386 if ($nth_copy == null) { 387 $nth_copy = $matches[1] + 1; 388 } 389 } else { 390 if (substr($filenameWithoutExtension, -strlen(' ' . $lng->txt('copy_of_suffix'))) 391 == ' ' . $lng->txt('copy_of_suffix') 392 ) { 393 // this is going to be the second copy of the filename 394 $filenameWithoutCopy = substr($filenameWithoutExtension, 0, -strlen(' ' 395 . $lng->txt('copy_of_suffix'))); 396 if ($nth_copy == null) { 397 $nth_copy = 2; 398 } 399 } else { 400 // this is going to be the first copy of the filename 401 $filenameWithoutCopy = $filenameWithoutExtension; 402 if ($nth_copy == null) { 403 $nth_copy = 1; 404 } 405 } 406 } 407 408 // Construct the new filename 409 if ($nth_copy > 1) { 410 // this is at least the second copy of the filename, append " - Copy ($nth_copy)" 411 $newFilename = $filenameWithoutCopy . sprintf(' ' 412 . $lng->txt('copy_n_of_suffix'), $nth_copy) 413 . $extension; 414 } else { 415 // this is the first copy of the filename, append " - Copy" 416 $newFilename = $filenameWithoutCopy . ' ' . $lng->txt('copy_of_suffix') . $extension; 417 } 418 419 return $newFilename; 420 } 421 422 423 /** 424 * Gets the permanent download link for the file. 425 * 426 * @param int $ref_id 427 * 428 * @return string 429 */ 430 public static function _getPermanentDownloadLink($ref_id) 431 { 432 return ilLink::_getStaticLink($ref_id, "file", true, "_download"); 433 } 434 435 436 /** 437 * @param array $a_obj_ids 438 * @param int[] $a_ref_ids 439 */ 440 public static function _preloadData($a_obj_ids, $a_ref_ids) 441 { 442 global $DIC; 443 444 self::$preload_list_gui_data = array(); 445 446 $set = $DIC->database()->query("SELECT obj_id,max(hdate) latest" . " FROM history" 447 . " WHERE obj_type = " . $DIC->database()->quote("file", "text") . " AND " 448 . $DIC->database()->in("obj_id", $a_obj_ids, "", "integer") . " GROUP BY obj_id"); 449 while ($row = $DIC->database()->fetchAssoc($set)) { 450 self::$preload_list_gui_data[$row["obj_id"]]["date"] = $row["latest"]; 451 } 452 453 $set = $DIC->database()->query("SELECT file_size, version, file_id, page_count" . " FROM file_data" . " WHERE " 454 . $DIC->database()->in("file_id", $a_obj_ids, "", "integer")); 455 while ($row = $DIC->database()->fetchAssoc($set)) { 456 self::$preload_list_gui_data[$row["file_id"]]["size"] = $row["file_size"]; 457 self::$preload_list_gui_data[$row["file_id"]]["version"] = $row["version"]; 458 self::$preload_list_gui_data[$row["file_id"]]["page_count"] = $row["page_count"]; 459 } 460 } 461 462 463 /** 464 * @param $a_obj_id 465 * 466 * @return array 467 */ 468 public static function getListGUIData($a_obj_id) 469 { 470 if (isset(self::$preload_list_gui_data[$a_obj_id])) { 471 return self::$preload_list_gui_data[$a_obj_id]; 472 } 473 } 474} 475