· 8 years ago · Mar 04, 2018, 08:28 AM
1<?php
2 define('KEYFILE', MANIFEST . '/signedfileuploadkey.pem');
3
4 Class fieldSignedFileUpload extends Field {
5
6 public function __construct(&$parent){
7 parent::__construct($parent);
8
9 $this->_name = __('Signed File Upload');
10 $this->_required = true;
11
12 $this->set('required', 'yes');
13 }
14
15 public function canFilter() {
16 return true;
17 }
18
19 public function canImport(){
20 return true;
21 }
22
23 public function buildSortingSQL(&$joins, &$where, &$sort, $order='ASC'){
24 $joins .= "INNER JOIN `tbl_entries_data_".$this->get('id')."` AS `ed` ON (`e`.`id` = `ed`.`entry_id`) ";
25 $sort = 'ORDER BY ' . (in_array(strtolower($order), array('random', 'rand')) ? 'RAND()' : "`ed`.`file` $order");
26 }
27
28 public function buildDSRetrivalSQL($data, &$joins, &$where, $andOperation = false) {
29 $entry_id = $this->get('id');
30
31 if (preg_match('/^mimetype:/', $data[0])) {
32 $data[0] = str_replace('mimetype:', '', $data[0]);
33 $column = 'mimetype';
34
35 } else if (preg_match('/^size:/', $data[0])) {
36 $data[0] = str_replace('size:', '', $data[0]);
37 $column = 'size';
38
39 } else {
40 $column = 'file';
41 }
42
43 if (self::isFilterRegex($data[0])) {
44 $this->_key++;
45 $pattern = str_replace('regexp:', '', $this->cleanValue($data[0]));
46 $joins .= "
47 LEFT JOIN
48 `tbl_entries_data_{$entry_id}` AS t{$entry_id}_{$this->_key}
49 ON (e.id = t{$entry_id}_{$this->_key}.entry_id)
50 ";
51 $where .= "
52 AND t{$entry_id}_{$this->_key}.{$column} REGEXP '{$pattern}'
53 ";
54
55 } elseif ($andOperation) {
56 foreach ($data as $value) {
57 $this->_key++;
58 $value = $this->cleanValue($value);
59 $joins .= "
60 LEFT JOIN
61 `tbl_entries_data_{$entry_id}` AS t{$entry_id}_{$this->_key}
62 ON (e.id = t{$entry_id}_{$this->_key}.entry_id)
63 ";
64 $where .= "
65 AND t{$entry_id}_{$this->_key}.{$column} = '{$value}'
66 ";
67 }
68
69 } else {
70 if (!is_array($data)) $data = array($data);
71
72 foreach ($data as &$value) {
73 $value = $this->cleanValue($value);
74 }
75
76 $this->_key++;
77 $data = implode("', '", $data);
78 $joins .= "
79 LEFT JOIN
80 `tbl_entries_data_{$entry_id}` AS t{$entry_id}_{$this->_key}
81 ON (e.id = t{$entry_id}_{$this->_key}.entry_id)
82 ";
83 $where .= "
84 AND t{$entry_id}_{$this->_key}.{$column} IN ('{$data}')
85 ";
86 }
87
88 return true;
89 }
90
91 public function displayPublishPanel(&$wrapper, $data=NULL, $flagWithError=NULL, $fieldnamePrefix=NULL, $fieldnamePostfix=NULL){
92
93 if(!$flagWithError && !is_writable(DOCROOT . $this->get('destination') . '/'))
94 $flagWithError = __('Destination folder, <code>%s</code>, is not writable. Please check permissions.', array($this->get('destination')));
95
96 $label = Widget::Label($this->get('label'));
97 $class = 'file';
98 $label->setAttribute('class', $class);
99 if($this->get('required') != 'yes') $label->appendChild(new XMLElement('i', __('Optional')));
100
101 $span = new XMLElement('span');
102 if($data['file']) $span->appendChild(Widget::Anchor('/workspace' . $data['file'], URL . '/workspace' . $data['file']));
103
104 $span->appendChild(Widget::Input('fields'.$fieldnamePrefix.'['.$this->get('element_name').']'.$fieldnamePostfix, $data['file'], ($data['file'] ? 'hidden' : 'file')));
105
106 $label->appendChild($span);
107
108 if($flagWithError != NULL) $wrapper->appendChild(Widget::wrapFormElementWithError($label, $flagWithError));
109 else $wrapper->appendChild($label);
110
111 $keylabel = Widget::Label('File Signature');
112 $wrapper->appendChild($keylabel, $data['signature']);
113
114 }
115
116 function isSortable(){
117 return true;
118 }
119
120 public function entryDataCleanup($entry_id, $data){
121 $file_location = WORKSPACE . '/' . ltrim($data['file'], '/');
122
123 if(file_exists($file_location)) General::deleteFile($file_location);
124
125 parent::entryDataCleanup($entry_id);
126
127 return true;
128 }
129
130 public function checkFields(&$errors, $checkForDuplicates=true){
131
132 if(!is_writable(DOCROOT . $this->get('destination') . '/'))
133 $errors['destination'] = __('Folder is not writable. Please check permissions.');
134
135 parent::checkFields($errors, $checkForDuplicates);
136 }
137
138 function commit(){
139
140 if(!parent::commit()) return false;
141
142 $id = $this->get('id');
143
144 if($id === false) return false;
145
146 $fields = array();
147
148 $fields['entry_id'] = $id;
149 $fields['destination'] = $this->get('destination');
150 $fields['validator'] = ($fields['validator'] == 'custom' ? NULL : $this->get('validator'));
151
152 $this->_engine->Database->query("DELETE FROM `tbl_fields_".$this->handle()."` WHERE `entry_id` = '$id' LIMIT 1");
153 return $this->_engine->Database->insert($fields, 'tbl_fields_' . $this->handle());
154
155 }
156
157 function prepareTableValue($data, XMLElement $link=NULL){
158 if(!$file = $data['file']) return NULL;
159
160 if($link){
161 $link->setValue(basename($file));
162 //$view_link = Widget::Anchor('(view)', URL . '/workspace' . $file);
163 return $link->generate(); // . ' ' . $view_link->generate();
164 }
165
166 else{
167 $link = Widget::Anchor(basename($file), URL . '/workspace' . $file);
168 return $link->generate();
169 }
170
171 }
172
173 function appendFormattedElement(&$wrapper, $data){
174 $item = new XMLElement($this->get('element_name'));
175
176 $item->setAttributeArray(array(
177 'size' => General::formatFilesize(filesize(WORKSPACE . $data['file'])),
178 'path' => str_replace(WORKSPACE, NULL, dirname(WORKSPACE . $data['file'])),
179 'type' => $data['mimetype'],
180 'signature' => $data['signature']
181 ));
182
183 $item->appendChild(new XMLElement('filename', General::sanitize(basename($data['file']))));
184
185 $m = unserialize($data['meta']);
186
187 if(is_array($m) && !empty($m)){
188 $item->appendChild(new XMLElement('meta', NULL, $m));
189 }
190
191 $wrapper->appendChild($item);
192 }
193
194 function displaySettingsPanel(&$wrapper, $errors=NULL){
195
196 parent::displaySettingsPanel($wrapper, $errors);
197
198 ## Destination Folder
199 $ignore = array('events', 'data-sources', 'text-formatters', 'pages', 'utilities');
200 $directories = General::listDirStructure(WORKSPACE, true, 'asc', DOCROOT, $ignore);
201
202 $label = Widget::Label(__('Destination Directory'));
203
204 $options = array();
205 $options[] = array('/workspace', false, '/workspace');
206 if(!empty($directories) && is_array($directories)){
207 foreach($directories as $d) {
208 $d = '/' . trim($d, '/');
209 if(!in_array($d, $ignore)) $options[] = array($d, ($this->get('destination') == $d), $d);
210 }
211 }
212
213 $label->appendChild(Widget::Select('fields['.$this->get('sortorder').'][destination]', $options));
214
215 if(isset($errors['destination'])) $wrapper->appendChild(Widget::wrapFormElementWithError($label, $errors['destination']));
216 else $wrapper->appendChild($label);
217
218 $this->buildValidationSelect($wrapper, $this->get('validator'), 'fields['.$this->get('sortorder').'][validator]', 'upload');
219
220 $this->appendRequiredCheckbox($wrapper);
221 $this->appendShowColumnCheckbox($wrapper);
222
223 }
224
225 function checkPostFieldData($data, &$message, $entry_id=NULL){
226
227 /*
228 UPLOAD_ERR_OK
229 Value: 0; There is no error, the file uploaded with success.
230
231 UPLOAD_ERR_INI_SIZE
232 Value: 1; The uploaded file exceeds the upload_max_filesize directive in php.ini.
233
234 UPLOAD_ERR_FORM_SIZE
235 Value: 2; The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
236
237 UPLOAD_ERR_PARTIAL
238 Value: 3; The uploaded file was only partially uploaded.
239
240 UPLOAD_ERR_NO_FILE
241 Value: 4; No file was uploaded.
242
243 UPLOAD_ERR_NO_TMP_DIR
244 Value: 6; Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
245
246 UPLOAD_ERR_CANT_WRITE
247 Value: 7; Failed to write file to disk. Introduced in PHP 5.1.0.
248
249 UPLOAD_ERR_EXTENSION
250 Value: 8; File upload stopped by extension. Introduced in PHP 5.2.0.
251 */
252
253 // Array
254 // (
255 // [name] => filename.pdf
256 // [type] => application/pdf
257 // [tmp_name] => /tmp/php/phpYtdlCl
258 // [error] => 0
259 // [size] => 16214
260 // )
261
262 $message = NULL;
263
264 if(empty($data) || $data['error'] == UPLOAD_ERR_NO_FILE) {
265
266 if($this->get('required') == 'yes'){
267 $message = __("'%s' is a required field.", $this->get('label'));
268 return self::__MISSING_FIELDS__;
269 }
270
271 return self::__OK__;
272 }
273
274 ## Its not an array, so just retain the current data and return
275 if(!is_array($data)) return self::__OK__;
276
277 if(!is_writable(DOCROOT . $this->get('destination') . '/')){
278 $message = __('Destination folder, <code>%s</code>, is not writable. Please check permissions.', array($this->get('destination')));
279 return self::__ERROR__;
280 }
281
282 if($data['error'] != UPLOAD_ERR_NO_FILE && $data['error'] != UPLOAD_ERR_OK){
283
284 switch($data['error']){
285
286 case UPLOAD_ERR_INI_SIZE:
287 $message = __('File chosen in "%1$s" exceeds the maximum allowed upload size of %2$s specified by your host.', array($this->get('label'), (is_numeric(ini_get('upload_max_filesize')) ? General::formatFilesize(ini_get('upload_max_filesize')) : ini_get('upload_max_filesize'))));
288 break;
289
290 case UPLOAD_ERR_FORM_SIZE:
291 $message = __('File chosen in "%1$s" exceeds the maximum allowed upload size of %2$s, specified by Symphony.', array($this->get('label'), General::formatFilesize($this->_engine->Configuration->get('max_upload_size', 'admin'))));
292 break;
293
294 case UPLOAD_ERR_PARTIAL:
295 $message = __("File chosen in '%s' was only partially uploaded due to an error.", array($this->get('label')));
296 break;
297
298 case UPLOAD_ERR_NO_TMP_DIR:
299 $message = __("File chosen in '%s' was only partially uploaded due to an error.", array($this->get('label')));
300 break;
301
302 case UPLOAD_ERR_CANT_WRITE:
303 $message = __("Uploading '%s' failed. Could not write temporary file to disk.", array($this->get('label')));
304 break;
305
306 case UPLOAD_ERR_EXTENSION:
307 $message = __("Uploading '%s' failed. File upload stopped by extension.", array($this->get('label')));
308 break;
309
310 }
311
312 return self::__ERROR_CUSTOM__;
313
314 }
315
316 ## Sanitize the filename
317 $data['name'] = Lang::createFilename($data['name']);
318
319 if($this->get('validator') != NULL){
320 $rule = $this->get('validator');
321
322 if(!General::validateString($data['name'], $rule)){
323 $message = __("File chosen in '%s' does not match allowable file types for that field.", array($this->get('label')));
324 return self::__INVALID_FIELDS__;
325 }
326
327 }
328
329 $abs_path = DOCROOT . '/' . trim($this->get('destination'), '/');
330 $new_file = $abs_path . '/' . $data['name'];
331 $existing_file = NULL;
332
333 if($entry_id){
334 $row = $this->Database->fetchRow(0, "SELECT * FROM `tbl_entries_data_".$this->get('id')."` WHERE `entry_id` = '$entry_id' LIMIT 1");
335 $existing_file = $abs_path . '/' . trim($row['file'], '/');
336 }
337
338 if(($existing_file != $new_file) && file_exists($new_file)){
339 $message = __('A file with the name %1$s already exists in %2$s. Please rename the file first, or choose another.', array($data['name'], $this->get('destination')));
340 return self::__INVALID_FIELDS__;
341 }
342
343 return self::__OK__;
344
345 }
346
347 function processRawFieldData($data, &$status, &$message, $simulate=false, $entry_id=NULL){
348
349 $status = self::__OK__;
350
351 ## Its not an array, so just retain the current data and return
352 if(!is_array($data)){
353
354 $status = self::__OK__;
355
356 // Do a simple reconstruction of the file meta information. This is a workaround for
357 // bug which causes all meta information to be dropped
358 return array(
359 'file' => $data,
360 'mimetype' => self::__sniffMIMEType($data),
361 'size' => filesize(WORKSPACE . $data),
362 'meta' => serialize(self::getMetaInfo(WORKSPACE . $data, self::__sniffMIMEType($data))),
363 'signature' => $this->signatureForFilename(WORKSPACE . $data)
364 );
365
366 }
367
368 if($simulate) return;
369
370 if($data['error'] == UPLOAD_ERR_NO_FILE || $data['error'] != UPLOAD_ERR_OK) return;
371
372 ## Sanitize the filename
373 $data['name'] = Lang::createFilename($data['name']);
374
375 ## Upload the new file
376 $abs_path = DOCROOT . '/' . trim($this->get('destination'), '/');
377 $rel_path = str_replace('/workspace', '', $this->get('destination'));
378
379 print "<br />abs_path: {$abs_path}<br />";die();
380
381 if(!General::uploadFile($abs_path, $data['name'], $data['tmp_name'], $this->_engine->Configuration->get('write_mode', 'file'))){
382
383 $message = __('There was an error while trying to upload the file <code>%1$s</code> to the target directory <code>%2$s</code>.', array($data['name'], 'workspace/'.ltrim($rel_path, '/')));
384 $status = self::__ERROR_CUSTOM__;
385 return;
386 }
387
388 if($entry_id){
389 $row = $this->Database->fetchRow(0, "SELECT * FROM `tbl_entries_data_".$this->get('id')."` WHERE `entry_id` = '$entry_id' LIMIT 1");
390 $existing_file = $abs_path . '/' . basename($row['file']);
391
392 General::deleteFile($existing_file);
393 }
394
395 $status = self::__OK__;
396
397 $file = rtrim($rel_path, '/') . '/' . trim($data['name'], '/');
398
399 $this->set('signature', $this->signatureForFilename(WORKSPACE . $data));
400
401 return array(
402 'file' => $file,
403 'size' => $data['size'],
404 'mimetype' => $data['type'],
405 'meta' => serialize(self::getMetaInfo(WORKSPACE . $file, $data['type'])),
406 'signature' => $data['signature']
407 );
408
409 }
410
411 private static function __sniffMIMEType($file){
412
413 $imageMimeTypes = array(
414 'image/gif',
415 'image/jpg',
416 'image/jpeg',
417 'image/png',
418 );
419
420 if(in_array('image/' . General::getExtension($file), $imageMimeTypes)) return 'image/' . General::getExtension($file);
421
422 return 'unknown';
423 }
424
425 public static function getMetaInfo($file, $type) {
426
427 $imageMimeTypes = array(
428 'image/gif',
429 'image/jpg',
430 'image/jpeg',
431 'image/png',
432 );
433
434 $meta = array();
435
436 $meta['creation'] = DateTimeObj::get('c', filemtime($file));
437
438 if(in_array($type, $imageMimeTypes) && $array = @getimagesize($file)){
439 $meta['width'] = $array[0];
440 $meta['height'] = $array[1];
441 }
442
443 return $meta;
444
445 }
446
447
448 public function createTable(){
449 return $this->_engine->Database->query(
450 "CREATE TABLE IF NOT EXISTS `tbl_entries_data_" . $this->get('id') . "` (
451 `id` int(11) unsigned NOT NULL auto_increment,
452 `entry_id` int(11) unsigned NOT NULL,
453 `file` varchar(255) default NULL,
454 `size` int(11) unsigned NOT NULL,
455 `mimetype` varchar(50) NOT NULL,
456 `meta` varchar(255) default NULL,
457 `signature` varchar(255) default NULL,
458 PRIMARY KEY (`id`),
459 KEY `entry_id` (`entry_id`),
460 KEY `file` (`file`),
461 KEY `mimetype` (`mimetype`)
462 ) TYPE=MyISAM");
463 }
464
465 public function getSignatureKey(){
466 return @file_get_contents(KEYFILE);
467 }
468
469 public function signatureForFilename($filename) {
470 return shell_exec('openssl dgst -sha1 -binary < "'.$filename.'" | openssl dgst -dss1 -sign "'.KEYFILE.'" | openssl enc -base64');
471 }
472
473 public function getExampleFormMarkup(){
474 $label = Widget::Label($this->get('label'));
475 $label->appendChild(Widget::Input('fields['.$this->get('element_name').']', NULL, 'file'));
476
477 return $label;
478 }
479
480 }
481
482?>