PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/media/zoo/elements/download/download.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 428 lines | 205 code | 80 blank | 143 comment | 54 complexity | bb49124a2816e3c83e0733f1100ef803 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * @package com_zoo
  4. * @author YOOtheme http://www.yootheme.com
  5. * @copyright Copyright (C) YOOtheme GmbH
  6. * @license http://www.gnu.org/licenses/gpl.html GNU/GPL
  7. */
  8. // register ElementFile class
  9. App::getInstance('zoo')->loader->register('ElementFile', 'elements:file/file.php');
  10. /*
  11. Class: ElementDownload
  12. The file download element class
  13. */
  14. class ElementDownload extends ElementFile implements iSubmittable {
  15. /*
  16. Function: Constructor
  17. */
  18. public function __construct() {
  19. // call parent constructor
  20. parent::__construct();
  21. // set defaults
  22. $this->config->set('secret', $this->app->system->config->getValue('config.secret'));
  23. // set callbacks
  24. $this->registerCallback('download');
  25. if ($this->app->system->application->isAdmin()) {
  26. $this->registerCallback('reset');
  27. $this->registerCallback('files');
  28. }
  29. }
  30. /*
  31. Function: getSize
  32. Gets the download file size.
  33. Returns:
  34. String - Download file with KB/MB suffix
  35. */
  36. public function getSize() {
  37. return $this->app->filesystem->formatFilesize($this->get('size', 0));
  38. }
  39. /*
  40. Function: getSize
  41. Gets the download file size.
  42. Returns:
  43. String - Download file with KB/MB suffix
  44. */
  45. public function isDownloadLimitReached() {
  46. return ($limit = $this->get('download_limit')) && $this->get('hits', 0) >= $limit;
  47. }
  48. /*
  49. Function: getLink
  50. Gets the link to the download.
  51. Returns:
  52. String - link
  53. */
  54. public function getLink() {
  55. // init vars
  56. $download_mode = $this->config->get('download_mode');
  57. // create download link
  58. $query = array('task' => 'callelement', 'format' => 'raw', 'item_id' => $this->_item->id, 'element' => $this->identifier, 'method' => 'download');
  59. if ($download_mode == 1) {
  60. return $this->app->link($query);
  61. } else if ($download_mode == 2) {
  62. $query['args[0]'] = $this->filecheck();
  63. return $this->app->link($query);
  64. } else {
  65. return $this->get('file');
  66. }
  67. }
  68. /*
  69. Function: render
  70. Renders the element.
  71. Parameters:
  72. $params - render parameter
  73. Returns:
  74. String - html
  75. */
  76. public function render($params = array()) {
  77. // init vars
  78. $params = $this->app->data->create($params);
  79. $file = $this->get('file');
  80. $filename = basename($file);
  81. // render layout
  82. if ($layout = $this->getLayout()) {
  83. return $this->renderLayout($layout,
  84. array(
  85. 'file' => $file,
  86. 'filename' => $filename,
  87. 'size' => $this->getSize(),
  88. 'hits' => (int) $this->get('hits', 0),
  89. 'download_name' => $this->app->string->str_ireplace('{filename}', $filename, $params->get('download_name', '')),
  90. 'download_link' => $this->getLink(),
  91. 'filetype' => $this->getExtension(),
  92. 'display' => $params->get('display', null),
  93. 'limit_reached' => $this->isDownloadLimitReached(),
  94. 'download_limit' => $this->get('download_limit')
  95. )
  96. );
  97. }
  98. }
  99. /*
  100. Function: download
  101. Download the file.
  102. Returns:
  103. Binary - File data
  104. */
  105. public function download($check = '') {
  106. // init vars
  107. $filepath = $this->app->path->path('root:'.$this->get('file'));
  108. $download_mode = $this->config->get('download_mode');
  109. // check access
  110. if (!$this->canAccess()) {
  111. header('Content-Type: text/html');
  112. echo JText::_('Unable to access download!');
  113. return;
  114. }
  115. // check limit
  116. if ($this->isDownloadLimitReached()) {
  117. header('Content-Type: text/html');
  118. echo JText::_('Download limit reached!');
  119. return;
  120. }
  121. // trigger on download event
  122. $canDownload = true;
  123. $this->app->event->dispatcher->notify($this->app->event->create($this, 'element:download', array('check' => $check, 'canDownload' => &$canDownload)));
  124. if ($canDownload) {
  125. // output file
  126. if ($download_mode == 1 && is_readable($filepath) && is_file($filepath)) {
  127. $this->set('hits', $this->get('hits', 0) + 1);
  128. $this->app->filesystem->output($filepath);
  129. } else if ($download_mode == 2 && $this->filecheck() == $check && is_readable($filepath) && is_file($filepath)) {
  130. $this->set('hits', $this->get('hits', 0) + 1);
  131. $this->app->filesystem->output($filepath);
  132. } else {
  133. header('Content-Type: text/html');
  134. echo JText::_('Invalid file!');
  135. }
  136. // save item
  137. $this->app->table->item->save($this->getItem());
  138. }
  139. }
  140. /*
  141. Function: filecheck
  142. Get the file check string.
  143. Returns:
  144. String - md5(file + secret + date)
  145. */
  146. public function filecheck() {
  147. return md5($this->get('file').$this->config->get('secret').date('Y-m-d'));
  148. }
  149. /*
  150. Function: edit
  151. Renders the edit form field.
  152. Returns:
  153. String - html
  154. */
  155. public function edit(){
  156. // create info
  157. $info[] = JText::_('Size').': '.$this->getSize();
  158. $info[] = JText::_('Hits').': '.(int) $this->get('hits', 0);
  159. $info = ' ('.implode(', ', $info).')';
  160. if ($layout = $this->getLayout('edit.php')) {
  161. return $this->renderLayout($layout,
  162. array(
  163. 'info' => $info,
  164. 'hits' => $this->get('hits', 0)
  165. )
  166. );
  167. }
  168. }
  169. /*
  170. Function: loadAssets
  171. Load elements css/js assets.
  172. Returns:
  173. Void
  174. */
  175. public function loadAssets() {
  176. parent::loadAssets();
  177. $this->app->document->addScript('elements:download/assets/js/download.js');
  178. }
  179. public function reset() {
  180. $this->set('hits', 0);
  181. //save item
  182. $this->app->table->item->save($this->getItem());
  183. return $this->edit();
  184. }
  185. /*
  186. Function: bindData
  187. Set data through data array.
  188. Parameters:
  189. $data - array
  190. Returns:
  191. Void
  192. */
  193. public function bindData($data = array()) {
  194. parent::bindData($data);
  195. // add size to data
  196. $filepath = $this->app->path->path('root:'.$this->get('file'));
  197. if (is_readable($filepath) && is_file($filepath)) {
  198. $this->set('size', sprintf('%u', filesize($filepath)));
  199. } else {
  200. $this->set('size', 0);
  201. }
  202. }
  203. /*
  204. Function: renderSubmission
  205. Renders the element in submission.
  206. Parameters:
  207. $params - AppData submission parameters
  208. Returns:
  209. String - html
  210. */
  211. public function renderSubmission($params = array()) {
  212. // get params
  213. $trusted_mode = $params->get('trusted_mode');
  214. // init vars
  215. $upload = $this->get('file');
  216. if (empty($upload) && $trusted_mode) {
  217. $upload = $this->get('upload');
  218. }
  219. // is uploaded file
  220. $upload = is_array($upload) ? '' : $upload;
  221. // build upload select
  222. $lists = array();
  223. if ($trusted_mode) {
  224. $options = array($this->app->html->_('select.option', '', '- '.JText::_('Select File').' -'));
  225. if (!empty($upload) && !$this->_inUploadPath($upload)) {
  226. $options[] = $this->app->html->_('select.option', $upload, '- '.JText::_('No Change').' -');
  227. }
  228. foreach ($this->app->path->files('root:'.$this->_getUploadPath()) as $file) {
  229. $options[] = $this->app->html->_('select.option', $this->_getUploadPath().'/'.$file, basename($file));
  230. }
  231. $lists['upload_select'] = $this->app->html->_('select.genericlist', $options, $this->getControlName('upload'), 'class="upload"', 'value', 'text', $upload);
  232. }
  233. if (!empty($upload)) {
  234. $upload = basename($upload);
  235. }
  236. if ($layout = $this->getLayout('submission.php')) {
  237. return $this->renderLayout($layout,
  238. compact('lists', 'upload', 'trusted_mode')
  239. );
  240. }
  241. }
  242. /*
  243. Function: validateSubmission
  244. Validates the submitted element
  245. Parameters:
  246. $value - AppData value
  247. $params - AppData submission parameters
  248. Returns:
  249. Array - cleaned value
  250. */
  251. public function validateSubmission($value, $params) {
  252. // init vars
  253. $trusted_mode = $params->get('trusted_mode');
  254. // get old file value
  255. $old_file = $this->get('file');
  256. $file = '';
  257. // get file from select list
  258. if ($trusted_mode && $file = $value->get('upload')) {
  259. if (!$this->_inUploadPath($file) && $file != $old_file) {
  260. throw new AppValidatorException(sprintf('This file is not located in the upload directory.'));
  261. }
  262. if (!JFile::exists($file)) {
  263. throw new AppValidatorException(sprintf('This file does not exist.'));
  264. }
  265. // get file from upload
  266. } else {
  267. try {
  268. // get the uploaded file information
  269. $userfile = $value->get('userfile', null);
  270. // get legal extensions
  271. $extensions = array_map(create_function('$ext', 'return strtolower(trim($ext));'), explode(',', $this->config->get('upload_extensions', 'png,jpg,doc,mp3,mov,avi,mpg,zip,rar,gz')));
  272. //get legal mime types
  273. $legal_mime_types = $this->app->data->create(array_intersect_key($this->app->filesystem->getMimeMapping(), array_flip($extensions)))->flattenRecursive();
  274. // get max upload size
  275. $max_upload_size = $this->config->get('max_upload_size', '512') * 1024;
  276. $max_upload_size = empty($max_upload_size) ? null : $max_upload_size;
  277. // validate
  278. $file = $this->app->validator
  279. ->create('file', array('mime_types' => $legal_mime_types, 'max_size' => $max_upload_size))
  280. ->addMessage('mime_types', 'Uploaded file is not of a permitted type.')
  281. ->clean($userfile);
  282. } catch (AppValidatorException $e) {
  283. if ($e->getCode() != UPLOAD_ERR_NO_FILE) {
  284. throw $e;
  285. }
  286. if (!$trusted_mode && $old_file && $value->get('upload')) {
  287. $file = $old_file;
  288. }
  289. }
  290. }
  291. if ($params->get('required') && empty($file)) {
  292. throw new AppValidatorException('Please select a file to upload.');
  293. }
  294. $download_limit = (string) $this->app->validator
  295. ->create('integer', array('required' => false), array('number' => 'The Download Limit needs to be a number.'))
  296. ->clean($value->get('download_limit'));
  297. // connect to submission beforesave event
  298. $this->app->event->dispatcher->connect('submission:beforesave', array($this, 'submissionBeforeSave'));
  299. return compact('file', 'download_limit');
  300. }
  301. /*
  302. Function: submissionBeforeSave
  303. Callback before item submission is saved
  304. Returns:
  305. void
  306. */
  307. public function submissionBeforeSave() {
  308. // get the uploaded file information
  309. if (($userfile = $this->get('file')) && is_array($userfile)) {
  310. // get file name
  311. $ext = $this->app->filesystem->getExtension($userfile['name']);
  312. $base_path = JPATH_ROOT . '/' . $this->_getUploadPath() . '/';
  313. $file = $base_path . $userfile['name'];
  314. $filename = basename($file, '.'.$ext);
  315. $i = 1;
  316. while (JFile::exists($file)) {
  317. $file = $base_path . $filename . '-' . $i++ . '.' . $ext;
  318. }
  319. if (!JFile::upload($userfile['tmp_name'], $file)) {
  320. throw new AppException('Unable to upload file.');
  321. }
  322. $this->app->zoo->putIndexFile(dirname($file));
  323. $this->set('file', $this->app->path->relative($file));
  324. }
  325. }
  326. protected function _inUploadPath($image) {
  327. return $this->_getUploadPath() == dirname($image);
  328. }
  329. protected function _getUploadPath() {
  330. return trim(trim($this->config->get('upload_directory', 'images/zoo/uploads/')), '\/');
  331. }
  332. }