phpthumbof.class.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. <?php
  2. /**
  3. * pThumb
  4. * Copyright 2013, 2014 Jason Grant
  5. *
  6. * Please see the GitHub page for documentation or to report bugs:
  7. * https://github.com/oo12/phpThumbOf
  8. *
  9. * Forked from phpThumbOf 1.4.0
  10. * Copyright 2009-2012 by Shaun McCormick <shaun@modx.com>
  11. *
  12. * pThumb is free software; you can redistribute it and/or modify it
  13. * under the terms of the GNU General Public License as published by the Free
  14. * Software Foundation; either version 2 of the License, or (at your option) any
  15. * later version.
  16. *
  17. * pThumb is distributed in the hope that it will be useful, but WITHOUT ANY
  18. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  19. * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License along with
  22. * phpThumbOf; if not, write to the Free Software Foundation, Inc., 59 Temple
  23. * Place, Suite 330, Boston, MA 02111-1307 USA
  24. */
  25. class phpThumbOf {
  26. public $phpThumb;
  27. protected $modx;
  28. protected $config;
  29. protected $cacheimgRegex;
  30. private $input;
  31. function __construct(modX &$modx, &$settings_cache, $options, $s3info = 0) {
  32. $this->modx =& $modx;
  33. $this->config =& $settings_cache;
  34. if (empty($this->config)) { // first time through, get and store all the settings
  35. $this->config['assetsPath'] = $modx->getOption('assets_path', null, MODX_ASSETS_PATH);
  36. $this->config['httpHost'] = $modx->getOption('http_host', null, MODX_HTTP_HOST);
  37. if ( $this->config['use_ptcache'] = $modx->getOption('pthumb.use_ptcache', null, TRUE) ) {
  38. $this->config['cachePath'] = $modx->getOption('pthumb.ptcache_location', null, 'assets/image-cache', TRUE);
  39. if ($this->config['cachePath'] === '/') { // for safety, pThumb cache location has to be a subdir, can't be the web root
  40. $this->config['cachePath'] = 'assets/image-cache';
  41. }
  42. $this->config['cachePath'] = MODX_BASE_PATH . $this->config['cachePath'];
  43. $this->config['imagesBasedir'] = trim($modx->getOption('pthumb.ptcache_images_basedir', null, 'assets'), '/') . '/';
  44. $this->config['imagesBasedirLen'] = strlen($this->config['imagesBasedir']);
  45. }
  46. else {
  47. $this->config['cachePath'] = $modx->getOption('phpthumbof.cache_path', null, "{$this->config['assetsPath']}components/phpthumbof/cache", TRUE);
  48. $this->config['cachePath'] = str_replace(array('[[+assets_path]]', '[[+base_path]]'), array($this->config['assetsPath'], MODX_BASE_PATH), $this->config['cachePath']);
  49. $this->config['postfixPropertyHash'] = $modx->getOption('phpthumbof.postfix_property_hash', null, TRUE);
  50. }
  51. $this->config['cachePath'] = rtrim(str_replace('//', '/', $this->config['cachePath']), '/') . '/'; // just in case
  52. if (!is_writable($this->config['cachePath']) && !$modx->cacheManager->writeTree($this->config['cachePath'])) { // check cache writability
  53. $modx->log(modX::LOG_LEVEL_ERROR, "[pThumb] Cache path not writable: {$this->config['cachePath']}");
  54. $this->config['cacheNotWritable'] = true;
  55. return;
  56. }
  57. $this->config['cacheNotWritable'] = false;
  58. $cacheurl = rtrim($modx->getOption('phpthumbof.cache_url', null, $modx->getOption('base_url', null, MODX_BASE_URL), true), '/');
  59. $this->config['cachePathUrl'] = str_replace(MODX_BASE_PATH, "$cacheurl/", $this->config['cachePath']);
  60. $this->config['remoteImagesCachePath'] = "{$this->config['assetsPath']}components/phpthumbof/cache/remote-images/";
  61. $this->config['checkModTime'] = $modx->getOption('phpthumbof.check_mod_time', null, FALSE);
  62. parse_str($modx->getOption('pthumb.global_defaults', null, ''), $this->config['globalDefaults']);
  63. $this->config['useResizerGlobal'] = $modx->getOption('phpthumbof.use_resizer', null, FALSE);
  64. $this->config['s3outputMSglobal'] = $modx->getOption('pthumb.s3_output', null, 0, true);
  65. if ( $this->config['s3cachePath'] = trim($modx->getOption('pthumb.s3_cache_path', null, ''), '/') ) {
  66. $this->config['s3cachePath'] .= '/'; // only added if the string isn't empty
  67. }
  68. $this->config['s3multiImgGlobal'] = $s3info ? true : $modx->getOption('pthumb.s3_multi_img', null, false);
  69. if ($s3info) { // used by the cache cleaner class
  70. $this->cacheimgRegex = '/^' . str_replace('/', '\/', $this->config['s3cachePath']) . '.+\.(?:[0-9a-f]{8}|[0-9a-f]{32})\.(?:jpe?g|png|gif)$/'; // for safety, only select images with a hash
  71. }
  72. }
  73. // these can't be cached
  74. $this->config['debug'] = empty($options['debug']) ? FALSE : TRUE;
  75. $this->config['useResizer'] = isset($options['useResizer']) ? $options['useResizer'] : $this->config['useResizerGlobal'];
  76. // setup any S3 output media source
  77. if ( $this->config['s3outputMS'] = (int) (isset($options['s3output']) ? $options['s3output'] : $this->config['s3outputMSglobal']) ) {
  78. $this->config['s3outKey'] = "s3out{$this->config['s3outputMS']}";
  79. if (!isset($this->config[$this->config['s3outKey']])) { // if this MS isn't cached already
  80. $this->config["{$this->config['s3outKey']}_ok"] = false;
  81. $this->config[$this->config['s3outKey']] = $modx->getObject('modMediaSource', $this->config['s3outputMS']);
  82. $s3obj =& $this->config[$this->config['s3outKey']];
  83. if (strpos(get_class($s3obj), 'modS3MediaSource') === false) { // check for valid S3 media source
  84. $modx->log(modX::LOG_LEVEL_ERROR, "[pThumb] No such S3 output media source: {$this->config['s3outputMS']}");
  85. $this->config['s3outputMS'] = 0; // prevent any further S3 processing this time through
  86. $this->config[$this->config['s3outKey']] = false;
  87. }
  88. else { // initialize MS
  89. $this->config["{$this->config['s3outKey']}_ok"] = true;
  90. $s3properties = $s3obj->getPropertyList();
  91. $this->config["{$this->config['s3outKey']}_url"] = $s3properties['url'];
  92. $s3obj->bucket = $s3properties['bucket'];
  93. include_once MODX_CORE_PATH . 'model/aws/sdk.class.php';
  94. define('AWS_KEY', $s3properties['key']);
  95. define('AWS_SECRET_KEY', $s3properties['secret_key']);
  96. try { $s3obj->driver = new AmazonS3(); }
  97. catch (Exception $e) {
  98. $modx->log(modX::LOG_LEVEL_ERROR, "[pThumb] Error connecting to S3 media source {$this->config['s3outputMS']}: " . $e->getMessage());
  99. $this->config["{$this->config['s3outKey']}_ok"] = false;
  100. $this->config['s3outputMS'] = 0;
  101. }
  102. }
  103. }
  104. }
  105. $this->config['s3multiImg'] = isset($options['s3multiImg']) ? $options['s3multiImg'] : $this->config['s3multiImgGlobal'];
  106. if ($this->config['s3outputMS'] && $this->config["{$this->config['s3outKey']}_ok"] && $this->config['s3multiImg'] && !isset($this->config[$this->config['s3outKey'] . '_images'])) { // get a list of all objects in the bucket
  107. $s3obj =& $this->config[$this->config['s3outKey']];
  108. $opt = array();
  109. $objects = array();
  110. do { // list_objects only gets 1000 objects at a time, so we'll loop if necessary
  111. $list = $s3obj->driver->list_objects($s3obj->bucket, $opt);
  112. if (is_string($list->body)) {
  113. $list->body = new CFSimpleXML($list->body);
  114. }
  115. if ($s3info) { // also store last modified time and file size
  116. foreach ($list->body->Contents as $obj) {
  117. $key = (string) $obj->Key;
  118. if (preg_match($this->cacheimgRegex, $key)) {
  119. $objects[$key] = array(
  120. 'mod' => strtotime($obj->LastModified),
  121. 'size' => (int) $obj->Size
  122. );
  123. }
  124. }
  125. }
  126. elseif ( $keys = $list->body->query('descendant-or-self::Key')->map_string(null) ) { // otherwise just get object names
  127. $objects = array_merge($objects, $keys);
  128. }
  129. $body = (array) $list->body;
  130. $opt = array('marker' => (isset($body['Contents']) && is_array($body['Contents'])) ? ((string) end($body['Contents'])->Key) : ((string) $list->body->Contents->Key)); // set starting point for next request
  131. } while ((string) $list->body->IsTruncated === 'true');
  132. $this->config[$this->config['s3outKey'] . '_images'] = $objects;
  133. unset($objects);
  134. }
  135. }
  136. /*
  137. * Write current resource id, image filename and $msg to the MODX error log.
  138. * if $phpthumbDebug, also write the phpThumb debugmessages array
  139. */
  140. public function debugmsg($msg, $phpthumbDebug = FALSE) {
  141. $logmsg = '[pThumb] ' . (isset($this->modx->resource) ? "Resource: {$this->modx->resource->get('id')} || " : '') .
  142. 'Image: ' . (isset($this->input) ? $this->input : '(none)') .
  143. ($msg ? "\n$msg" : '');
  144. if ($phpthumbDebug && isset($this->phpThumb->debugmessages)) {
  145. $logmsg .= ($this->config['useResizer'] ? "\nResizer" : "\nphpThumb") .
  146. ' debug output:' . substr(print_r($this->phpThumb->debugmessages, TRUE), 7, -2) .
  147. "----------------------\n";
  148. }
  149. $this->modx->log(modX::LOG_LEVEL_ERROR, $logmsg);
  150. }
  151. /*
  152. * Create a thumnail from $src with $options
  153. * $src can be a path/filename or URL and absolute or relative
  154. * Returns the filename of the cached image on success or $src on failure
  155. */
  156. public function createThumbnail($src, $options) {
  157. $src = str_replace('/./', '/', $src); // get rid of any /./ instances in the path
  158. $output = array(
  159. 'src' => $src,
  160. 'file' => '',
  161. 'width' => '',
  162. 'height' => '',
  163. 'outputDims' => false,
  164. 'success' => false
  165. );
  166. if ($this->config['cacheNotWritable']) {
  167. return $output;
  168. }
  169. /* Find input file */
  170. $isRemote = preg_match('/^(?:https?:)?\/\/((?:.+?)\.(?:.+?))\/(.+)/i', $src, $matches); // check for absolute URLs
  171. if ($isRemote && $this->config['httpHost'] === strtolower($matches[1])) { // if it's the same server we're running on
  172. $isRemote = false; // then it's not really remote
  173. $src = $matches[2]; // we just need the path and filename
  174. }
  175. if ($isRemote) { // if we've got a real remote image to work with
  176. $hashExtras = $matches[1]; // we'll put the remote site name into the hash later
  177. $remoteUrl = explode('?', $matches[2]); // break off any query string
  178. $remoteUrl[0] = rawurldecode($remoteUrl[0]); // just in case?
  179. $inputParts = pathinfo($remoteUrl[0]);
  180. $inputParts['dirname'] = $inputParts['dirname'] === '.' ? '' : "{$inputParts['dirname']}/"; // remove '.' if in top level dir
  181. $cachebuster = '.';
  182. if (isset($remoteUrl[1])) {
  183. $hashExtras .= $remoteUrl[1];
  184. $cachebuster .= hash('crc32', $remoteUrl[1]) . '.';
  185. }
  186. $remoteCacheName = "{$inputParts['filename']}$cachebuster{$inputParts['extension']}"; // hash any query string to allow for cache busting
  187. $remoteFilePath = "{$this->config['remoteImagesCachePath']}{$matches[1]}/{$inputParts['dirname']}";
  188. $file = "$remoteFilePath$remoteCacheName";
  189. if (!file_exists($file)) { // if it's not in our cache, go get it
  190. if (!is_writable($remoteFilePath)) {
  191. if ( !$this->modx->cacheManager->writeTree($remoteFilePath) ) {
  192. $this->modx->log(modX::LOG_LEVEL_ERROR, "[pThumb] Remote images cache path not writable: $remoteFilePath");
  193. return $output;
  194. }
  195. }
  196. if (!isset($this->config['remoteTimeout'])) { // first time through set up any additional remote images settings
  197. $this->config['remoteTimeout'] = (int) $this->modx->getOption('phpthumbof.remote_timeout', null, 5); // in seconds. For fetching remote images
  198. }
  199. $fh = fopen($file, 'wb');
  200. if (!$fh) {
  201. $this->debugmsg("[pThumb remote images] Unable to write to cache file: $file *** Skipping ***");
  202. return $output;
  203. }
  204. $curlFail = FALSE;
  205. if ($src[0] === '/') { //cURL doesn't like protocol-relative URLs, so add http or https
  206. $src = (empty($_SERVER['HTTPS']) ? 'http:' : 'https:') . $src;
  207. }
  208. $ch = curl_init(str_replace(' ', '%20', $src));
  209. curl_setopt_array($ch, array(
  210. CURLOPT_TIMEOUT => $this->config['remoteTimeout'],
  211. CURLOPT_FILE => $fh,
  212. CURLOPT_FAILONERROR => TRUE
  213. ));
  214. curl_exec($ch); // download the file and store it in $fh
  215. if (curl_errno($ch)) {
  216. $this->debugmsg("[pThumb remote images] Retrieving $src\nTarget filename: $file\ncURL error: " . curl_error($ch) . " *** Skipping ***\n");
  217. $curlFail = TRUE;
  218. }
  219. curl_close($ch);
  220. fclose($fh);
  221. if ($curlFail || !getimagesize($file)) { // if we didn't get an image, skip and remove from cache
  222. $this->debugmsg("[pThumb remote images] Failed to cache $src");
  223. unlink($file);
  224. return $output;
  225. }
  226. }
  227. }
  228. else { // it's a local file
  229. if (is_readable($src)) { // if we've already got an existing file, keep going
  230. $file = $src;
  231. }
  232. else { // otherwise prepend base_path and try again
  233. $file = MODX_BASE_PATH . rawurldecode(ltrim($src, '/')); // Fix spaces and other encoded characters in the filename
  234. if (!is_readable($file)) { // still can't find it? We'll try to correct a couple common problems.
  235. if (!isset($this->config['basePathCheck'])) {
  236. $this->config['basePathCheck'] = MODX_BASE_PATH . ltrim($this->modx->getOption('base_url'), '/');
  237. }
  238. $file = str_replace($this->config['basePathCheck'], MODX_BASE_PATH, $file); // if MODX is in a subdir, keep this subdir name from occuring twice. Also remove base_url, which might just be added by a context
  239. if (!is_readable($file)) { // Time to declare failure
  240. $this->debugmsg('File not ' . (file_exists($file) ? 'readable': 'found') . ": $file *** Skipping ***");
  241. return $output;
  242. }
  243. }
  244. }
  245. if (is_dir($file)) {
  246. $this->debugmsg("$file is a directory *** Skipping ***");
  247. return $output;
  248. }
  249. }
  250. $this->input = $output['file'] = $file;
  251. /* Process options. Set $ptOptions */
  252. if (!is_array($options)) { // convert options string to array
  253. parse_str($options, $ptOptions);
  254. }
  255. else { // otherwise use the original phpThumbOf code
  256. $ptOptions = array();
  257. foreach ($options as $opt) {
  258. $opt = explode('=', $opt);
  259. $key = str_replace('[]','',$opt[0]);
  260. if (!empty($key)) {
  261. /* allow arrays of options */
  262. if (isset($ptOptions[$key])) {
  263. if (is_string($ptOptions[$key])) {
  264. $ptOptions[$key] = array($ptOptions[$key]);
  265. }
  266. $ptOptions[$key][] = $opt[1];
  267. } else { /* otherwise pass in as string */
  268. $ptOptions[$key] = $opt[1];
  269. }
  270. }
  271. }
  272. }
  273. if (!$isRemote) { // remote stuff has already been set up above
  274. $inputParts = pathinfo($this->input);
  275. $hashExtras = '';
  276. }
  277. if (empty($ptOptions['f'])) { // if filetype isn't already set, set it based on extension
  278. $ext = strtolower($inputParts['extension']);
  279. $ptOptions['f'] = ($ext === 'png' || $ext === 'gif') ? $ext : 'jpeg';
  280. }
  281. $output['outputDims'] = !empty($ptOptions['dims']);
  282. $ptOptions = array_merge($this->config['globalDefaults'], $ptOptions);
  283. /* Determine cache filename. Set $cacheKey and $cacheUrl */
  284. $modflags = (int) $this->config['useResizer']; // keep cached image from being stale if useResizer changes
  285. if ($this->config['checkModTime']) {
  286. $modflags .= filemtime($this->input);
  287. }
  288. $cacheFilename = $inputParts['filename'] . '.';
  289. if ($this->config['use_ptcache']) {
  290. if ($isRemote) {
  291. $cacheFilenamePrefix = $inputParts['dirname'];
  292. }
  293. else {
  294. $inputParts['dirname'] .= '/';
  295. $baseDirOffset = strpos($inputParts['dirname'], $this->config['imagesBasedir']);
  296. if ($baseDirOffset === false) { // not coming from imagesBasedir, so throw it in the top level of the cache
  297. $cacheFilenamePrefix = '';
  298. }
  299. else { // trim off everything before and including imagesBasedir
  300. $cacheFilenamePrefix = substr($inputParts['dirname'], $baseDirOffset + $this->config['imagesBasedirLen']);
  301. }
  302. }
  303. $cacheFilenamePath = "{$this->config['cachePath']}$cacheFilenamePrefix";
  304. $cacheFilename .= hash('crc32', $modflags . json_encode($ptOptions) . $hashExtras) . '.';
  305. }
  306. else { // use classic phpThumbOf cache
  307. $cacheFilenamePrefix = '';
  308. if ($this->config['postfixPropertyHash']) {
  309. $cacheFilename .= md5("$modflags{$inputParts['dirname']}" . json_encode($ptOptions) . $hashExtras) . '.';
  310. }
  311. }
  312. $cacheFilename .= $ptOptions['f'] === 'jpeg' ? 'jpg' : $ptOptions['f']; // extension
  313. $cacheKey = "{$this->config['cachePath']}$cacheFilenamePrefix$cacheFilename";
  314. $cacheUrl = "{$this->config['cachePathUrl']}$cacheFilenamePrefix" . rawurlencode($cacheFilename);
  315. /* Look for cached file */
  316. $s3ok = false;
  317. if ($this->config['s3outputMS']) { // check for file in S3 MS
  318. $s3out =& $this->config[$this->config['s3outKey']];
  319. $cacheFilenamePrefix = $this->config['s3cachePath'] . $cacheFilenamePrefix;
  320. $s3cacheUrl = $this->config["{$this->config['s3outKey']}_url"] . $cacheFilenamePrefix . rawurlencode($cacheFilename);
  321. $cacheFilename = "$cacheFilenamePrefix$cacheFilename";
  322. if (isset($this->config[$this->config['s3outKey'] . '_images'])) { // we have a list of all objects in the bucket
  323. $s3ok = true;
  324. $output['success'] = in_array($cacheFilename, $this->config[$this->config['s3outKey'] . '_images'], true);
  325. }
  326. elseif ($this->config["{$this->config['s3outKey']}_ok"]) { // otherwise check individual object
  327. $s3ok = true;
  328. $output['success'] = $s3out->driver->if_object_exists($s3out->bucket, $cacheFilename);
  329. }
  330. }
  331. if (file_exists($cacheKey)) {
  332. $output['file'] = $cacheKey;
  333. if (!$s3ok) { // thumbnail in local cache, not using S3 or S3 didn't initialize
  334. $output['success'] = true;
  335. $output['src'] = $cacheUrl;
  336. return $output;
  337. }
  338. elseif ($output['success']) { // thumbnail in both local and S3 caches
  339. $output['src'] = $s3cacheUrl;
  340. return $output;
  341. }
  342. $output['success'] = true;
  343. }
  344. elseif ($output['success']) { // thumbnail on S3, but not in local cache
  345. $output['file'] = '';
  346. $output['src'] = $s3cacheUrl;
  347. return $output;
  348. }
  349. else {
  350. /* Generate Thumbnail */
  351. if ($this->config['use_ptcache'] && !is_writable($cacheFilenamePath)) { // make sure pThumb cache location exists
  352. if ( !$this->modx->cacheManager->writeTree($cacheFilenamePath) ) {
  353. $this->modx->log(modX::LOG_LEVEL_ERROR, "[pThumb] Cache path not writable: $cacheFilenamePath");
  354. return $output;
  355. }
  356. }
  357. if ($this->config['useResizer']) { // use Resizer
  358. static $resizer_obj = array();
  359. if (!class_exists('Resizer')) { // set up Resizer. We'll reuse this object for any subsequent images on the page
  360. if (!$this->modx->loadClass('Resizer', MODX_CORE_PATH . 'components/resizer/model/', true, true)) {
  361. $this->debugmsg('Could not load Resizer class.');
  362. return $output;
  363. }
  364. $resizer_obj[0] = new Resizer($this->modx); // we'll reuse this same object for all subsequent images
  365. $resizer_obj[0]->debug = $this->config['debug'];
  366. }
  367. else { // We've already got a Resizer object and will just clear out its debug log
  368. $resizer_obj[0]->resetDebug();
  369. }
  370. $this->phpThumb = $resizer_obj[0];
  371. $output['success'] = $this->phpThumb->processImage($this->input, $cacheKey, $ptOptions);
  372. if ($output['success']) {
  373. $output['width'] = $this->phpThumb->width;
  374. $output['height'] = $this->phpThumb->height;
  375. }
  376. }
  377. else { // use phpThumb
  378. if (!class_exists('phpthumb', FALSE)) {
  379. if (!$this->modx->loadClass('phpthumb', MODX_CORE_PATH . 'model/phpthumb/', true, true)) {
  380. $this->debugmsg('Could not load phpthumb class.');
  381. return $output;
  382. }
  383. }
  384. if (!isset($this->config['modphpthumb'])) { // make sure we get a few relevant system settings
  385. $this->config['modphpthumb'] = array();
  386. $this->config['modphpthumb']['config_allow_src_above_docroot'] = (boolean) $this->modx->getOption('phpthumb_allow_src_above_docroot', null, false);
  387. $this->config['modphpthumb']['zc'] = $this->modx->getOption('phpthumb_zoomcrop', null, 0);
  388. $this->config['modphpthumb']['far'] = $this->modx->getOption('phpthumb_far', null, 'C');
  389. $this->config['modphpthumb']['config_ttf_directory'] = MODX_CORE_PATH . 'model/phpthumb/fonts/';
  390. $this->config['modphpthumb']['config_document_root'] = $this->modx->getOption('phpthumb_document_root', null, '');
  391. }
  392. $this->phpThumb = new phpthumb(); // unfortunately we have to create a new object for each image!
  393. foreach ($this->config['modphpthumb'] as $param => $value) { // add MODX system settings
  394. $this->phpThumb->$param = $value;
  395. }
  396. foreach ($ptOptions as $param => $value) { // add options passed to the snippet
  397. $this->phpThumb->setParameter($param, $value);
  398. }
  399. // try to avert problems when $_SERVER['DOCUMENT_ROOT'] is different than MODX_BASE_PATH
  400. if (!$this->phpThumb->config_document_root) {
  401. $this->phpThumb->config_document_root = MODX_BASE_PATH; // default if nothing set from system settings
  402. }
  403. $this->phpThumb->config_cache_directory = "{$this->config['cachePath']}$cacheFilenamePrefix"; // doesn't matter, but saves phpThumb some frustration
  404. $this->phpThumb->setSourceFilename(($this->input[0] === '/' || $this->input[1] === ':') ? $this->input : MODX_BASE_PATH . $this->input);
  405. if (!$this->phpThumb->GenerateThumbnail()) { // create the thumbnail
  406. $this->debugmsg('Could not generate thumbnail', TRUE);
  407. return $output;
  408. }
  409. $output['success'] = $this->phpThumb->RenderToFile($cacheKey);
  410. }
  411. if ($output['success']) {
  412. $output['file'] = $cacheKey;
  413. if (!isset($this->config['newFilePermissions'])) {
  414. $this->config['newFilePermissions'] = octdec($this->modx->getOption('new_file_permissions', null, '0664'));
  415. }
  416. chmod($cacheKey, $this->config['newFilePermissions']); // make sure file permissions are correct
  417. }
  418. }
  419. if ($output['success']) {
  420. $output['src'] = $cacheUrl;
  421. if ($s3ok) { // write to S3
  422. if (!isset($this->config['s3headers'])) { // first time through set up additional headers
  423. $this->config['s3headers'] = array();
  424. $s3headers = explode("\n", $this->modx->getOption('pthumb.s3_headers', null, ''));
  425. foreach ($s3headers as $header) {
  426. $header = explode(':', $header);
  427. if (isset($header[1])) {
  428. $this->config['s3headers'][trim($header[0])] = trim($header[1]);
  429. }
  430. }
  431. }
  432. $s3response = $s3out->driver->create_object($s3out->bucket, $cacheFilename, array(
  433. 'fileUpload' => $cacheKey,
  434. 'acl' => AmazonS3::ACL_PUBLIC,
  435. 'headers' => $this->config['s3headers']
  436. ));
  437. if ($s3response->isOK()) {
  438. if (isset($this->config[$this->config['s3outKey'] . '_images'])) {
  439. $this->config[$this->config['s3outKey'] . '_images'][] = $cacheFilename;
  440. }
  441. $output['src'] = $s3cacheUrl;
  442. }
  443. else { $this->debugmsg("Error uploading $cacheFilename to S3 bucket {$s3out->bucket} (media source {$this->config['s3outputMS']})"); }
  444. }
  445. }
  446. else { $this->debugmsg("Could not cache thumbnail to file at: {$cacheKey}", TRUE); }
  447. return $output;
  448. }
  449. }