file.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <?php
  2. /**
  3. * HTML class for a file upload field
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * LICENSE: This source file is subject to version 3.01 of the PHP license
  8. * that is available through the world-wide-web at the following URI:
  9. * http://www.php.net/license/3_01.txt If you did not receive a copy of
  10. * the PHP License and are unable to obtain it through the web, please
  11. * send a note to license@php.net so we can mail you a copy immediately.
  12. *
  13. * @category HTML
  14. * @package HTML_QuickForm
  15. * @author Adam Daniel <adaniel1@eesus.jnj.com>
  16. * @author Bertrand Mansion <bmansion@mamasam.com>
  17. * @author Alexey Borzov <avb@php.net>
  18. * @copyright 2001-2009 The PHP Group
  19. * @license http://www.php.net/license/3_01.txt PHP License 3.01
  20. * @version CVS: $Id: file.php,v 1.25 2009/04/04 21:34:02 avb Exp $
  21. * @link http://pear.php.net/package/HTML_QuickForm
  22. */
  23. /**
  24. * HTML class for a file upload field
  25. *
  26. * @category HTML
  27. * @package HTML_QuickForm
  28. * @author Adam Daniel <adaniel1@eesus.jnj.com>
  29. * @author Bertrand Mansion <bmansion@mamasam.com>
  30. * @author Alexey Borzov <avb@php.net>
  31. * @version Release: 3.2.11
  32. * @since 1.0
  33. */
  34. class HTML_QuickForm_file extends HTML_QuickForm_input
  35. {
  36. /**
  37. * Uploaded file data, from $_FILES
  38. * @var array
  39. */
  40. var $_value = null;
  41. /**
  42. * Class constructor
  43. *
  44. * @param string Input field name attribute
  45. * @param string Input field label
  46. * @param mixed (optional)Either a typical HTML attribute string
  47. * or an associative array
  48. * @since 1.0
  49. * @access public
  50. */
  51. public function __construct($elementName=null, $elementLabel=null, $attributes=null)
  52. {
  53. parent::__construct($elementName, $elementLabel, $attributes);
  54. $this->setType('file');
  55. }
  56. /**
  57. * Sets size of file element
  58. *
  59. * @param int Size of file element
  60. * @since 1.0
  61. * @access public
  62. */
  63. function setSize($size)
  64. {
  65. $this->updateAttributes(array('size' => $size));
  66. }
  67. /**
  68. * Returns size of file element
  69. *
  70. * @since 1.0
  71. * @access public
  72. * @return int
  73. */
  74. function getSize()
  75. {
  76. return $this->getAttribute('size');
  77. }
  78. /**
  79. * Freeze the element so that only its value is returned
  80. *
  81. * @access public
  82. * @return bool
  83. */
  84. function freeze()
  85. {
  86. return false;
  87. }
  88. /**
  89. * Sets value for file element.
  90. *
  91. * Actually this does nothing. The function is defined here to override
  92. * HTML_Quickform_input's behaviour of setting the 'value' attribute. As
  93. * no sane user-agent uses <input type="file">'s value for anything
  94. * (because of security implications) we implement file's value as a
  95. * read-only property with a special meaning.
  96. *
  97. * @param mixed Value for file element
  98. * @since 3.0
  99. * @access public
  100. */
  101. function setValue($value)
  102. {
  103. return null;
  104. }
  105. /**
  106. * Returns information about the uploaded file
  107. *
  108. * @since 3.0
  109. * @access public
  110. * @return array
  111. */
  112. public function getValue()
  113. {
  114. return $this->_value;
  115. } // end func getValue
  116. // }}}
  117. // {{{ onQuickFormEvent()
  118. /**
  119. * Called by HTML_QuickForm whenever form event is made on this element
  120. *
  121. * @param string Name of event
  122. * @param mixed event arguments
  123. * @param object calling object
  124. * @since 1.0
  125. * @access public
  126. * @return bool
  127. */
  128. public function onQuickFormEvent($event, $arg, &$caller)
  129. {
  130. switch ($event) {
  131. case 'updateValue':
  132. if ($caller->getAttribute('method') == 'get') {
  133. throw new \Exception('Cannot add a file upload field to a GET method form');
  134. }
  135. $this->_value = $this->_findValue();
  136. $caller->updateAttributes(array('enctype' => 'multipart/form-data'));
  137. $caller->setMaxFileSize();
  138. break;
  139. case 'addElement':
  140. $this->onQuickFormEvent('createElement', $arg, $caller);
  141. return $this->onQuickFormEvent('updateValue', null, $caller);
  142. break;
  143. case 'createElement':
  144. //$className = get_class($this);
  145. //$this &= new $className($arg[0], $arg[1], $arg[2]);
  146. break;
  147. }
  148. return true;
  149. }
  150. /**
  151. * Moves an uploaded file into the destination
  152. *
  153. * @param string Destination directory path
  154. * @param string New file name
  155. * @access public
  156. * @return bool Whether the file was moved successfully
  157. */
  158. public function moveUploadedFile($dest, $fileName = '')
  159. {
  160. if ($dest != '' && substr($dest, -1) != '/') {
  161. $dest .= '/';
  162. }
  163. $fileName = ($fileName != '') ? $fileName : basename($this->_value['name']);
  164. return move_uploaded_file($this->_value['tmp_name'], $dest . $fileName);
  165. }
  166. /**
  167. * Checks if the element contains an uploaded file
  168. *
  169. * @access public
  170. * @return bool true if file has been uploaded, false otherwise
  171. */
  172. public function isUploadedFile()
  173. {
  174. return self::_ruleIsUploadedFile($this->_value);
  175. }
  176. /**
  177. * Checks if the given element contains an uploaded file
  178. *
  179. * @param array Uploaded file info (from $_FILES)
  180. * @access private
  181. * @return bool true if file has been uploaded, false otherwise
  182. */
  183. public static function _ruleIsUploadedFile($elementValue)
  184. {
  185. if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
  186. (!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')) {
  187. return is_uploaded_file($elementValue['tmp_name']);
  188. } else {
  189. return false;
  190. }
  191. }
  192. /**
  193. * Tries to find the element value from the values array
  194. *
  195. * Needs to be redefined here as $_FILES is populated differently from
  196. * other arrays when element name is of the form foo[bar]
  197. *
  198. * @access public
  199. * @return mixed
  200. */
  201. public function _findValue(&$values = null)
  202. {
  203. if (empty($_FILES)) {
  204. return null;
  205. }
  206. $elementName = $this->getName();
  207. if (isset($_FILES[$elementName])) {
  208. return $_FILES[$elementName];
  209. } elseif (false !== ($pos = strpos($elementName, '['))) {
  210. $base = str_replace(
  211. array('\\', '\''), array('\\\\', '\\\''),
  212. substr($elementName, 0, $pos)
  213. );
  214. $idx = "['" . str_replace(
  215. array('\\', '\'', ']', '['), array('\\\\', '\\\'', '', "']['"),
  216. substr($elementName, $pos + 1, -1)
  217. ) . "']";
  218. $props = array('name', 'type', 'size', 'tmp_name', 'error');
  219. $code = "if (!isset(\$_FILES['{$base}']['name']{$idx})) {\n" .
  220. " return null;\n" .
  221. "} else {\n" .
  222. " \$value = array();\n";
  223. foreach ($props as $prop) {
  224. $code .= " \$value['{$prop}'] = \$_FILES['{$base}']['{$prop}']{$idx};\n";
  225. }
  226. return eval($code . " return \$value;\n}\n");
  227. } else {
  228. return null;
  229. }
  230. }
  231. /**
  232. * @return string
  233. */
  234. public function getElementJS($param)
  235. {
  236. $id = $this->getAttribute('id');
  237. $ratio = 'aspectRatio: 16 / 9';
  238. if (!empty($param['ratio'])) {
  239. $ratio = 'aspectRatio: '.$param['ratio'].',';
  240. }
  241. $scalable = 'false';
  242. if (!empty($param['scalable']) && $param['scalable'] != 'false') {
  243. $ratio = '';
  244. $scalable = $param['scalable'];
  245. }
  246. return '<script>
  247. $(function() {
  248. var $inputFile = $(\'#'.$id.'\'),
  249. $image = $(\'#'.$id.'_preview_image\'),
  250. $input = $(\'[name="'.$id.'_crop_result"]\'),
  251. $cropButton = $(\'#'.$id.'_crop_button\'),
  252. $formGroup = $(\'#'.$id.'-form-group\');
  253. function isValidType(file) {
  254. var fileTypes = [\'image/jpg\', \'image/jpeg\', \'image/gif\', \'image/png\'];
  255. for(var i = 0; i < fileTypes.length; i++) {
  256. if(file.type === fileTypes[i]) {
  257. return true;
  258. }
  259. }
  260. return false;
  261. }
  262. function imageCropper() {
  263. $formGroup.show();
  264. $cropButton.show();
  265. $image
  266. .cropper(\'destroy\')
  267. .cropper({
  268. '.$ratio.'
  269. responsive : true,
  270. center : false,
  271. guides : false,
  272. movable: false,
  273. zoomable: false,
  274. rotatable: false,
  275. scalable: '.$scalable.',
  276. crop: function(e) {
  277. // Output the result data for cropping image.
  278. $input.val(e.x + \',\' + e.y + \',\' + e.width + \',\' + e.height);
  279. }
  280. });
  281. }
  282. $inputFile.on(\'change\', function () {
  283. var inputFile = this,
  284. file = inputFile.files[0],
  285. fileReader = new FileReader();
  286. if (!isValidType(file)) {
  287. $formGroup.hide();
  288. $cropButton.hide();
  289. if (inputFile.setCustomValidity) {
  290. inputFile.setCustomValidity(
  291. inputFile.title ? inputFile.title : \''.get_lang('OnlyImagesAllowed').'\'
  292. );
  293. }
  294. return;
  295. }
  296. if (inputFile.setCustomValidity) {
  297. inputFile.setCustomValidity(\'\');
  298. }
  299. fileReader.readAsDataURL(file);
  300. fileReader.onload = function () {
  301. $image
  302. .attr(\'src\', this.result)
  303. .on(\'load\', imageCropper);
  304. };
  305. });
  306. $cropButton.on(\'click\', function () {
  307. var canvas = $image.cropper(\'getCroppedCanvas\'),
  308. dataUrl = canvas.toDataURL();
  309. $image.attr(\'src\', dataUrl).cropper(\'destroy\').off(\'load\', imageCropper);
  310. $(\'[name="'.$id.'_crop_image_base_64"]\').val(dataUrl);
  311. $cropButton.hide();
  312. });
  313. });
  314. </script>';
  315. }
  316. /**
  317. * @return string
  318. */
  319. public function toHtml()
  320. {
  321. $js = '';
  322. if (isset($this->_attributes['crop_image'])) {
  323. $ratio = '16 / 9';
  324. if (!empty($this->_attributes['crop_ratio'])) {
  325. $ratio = $this->_attributes['crop_ratio'];
  326. }
  327. $scalable = 'false';
  328. if (!empty($this->_attributes['crop_scalable'])) {
  329. $scalable = $this->_attributes['crop_scalable'];
  330. }
  331. $js = $this->getElementJS(array('ratio' => $ratio, 'scalable' => $scalable));
  332. }
  333. if ($this->isFrozen()) {
  334. return $this->getFrozenHtml();
  335. } else {
  336. $class = '';
  337. if (isset($this->_attributes['custom']) && $this->_attributes['custom']) {
  338. $class = 'input-file';
  339. }
  340. return $js.$this->_getTabs().
  341. '<input class="'.$class.'" '.$this->_getAttrString($this->_attributes).' />';
  342. }
  343. }
  344. /**
  345. * @param string $layout
  346. *
  347. * @return string
  348. */
  349. public function getTemplate($layout)
  350. {
  351. $name = $this->getName();
  352. $attributes = $this->getAttributes();
  353. $size = $this->calculateSize();
  354. switch ($layout) {
  355. case FormValidator::LAYOUT_INLINE:
  356. return '
  357. <div class="form-group {error_class}">
  358. <label {label-for} >
  359. <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
  360. {label}
  361. </label>
  362. {element}
  363. </div>';
  364. break;
  365. case FormValidator::LAYOUT_HORIZONTAL:
  366. if (isset($attributes['custom']) && $attributes['custom']) {
  367. $template = '
  368. <div class="input-file-container">
  369. {element}
  370. <label tabindex="0" {label-for} class="input-file-trigger">
  371. <i class="fa fa-picture-o fa-lg" aria-hidden="true"></i> {label}
  372. </label>
  373. </div>
  374. <p class="file-return"></p>
  375. <script>
  376. document.querySelector("html").classList.add(\'js\');
  377. var fileInput = document.querySelector( ".input-file" ),
  378. button = document.querySelector( ".input-file-trigger" ),
  379. the_return = document.querySelector(".file-return");
  380. button.addEventListener("keydown", function(event) {
  381. if ( event.keyCode == 13 || event.keyCode == 32 ) {
  382. fileInput.focus();
  383. }
  384. });
  385. button.addEventListener("click", function(event) {
  386. fileInput.focus();
  387. return false;
  388. });
  389. fileInput.addEventListener("change", function(event) {
  390. fileName = this.value;
  391. if (this.files[0]) {
  392. fileName = this.files[0].name;
  393. }
  394. the_return.innerHTML = fileName;
  395. });
  396. </script>
  397. ';
  398. } else {
  399. $template = '
  400. <div id="file_'.$name.'" class="form-group {error_class}">
  401. <label {label-for} class="col-sm-'.$size[0].' control-label" >
  402. <!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
  403. {label}
  404. </label>
  405. <div class="col-sm-'.$size[1].'">
  406. {icon}
  407. {element}
  408. <!-- BEGIN label_2 -->
  409. <p class="help-block">{label_2}</p>
  410. <!-- END label_2 -->
  411. <!-- BEGIN error -->
  412. <span class="help-inline help-block">{error}</span>
  413. <!-- END error -->
  414. </div>
  415. <div class="col-sm-'.$size[2].'">
  416. <!-- BEGIN label_3 -->
  417. {label_3}
  418. <!-- END label_3 -->
  419. </div>
  420. </div>';
  421. }
  422. return $template;
  423. break;
  424. case FormValidator::LAYOUT_BOX_NO_LABEL:
  425. return '
  426. <label {label-for}>{label}</label>
  427. <div class="input-group">
  428. {icon}
  429. {element}
  430. </div>';
  431. break;
  432. }
  433. }
  434. }