PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/jelly/core/field/file.php

https://github.com/bionicmaster/jelly
PHP | 254 lines | 129 code | 30 blank | 95 comment | 12 complexity | f0f29a90a588f8fbd1433eee91b3d1f7 MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Handles file uploads
  4. *
  5. * Since this field is ultimately just a varchar in the database, it
  6. * doesn't really make sense to put rules like Upload::valid or Upload::type
  7. * on the validation object; if you ever want to NULL out the field, the validation
  8. * will fail!
  9. *
  10. * @package Jelly
  11. * @category Fields
  12. * @author Jonathan Geiger
  13. * @copyright (c) 2010-2011 Jonathan Geiger
  14. * @license http://www.opensource.org/licenses/isc-license.txt
  15. */
  16. abstract class Jelly_Core_Field_File extends Jelly_Field implements Jelly_Field_Supports_Save {
  17. /**
  18. * @var boolean whether or not to delete the old file when a new file is added
  19. */
  20. public $delete_old_file = TRUE;
  21. /**
  22. * @var string the path to save the file in
  23. */
  24. public $path = NULL;
  25. /**
  26. * @var array valid types for the file
  27. */
  28. public $types = array();
  29. /**
  30. * @var string the filename that will be saved
  31. */
  32. protected $_filename;
  33. /**
  34. * @var boolean file is automatically deleted if set to TRUE.
  35. */
  36. public $delete_file = FALSE;
  37. /**
  38. * Ensures there is a path for saving set.
  39. *
  40. * @param array $options
  41. */
  42. public function __construct($options = array())
  43. {
  44. parent::__construct($options);
  45. // Set the path
  46. $this->path = $this->_check_path($this->path);
  47. }
  48. /**
  49. * Adds a rule that uploads the file.
  50. *
  51. * @param Jelly_Model $model
  52. * @param string $column
  53. * @return void
  54. */
  55. public function initialize($model, $column)
  56. {
  57. parent::initialize($model, $column);
  58. // Add a rule to save the file when validating
  59. $this->rules[] = array(array(':field', '_upload'), array(':validation', ':model', ':field'));
  60. }
  61. /**
  62. * Implementation for Jelly_Field_Supports_Save.
  63. *
  64. * @param Jelly_Model $model
  65. * @param mixed $value
  66. * @param boolean $loaded
  67. * @return void
  68. */
  69. public function save($model, $value, $loaded)
  70. {
  71. if ($this->_filename)
  72. {
  73. return $this->_filename;
  74. }
  75. else
  76. {
  77. if (is_array($value) AND empty($value['name']))
  78. {
  79. // Set value to empty string if nothing is uploaded
  80. $value = '';
  81. }
  82. return $value;
  83. }
  84. }
  85. /**
  86. * Deletes the file if automatic file deletion
  87. * is enabled.
  88. *
  89. * @param Jelly_Model $model
  90. * @param mixed $key
  91. * @return void
  92. */
  93. public function delete($model, $key)
  94. {
  95. // Set the field name
  96. $field = $this->name;
  97. // Set file
  98. $file = $this->path.$model->$field;
  99. if ($this->delete_file AND is_file($file))
  100. {
  101. // Delete file
  102. unlink($file);
  103. }
  104. return;
  105. }
  106. /**
  107. * Logic to deal with uploading the image file and generating thumbnails according to
  108. * what has been specified in the $thumbnails array.
  109. *
  110. * @param Validation $validation
  111. * @param Jelly_Model $model
  112. * @param Jelly_Field $field
  113. * @return bool
  114. */
  115. public function _upload(Validation $validation, $model, $field)
  116. {
  117. // Get the file from the validation object
  118. $file = $validation[$field];
  119. if ( ! is_array($file) OR ! isset($file['name']) OR empty($file['name']))
  120. {
  121. // Nothing uploaded
  122. return FALSE;
  123. }
  124. if ($validation->errors())
  125. {
  126. // Don't bother uploading
  127. return FALSE;
  128. }
  129. // Check if it's a valid file
  130. if ( ! Upload::not_empty($file) OR ! Upload::valid($file))
  131. {
  132. // Add error
  133. $validation->error($field, 'invalid_file');
  134. return FALSE;
  135. }
  136. // Check to see if it's a valid type
  137. if ($this->types AND ! Upload::type($file, $this->types))
  138. {
  139. // Add error
  140. $validation->error($field, 'invalid_type');
  141. return FALSE;
  142. }
  143. // Sanitize the filename
  144. $file['name'] = preg_replace('/[^a-z0-9-\.]/', '-', strtolower($file['name']));
  145. // Strip multiple dashes
  146. $file['name'] = preg_replace('/-{2,}/', '-', $file['name']);
  147. // Upload a file?
  148. if (($filename = Upload::save($file, NULL, $this->path)) !== FALSE)
  149. {
  150. // Standardise slashes
  151. $filename = str_replace('\\', '/', $filename);
  152. // Chop off the original path
  153. $value = str_replace($this->path, '', $filename);
  154. // Ensure we have no leading slash
  155. if (is_string($value))
  156. {
  157. $value = trim($value, '/');
  158. }
  159. // Garbage collect
  160. $this->_delete_old_file($model->original($this->name), $this->path);
  161. // Set the saved filename
  162. $this->_filename = $value;
  163. }
  164. else
  165. {
  166. // Add error
  167. $validation->error($field, 'invalid_file');
  168. return FALSE;
  169. }
  170. return TRUE;
  171. }
  172. /**
  173. * Checks that a given path exists and is writable and that it has a trailing slash.
  174. *
  175. * (pulled out into a method so that it can be reused easily by image subclass)
  176. *
  177. * @param string $path
  178. * @return string the path - making sure it has a trailing slash
  179. */
  180. protected function _check_path($path)
  181. {
  182. // Normalize the path
  183. $path = str_replace('\\', '/', realpath($path));
  184. // Ensure we have a trailing slash
  185. if ( ! empty($path) AND is_writable($path))
  186. {
  187. $path = rtrim($path, '/').'/';
  188. }
  189. else
  190. {
  191. throw new Kohana_Exception(get_class($this).' must have a `path` property set that points to a writable directory');
  192. }
  193. return $path;
  194. }
  195. /**
  196. * Deletes the previously used file if necessary.
  197. *
  198. * @param string $filename
  199. * @param string $path
  200. * @return void
  201. */
  202. protected function _delete_old_file($filename, $path)
  203. {
  204. // Delete the old file if we need to
  205. if ($this->delete_old_file AND $filename != $this->default)
  206. {
  207. // Set the file path
  208. $path = $path.$filename;
  209. // Check if file exists
  210. if (file_exists($path))
  211. {
  212. // Delete file
  213. unlink($path);
  214. }
  215. }
  216. }
  217. } // End Jelly_Core_Field_File