Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • ConfigBlock
  • ConfigOperation
  • DeleteOperation
  • DownloadOperation
  • EditBlock
  • File
  • FileUpload
  • GetOperation
  • ManageBlock
  • Model
  • Module
  • SaveOperation
  • UploadOperation
  1 <?php
  2 
  3 /*
  4  * This file is part of the Icybee package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace Icybee\Modules\Files;
 13 
 14 use ICanBoogie\HTTP\Request;
 15 use ICanBoogie\strip_root;
 16 
 17 /**
 18  * Save a file.
 19  *
 20  * @property-read \ICanBoogie\HTTP\File|null $file The file associated with the request.
 21  */
 22 class SaveOperation extends \Icybee\Modules\Nodes\SaveOperation
 23 {
 24     /**
 25      * Name of the _userfile_ slot.
 26      *
 27      * @var string
 28      */
 29     const USERFILE = 'path';
 30 
 31     /**
 32      * @var \ICanBoogie\HTTP\File|bool The optional file to save with the record.
 33      */
 34     protected $file;
 35 
 36     protected function get_file()
 37     {
 38         return $this->file;
 39     }
 40 
 41     /**
 42      * @var array Accepted file types.
 43      */
 44     protected $accept;
 45 
 46     /**
 47      * Unset the `mime` and `size` properties because they cannot be updated by the user. If the
 48      * `file` property is defined, which is the case when an asynchronous upload happend, it is
 49      * copied to the `path` property.
 50      */
 51     protected function lazy_get_properties()
 52     {
 53         $properties = parent::lazy_get_properties() + [
 54 
 55             'description' => ''
 56 
 57         ];
 58 
 59         #
 60         # File:PATH is set to true when the file is not mandatory and there is no uploaded file in
 61         # order for the form still validates, in which case the property must be unset, otherwise
 62         # the boolean is used a the new path.
 63         #
 64 
 65         if (isset($properties[File::PATH]) && $properties[File::PATH] === true)
 66         {
 67             unset($properties[File::PATH]);
 68         }
 69 
 70         if ($this->file)
 71         {
 72             $properties[File::HTTP_FILE] = $this->file;
 73         }
 74 
 75         return $properties;
 76     }
 77 
 78     /**
 79      * The temporary files stored in the repository are cleaned before the operation is processed.
 80      */
 81     public function __invoke(Request $request)
 82     {
 83         $this->module->clean_temporary_files();
 84 
 85         return parent::__invoke($request);
 86     }
 87 
 88     /**
 89      * If PATH is not defined, we check for a file upload, which is required if the operation key
 90      * is empty. If a file upload is found, the Uploaded object is set as the operation `file`
 91      * property, and the PATH parameter of the operation is set to the file location.
 92      *
 93      * Note that if the upload is not required - because the operation key is defined for updating
 94      * an entry - the PATH parameter of the operation is set to TRUE to avoid error reporting from
 95      * the form validation.
 96      *
 97      * TODO: maybe this is not ideal, since the file upload should be made optionnal when the form
 98      * is generated to edit existing entries.
 99      */
100     protected function control(array $controls)
101     {
102         global $core;
103 
104         $request = $this->request;
105 
106         $path = $request[File::PATH];
107         $file = null;
108 
109         /* @var $file \ICanBoogie\HTTP\File */
110 
111         $file = $request->files[self::USERFILE];
112 
113         if ($file && $file->is_valid)
114         {
115             $filename = strtr(uniqid(null, true), '.', '-') . $file->extension;
116             $pathname = \ICanBoogie\REPOSITORY . 'tmp' . DIRECTORY_SEPARATOR . $filename;
117 
118             $file->move($pathname);
119         }
120         else if ($path && strpos($path, \ICanBoogie\strip_root(\ICanBoogie\REPOSITORY . "files")) !== 0)
121         {
122             $file = $this->resolve_request_file_from_pathname($path);
123 
124             if (!$file)
125             {
126                 $this->response->errors[File::PATH] = $this->response->errors->format("Invalid or delete file: %pathname", [ 'pathname' => $path ]);
127             }
128         }
129 
130         unset($request[File::PATH]);
131         unset($request[File::MIME]);
132         unset($request[File::SIZE]);
133 
134         $this->file = $file;
135 
136         if ($file)
137         {
138             #
139             # This is used during form validation.
140             #
141 
142             $request[File::PATH] = true;
143         }
144 
145         return parent::control($controls);
146     }
147 
148     /**
149      * The method validates unless there was an error during the file upload.
150      */
151     protected function validate(\ICanboogie\Errors $errors)
152     {
153         global $core;
154 
155         $file = $this->file;
156 
157         if ($file)
158         {
159             $error_message = $file->error_message;
160 
161             $max_file_size = $core->registry["{$this->module->flat_id}.max_file_size"];
162 
163             if ($max_file_size && $max_file_size < $file->size)
164             {
165                 $error_message = $errors->format("Maximum file size is :size Mb", [ ':size' => round($max_file_size / 1024) ]);
166             }
167 
168             if ($this->accept && !$file->match($this->accept))
169             {
170                 $error_message = $errors->format("Only the following file types are accepted: %accepted.", [ '%accepted' => implode(', ', $this->accept) ]);
171             }
172 
173             if ($error_message)
174             {
175                 $errors[File::PATH] = $errors->format('Unable to upload file %file: :message.', [
176 
177                     '%file' => $file->name,
178                     ':message' => $error_message
179 
180                 ]);
181             }
182         }
183         else if (!$this->key)
184         {
185             $errors[File::PATH] = $errors->format("File is required.");
186         }
187 
188         return parent::validate($errors);
189     }
190 
191     /**
192      * Trigger a {@link File\MoveEvent} when the path of the updated record is updated.
193      */
194     protected function process()
195     {
196         $record = $this->record;
197         $oldpath = $record ? $record->path : null;
198 
199         $rc = parent::process();
200 
201         if ($oldpath)
202         {
203             $newpath = $this->module->model
204             ->select('path')
205             ->filter_by_nid($rc['key'])
206             ->rc;
207 
208             if ($oldpath != $newpath)
209             {
210                 new File\MoveEvent($record, $oldpath, $newpath);
211             }
212         }
213 
214         return $rc;
215     }
216 
217     protected function resolve_request_file_from_pathname($pathname)
218     {
219         $filename = basename($pathname);
220         $info_pathname = \ICanBoogie\REPOSITORY . 'tmp' . DIRECTORY_SEPARATOR . $filename . '.info';
221 
222         if (!file_exists($info_pathname))
223         {
224             return;
225         }
226 
227         $properties = json_decode(file_get_contents($info_pathname), true);
228 
229         if (!$properties)
230         {
231             return;
232         }
233 
234         return \ICanBoogie\HTTP\File::from($properties);
235     }
236 }
237 
238 namespace Icybee\Modules\Files\File;
239 
240 /**
241  * Event class for the `Icybee\Modules\Files\File` event.
242  */
243 class MoveEvent extends \ICanBoogie\Event
244 {
245     /**
246      * Previous path.
247      *
248      * @var string
249      */
250     public $from;
251 
252     /**
253      * New path.
254      *
255      * @var string
256      */
257     public $to;
258 
259     /**
260      * The event is constructed with the type `move`.
261      *
262      * @param \Icybee\Modules\Files\File $target
263      * @param string $from Previous path.
264      * @param string $to New path.
265      */
266     public function __construct(\Icybee\Modules\Files\File $target, $from, $to)
267     {
268         $this->from = $from;
269         $this->to = $to;
270 
271         parent::__construct($target, 'move');
272     }
273 }
Autodoc API documentation generated by ApiGen 2.8.0