SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/FilesBackendDirectory.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16. use Exception;
  17.  
  18. use karmabunny\pdb\Exceptions\RowMissingException;
  19.  
  20.  
  21. /**
  22. * Backend for the files module which stores files in a local directory
  23. **/
  24. class FilesBackendDirectory extends FilesBackend
  25. {
  26.  
  27. /**
  28.   * Returns the relative URL for a given file.
  29.   *
  30.   * Use for content areas.
  31.   *
  32.   * @param int $id ID of entry in files table, or (deprecated) string: filename
  33.   * @return string e.g. file/download/123
  34.   */
  35. public function relUrl($id)
  36. {
  37. if (preg_match('/^[0-9]+$/', $id)) {
  38. return 'file/download/' . $id;
  39. }
  40.  
  41. $filename = $id;
  42. if (!$this->exists($filename)) {
  43. try {
  44. return File::lookupReplacementUrl($filename);
  45. } catch (RowMissingException $ex) {
  46. // No problem, return original (broken) URL
  47. }
  48. }
  49. return 'files/' . Enc::url($filename);
  50. }
  51.  
  52.  
  53. /**
  54.   * Returns the absolute URL for a given file id, including domain.
  55.   *
  56.   * @param int $id ID of entry in files table, or (deprecated) string: filename
  57.   * @return string e.g. http://example.com/file/download/123
  58.   */
  59. public function absUrl($id)
  60. {
  61. return Sprout::absRoot() . $this->relUrl($id);
  62. }
  63.  
  64.  
  65. /**
  66.   * Returns the relative URL for a dynamically resized image.
  67.   *
  68.   * Size formatting is as per {@see File::parseSizeString}, e.g. c400x300
  69.   *
  70.   * @param int $id ID or filename from record in files table
  71.   * @param string $size A code as per {@see File::parseSizeString}
  72.   * @return string HTML-safe relative URL, e.g. file/resize/c400x300/123_example.jpg
  73.   */
  74. public function resizeUrl($id, $size)
  75. {
  76. if (preg_match('/^[0-9]+$/', $id)) {
  77. try {
  78. $file_details = File::getDetails($id);
  79. $signature = Security::serverKeySign(['filename' => $file_details['filename'], 'size' => $size]);
  80. return sprintf('file/resize/%s/%s?s=%s', Enc::url($size), Enc::url($file_details['filename']), $signature);
  81. } catch (Exception $ex) {
  82. // This is doomed to fail
  83. return sprintf('file/resize/%s/missing.png', Enc::url($size));
  84. }
  85. }
  86.  
  87. $filename = $id;
  88. $signature = Security::serverKeySign(['filename' => $filename, 'size' => $size]);
  89.  
  90. if ($this->exists($filename)) {
  91. return sprintf('file/resize/%s/%s?s=%s', Enc::url($size), Enc::url($filename), $signature);
  92. }
  93.  
  94. try {
  95. $replacement = File::lookupReplacementUrl($filename);
  96.  
  97. if (preg_match('#^file/download/([0-9]+)$#', $replacement)) {
  98. $id = substr($replacement, strlen('file/download/'));
  99. $file_details = File::getDetails($id);
  100. if ($this->exists($file_details['filename'])) {
  101. return sprintf('file/resize/%s/%s?s=%s', Enc::url($size), Enc::url($file_details['filename']), $signature);
  102. }
  103. }
  104. } catch (Exception $ex) {
  105. }
  106. return sprintf('file/resize/%s/missing.png', Enc::url($size));
  107. }
  108.  
  109.  
  110. /**
  111.   * Returns TRUE if the file exists, and FALSE if it does not
  112.   **/
  113. public function exists($filename)
  114. {
  115. return file_exists(DOCROOT . 'files/' . $filename);
  116. }
  117.  
  118.  
  119. /**
  120.   * Returns the size, in bytes, of the specified file
  121.   **/
  122. public function size($filename)
  123. {
  124. return @filesize(DOCROOT . 'files/' . $filename);
  125. }
  126.  
  127.  
  128. /**
  129.   * Returns the modified time, in unix timestamp format, of the specified file
  130.   **/
  131. public function mtime($filename)
  132. {
  133. return @filemtime(DOCROOT . 'files/' . $filename);
  134. }
  135.  
  136.  
  137. /**
  138.   * Sets access and modification time of file
  139.   * @return bool True if successful
  140.   */
  141. public function touch($filename)
  142. {
  143. return @touch(DOCROOT . 'files/' . $filename);
  144. }
  145.  
  146.  
  147. /**
  148.   * Returns the size of an image, or false on failure.
  149.   *
  150.   * Output format is the same as getimagesize, but will be at a minimum:
  151.   * [0] => width, [1] => height, [2] => type
  152.   **/
  153. public function imageSize($filename)
  154. {
  155. return @getimagesize(DOCROOT . 'files/' . $filename);
  156. }
  157.  
  158.  
  159. /**
  160.   * Delete a file
  161.   **/
  162. public function delete($filename)
  163. {
  164. return @unlink(DOCROOT . 'files/' . $filename);
  165. }
  166.  
  167.  
  168. /**
  169.   * Returns all files which match the specified mask.
  170.   * I have a feeling this returns other sizes (e.g. .small) as well - which may not be ideal.
  171.   **/
  172. public function glob($mask)
  173. {
  174. $result = glob(DOCROOT . 'files/' . $mask);
  175. foreach ($result as &$res) {
  176. $res = basename($res);
  177. }
  178. return $result;
  179. }
  180.  
  181.  
  182. /**
  183.   * This is the equivalent of the php readfile function
  184.   **/
  185. public function readfile($filename)
  186. {
  187. return readfile(DOCROOT . 'files/' . $filename);
  188. }
  189.  
  190.  
  191. /**
  192.   * Returns file content as a string. Basically the same as file_get_contents
  193.   **/
  194. public function getString($filename)
  195. {
  196. return file_get_contents(DOCROOT . 'files/' . $filename);
  197. }
  198.  
  199.  
  200. /**
  201.   * Saves file content as a string. Basically the same as file_put_contents
  202.   **/
  203. public function putString($filename, $content)
  204. {
  205. $res = @file_put_contents(DOCROOT . 'files/' . $filename, $content);
  206. if (! $res) return false;
  207.  
  208. $res = @chmod(DOCROOT . 'files/' . $filename, 0666);
  209. if (! $res) return false;
  210.  
  211. $res = Replication::postFileUpdate($filename);
  212. if (! $res) return false;
  213.  
  214. return true;
  215. }
  216.  
  217.  
  218. /**
  219.   * Saves file content from a stream. Basically just fopen/stream_copy_to_stream/fclose
  220.   **/
  221. public function putStream($filename, $stream)
  222. {
  223. $fp = @fopen(DOCROOT . 'files/' . $filename, 'w');
  224. if (! $fp) return false;
  225.  
  226. $res = @stream_copy_to_stream($stream, $fp);
  227. if (! $res) return false;
  228.  
  229. $res = @fclose($fp);
  230. if (! $res) return false;
  231.  
  232. $res = @chmod(DOCROOT . 'files/' . $filename, 0666);
  233. if (! $res) return false;
  234.  
  235. $res = Replication::postFileUpdate($filename);
  236. if (! $res) return false;
  237.  
  238. return true;
  239. }
  240.  
  241.  
  242. /**
  243.   * Saves file content from an existing file
  244.   **/
  245. public function putExisting($filename, $existing)
  246. {
  247. $res = @copy($existing, DOCROOT . 'files/' . $filename);
  248. if (! $res) return false;
  249.  
  250. if ((fileperms(DOCROOT . 'files/' . $filename) & 0666) != 0666) {
  251. $res = @chmod(DOCROOT . 'files/' . $filename, 0666);
  252. if (!$res) return false;
  253. }
  254.  
  255. $res = Replication::postFileUpdate($filename);
  256. if (! $res) return false;
  257.  
  258. return true;
  259. }
  260.  
  261.  
  262. /**
  263.   * Create a copy of the file in a temporary directory.
  264.   * Don't forget to File::destroy_local_copy($temp_filename) when you're done!
  265.   *
  266.   * @param string $filename The file to copy into a temporary location
  267.   * @return string Temp filename or NULL on error
  268.   **/
  269. public function createLocalCopy($filename)
  270. {
  271. $temp_filename = APPPATH . 'temp/' . time() . '_' . $filename;
  272.  
  273. $res = @copy(DOCROOT . 'files/' . $filename, $temp_filename);
  274. if (! $res) return null;
  275.  
  276. return $temp_filename;
  277. }
  278.  
  279.  
  280. /**
  281.   * Remove a local copy of a file
  282.   *
  283.   * @param string $temp_filename The filename returned by createLocalCopy
  284.   **/
  285. public function cleanupLocalCopy($temp_filename)
  286. {
  287. @unlink($temp_filename);
  288. }
  289.  
  290.  
  291. /**
  292.   * Moves an uploaded file into the repository.
  293.   * Returns TRUE on success, FALSE on failure.
  294.   **/
  295. public function moveUpload($src, $filename)
  296. {
  297. if (is_link($src)) {
  298. // Don't attempt to move symlink onto itself
  299. if (realpath(readlink($src)) == realpath(DOCROOT . 'files/' . $filename)) {
  300. @unlink($src);
  301. return true;
  302. }
  303.  
  304. // Move file symlink points to, rather than symlink itself
  305. $src = readlink($src);
  306. }
  307.  
  308. $res = @rename($src, DOCROOT . 'files/' . $filename);
  309. if (! $res) return false;
  310.  
  311. $res = @chmod(DOCROOT . 'files/' . $filename, 0666);
  312. if (! $res) return false;
  313.  
  314. $res = Replication::postFileUpdate($filename);
  315. if (! $res) return false;
  316.  
  317. return true;
  318. }
  319.  
  320. }
  321.