/** * Sets a column to be a file upload column * * Configuring a column to be a file upload column means that whenever * fActiveRecord::populate() is called for an fActiveRecord object, any * appropriately named file uploads (via `$_FILES`) will be moved into * the directory for this column. * * Setting the column to a file path will cause the specified file to * be copied into the directory for this column. * * @param mixed $class The class name or instance of the class * @param string $column The column to set as a file upload column * @param fDirectory|string $directory The directory to upload/move to * @return void */ public static function configureFileUploadColumn($class, $column, $directory) { $class = fORM::getClass($class); $table = fORM::tablize($class); $schema = fORMSchema::retrieve($class); $data_type = $schema->getColumnInfo($table, $column, 'type'); $valid_data_types = array('varchar', 'char', 'text'); if (!in_array($data_type, $valid_data_types)) { throw new fProgrammerException('The column specified, %1$s, is a %2$s column. Must be one of %3$s to be set as a file upload column.', $column, $data_type, join(', ', $valid_data_types)); } if (!is_object($directory)) { $directory = new fDirectory($directory); } if (!$directory->isWritable()) { throw new fEnvironmentException('The file upload directory, %s, is not writable', $directory->getPath()); } $camelized_column = fGrammar::camelize($column, TRUE); fORM::registerActiveRecordMethod($class, 'upload' . $camelized_column, self::upload); fORM::registerActiveRecordMethod($class, 'set' . $camelized_column, self::set); fORM::registerActiveRecordMethod($class, 'encode' . $camelized_column, self::encode); fORM::registerActiveRecordMethod($class, 'prepare' . $camelized_column, self::prepare); fORM::registerReflectCallback($class, self::reflect); fORM::registerInspectCallback($class, $column, self::inspect); fORM::registerReplicateCallback($class, $column, self::replicate); fORM::registerObjectifyCallback($class, $column, self::objectify); $only_once_hooks = array('post-begin::delete()' => self::begin, 'pre-commit::delete()' => self::delete, 'post-commit::delete()' => self::commit, 'post-rollback::delete()' => self::rollback, 'post::populate()' => self::populate, 'post-begin::store()' => self::begin, 'post-validate::store()' => self::moveFromTemp, 'pre-commit::store()' => self::deleteOld, 'post-commit::store()' => self::commit, 'post-rollback::store()' => self::rollback, 'post::validate()' => self::validate); foreach ($only_once_hooks as $hook => $callback) { if (!fORM::checkHookCallback($class, $hook, $callback)) { fORM::registerHookCallback($class, $hook, $callback); } } if (empty(self::$file_upload_columns[$class])) { self::$file_upload_columns[$class] = array(); } self::$file_upload_columns[$class][$column] = $directory; }
/** * Moves an uploaded file from the temp directory to a permanent location * * @throws fValidationException When `$directory` is somehow invalid or ::validate() thows an exception * * @param string|fDirectory $directory The directory to upload the file to * @param string $field The file upload field to get the file from * @param mixed $index If the field was an array file upload field, upload the file corresponding to this index * @return fFile|NULL An fFile (or fImage) object, or `NULL` if no file was uploaded */ public function move($directory, $field, $index = NULL) { if (!is_object($directory)) { $directory = new fDirectory($directory); } if (!$directory->isWritable()) { throw new fProgrammerException('The directory specified, %s, is not writable', $directory->getPath()); } if (!self::check($field)) { throw new fProgrammerException('The field specified, %s, does not appear to be a file upload field', $field); } $file_array = $this->extractFileUploadArray($field, $index); $error = $this->validateField($file_array); if ($error) { throw new fValidationException($error); } // This will only ever be true if the file is optional if ($file_array['name'] == '' || $file_array['tmp_name'] == '' || $file_array['size'] == 0) { return NULL; } $file_name = fFilesystem::makeURLSafe($file_array['name']); $file_name = $directory->getPath() . $file_name; if (!$this->enable_overwrite) { $file_name = fFilesystem::makeUniqueName($file_name); } if (!move_uploaded_file($file_array['tmp_name'], $file_name)) { throw new fEnvironmentException('There was an error moving the uploaded file'); } if (!chmod($file_name, 0644)) { throw new fEnvironmentException('Unable to change permissions on the uploaded file'); } return fFilesystem::createObject($file_name); }
/** * Sets the path to store session files in * * This method should always be called with a non-standard directory * whenever ::setLength() is called to ensure that another site on the * server does not garbage collect the session files for this site. * * Standard session directories usually include `/tmp` and `/var/tmp`. * * @param string|fDirectory $directory The directory to store session files in * @return void */ public static function setPath($directory) { if (self::$open || isset($_SESSION)) { throw new fProgrammerException('%1$s must be called before any of %2$s, %3$s, %4$s, %5$s, %6$s, %7$s or %8$s', __CLASS__ . '::setPath()', __CLASS__ . '::add()', __CLASS__ . '::clear()', __CLASS__ . '::enablePersistence()', __CLASS__ . '::get()', __CLASS__ . '::open()', __CLASS__ . '::set()', 'session_start()'); } if (!$directory instanceof fDirectory) { $directory = new fDirectory($directory); } if (!$directory->isWritable()) { throw new fEnvironmentException('The directory specified, %s, is not writable', $directory->getPath()); } session_save_path($directory->getPath()); }
/** * Renames the current directory * * This operation will NOT be performed until the filesystem transaction * has been committed, if a transaction is in progress. Any non-Flourish * code (PHP or system) will still see this directory (and all contained * files/dirs) as existing with the old paths until that point. * * @param string $new_dirname The new full path to the directory or a new name in the current parent directory * @param boolean $overwrite If the new dirname already exists, TRUE will cause the file to be overwritten, FALSE will cause the new filename to change * @return void */ public function rename($new_dirname, $overwrite) { $this->tossIfDeleted(); if (!$this->getParent()->isWritable()) { throw new fEnvironmentException('The directory, %s, can not be renamed because the directory containing it is not writable', $this->directory); } // If the dirname does not contain any folder traversal, rename the dir in the current parent directory if (preg_match('#^[^/\\\\]+$#D', $new_dirname)) { $new_dirname = $this->getParent()->getPath() . $new_dirname; } $info = fFilesystem::getPathInfo($new_dirname); if (!file_exists($info['dirname'])) { throw new fProgrammerException('The new directory name specified, %s, is inside of a directory that does not exist', $new_dirname); } if (file_exists($new_dirname)) { if (!is_writable($new_dirname)) { throw new fEnvironmentException('The new directory name specified, %s, already exists, but is not writable', $new_dirname); } if (!$overwrite) { $new_dirname = fFilesystem::makeUniqueName($new_dirname); } } else { $parent_dir = new fDirectory($info['dirname']); if (!$parent_dir->isWritable()) { throw new fEnvironmentException('The new directory name specified, %s, is inside of a directory that is not writable', $new_dirname); } } rename($this->directory, $new_dirname); // Make the dirname absolute $new_dirname = fDirectory::makeCanonical(realpath($new_dirname)); // Allow filesystem transactions if (fFilesystem::isInsideTransaction()) { fFilesystem::rename($this->directory, $new_dirname); } fFilesystem::updateFilenameMapForDirectory($this->directory, $new_dirname); }
/** * Renames the current file * * If the filename already exists and the overwrite flag is set to false, * a new filename will be created. * * This operation will be reverted if a filesystem transaction is in * progress and is later rolled back. * * @param string $new_filename The new full path to the file or a new filename in the current directory * @param boolean $overwrite If the new filename already exists, `TRUE` will cause the file to be overwritten, `FALSE` will cause the new filename to change * @return fFile The file object, to allow for method chaining */ public function rename($new_filename, $overwrite) { $this->tossIfDeleted(); if (!$this->getParent()->isWritable()) { throw new fEnvironmentException('The file, %s, can not be renamed because the directory containing it is not writable', $this->file); } // If the filename does not contain any folder traversal, rename the file in the current directory if (preg_match('#^[^/\\\\]+$#D', $new_filename)) { $new_filename = $this->getParent()->getPath() . $new_filename; } $info = fFilesystem::getPathInfo($new_filename); if (!file_exists($info['dirname'])) { throw new fProgrammerException('The new filename specified, %s, is inside of a directory that does not exist', $new_filename); } // Make the filename absolute $new_filename = fDirectory::makeCanonical(realpath($info['dirname'])) . $info['basename']; if ($this->file == $new_filename && $overwrite) { return $this; } if (file_exists($new_filename) && !$overwrite) { $new_filename = fFilesystem::makeUniqueName($new_filename); } if (file_exists($new_filename)) { if (!is_writable($new_filename)) { throw new fEnvironmentException('The new filename specified, %s, already exists, but is not writable', $new_filename); } if (fFilesystem::isInsideTransaction()) { fFilesystem::recordWrite(new fFile($new_filename)); } // Windows requires that the existing file be deleted before being replaced unlink($new_filename); } else { $new_dir = new fDirectory($info['dirname']); if (!$new_dir->isWritable()) { throw new fEnvironmentException('The new filename specified, %s, is inside of a directory that is not writable', $new_filename); } } rename($this->file, $new_filename); // Allow filesystem transactions if (fFilesystem::isInsideTransaction()) { fFilesystem::recordRename($this->file, $new_filename); } fFilesystem::updateFilenameMap($this->file, $new_filename); return $this; }
/** * Moves an uploaded file from the temp directory to a permanent location * * @throws fValidationException When `$directory` is somehow invalid or ::validate() thows an exception * * @param string|fDirectory $directory The directory to upload the file to * @param string $field The file upload field to get the file from * @param integer $index If the field was an array file upload field, upload the file corresponding to this index * @return fFile An fFile (or fImage) object */ public function move($directory, $field, $index = NULL, $param_filename = NULL) { if (!is_object($directory)) { $directory = new fDirectory($directory); } if (!$directory->isWritable()) { throw new fProgrammerException('The directory specified, %s, is not writable', $directory->getPath()); } if (!self::check($field)) { throw new fProgrammerException('The field specified, %s, does not appear to be a file upload field', $field); } $file_array = $this->validate($field, $index); $file_name = fFilesystem::makeURLSafe($param_filename == NULL ? $file_array['name'] : $param_filename); $file_name = $directory->getPath() . $file_name; if (!$this->enable_overwrite) { $file_name = fFilesystem::makeUniqueName($file_name); } if (!move_uploaded_file($file_array['tmp_name'], $file_name)) { throw new fEnvironmentException('There was an error moving the uploaded file'); } if (!chmod($file_name, 0644)) { throw new fEnvironmentException('Unable to change permissions on the uploaded file'); } return fFilesystem::createObject($file_name); }
/** * Sets a custom directory to use for the ImageMagick temporary files * * @param string $temp_dir The directory to use for the ImageMagick temp dir * @return void */ public static function setImageMagickTempDir($temp_dir) { $temp_dir = new fDirectory($temp_dir); if (!$temp_dir->isWritable()) { throw new fEnvironmentException('The ImageMagick temp directory specified, %s, does not appear to be writable', $temp_dir->getPath()); } self::$imagemagick_temp_dir = $temp_dir->getPath(); }