PageRenderTime 32ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Reviews/Base/UploadHandler.php

https://github.com/zikula-modules/Reviews
PHP | 404 lines | 233 code | 51 blank | 120 comment | 48 complexity | 8ad2b2d904d1bad5db0c7442139c78e0 MD5 | raw file
  1. <?php
  2. /**
  3. * Reviews.
  4. *
  5. * @copyright Michael Ueberschaer (MU)
  6. * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
  7. * @package Reviews
  8. * @author Michael Ueberschaer <kontakt@webdesign-in-bremen.com>.
  9. * @link http://webdesign-in-bremen.com
  10. * @link http://zikula.org
  11. * @version Generated by ModuleStudio 0.6.2 (http://modulestudio.de).
  12. */
  13. /**
  14. * Upload handler base class.
  15. */
  16. class Reviews_Base_UploadHandler
  17. {
  18. /**
  19. * @var array List of object types with upload fields.
  20. */
  21. protected $allowedObjectTypes;
  22. /**
  23. * @var array List of file types to be considered as images.
  24. */
  25. protected $imageFileTypes;
  26. /**
  27. * @var array List of dangerous file types to be rejected.
  28. */
  29. protected $forbiddenFileTypes;
  30. /**
  31. * @var array List of allowed file sizes per field.
  32. */
  33. protected $allowedFileSizes;
  34. /**
  35. * Constructor initialising the supported object types.
  36. */
  37. public function __construct()
  38. {
  39. $this->allowedObjectTypes = array('review');
  40. $this->imageFileTypes = array('gif', 'jpeg', 'jpg', 'png', 'swf');
  41. $this->forbiddenFileTypes = array('cgi', 'pl', 'asp', 'phtml', 'php', 'php3', 'php4', 'php5', 'exe', 'com', 'bat', 'jsp', 'cfm', 'shtml');
  42. $this->allowedFileSizes = array('review' => array('coverUpload' => 102400));
  43. }
  44. /**
  45. * Process a file upload.
  46. *
  47. * @param string $objectType Currently treated entity type.
  48. * @param string $fileData Form data array.
  49. * @param string $fieldName Name of upload field.
  50. *
  51. * @return array Resulting file name and collected meta data.
  52. */
  53. public function performFileUpload($objectType, $fileData, $fieldName)
  54. {
  55. $dom = ZLanguage::getModuleDomain('Reviews');
  56. $result = array('fileName' => '',
  57. 'metaData' => array());
  58. // check whether uploads are allowed for the given object type
  59. if (!in_array($objectType, $this->allowedObjectTypes)) {
  60. return $result;
  61. }
  62. // perform validation
  63. if (!$this->validateFileUpload($objectType, $fileData[$fieldName], $fieldName)) {
  64. // skip this upload field
  65. return $result;
  66. }
  67. // retrieve the final file name
  68. $fileName = $fileData[$fieldName]['name'];
  69. $fileNameParts = explode('.', $fileName);
  70. $extension = strtolower($fileNameParts[count($fileNameParts) - 1]);
  71. $extension = str_replace('jpeg', 'jpg', $extension);
  72. $fileNameParts[count($fileNameParts) - 1] = $extension;
  73. $fileName = implode('.', $fileNameParts);
  74. $serviceManager = ServiceUtil::getManager();
  75. $controllerHelper = new Reviews_Util_Controller($serviceManager);
  76. // retrieve the final file name
  77. try {
  78. $basePath = $controllerHelper->getFileBaseFolder($objectType, $fieldName);
  79. } catch (\Exception $e) {
  80. return LogUtil::registerError($e->getMessage());
  81. }
  82. $fileName = $this->determineFileName($objectType, $fieldName, $basePath, $fileName, $extension);
  83. if (!move_uploaded_file($fileData[$fieldName]['tmp_name'], $basePath . $fileName)) {
  84. return LogUtil::registerError(__('Error! Could not move your file to the destination folder.', $dom));
  85. }
  86. // collect data to return
  87. $result['fileName'] = $fileName;
  88. $result['metaData'] = $this->readMetaDataForFile($fileName, $basePath . $fileName);
  89. return $result;
  90. }
  91. /**
  92. * Check if an upload file meets all validation criteria.
  93. *
  94. * @param string $objectType Currently treated entity type.
  95. * @param array $file Reference to data of uploaded file.
  96. * @param string $fieldName Name of upload field.
  97. *
  98. * @return boolean true if file is valid else false
  99. */
  100. protected function validateFileUpload($objectType, $file, $fieldName)
  101. {
  102. $dom = ZLanguage::getModuleDomain('Reviews');
  103. $serviceManager = ServiceUtil::getManager();
  104. // check if a file has been uploaded properly without errors
  105. if ((!is_array($file)) || (is_array($file) && ($file['error'] != '0'))) {
  106. if (is_array($file)) {
  107. return $this->handleError($file);
  108. }
  109. return LogUtil::registerError(__('Error! No file found.', $dom));
  110. }
  111. // extract file extension
  112. $fileName = $file['name'];
  113. $fileNameParts = explode('.', $fileName);
  114. $extension = strtolower($fileNameParts[count($fileNameParts) - 1]);
  115. $extension = str_replace('jpeg', 'jpg', $extension);
  116. // validate extension
  117. $isValidExtension = $this->isAllowedFileExtension($objectType, $fieldName, $extension);
  118. if ($isValidExtension === false) {
  119. return LogUtil::registerError(__('Error! This file type is not allowed. Please choose another file format.', $dom));
  120. }
  121. // validate file size
  122. $maxSize = $this->allowedFileSizes[$objectType][$fieldName];
  123. if ($maxSize > 0) {
  124. $fileSize = filesize($file['tmp_name']);
  125. if ($fileSize > $maxSize) {
  126. $maxSizeKB = $maxSize / 1024;
  127. if ($maxSizeKB < 1024) {
  128. $maxSizeKB = DataUtil::formatNumber($maxSizeKB);
  129. return LogUtil::registerError(__f('Error! Your file is too big. Please keep it smaller than %s kilobytes.', array($maxSizeKB), $dom));
  130. }
  131. $maxSizeMB = $maxSizeKB / 1024;
  132. $maxSizeMB = DataUtil::formatNumber($maxSizeMB);
  133. return LogUtil::registerError(__f('Error! Your file is too big. Please keep it smaller than %s megabytes.', array($maxSizeMB), $dom));
  134. }
  135. }
  136. // validate image file
  137. $isImage = in_array($extension, $this->imageFileTypes);
  138. if ($isImage) {
  139. $imgInfo = getimagesize($file['tmp_name']);
  140. if (!is_array($imgInfo) || !$imgInfo[0] || !$imgInfo[1]) {
  141. return LogUtil::registerError(__('Error! This file type seems not to be a valid image.', $dom));
  142. }
  143. }
  144. return true;
  145. }
  146. /**
  147. * Read meta data from a certain file.
  148. *
  149. * @param string $fileName Name of file to be processed.
  150. * @param string $filePath Path to file to be processed.
  151. *
  152. * @return array collected meta data
  153. */
  154. public function readMetaDataForFile($fileName, $filePath)
  155. {
  156. $meta = array();
  157. if (empty($fileName)) {
  158. return $meta;
  159. }
  160. $extensionarr = explode('.', $fileName);
  161. $meta = array();
  162. $meta['extension'] = strtolower($extensionarr[count($extensionarr) - 1]);
  163. $meta['size'] = filesize($filePath);
  164. $meta['isImage'] = (in_array($meta['extension'], $this->imageFileTypes) ? true : false);
  165. if (!$meta['isImage']) {
  166. return $meta;
  167. }
  168. if ($meta['extension'] == 'swf') {
  169. $meta['isImage'] = false;
  170. }
  171. $imgInfo = getimagesize($filePath);
  172. if (!is_array($imgInfo)) {
  173. return $meta;
  174. }
  175. $meta['width'] = $imgInfo[0];
  176. $meta['height'] = $imgInfo[1];
  177. if ($imgInfo[1] < $imgInfo[0]) {
  178. $meta['format'] = 'landscape';
  179. } elseif ($imgInfo[1] > $imgInfo[0]) {
  180. $meta['format'] = 'portrait';
  181. } else {
  182. $meta['format'] = 'square';
  183. }
  184. return $meta;
  185. }
  186. /**
  187. * Determines the allowed file extensions for a given object type.
  188. *
  189. * @param string $objectType Currently treated entity type.
  190. * @param string $fieldName Name of upload field.
  191. * @param string $extension Input file extension.
  192. *
  193. * @return array the list of allowed file extensions
  194. */
  195. protected function isAllowedFileExtension($objectType, $fieldName, $extension)
  196. {
  197. // determine the allowed extensions
  198. $allowedExtensions = array();
  199. switch ($objectType) {
  200. case 'review':
  201. $allowedExtensions = array('gif', 'jpeg', 'jpg', 'png');
  202. break;
  203. }
  204. if (count($allowedExtensions) > 0) {
  205. if (!in_array($extension, $allowedExtensions)) {
  206. return false;
  207. }
  208. }
  209. if (in_array($extension, $this->forbiddenFileTypes)) {
  210. return false;
  211. }
  212. return true;
  213. }
  214. /**
  215. * Determines the final filename for a given input filename.
  216. *
  217. * It considers different strategies for computing the result.
  218. *
  219. * @param string $objectType Currently treated entity type.
  220. * @param string $fieldName Name of upload field.
  221. * @param string $basePath Base path for file storage.
  222. * @param string $fileName Input file name.
  223. * @param string $extension Input file extension.
  224. *
  225. * @return string the resulting file name
  226. */
  227. protected function determineFileName($objectType, $fieldName, $basePath, $fileName, $extension)
  228. {
  229. $backupFileName = $fileName;
  230. $namingScheme = 0;
  231. switch ($objectType) {
  232. case 'review':
  233. $namingScheme = 0;
  234. break;
  235. }
  236. $iterIndex = -1;
  237. do {
  238. if ($namingScheme == 0) {
  239. // original file name
  240. $fileNameCharCount = strlen($fileName);
  241. for ($y = 0; $y < $fileNameCharCount; $y++) {
  242. if (preg_match('/[^0-9A-Za-z_\.]/', $fileName[$y])) {
  243. $fileName[$y] = '_';
  244. }
  245. }
  246. // append incremented number
  247. if ($iterIndex > 0) {
  248. // strip off extension
  249. $fileName = str_replace('.' . $extension, '', $backupFileName);
  250. // add iterated number
  251. $fileName .= (string) ++$iterIndex;
  252. // readd extension
  253. $fileName .= '.' . $extension;
  254. } else {
  255. $iterIndex++;
  256. }
  257. } else if ($namingScheme == 1) {
  258. // md5 name
  259. $fileName = md5(uniqid(mt_rand(), TRUE)) . '.' . $extension;
  260. } else if ($namingScheme == 2) {
  261. // prefix with random number
  262. $fileName = $fieldName . mt_rand(1, 999999) . '.' . $extension;
  263. }
  264. }
  265. while (file_exists($basePath . $fileName)); // repeat until we have a new name
  266. // return the new file name
  267. return $fileName;
  268. }
  269. /**
  270. * Error handling helper method.
  271. *
  272. * @param array $file File array from $_FILES.
  273. *
  274. * @return boolean false
  275. */
  276. private function handleError($file)
  277. {
  278. $dom = ZLanguage::getModuleDomain('Reviews');
  279. $errmsg = '';
  280. switch ($file['error']) {
  281. case UPLOAD_ERR_OK: //no error; possible file attack!
  282. $errmsg = __('Unknown error', $dom);
  283. break;
  284. case UPLOAD_ERR_INI_SIZE: //uploaded file exceeds the upload_max_filesize directive in php.ini
  285. $errmsg = __('File too big', $dom);
  286. break;
  287. case UPLOAD_ERR_FORM_SIZE: //uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
  288. $errmsg = __('File too big', $dom);
  289. break;
  290. case UPLOAD_ERR_PARTIAL: //uploaded file was only partially uploaded
  291. $errmsg = __('File uploaded partially', $dom);
  292. break;
  293. case UPLOAD_ERR_NO_FILE: //no file was uploaded
  294. $errmsg = __('No file uploaded', $dom);
  295. break;
  296. case UPLOAD_ERR_NO_TMP_DIR: //missing a temporary folder
  297. $errmsg = __('No tmp folder', $dom);
  298. break;
  299. default: //a default (error, just in case! :)
  300. $errmsg = __('Unknown error', $dom);
  301. break;
  302. }
  303. return LogUtil::registerError(__('Error with upload: ', $dom) . $errmsg);
  304. }
  305. /**
  306. * Deletes an existing upload file.
  307. * For images the thumbnails are removed, too.
  308. *
  309. * @param string $objectType Currently treated entity type.
  310. * @param string $objectData Object data array.
  311. * @param string $fieldName Name of upload field.
  312. * @param integer $objectId Primary identifier of the given object.
  313. *
  314. * @return mixed Array with updated object data on success, else false.
  315. */
  316. public function deleteUploadFile($objectType, $objectData, $fieldName, $objectId)
  317. {
  318. if (!in_array($objectType, $this->allowedObjectTypes)) {
  319. return false;
  320. }
  321. if (empty($objectData[$fieldName])) {
  322. return $objectData;
  323. }
  324. $serviceManager = ServiceUtil::getManager();
  325. $controllerHelper = new Reviews_Util_Controller($serviceManager);
  326. // determine file system information
  327. try {
  328. $basePath = $controllerHelper->getFileBaseFolder($objectType, $fieldName);
  329. } catch (\Exception $e) {
  330. LogUtil::registerError($e->getMessage());
  331. return $objectData;
  332. }
  333. $fileName = $objectData[$fieldName];
  334. // path to original file
  335. $filePath = $basePath . $fileName;
  336. // check whether we have to consider thumbnails, too
  337. $fileExtension = FileUtil::getExtension($fileName, false);
  338. if (in_array($fileExtension, $this->imageFileTypes) && $fileExtension != 'swf') {
  339. // remove thumbnail images as well
  340. $manager = ServiceUtil::getManager()->getService('systemplugin.imagine.manager');
  341. $manager->setModule('Reviews');
  342. $fullObjectId = $objectType . '-' . $objectId;
  343. $manager->removeImageThumbs($filePath, $fullObjectId);
  344. }
  345. // remove original file
  346. if (!unlink($filePath)) {
  347. return false;
  348. }
  349. $objectData[$fieldName] = '';
  350. $objectData[$fieldName . 'Meta'] = array();
  351. return $objectData;
  352. }
  353. }