/[drupal]/contributions/modules/fileapi/fileapi.module
ViewVC logotype

Contents of /contributions/modules/fileapi/fileapi.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download) (as text)
Fri Jul 25 00:49:04 2008 UTC (16 months ago) by dopry
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +18 -802 lines
File MIME type: text/x-php
cut out some of the fat and get the module relatively happy with being enabled on D6.
1 <?php
2 // $Id: fileapi.module,v 1.3 2008/07/24 17:41:23 dopry Exp $
3
4 /**
5 * @file
6 *
7 * An object oriented File interface for Drupal.
8 * Previous versions of fileAPI have been abandoned.
9 */
10
11 define('FILEAPI_EXISTS_RENAME', 0);
12 define('FILEAPI_EXISTS_REPLACE', 1);
13 define('FILEAPI_EXISTS_ERROR', 2);
14
15
16 /**
17 * Base file class.
18 *
19 * The drupal_file class represents a physical file stored in Drupal's
20 * 'File System Path'. It is important to understand that copy, move,
21 * delete, etc method affect both the the physical file the object
22 * represents and the database record for the file.
23 *
24 * Example Factory Usage:
25 *
26 * $factory = new drupal_file();
27 * $file = $factory->load_id(87);
28 * $another = $file->load_path('images/logo.png');
29 *
30 * $file = drupal_file::load_id(24);
31 *
32 * $yet_another = $file->load('path', 'uploads/myfile.pdf');
33 *
34 *
35 * if ($yet_another->delete()) unset($yet_another);
36 *
37 */
38
39 class fileapi_file {
40
41 // @see drupal_file::load()
42 protected static $files = array();
43
44 public $fid = 0;
45 public $uid = 1;
46 public $filename = '';
47 public $filepath = '';
48 public $filemime = 'application/octet-stream';
49 public $filesize = 0;
50 public $permanent = 0;
51 public $timestamp = 0;
52
53 /**
54 * drupal_file constructor.
55 *
56 * @param object $file (optional) stdClass object to initialize self with.
57 */
58 function __construct($file = false) {
59 if (!$file || !is_object($file)) return;
60 // assign all initialized properties to self.
61 foreach ($file as $key => $value) {
62 $this->$key = $value;
63 }
64 }
65
66 /**
67 * Create a copy a drupal_file.
68 *
69 * @param string $destination (optional) @see file_copy.
70 * @param int $replace (optional) @see file_destination
71 * @return object|bool drupal_file if the copy is successful, or FALSE
72 * @see file_copy()
73 */
74 function copy($destination = 0, $replace = FILE_EXISTS_RENAME) {
75 if ($result = file_copy($this->filepath, $destination, $replace)) {
76 $file = clone $this;
77 $file->fid = null;
78 $file->filename = basename($result);
79 $file->filepath = $result;
80 if ($file->save()) {
81 module_invoke_all('file_copy', $this, $file);
82 return $file;
83 }
84 }
85 return FALSE;
86 }
87
88
89 /**
90 * Delete a file and its database record.
91 *
92 * @param $force
93 * Boolean indicating that the file should be deleted even if
94 * hook_file_references() reports that the file is in use.
95 * @return mixed
96 * TRUE for success, array for reference count, or FALSE in the event
97 * of an error.
98 *
99 * @see hook_file_references()
100 */
101 function delete($force = FALSE) {
102 // If any module returns a value from the reference hook, the
103 // file will not be deleted from Drupal, but file_delete will
104 // return a populated array that tests as TRUE.
105 if (!$force && ($references = module_invoke_all('file_references', $this))) {
106 return $references;
107 }
108
109 // Let other modules clean up on delete.
110 module_invoke_all('file_delete', $this);
111
112 // Make sure the file is deleted before removing its row from the
113 // database, so UIs can still find the file in the database.
114 if (file_delete($this->filepath)) {
115 db_query('DELETE FROM {files} WHERE fid = %d', $this->fid);
116 // remove internally used static cache entries.
117 $this->reset_cache('fid::'. $this->fid);
118 $this->reset_cache('filepath::'. $this->filepath);
119 return TRUE;
120 }
121 return FALSE;
122 }
123
124 /**
125 * Return the first matching file in the files table. This is a simple single
126 * object loader it in combination with the static $files variable allows all
127 * drupal file objects to also act as factories and share the same static cache.
128 *
129 * @param string key (required) database column to use in where condition.
130 * @param int|string value (required) the value of the column to use in the where condition.
131 * @return object|bool A Drupal file object or false if a file was not found.
132 * @see drupal_file::load(), drupal_file::load_path()
133 */
134 function load($column, $value) {
135 // set a cache id based on key and value so we can statically cache
136 // all simple loads.
137 $cid = $column . '::' . $value;
138 if (empty(self::$files[$cid])) {
139 $file = db_fetch_object(db_query('SELECT f.* FROM {files} f WHERE f.%s = %d', $column, $value));
140 $class = __CLASS__;
141 $file = new $class($file);
142 }
143 module_invoke_all('file_load', $file);
144 self::$files[$cid] = $file;
145 // Files are not cloned, because there is in fact only one.
146 return self::$files[$cid];
147 }
148
149 /**
150 * Load a file object from the database by id.
151 *
152 * @param int $id A file id. (required)
153 * @return object|bool A Drupal file object or false if a file was not found.
154 * @see: drupal_file::load()
155 */
156 function load_id($id) {
157 return $this->_load('fid', $id);
158 }
159
160 /**
161 * Load a file object from the database by path.
162 *
163 * @param string $path A path to a file. (required)
164 * @return object|bool A Drupal file object or false if a file was not found.
165 * @see: drupal_file::load()
166 */
167 function load_path($path) {
168 return $this->_load('filepath', $path);
169 }
170
171 /**
172 * Move a drupal_file.
173 *
174 * @param string $destination (optional) @see file_copy.
175 * @param int $replace (optional) @see file_destination
176 * @return object|bool drupal_file if the copy is successful, or FALSE
177 * @see file_copy()
178 */
179 function move($destination = 0, $replace = FILE_EXISTS_RENAME) {
180 if ($result = file_move($this->filepath, $destination, $replace)) {
181 $file = clone $this;
182 $file->filename = basename($result);
183 $file->filepath = $result;
184 if ($file = file_save($file)) {
185 module_invoke_all('file_move', $this, $file);
186 return $file;
187 }
188 }
189 return FALSE;
190 }
191
192 /**
193 * Return a path for a file.
194 *
195 * This function will return a full stream reference to a file,
196 * with special handling for replacing public:// and private://
197 *
198 */
199 function path() {
200 $path = str_replace('private://', $this->path_private, $this->filepath);
201 $path = str_replace('public://', $this->path_public, $this->filepath);
202 return $path;
203 }
204
205 /**
206 * Determine the default public 'files' directory.
207 *
208 * @return string The path to Drupal's public 'files' directory.
209 */
210 function path_public() {
211 return variable_get('file_directory_path', conf_path() . '/files');
212 }
213
214 /**
215 * Determine the default private 'files' directory.
216 *
217 * @return string The path to Drupal's private 'files' directory.
218 */
219 function path_private() {
220 return variable_get('file_directory_private', '../priavte-files');
221 }
222
223
224 /**
225 * Reset the shared static cache.
226 */
227 public function reset_cache($cid = false) {
228 // no cache id, reset the entire cache.
229 if (!$cid) {
230 self::$files = array();
231 }
232 elseif (isset(self::$files[$cid])) {
233 unset(self::$files[$cid]);
234 }
235 }
236
237 /**
238 * Save the current state of a drupal_file in the database.
239 * If the file->fid is empty a new database record will be added.
240 *
241 * @return bool TRUE if save succeeded, FALSE if save failed.
242 */
243 function save() {
244 $this->timestamp = time();
245 $this->filesize = filesize($this->filepath);
246
247 if (empty($this->fid)) {
248 $result = drupal_write_record('files', $this);
249 module_invoke_all('file_insert', $this);
250 }
251 else {
252 $result = drupal_write_record('files', $this, 'fid');
253 module_invoke_all('file_update', $this);
254 }
255 return $result;
256 }
257
258 /**
259 * Mark a file as permanent.
260 *
261 * @return bool
262 */
263 function set_permanent() {
264 if (db_query('UPDATE {files} SET permanent=%d', 1, $this->fid)) {
265 $this->permanent = $status;
266 module_invoke_all('file_set_permanent', $this);
267 return true;
268 }
269 return false;
270 }
271
272 /**
273 * Create a URL to the file.
274 */
275 function url() {
276 // should be delegated to the stream handler.
277 $url = str_replace('private://', url('system/files/', array('absolute' => TRUE)));
278 $url = str_replace('public://', $GLOBALS['base_url'] .'/');
279 return $url;
280 }
281 }
282
283 /**
284 * Uploaded files class.
285 *
286 * This class manages uploaded files.
287 *
288 * Example Usage:
289 *
290 * $file = drupal_file_upload::load('upload');
291 * $file->validate($validators);
292 *
293 * // you will only be able to save the file if validated.
294 * $file->save();
295 *
296 * $file->move('/somewhere/that/is/not/the/tmp/dir');
297 * $file->set_permanent();
298 *
299 *
300 */
301
302 class fileapi_file_upload extends fileapi_file {
303
304 public $source = '';
305 public $errors;
306
307
308 function save() {
309 // validate if not already validated, error is validation didn't pass.
310 if (!$this->validate()) {
311 return false;
312 }
313 // rename the file to its original name.
314
315 parent::save();
316 }
317
318 function validate($validators = array()) {
319 if (!isset($this->errors)) {
320 // Default validation for all uploads.
321 $validators['file_validate_name_length'] = array();
322
323 $this->errors = array();
324 foreach ($validators as $function => $args) {
325 array_unshift($args, $file);
326 $errors = array_merge($errors, call_user_func_array($function, $args));
327 }
328 }
329 return !empty($this->errors);
330 }
331
332 function errors() {
333 return $this->errors;
334 }
335
336 function move($dest) {
337 // validate if not already validated, error is validation didn't pass.
338 if (!$this->fid && !$this->save()) {
339 return false;
340 }
341 // for upload files we changed the filename in the path to a junk string...
342
343 }
344
345 /**
346 * Saves a file upload to a temporary location
347 *
348 * The file will be added to the files table as a temporary file. Temporary
349 * files are periodically cleaned. To make the file permanent file call
350 * it's set_permanent() method.
351 *
352 * @param $source
353 * A string specifying the name of the upload field to save.
354 * @param $validators
355 * An optional, associative array of callback functions used to validate the
356 * file. The keys are function names and the values arrays of callback
357 * parameters which will be passed in after the user and file objects. The
358 * functions should return an array of error messages, an empty array
359 * indicates that the file passed validation. The functions will be called in
360 * the order specified.
361 * @param $destination
362 * A string containing the directory $source should be copied to. If this is
363 * not provided or is not writable, the temporary directory will be used.
364 * @param $replace
365 * A boolean indicating whether an existing file of the same name in the
366 * destination directory should overwritten. A false value will generate a
367 * new, unique filename in the destination directory.
368 * @return
369 * An object containing the file information, or FALSE in the event of an
370 * error.
371 */
372
373 static function save_upload($source) {
374 // check and see if there were any errors.
375 if (drupal_file_upload::upload_error($source)) {
376 return false;
377 }
378
379 // Begin building file object.
380 $file = new stdClass();
381 $file->source = $source;
382 $file->uid = $user->uid;
383 $file->filename = basename($_FILES['files']['name'][$source]);
384 // create a tmp path to use until the file is save to a final location else where.
385 // we just use a random string to defang the file for processing in tmp.
386 $file->filepath = file_destination(uniqid(), file_directory_temp(), FALSE);
387 $file->filemime = $_FILES['files']['type'][$source];
388 $file->filesize = $_FILES['files']['size'][$source];
389
390
391 // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
392 // directory. This overcomes open_basedir restrictions for future file
393 // operations.
394 if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) {
395 form_set_error($source, t('File upload error. Could not move uploaded file.'));
396 watchdog('file api', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->filepath));
397 return false;
398 }
399 return $file;
400 }
401
402 function upload_error($errno) {
403 // If no file was uploaded there is an error. :)
404 if (empty($_FILES['files']['tmp_name'][$source]) || !is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
405 return t('No file uploaded');
406 }
407
408
409 // @see http://php.net/manual/en/features.file-upload.errors.php
410 switch ($_FILES['files']['error'][$source]) {
411 case UPLOAD_ERR_OK:
412 return false;
413
414 case UPLOAD_ERR_INI_SIZE:
415 case UPLOAD_ERR_FORM_SIZE:
416 return t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $source, '%maxsize' => format_size(file_upload_max_size())));
417
418 case UPLOAD_ERR_PARTIAL:
419 case UPLOAD_ERR_NO_FILE:
420 return t('The file %file could not be saved, because the upload did not complete.', array('%file' => $source));
421
422 case UPLOAD_ERR_NO_TMP_DIR:
423 return t('The file %file could not be saved, because the PHP upload_tmp_dir does not exist.', array('%file' => $source));
424
425 case UPLOAD_ERR_CANT_WRITE:
426 return t('The file %file could not be saved, because the file could not be written to the disk.', array('%file' => $source));
427
428 case UPLOAD_ERR_EXTENSION:
429 return t('The file %file could not be saved, because the upload was stopped by a php extension.', array('%file' => $source));
430
431 // Unknown error
432 default:
433 return t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $source));
434 }
435 }
436
437 // Use static load as the entry point to keep the API interface
438 // consistent.
439 function load($source) {
440 if (isset(self::$files[$source])) return self::$files[$source];
441
442 // attempt to save the upload.
443 if ($file = self::save_upload($source)) {
444 $class = __CLASS__;
445 return new $class($file);
446 }
447 }
448
449 }
450
451
452 /**
453 * This class creates files from strings.
454 */
455 class fileapi_file_data extends fileapi_file {
456 function load($data) {
457 $file = stdClass();
458 $file->filepath = tempnam(__CLASS__, file_directory_temp());
459 $file->mime = 'text/plain';
460 if (file_put_contents($file->filepath, $data) === FALSE) {
461 drupal_set_message(t('The file could not be created.'), 'error');
462 return FALSE;
463 }
464 $class = __CLASS__;
465 return new $class($file);
466 }
467 }
468
469 /**
470 * This class imports files from streams.
471 */
472 class fileapi_file_stream extends fileapi_file {
473 function load($url) {
474 if (!isset(self::$files[$url])) {
475 $file = stdClass();
476 $file->filepath = tempnam(__CLASS__, file_directory_temp());
477
478 if ($data = file_get_contents($url) && file_put_contents($data, $file->filepath)) {
479 $class = __CLASS__;
480 self::$files[$url] = new $class($file);
481 }
482 }
483 return self::$files[$url];
484 }
485 }

  ViewVC Help
Powered by ViewVC 1.1.2