PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/controllers/api/media.php

https://github.com/PHPFrame/Mashine
PHP | 495 lines | 386 code | 47 blank | 62 comment | 42 complexity | 130ec516d608e09a98185de049b0f809 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * src/controllers/api/media.php
  4. *
  5. * PHP version 5
  6. *
  7. * @category PHPFrame_Applications
  8. * @package Mashine
  9. * @subpackage ApiControllers
  10. * @author Lupo Montero <lupo@e-noise.com>
  11. * @copyright 2010 E-NOISE.COM LIMITED
  12. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  13. * @link http://github.com/E-NOISE/Mashine
  14. */
  15. /**
  16. * Media API Controller.
  17. *
  18. * @category PHPFrame_Applications
  19. * @package Mashine
  20. * @author Lupo Montero <lupo@e-noise.com>
  21. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  22. * @link http://github.com/E-NOISE/Mashine
  23. * @since 1.0
  24. */
  25. class MediaApiController extends PHPFrame_RESTfulController
  26. {
  27. private $_mapper, $_image_processor;
  28. /**
  29. * Get media.
  30. *
  31. * @param int $node [Optional] Relative path to directory or media file
  32. * (relative to the upload_dir).
  33. *
  34. * @return array|object Either a single media item object or an array
  35. * containing many media objects.
  36. * @since 1.0
  37. */
  38. public function get($node="", $limit=10, $page=1)
  39. {
  40. if (empty($limit)) {
  41. $limit = 10;
  42. }
  43. if (empty($page)) {
  44. $page = 1;
  45. }
  46. $ret = $this->_createNode($node);
  47. if (!$this->returnInternalPHP()) {
  48. $ret = $ret->getRestfulRepresentation();
  49. }
  50. return $this->handleReturnValue($ret);
  51. }
  52. /**
  53. * Handle image upload.
  54. *
  55. * @param string $parent [Optional]
  56. *
  57. * @todo this method uses global files array from request to access files
  58. * sent by client. need to find a nice and restful way of implementing this.
  59. * It is a but messy now :-(
  60. *
  61. * @return void
  62. * @since 2.0
  63. */
  64. public function upload($parent="")
  65. {
  66. $parent = $this->_filterNodePath($parent);
  67. $files = $this->request()->files();
  68. if (!array_key_exists("upload_file", $files)
  69. || !array_key_exists("name", $files["upload_file"])
  70. || empty($files["upload_file"]["name"])
  71. ) {
  72. throw new Exception(MediaLang::UPLOAD_ERROR_NO_FILE_SENT);
  73. }
  74. $options = $this->request()->param("_options");
  75. $options = $options->filterByPrefix("mediaplugin_");
  76. $dest = $this->app()->getInstallDir().DS."public".DS;
  77. $dest .= $options["upload_dir"];
  78. $accept = "image/jpeg,image/jpg,image/gif,image/png,application/zip";
  79. $accept .= ",application/x-zip,application/x-zip-compressed";
  80. if ($parent) {
  81. $dest .= DS.$parent;
  82. }
  83. $upload_file = PHPFrame_Filesystem::upload(
  84. $files["upload_file"],
  85. $dest,
  86. $accept
  87. );
  88. $parent = $this->_createNode($parent);
  89. $config = $parent->getConfig();
  90. $finfo = $upload_file["finfo"];
  91. $mimetype = $upload_file["mimetype"];
  92. switch ($mimetype) {
  93. case "image/jpeg" :
  94. case "image/jpg" :
  95. case "image/png" :
  96. case "image/gif" :
  97. $this->_resizeImage($finfo->getRealPath(), $config);
  98. $this->_createThumb($finfo->getRealPath(), $config);
  99. $rel_path = $parent->getRelativePath()."/".$finfo->getFilename();
  100. $ret = $this->_createNode($rel_path);
  101. break;
  102. case "application/zip" :
  103. case "application/x-zip" :
  104. case "application/x-zip-compressed" :
  105. $this->_handleZipUpload($finfo->getRealPath(), $config);
  106. $ret = $this->_createNode($parent->getRelativePath());
  107. break;
  108. default :
  109. throw new RuntimeException(MediaLang::UPLOAD_ERROR_FILETYPE);
  110. break;
  111. }
  112. if (!$this->returnInternalPHP()) {
  113. $ret = $ret->getRestfulRepresentation();
  114. }
  115. return $this->handleReturnValue($ret);
  116. }
  117. /**
  118. * Delete media.
  119. *
  120. * @param int $node The media item's relative path.
  121. *
  122. * @return void
  123. * @since 1.0
  124. */
  125. public function delete($node)
  126. {
  127. $node = $this->_createNode($node);
  128. if ($node->isDir()) {
  129. $thumb_dir = $node->getRealPath().DS."thumb";
  130. if (is_dir($thumb_dir) && !rmdir($thumb_dir)) {
  131. throw new Exception(MediaLang::DIR_DELETE_ERROR);
  132. }
  133. if (!rmdir($node->getRealPath())) {
  134. throw new Exception(MediaLang::DIR_DELETE_ERROR);
  135. }
  136. $ret = array("success"=>MediaLang::DIR_DELETE_OK);
  137. } else {
  138. $caption_path = $node->getCaptionPath();
  139. if (is_file($caption_path) && !unlink($caption_path)) {
  140. throw new Exception(MediaLang::CAPTION_DELETE_ERROR);
  141. }
  142. $thumb_path = $node->getThumbPath();
  143. if (!preg_match("/no_thumb\.png$/", $thumb_path)
  144. && is_file($thumb_path)
  145. && !unlink($thumb_path)
  146. ) {
  147. throw new Exception(MediaLang::THUMB_DELETE_ERROR);
  148. }
  149. if (!unlink($node->getRealPath())) {
  150. throw new Exception(MediaLang::FILE_DELETE_ERROR);
  151. }
  152. $ret = array("success"=>MediaLang::FILE_DELETE_OK);
  153. }
  154. return $this->handleReturnValue($ret);
  155. }
  156. public function mkdir($parent, $name)
  157. {
  158. $name = PHPFrame_Filesystem::filterFilename($name, true);
  159. $parent_node = $this->_createNode($parent, "dir");
  160. $new_dir_path = $parent_node->getRealPath().DS.$name;
  161. if (is_dir($new_dir_path)) {
  162. throw new Exception(MediaLang::NEW_DIR_ERROR_ALREADY_EXISTS);
  163. } elseif (!@mkdir($new_dir_path)) {
  164. throw new Exception(MediaLang::NEW_DIR_ERROR);
  165. }
  166. $parent = (empty($parent)) ? "" : $parent."/";
  167. $ret = $this->_createNode($parent.$name, "dir");
  168. if (!$this->returnInternalPHP()) {
  169. $ret = $ret->getRestfulRepresentation();
  170. }
  171. return $this->handleReturnValue($ret);
  172. }
  173. public function generate_thumbs($node)
  174. {
  175. $node = $this->_createNode($node);
  176. $config = $node->getConfig();
  177. $resize_ok = true;
  178. if ($node instanceof Image) {
  179. $resize_ok = $this->_createThumb($node->getRealPath(), $config);
  180. } elseif ($node instanceof MediaDirectory) {
  181. foreach ($node as $child) {
  182. if ($child instanceof Image) {
  183. if (!$this->_createThumb($child->getRealPath(), $config)) {
  184. $resize_ok = false;
  185. break;
  186. }
  187. }
  188. }
  189. }
  190. if (!$resize_ok) {
  191. throw new Exception("Error generating thumbnails.");
  192. }
  193. if (!$this->returnInternalPHP()) {
  194. $node = $node->getRestfulRepresentation();
  195. }
  196. return $this->handleReturnValue($node);
  197. }
  198. public function resize($node)
  199. {
  200. $node = $this->_createNode($node);
  201. $config = $node->getConfig();
  202. $resize_ok = true;
  203. if ($node instanceof Image) {
  204. $resize_ok = $this->_resizeImage($node->getRealPath(), $config);
  205. } elseif ($node instanceof MediaDirectory) {
  206. foreach ($node as $child) {
  207. if ($child instanceof Image) {
  208. if (!$this->_resizeImage($child->getRealPath(), $config)) {
  209. $resize_ok = false;
  210. break;
  211. }
  212. }
  213. }
  214. }
  215. if (!$resize_ok) {
  216. throw new Exception("Error resizing images.");
  217. }
  218. if (!$this->returnInternalPHP()) {
  219. $node = $node->getRestfulRepresentation();
  220. }
  221. return $this->handleReturnValue($node);
  222. }
  223. /**
  224. * Get/set current gallery node.
  225. *
  226. * @param string $str Relative path to current node.
  227. * @param string $ensure_type [Optional] Either 'dir' or 'img'.
  228. *
  229. * @return MediaDirectory
  230. * @since 2.0
  231. */
  232. private function _createNode($str, $ensure_type=null)
  233. {
  234. $str = $this->_filterNodePath($str);
  235. if (!in_array($ensure_type, array(null, "dir", "img"))) {
  236. $msg = "Can not ensure unknown node type!";
  237. throw new InvalidArgumentException($msg);
  238. }
  239. $options = $this->request()->param("_options");
  240. $upload_dir = $options["mediaplugin_upload_dir"];
  241. $node_path = $this->app()->getInstallDir().DS."public".DS.$upload_dir;
  242. $node_path .= DS.$str;
  243. if (is_dir($node_path)) {
  244. $node_class = "MediaDirectory";
  245. } elseif (is_file($node_path)) {
  246. $node_class = "Image";
  247. } else {
  248. $msg = "Can not create node object. Requested file does not exist.";
  249. throw new RuntimeException($msg);
  250. }
  251. $ensure_class = null;
  252. if ($ensure_type == "dir") {
  253. $ensure_class = "MediaDirectory";
  254. } elseif ($ensure_type == "img") {
  255. $ensure_class = "Image";
  256. }
  257. if ($ensure_class && $node_class != $ensure_class) {
  258. $msg = "Node is not of expected type. The given path point's to a ";
  259. $msg .= ($ensure_type == "img") ? "directory" : "image";
  260. $msg .= " and tried to ensure type was of type '";
  261. $msg .= ($ensure_type == "img") ? "image" : "directory";
  262. $msg .= "'.";
  263. throw new RuntimeException($msg);
  264. }
  265. $options = $this->request()->param("_options");
  266. $config = $options->filterByPrefix("mediaplugin_");
  267. $config["site_path"] = $this->app()->getInstallDir().DS."public";
  268. $config["site_url"] = $this->app()->config()->get("base_url");
  269. return new $node_class($config, $str);
  270. }
  271. /**
  272. * Filter dir or img node relative path.
  273. *
  274. * @param string $str The string to filter.
  275. * @param bool $sanitise [Optional] Flag indicating whether or not to
  276. * sanitise (remove dodgy characters).
  277. *
  278. * @return string
  279. * @throws InvalidArgumentException
  280. * @since 2.0
  281. */
  282. private function _filterNodePath($str, $sanitise=false)
  283. {
  284. $parts = explode(DS, $str);
  285. $clean = array();
  286. foreach ($parts as $part) {
  287. $clean[] = PHPFrame_Filesystem::filterFilename($part, $sanitise);
  288. }
  289. return implode(DS, $clean);
  290. }
  291. /**
  292. * Handle zip file uploaded to gallery.
  293. *
  294. * @param string $fname Absolute path to uploaded zip file.
  295. * @param array $config Configuration array of the parent dir.
  296. *
  297. * @return void
  298. * @since 2.0
  299. */
  300. private function _handleZipUpload($fname, array $config)
  301. {
  302. $archive_class_file = $this->app()->getInstallDir().DS."lib".DS;
  303. $archive_class_file .= "PEAR".DS."Archive".DS."Zip.php";
  304. include $archive_class_file;
  305. // Extract to tmp dir
  306. $tmp = PHPFrame_Filesystem::getSystemTempDir().DS.uniqid();
  307. if (is_dir($tmp)) {
  308. PHPFrame_Filesystem::rm($tmp, true);
  309. }
  310. PHPFrame_Filesystem::ensureWritableDir($tmp);
  311. $tmp = realpath($tmp);
  312. $archive = new Archive_Zip($fname);
  313. if (!$archive->extract(array("add_path"=>$tmp))) {
  314. throw new RuntimeException($archive->errorInfo());
  315. }
  316. $dir_it = new RecursiveDirectoryIterator($tmp);
  317. $mode = RecursiveIteratorIterator::LEAVES_ONLY;
  318. $iterator = new RecursiveIteratorIterator($dir_it, $mode);
  319. foreach ($iterator as $file) {
  320. switch (PHPFrame_Filesystem::getMimeType($file->getRealPath())) {
  321. case "image/jpg" :
  322. case "image/jpeg" :
  323. case "image/png" :
  324. case "image/gif" :
  325. $dest = substr($fname, 0, strrpos($fname, "."));
  326. PHPFrame_Filesystem::ensureWritableDir($dest);
  327. $dest .= DS.$file->getFilename();
  328. if (!copy($file->getRealPath(), $dest)) {
  329. $msg = MediaLang::UPLOAD_ERROR_EXTRACTING_ARCHIVE;
  330. throw new RuntimeException($msg);
  331. }
  332. $this->_resizeImage($dest, $config);
  333. $this->_createThumb($dest, $config);
  334. break;
  335. }
  336. }
  337. // Clean up
  338. PHPFrame_Filesystem::rm($fname);
  339. PHPFrame_Filesystem::rm($tmp, true);
  340. }
  341. /**
  342. * Resize image.
  343. *
  344. * @param string $fname Absolute path to image file.
  345. * @param array $config Configuration array of the parent dir.
  346. *
  347. * @return bool
  348. * @since 1.0
  349. */
  350. private function _resizeImage($fname, array $config)
  351. {
  352. $img_processor = $this->_getImageProcessor();
  353. $resize_ok = $img_processor->resize(
  354. $fname,
  355. $fname,
  356. $config["max_width"],
  357. $config["max_height"],
  358. $config["imgcomp"]
  359. );
  360. if (!$resize_ok) {
  361. //$this->raiseError(end($img_processor->getMessages()));
  362. return false;
  363. }
  364. return true;
  365. }
  366. /**
  367. * Create thumbnail.
  368. *
  369. * @param string $fname Absolute path to image file.
  370. * @param array $config Configuration array of the parent dir.
  371. *
  372. * @return void
  373. * @since 1.0
  374. */
  375. private function _createThumb($fname, array $config)
  376. {
  377. $array = explode(DS, $fname);
  378. $thumb_dir = "";
  379. $thumb_name = "";
  380. for ($i=0; $i<count($array); $i++) {
  381. if ($i > 0) {
  382. $thumb_dir .= DS;
  383. }
  384. if ($i == count($array)-1) {
  385. $thumb_dir .= "thumb".DS;
  386. $thumb_name = $array[$i];
  387. } else {
  388. $thumb_dir .= $array[$i];
  389. }
  390. }
  391. if (!is_dir($thumb_dir) && !mkdir($thumb_dir)) {
  392. $msg = sprintf(MediaLang::GENERATE_THUMB_ERROR, $thumb_dir);
  393. throw new RuntimeException($msg);
  394. }
  395. $img_processor = $this->_getImageProcessor();
  396. $resize_ok = $img_processor->resize(
  397. $fname,
  398. $thumb_dir.DS.$thumb_name,
  399. $config["thumb_width"],
  400. $config["thumb_height"],
  401. $config["imgcomp"]
  402. );
  403. if (!$resize_ok) {
  404. //$this->raiseError(end($img_processor->getMessages()));
  405. return false;
  406. }
  407. return true;
  408. }
  409. /**
  410. * Get ImageProcessor object.
  411. *
  412. * @return PHPFrame_ImageProcessor
  413. * @since 1.0
  414. */
  415. private function _getImageProcessor()
  416. {
  417. if (is_null($this->_image_processor)) {
  418. $this->_image_processor = new PHPFrame_ImageProcessor();
  419. }
  420. return $this->_image_processor;
  421. }
  422. }