Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
form_question_type_file_upload.inc
1 <?php
18 require_once dirname(__FILE__).'/../../form_question/form_question.inc';
19 require_once SQ_FUDGE_PATH.'/general/file_system.inc';
20 
21 
34 {
35 
36 
44  function Form_Question_Type_File_Upload($assetid=0, $data=Array())
45  {
46  $this->Form_Question($assetid, $data);
47 
48  }//end constructor
49 
50 
58  function _getAllowedLinks()
59  {
60  return Array();
61 
62  }//end _getAllowedLinks()
63 
64 
71  function getHtmlField()
72  {
73  $extras = $this->attr('extra');
74 
75  $name = 'q'.$this->id;
76 
77  // prepare a valid value for the field id
78  $extras = $extras.' id="'.str_replace(':', '_', $name).'"';
79 
80  if ($this->attr('tabindex')) {
81  $extras .= ' tabindex="'.$this->attr('tabindex').'"';
82  }
83 
84  ob_start();
85  $temp_path = array_get_index($this->extra_data, 'temp_filesystem_path', '');
86  if (!empty($temp_path)) {
87  ?><p>Current file: <?php echo basename($temp_path); ?>
88  <?php if (!$this->attr('is_required')) {
89  echo check_box($name.'_clear', 1, FALSE) ?> Clear?<?php
90  }
91  ?></p><?php
92  }
93  file_upload($name, $extras);
94  $html = ob_get_contents();
95  ob_end_clean();
96 
97  return $html;
98 
99  }//end getHtmlField()
100 
101 
108  function getValue()
109  {
110  if (is_null($this->value)) {
111  return '';
112  } else {
113  return $this->value;
114  }
115 
116  }//end getValue()
117 
118 
139  function hasValidValue($answer=NULL, $mute_errors=FALSE)
140  {
141 
142  if (isset($this->_tmp['file_info']) && $this->_tmp['file_info'] === FALSE) {
143  // there was an upload error
144  $this->failed_rules[] = translate('core_form_file_upload_error', $this->attr('name'));
145  return FALSE;
146  }
147 
148  $ok = TRUE;
149 
150  $none_uploaded = !isset($this->_tmp['file_info']) || (is_array($this->_tmp['file_info']) && empty($this->_tmp['file_info']));
151  $previously_uploaded = !empty($this->extra_data['temp_filesystem_path']);
152 
153  if ($none_uploaded && !$previously_uploaded && $this->attr('is_required')) {
154  // no file uploaded to required field (and none previously uploaded either)
155  if (strlen($this->attr('cust_required_error'))) {
156  $this->failed_rules[] = $this->attr('cust_required_error');
157  } else {
158  $this->failed_rules[] = translate('core_form_file_upload_none_rule', $this->attr('name'));
159  }
160  $ok = FALSE;
161  }
162 
163  if ($ok) {
164  return parent::hasValidValue($answer, $mute_errors);
165  }
166 
167  return FALSE;
168 
169  }//end hasValidValue()
170 
171 
178  function populate(Asset $parent=NULL)
179  {
180  $this->_tmp['file_info'] = get_file_upload_info('q'.$this->id);
181 
182  if (isset($this->_tmp['file_info']['name'])) {
183  $this->setValue(array_get_index($this->_tmp['file_info'], 'name', translate('core_form_file_upload_none')));
184  $this->getSummary();
185  //$this->_tmp['summary'] = $this->_tmp['file_info']['name'].', type '.$this->_tmp['file_info']['type'].', '.easy_filesize($this->_tmp['file_info']['size']);
186  } else {
187  // see if we have an incomplete file upload, if we do, then don't
188  // blank out the summary/value
189  $existing_file = FALSE;
190  $submission_path = $this->getTempFileUploadPath($parent);
191  require_once SQ_FUDGE_PATH.'/standards_lists/mime_types.inc';
192 
193  if (is_dir($submission_path)) {
194  $dir = new DirectoryIterator($submission_path);
195  foreach ($dir as $dir_entry) {
196  if ($dir_entry->isFile()) {
197  $this->setValue($dir_entry->getFilename());
198  //$this->_tmp['summary'] = $dir_entry->getFilename().', type '.$standards_lists_mime_types[get_file_type($dir_entry->getFilename())].', '.easy_filesize($dir_entry->getSize());
199  $this->getSummary();
200  $existing_file = TRUE;
201  break;
202  }
203  }
204  }
205 
206  if (!$existing_file) {
207  $this->setValue(translate('core_form_file_upload_none'));
208  }
209  }
210 
211  }//end populate()
212 
213 
222  function getAllowedRules()
223  {
224  return Array('file_size', 'file_type', 'file_virus_check');
225 
226  }//end getAllowedRules()
227 
228 
235  function getSummary()
236  {
237  //if (!isset($this->_tmp['summary'])) {
238  if (!empty($this->extra_data)) {
239  $value = reset($this->extra_data);
240  $key = key($this->extra_data);
241  $new_summary = '';
242  require SQ_FUDGE_PATH.'/standards_lists/mime_types.inc';
243 
244  switch($key) {
245  case 'temp_filesystem_path' :
246  $filepath = $value;
247  if (is_file($filepath)) {
248  $type = array_get_index($standards_lists_mime_types, get_file_type($filepath), '');
249  if (!empty($type)) $type = ', type '.$type;
250  $new_summary = basename($value).$type.', '.easy_filesize(filesize($filepath));
251  } else {
252  $new_summary = translate('core_form_file_upload_none');
253  }
254  break;
255 
256  case 'filesystem_path' :
257  $filepath = $value;
258  if (is_file($filepath)) {
259  $type = array_get_index($standards_lists_mime_types, get_file_type($filepath), '');
260  if (!empty($type)) $type = ', type '.$type;
261  $new_summary = basename($value).$type.', '.easy_filesize(filesize($filepath));
262  } else {
263  $new_summary = translate('core_form_file_upload_none');
264  }
265  break;
266 
267  case 'new_file_assetid' :
268  case 'existing_file_assetid' :
269  if (!empty($value)){
270  if (SQ_IN_BACKEND || SQ_IN_LIMBO) {
271  $new_summary = get_asset_tag_line($value);
272  } else {
273  $file_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($value);
274  $file_info = $file_asset->getExistingFile();
275  $type = array_get_index($standards_lists_mime_types, get_file_type($file_info['filename']), '');
276  if (!empty($type)) $type = ', type '.$type;
277  $new_summary = basename($file_info['filename']).$type.', '.easy_filesize($file_info['size']);
278  }
279  } else {
280  $new_summary = translate('core_form_file_upload_none');
281  }
282 
283  break;
284  }
285 
286  $this->_tmp['summary'] = $new_summary;
287  } else {
288  // Check that we are not going to have web path conflicts
289  require_once SQ_INCLUDE_PATH.'/general_occasional.inc';
290  require_once SQ_LIB_PATH.'/html_form/html_form.inc';
291  $valid_names = make_valid_web_paths(Array($this->getValue()));
292  $name = array_shift($valid_names);
293 
294  $sql = 'SELECT
295  a.assetid
296  FROM
297  '.SQ_TABLE_RUNNING_PREFIX.'ast a
298  JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk l
299  ON a.assetid = l.minorid
300  WHERE
301  l.majorid = :majorid
302  AND a.name = :name';
303 
304  try {
305  $query = MatrixDAL::preparePdoQuery($sql);
306  MatrixDAL::bindValueToPdo($query, 'majorid', $this->attr('create_location'));
307  MatrixDAL::bindValueToPdo($query, 'name', $name);
308  $existing_file = MatrixDAL::executePdoOne($query);
309  } catch (Exception $e) {
310  throw new Exception('Unable to get summary of answers for: '.$name.' due to database error: '.$e->getMessage());
311  }
312 
313  if (!empty($existing_file)) {
314  $existing_file_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($existing_file);
315  $info = $existing_file_asset->getExistingFile();
316  $assetid = $this->getUploadedFileId();
317  if (trim($assetid) != '') {
318  $this->_tmp['summary'] = get_asset_tag_line($assetid);
319  return $this->_tmp['summary'];
320  }//end if
321  $this->_tmp['summary'] = $this->getValue().', '.easy_filesize($info['size']);
322  } else {
323  $this->_tmp['summary'] = translate('core_form_file_upload_none');
324  }
325  }//end if Extra Data exists
326 
327  //}//end if temp summary exists
328 
329  return $this->_tmp['summary'];
330 
331  }//end getSummary()
332 
333 
340  function isEditable()
341  {
342  return FALSE;
343 
344  }//end isEditable()
345 
346 
355  function saveValue(Asset $parent)
356  {
357  $prefix = 'q'.$this->id;
358  if (isset($_POST[$prefix.'_clear'])) {
359  if (isset($this->extra_data['temp_filesystem_path'])) {
360  // Just nuke the directory, because this means there will be no
361  // files left. We want to be as clean as possible in the data/
362  // path
363  $temp_path = $this->extra_data['temp_filesystem_path'];
364  delete_directory(dirname($temp_path));
365 
366  $this->setValue('');
367  unset($this->_tmp['file_info']);
368  unset($this->extra_data['mime_type']);
369  unset($this->extra_data['temp_filesystem_path']);
370  }
371  }
372 
373  if (empty($this->_tmp['file_info'])) {
374  return TRUE;
375  }
376 
377  $file_info = $this->_tmp['file_info'];
378  $ok = TRUE;
379 
380  if ($file_info['error'] === UPLOAD_ERR_OK) {
381  $temp_path = $this->getTempFileUploadPath($parent).'/'.$this->getValue();
382  $this->createTempFileUpload($parent);
383  $this->extra_data = Array(
384  'temp_filesystem_path' => $temp_path,
385  'mime_type' => $file_info['type'],
386  );
387 
388  // We only have to upload this once, so remove file info
389  unset($this->_tmp['file_info']);
390  } else if ($file_info['error'] !== UPLOAD_ERR_NO_FILE) {
391  // If no file uploaded, then that is fine, but otherwise, error
392  $ok = FALSE;
393  }
394 
395  if (!isset($this->extra_data['temp_filesystem_path'])) {
396  $this->setValue('');
397  }
398 
399  return $ok;
400 
401  }//end saveValue()
402 
403 
409  public function onSubmitForm(Asset $parent)
410  {
411  // Do we have a create location?
412  $question_create_loc = $this->attr('create_location');
413  $ok = TRUE;
414  $mime_type = array_get_index($this->extra_data, 'mime_type', '');
415  $temp_path = array_get_index($this->extra_data, 'temp_filesystem_path', '');
416 
417  if (!empty($question_create_loc) && !empty($temp_path)) {
418  // If we are creating a File asset, we need to check whether we need
419  // to handle a web path conflict
420  require_once SQ_INCLUDE_PATH.'/general_occasional.inc';
421 
422  // We are only going to have one valid name, so this is no problem
423  $valid_names = make_valid_web_paths(Array($this->getValue()));
424  $name = array_shift($valid_names);
425 
426  $create_location = $GLOBALS['SQ_SYSTEM']->am->getAsset($question_create_loc);
427 
428  // Find an asset underneath the create location that has the same
429  // web path as the new would-be asset.
430  // Only consider significant links.
431  $sql = 'SELECT assetid
432  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_path
433  WHERE assetid IN
434  (SELECT a.assetid
435  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast a
436  JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk l
437  ON a.assetid = l.minorid
438  WHERE
439  l.majorid = :majorid
440  AND '.db_extras_bitand(MatrixDAL::getDbType(), 'l.link_type', ':link_types').' > 0
441  )
442  AND path = :webpath
443  ';
444  try {
445  $query = MatrixDAL::preparePdoQuery($sql);
446  MatrixDAL::bindValueToPdo($query, 'majorid', $question_create_loc);
447  MatrixDAL::bindValueToPdo($query, 'link_types', SQ_SC_LINK_WEB_PATHS);
448  MatrixDAL::bindValueToPdo($query, 'webpath', $name);
449  $existing_assetid = MatrixDAL::executePdoOne($query);
450  } catch (Exception $e) {
451  throw new Exception('Unable to check for web path conflicts for filename: '.$name.' due to database error: '.$e->getMessage());
452  }
453 
454  //check possible orphaned asset
455  if(empty($existing_assetid) && ($this->attr('overwrite_rule') == 'O' || $this->attr('overwrite_rule') == 'R')) {
456  $orphaned = NULL;
457  $parent_urls = $create_location->getLookups();
458  for ($j = 0, $num_parent_urls = count($parent_urls); $j < $num_parent_urls; $j++) {
459  $new_url = $parent_urls[$j]['url'].'/'.$name;
460  // check if another asset is using the new URL
461  $orphaned = $GLOBALS['SQ_SYSTEM']->am->getAssetFromURL(NULL, $new_url, TRUE, TRUE);
462  }
463  // if there is an asset using new url and it was not found previously by checking links
464  // we will treat it as an orphaned asset
465  if(!empty($orphaned)) {
466  // create the link for the orphaned asset
467  $GLOBALS['SQ_SYSTEM']->am->createAssetLink($create_location, $orphaned, SQ_LINK_TYPE_2, '');
468  $existing_assetid = $orphaned->id;
469  }
470  }
471 
472  $existing_asset = NULL;
473  if (!empty($existing_assetid)) {
474  switch ($this->attr('overwrite_rule')) {
475  case 'D' :
476  // D = Raise a Submission Error
477  $this->failed_rules[] = translate('core_form_file_upload_already_exists', $this->attr('name'));
478  $ok = FALSE;
479  break;
480 
481  case 'O' :
482  // O = Overwrite the file
483  $existing_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($existing_assetid);
484 
485  if (!($existing_asset instanceof File)) {
486  // We will not overwrite an asset that is not a File.
487  $this->failed_rules[] = 'Refusing to overwrite a non-File asset at web path '.$name.' - an uploaded file can only overwrite a File asset';
488  $ok = FALSE;
489  }
490  break;
491 
492  case 'R' :
493  // R = Rename the uploaded file
494  // Use webPathsInUse() to help us here.
495  $suggested_paths = $GLOBALS['SQ_SYSTEM']->am->webPathsInUse($create_location, Array($name), 0, TRUE);
496  $name = array_shift($suggested_paths);
497  break;
498  }
499  }
500 
501  // We are all good, so create the file
502  if ($ok) {
503 
504  if ($existing_asset) {
505  // update the existing file asset
506  $edit_fns = $existing_asset->getEditFns();
507 
508  $file_info = Array(
509  'tmp_name' => $temp_path,
510  'name' => $name,
511  'non_uploaded_file' => TRUE,
512  );
513 
514  $o = NULL;
515  //force to update the file
516  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
517  if (!$edit_fns->processFileUpload($existing_asset, $o, $existing_asset->getPrefix(), $file_info)) {
518  return FALSE;
519  }
520  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
521 
522  $this->extra_data = Array(
523  'existing_file_assetid' => $existing_asset->id,
524  'mime_type' => $mime_type,
525  'temp_filesystem_path' => $temp_path,
526  );
527 
528  } else {
529  $file_ext = get_file_type($this->getValue());
530 
531  // Switch based on the file extension
532  if (in_array($file_ext, Array('gif', 'jpg', 'jpeg', 'png'))) {
533  $file_type = 'Image';
534 
535  } else if (preg_match('/^do[ct][xm]?$/', $file_ext)) {
536  // DOC, DOT - MS Word (document, template respectively)
537  // DOCX, DOCM, DOTX, DOTM - MS Word 2007+ OOXML
538  $file_type = 'Word_Doc';
539 
540  } else if (preg_match('/^xl[st][xm]?$/', $file_ext)) {
541  // XLS, XLT - MS Excel (workbook, template respectively)
542  // XLSX, XLSM, XLTX, XLTM - MS Excel 2007+ OOXML
543  $file_type = 'Excel_Doc';
544 
545  } else if (preg_match('/^p(pt|ps|ot)[xm]?$/', $file_ext)) {
546  // PPT, PPS, POT - MS PowerPoint
547  // (presentation, slideshow, template respectively)
548  // [ext]X, [ext]M - MS PowerPoint 2007+ OOXML
549  $file_type = 'PowerPoint_Doc';
550 
551  } else if ($file_ext === 'pdf') {
552  $file_type = 'PDF_File';
553 
554  } else if ($file_ext === 'rtf') {
555  $file_type = 'RTF_File';
556 
557  } else if ($file_ext === 'txt') {
558  $file_type = 'Text_File';
559 
560  } else if (substr($mime_type, 0, 5) === 'text/') {
561  $file_type = 'Text_File';
562 
563  } else {
564  $file_type = 'File';
565 
566  }//end if on file extension
567 
568  $GLOBALS['SQ_SYSTEM']->am->includeAsset($file_type);
569 
570  $file_asset = new $file_type();
571  $file_asset->_tmp['uploading_file'] = TRUE;
572 
573  $link = Array(
574  'asset' => $GLOBALS['SQ_SYSTEM']->am->getAsset($question_create_loc),
575  'link_type' => SQ_LINK_TYPE_2,
576  );
577 
578  $file_info = Array(
579  'tmp_name' => $temp_path,
580  'name' => $name,
581  'non_uploaded_file' => TRUE,
582  );
583 
584  // We need to be able to create a file within the set
585  // create location, no matter who we are. To avoid errors
586  // regarding write permissions, we'll force this through
587  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
588  if (!$file_asset->create($link, $file_info)) {
589  $this->failed_rules[] = translate('core_form_file_upload_error', $this->attr('name'));
590  $ok = FALSE;
591  }
592  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
593 
594  unset($file_asset->_tmp['uploading_file']);
595 
596  $this->extra_data = Array(
597  'new_file_assetid' => $file_asset->id,
598  'mime_type' => $mime_type,
599  'temp_filesystem_path' => $temp_path,
600  );
601 
602  }//end if existing file
603 
604  }//end if OK
605 
606  }//end if create location exists
607 
608  return $ok;
609 
610  }//end onSubmitForm()
611 
612 
623  public function cleanUp(Asset $parent)
624  {
625  $filepath = array_get_index($this->extra_data, 'temp_filesystem_path', NULL);
626  if (!empty($filepath)) {
627  delete_directory(dirname($filepath));
628  }
629 
630  }//end cleanUp()
631 
632 
642  public function createTempFileUpload(Asset $parent)
643  {
644  $file_info = $this->_tmp['file_info'];
645 
646  if ($file_info['error'] === UPLOAD_ERR_OK) {
647  $submission_path = $this->getTempFileUploadPath($parent);
648  create_directory($submission_path);
649 
650  // Remove existing uploads, to ensure that there is only one file
651  // associated with an incomplete attachment
652  $dir = new DirectoryIterator($submission_path);
653  foreach ($dir as $dir_entry) {
654  if ($dir_entry->isFile()) {
655  unlink($dir_entry->getPathname());
656  }
657  }
658 
659  $submission_file_name = $submission_path.'/'.$file_info['name'];
660  move_uploaded_file($file_info['tmp_name'], $submission_file_name);
661  $this->setValue(basename($submission_file_name));
662  }
663 
664  }//end createTempFileUpload()
665 
666 
672  public function getTempFileUploadPath(Asset $parent)
673  {
674  if (method_exists($parent, 'getTempFileUploadPath')) {
675  $submission_path = $parent->getTempFileUploadPath();
676  } else {
677  $submission_path = $this->data_path.'/attachments/'.session_id().'_'.$parent->id;
678  }
679 
680  $submission_path .= '/'.str_replace(':', '_', $this->id);
681 
682  return $submission_path;
683  }
684 
685 
692  function getUploadedFileId()
693  {
694  $existing_file = NULL;
695  if ($this->value != translate('core_form_file_upload_none') && !empty($this->value)){
696  if (!empty($this->extra_data)) {
697  // We have a permanent filesystem path, ie. we don't have create location
698  if (isset($this->extra_data['filesystem_path'])) {
699  return NULL;
700  }
701  $existing_file = array_get_index($this->extra_data, 'new_file_assetid', NULL);
702  if (is_null($existing_file)) {
703  $existing_file = array_get_index($this->extra_data, 'existing_file_assetid', NULL);
704  }
705  }
706 
707  // No extra data, so use the old method
708  if (is_null($existing_file)) {
709  $create_loc = $this->attr('create_location');
710  if ($create_loc == 0) return NULL;
711  $create_loc_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($create_loc);
712  if (is_null($create_loc_asset)) return NULL;
713 
714  $db = MatrixDAL::getDb();
715  // Check that we are not going to have web path conflicts
716  require_once SQ_INCLUDE_PATH.'/general_occasional.inc';
717  $valid_names = make_valid_web_paths(Array($this->getValue()));
718  $name = array_shift($valid_names);
719 
720  $sql = 'SELECT
721  a.assetid
722  FROM
723  '.SQ_TABLE_RUNNING_PREFIX.'ast a
724  JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk l
725  ON a.assetid = l.minorid
726  WHERE
727  l.majorid = :create_loc
728  AND a.name = :name';
729 
730  try {
731  $query = MatrixDAL::preparePdoQuery($sql);
732  MatrixDAL::bindValueToPdo($query, 'create_loc', $create_loc);
733  MatrixDAL::bindValueToPdo($query, 'name', $name);
734  $existing_file = MatrixDAL::executePdoOne($query);
735  } catch (Exception $e) {
736  throw new Exception('Unable to get the uploaded file assetid due to database error: '.$e->getMessage());
737  }
738  }
739  }
740 
741  return $existing_file;
742 
743  }//end getUploadedFileId()
744 
745 
746 }//end class
747 ?>