CWIS Developer Documentation
File.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: File.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2010-2017 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
13 class File extends Item
14 {
15 
16  # ---- PUBLIC INTERFACE --------------------------------------------------
17 
18  # status codes (set by constructor and returned by File::Status())
19  const FILESTAT_OK = 0;
20  const FILESTAT_COPYERROR = 1;
25 
34  public static function Create($SourceFile, $DesiredFileName = NULL)
35  {
36  # check that file exists
37  if (!file_exists($SourceFile))
38  {
39  return self::FILESTAT_DOESNOTEXIST;
40  }
41 
42  # check that file is readable
43  if (!is_readable($SourceFile))
44  {
45  return self::FILESTAT_UNREADABLE;
46  }
47 
48  # check that file is not zero length
49  $FileSize = filesize($SourceFile);
50  if (!$FileSize)
51  {
52  return self::FILESTAT_ZEROLENGTH;
53  }
54 
55  # generate secret string (used to protect from unauthorized download)
56  srand((double)microtime() * 1000000);
57  $SecretString = sprintf("%04X", rand(1, 30000));
58 
59  # get next file ID by adding file to database
60  $DB = new Database();
61  $DB->Query("INSERT INTO Files (SecretString) VALUES ('".$SecretString."')");
62  $FileId = $DB->LastInsertId();
63 
64  # build name for stored file
65  $BaseFileName = ($DesiredFileName === NULL)
66  ? basename($SourceFile) : basename($DesiredFileName);
67  $StoredFile = sprintf(self::GetStorageDirectory()."/%06d-%s-%s",
68  $FileId, $SecretString, $BaseFileName);
69 
70  # attempt to copy file to storage
71  $Result = copy($SourceFile, $StoredFile);
72 
73  # if copy attempt failed
74  if ($Result === FALSE)
75  {
76  # remove file from database
77  $DB->Query("DELETE FROM Files WHERE FileId = ".$FileId);
78 
79  # report error to caller
80  return self::FILESTAT_COPYERROR;
81  }
82 
83  # attempt to get file type
84  $FileType = self::DetermineFileType($SourceFile);
85 
86  # save file info in database
87  $DB->Query("UPDATE Files SET"
88  ." FileName = '".addslashes($BaseFileName)."',"
89  ." FileType = '".addslashes($FileType)."',"
90  ." FileLength = '".addslashes($FileSize)."'"
91  ." WHERE FileId = ".$FileId);
92 
93  # instantiate new object and return it to caller
94  return new File($FileId);
95  }
96 
102  public function CreateCopy()
103  {
104  $Copy = self::Create($this->GetNameOfStoredFile(), $this->Name());
105  if (!$Copy instanceof self)
106  {
107  throw new Exception("Copy failed with error ".$Copy);
108  }
109  $Copy->ResourceId($this->ResourceId());
110  $Copy->FieldId($this->FieldId());
111  return $Copy;
112  }
113 
118  public function GetLength()
119  {
120  return $this->ValueCache["FileLength"];
121  }
122 
127  public function GetType()
128  {
129  return $this->ValueCache["FileType"];
130  }
131 
137  public function Comment($NewValue = DB_NOVALUE)
138  {
139  return $this->UpdateValue("FileComment", $NewValue);
140  }
141 
147  public function FieldId($NewValue = DB_NOVALUE)
148  {
149  return $this->UpdateValue("FieldId", $NewValue);
150  }
151 
157  public function ResourceId($NewValue = DB_NOVALUE)
158  {
159  return $this->UpdateValue("ResourceId", $NewValue);
160  }
161 
166  public function GetMimeType()
167  {
168  return strlen($this->GetType())
169  ? $this->GetType() : "application/octet-stream";
170  }
171 
177  public function GetLink()
178  {
179  # if CleanURLs are enabled, use the redirect that includes
180  # the file name so that browsers don't use index.php as the name
181  # for the downloaded file
182  if ($GLOBALS["G_PluginManager"]->PluginEnabled("CleanURLs"))
183  {
184  return "downloads/".$this->Id."/".rawurlencode($this->Name());
185  }
186 
187  # otherwise use the download portal
188  else
189  {
190  return "index.php?P=DownloadFile&Id=".$this->Id;
191  }
192  }
193 
198  public function Destroy()
199  {
200  # delete file
201  $FileName = $this->GetNameOfStoredFile();
202  if (file_exists($FileName))
203  {
204  unlink($FileName);
205  }
206 
207  # call parent method
208  parent::Destroy();
209  }
210 
216  public function Delete()
217  {
218  $this->Destroy();
219  }
220 
225  public function GetNameOfStoredFile()
226  {
227  # for each possible storage location
228  foreach (self::$StorageLocations as $Dir)
229  {
230  # build file name for that location
231  $FileName = sprintf($Dir."/%06d-%s-%s",
232  $this->Id, $this->ValueCache["SecretString"], $this->Name());
233 
234  # if file can be found in that location
235  if (file_exists($FileName))
236  {
237  # return file name to caller
238  return $FileName;
239  }
240  }
241 
242  # build file name for default (most preferred) location
243  $FileName = sprintf(self::GetStorageDirectory()."/%06d-%s-%s",
244  $this->Id, $this->ValueCache["SecretString"], $this->Name());
245 
246  # return file name to caller
247  return $FileName;
248  }
249 
254  public static function GetStorageDirectory()
255  {
256  # for each possible storage location
257  foreach (self::$StorageLocations as $Dir)
258  {
259  # if location exists and is writeable
260  if (is_dir($Dir) && is_writeable($Dir))
261  {
262  # return location to caller
263  return $Dir;
264  }
265  }
266 
267  # return default (most preferred) location to caller
268  return self::$StorageLocations[0];
269  }
270 
271 
272  # ---- PRIVATE INTERFACE -------------------------------------------------
273 
275  static private $StorageLocations = array(
276  "local/data/files",
277  "FileStorage",
278  );
279 
286  protected static function DetermineFileType($FileName)
287  {
288  $FileType = "";
289  if (function_exists("mime_content_type"))
290  {
291  $FileType = mime_content_type($FileName);
292  }
293  # Although mime_content_type is baked into PHP5 and PHP7, it is still
294  # part of extensions in some versions of PHP 5.x.
295  elseif (function_exists("finfo_open"))
296  {
297  $FInfoHandle = finfo_open(FILEINFO_MIME);
298  if ($FInfoHandle)
299  {
300  $Result = finfo_file($FInfoHandle, $FileName);
301  finfo_close($FInfoHandle);
302  if ($Result)
303  {
304  $FileType = $FInfoMime;
305  }
306  }
307  }
308 
309  # handle Office XML formats
310  # These are recognized by PHP as zip files (because they are), but
311  # IE (and maybe other things?) need a special-snowflake MIME type to
312  # handle them properly.
313  # For a list of the required types, see
314  # https://technet.microsoft.com/en-us/library/ee309278(office.12).aspx
315  if ($FileType == "application/zip; charset=binary")
316  {
317  $MsftPrefix = "application/vnd.openxmlformats-officedocument";
318 
319  $FileExt = strtolower(pathinfo($FileName, PATHINFO_EXTENSION));
320 
321  switch ($FileExt)
322  {
323  case "docx":
324  $FileType = $MstfPrefix.".wordprocessingml.document";
325  break;
326 
327  case "xlsx":
328  $FileType = $MsftPrefix.".spreadsheetml.sheet";
329  break;
330 
331  case "pptx":
332  $FileType = $MsftPrefix.".presentationml.slideshow";
333  break;
334 
335  default:
336  # do nothing
337  }
338  }
339 
340  return $FileType;
341  }
342 }
const FILESTAT_ZEROLENGTH
Definition: File.php:22
GetNameOfStoredFile()
Returns the relative link to the stored file.
Definition: File.php:225
Name($NewValue=DB_NOVALUE)
Get/set name of item.
Definition: Item.php:103
const FILESTAT_UNREADABLE
Definition: File.php:24
const FILESTAT_COPYERROR
Definition: File.php:20
GetType()
Gets the file&#39;s type.
Definition: File.php:127
Id()
Get item ID.
Definition: Item.php:81
const FILESTAT_DOESNOTEXIST
Definition: File.php:23
UpdateValue($ColumnName, $NewValue=DB_NOVALUE)
Convenience function to supply parameters to Database::UpdateValue().
Definition: Item.php:284
SQL database abstraction object with smart query caching.
Definition: Database.php:22
Destroy()
Deletes the file and removes its entry from the database.
Definition: File.php:198
static Create($SourceFile, $DesiredFileName=NULL)
Create a new File object using an existing file.
Definition: File.php:34
FieldId($NewValue=DB_NOVALUE)
Gets or sets the field ID of the File.
Definition: File.php:147
static DetermineFileType($FileName)
Get MIME type for specified file, if possible.
Definition: File.php:286
$DB
Definition: Item.php:210
GetLink()
Returns the relative download link to download the file.
Definition: File.php:177
const FILESTAT_PARAMERROR
Definition: File.php:21
Delete()
Deprecated method to delete file and remove entry from database.
Definition: File.php:216
GetMimeType()
Gets the MIME type of the file.
Definition: File.php:166
Common base class for persistent items store in database.
Definition: Item.php:13
const DB_NOVALUE
Definition: Database.php:1738
CreateCopy()
Create copy of File object.
Definition: File.php:102
GetLength()
Gets the length of the file.
Definition: File.php:118
static GetStorageDirectory()
Get file storage directory.
Definition: File.php:254
ResourceId($NewValue=DB_NOVALUE)
Gets or sets the resource ID of the File.
Definition: File.php:157
const FILESTAT_OK
Definition: File.php:19
Class representing a stored (usually uploaded) file.
Definition: File.php:13
Comment($NewValue=DB_NOVALUE)
Gets or sets the comment on the file.
Definition: File.php:137