mods3mediasource.class.php 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. require_once MODX_CORE_PATH . 'model/modx/sources/modmediasource.class.php';
  11. /**
  12. * Implements an Amazon S3-based media source, allowing basic manipulation, uploading and URL-retrieval of resources
  13. * in a specified S3 bucket.
  14. *
  15. * @package modx
  16. * @subpackage sources
  17. */
  18. class modS3MediaSource extends modMediaSource implements modMediaSourceInterface {
  19. /** @var AmazonS3 $driver */
  20. public $driver;
  21. /** @var string $bucket */
  22. public $bucket;
  23. /**
  24. * Override the constructor to always force S3 sources to not be streams.
  25. *
  26. * {@inheritDoc}
  27. *
  28. * @param xPDO $xpdo
  29. */
  30. public function __construct(xPDO & $xpdo) {
  31. parent::__construct($xpdo);
  32. $this->set('is_stream',false);
  33. }
  34. /**
  35. * Initializes S3 media class, getting the S3 driver and loading the bucket
  36. * @return boolean
  37. */
  38. public function initialize() {
  39. parent::initialize();
  40. $properties = $this->getPropertyList();
  41. if (!defined('AWS_KEY')) {
  42. define('AWS_KEY',$this->xpdo->getOption('key',$properties,''));
  43. define('AWS_SECRET_KEY',$this->xpdo->getOption('secret_key',$properties,''));
  44. /* (Not needed at this time)
  45. define('AWS_ACCOUNT_ID',$modx->getOption('aws.account_id',$config,''));
  46. define('AWS_CANONICAL_ID',$modx->getOption('aws.canonical_id',$config,''));
  47. define('AWS_CANONICAL_NAME',$modx->getOption('aws.canonical_name',$config,''));
  48. define('AWS_MFA_SERIAL',$modx->getOption('aws.mfa_serial',$config,''));
  49. define('AWS_CLOUDFRONT_KEYPAIR_ID',$modx->getOption('aws.cloudfront_keypair_id',$config,''));
  50. define('AWS_CLOUDFRONT_PRIVATE_KEY_PEM',$modx->getOption('aws.cloudfront_private_key_pem',$config,''));
  51. define('AWS_ENABLE_EXTENSIONS', 'false');*/
  52. }
  53. include_once $this->xpdo->getOption('core_path',null,MODX_CORE_PATH).'model/aws/sdk.class.php';
  54. $this->getDriver();
  55. $region = $this->xpdo->getOption('region',$properties,'');
  56. if (!empty($region)) {
  57. $this->driver->set_region($region);
  58. }
  59. $this->setBucket($this->xpdo->getOption('bucket',$properties,''));
  60. return true;
  61. }
  62. /**
  63. * Get the name of this source type
  64. * @return string
  65. */
  66. public function getTypeName() {
  67. $this->xpdo->lexicon->load('source');
  68. return $this->xpdo->lexicon('source_type.s3');
  69. }
  70. /**
  71. * Get the description of this source type
  72. * @return string
  73. */
  74. public function getTypeDescription() {
  75. $this->xpdo->lexicon->load('source');
  76. return $this->xpdo->lexicon('source_type.s3_desc');
  77. }
  78. /**
  79. * Gets the AmazonS3 class instance
  80. * @return AmazonS3
  81. */
  82. public function getDriver() {
  83. if (empty($this->driver)) {
  84. try {
  85. $this->driver = new AmazonS3();
  86. } catch (Exception $e) {
  87. $this->xpdo->log(modX::LOG_LEVEL_ERROR,'[modAws] Could not load AmazonS3 class: '.$e->getMessage());
  88. }
  89. }
  90. return $this->driver;
  91. }
  92. /**
  93. * Set the bucket for the connection to S3
  94. * @param string $bucket
  95. * @return void
  96. */
  97. public function setBucket($bucket) {
  98. $this->bucket = $bucket;
  99. if (strpos($bucket,'.') !== false) {
  100. $this->driver->enable_path_style(true);
  101. }
  102. }
  103. /**
  104. * Get a list of objects from within a bucket
  105. * @param string $dir
  106. * @return array
  107. */
  108. public function getS3ObjectList($dir) {
  109. $c['delimiter'] = '/';
  110. if (!empty($dir) && $dir != '/') { $c['prefix'] = $dir; }
  111. $list = array();
  112. $cps = $this->driver->list_objects($this->bucket,$c);
  113. foreach ($cps->body->CommonPrefixes as $prefix) {
  114. if (!empty($prefix->Prefix) && $prefix->Prefix != $dir && $prefix->Prefix != '/') {
  115. $list[] = (string)$prefix->Prefix;
  116. }
  117. }
  118. $response = $this->driver->get_object_list($this->bucket,$c);
  119. foreach ($response as $file) {
  120. $list[] = $file;
  121. }
  122. return $list;
  123. }
  124. /**
  125. * Get the ID of the edit file action
  126. *
  127. * @return boolean|int
  128. */
  129. public function getEditActionId() {
  130. return 'system/file/edit';
  131. }
  132. /**
  133. * @param string $path
  134. * @return array
  135. */
  136. public function getContainerList($path) {
  137. $properties = $this->getPropertyList();
  138. $list = $this->getS3ObjectList($path);
  139. $editAction = $this->getEditActionId();
  140. $useMultiByte = $this->ctx->getOption('use_multibyte', false);
  141. $encoding = $this->ctx->getOption('modx_charset', 'UTF-8');
  142. $imagesExts = $this->getOption('imageExtensions',$properties,'jpg,jpeg,png,gif,svg');
  143. $imagesExts = explode(',',$imagesExts);
  144. $hideTooltips = !empty($properties['hideTooltips']) && $properties['hideTooltips'] != 'false' ? true : false;
  145. $directories = array();
  146. $dirnames = array();
  147. $files = array();
  148. $filenames = array();
  149. foreach ($list as $idx => $currentPath) {
  150. if ($currentPath == $path) continue;
  151. $fileName = basename($currentPath);
  152. $isDir = substr(strrev($currentPath),0,1) === '/';
  153. $ext = pathinfo($fileName,PATHINFO_EXTENSION);
  154. $ext = $useMultiByte ? mb_strtolower($ext,$encoding) : strtolower($ext);
  155. $relativePath = $currentPath == '/' ? $currentPath : str_replace($path,'',$currentPath);
  156. $slashCount = substr_count($relativePath,'/');
  157. if (($slashCount > 1 && $isDir) || ($slashCount > 0 && !$isDir)) {
  158. continue;
  159. }
  160. $cls = array();
  161. if ($isDir) {
  162. $cls[] = 'folder';
  163. $dirnames[] = strtoupper($fileName);
  164. $directories[$currentPath] = array(
  165. 'id' => $currentPath,
  166. 'text' => $fileName,
  167. 'cls' => implode(' ',$cls),
  168. 'iconCls' => 'icon icon-folder',
  169. 'type' => 'dir',
  170. 'leaf' => false,
  171. 'path' => $currentPath,
  172. 'pathRelative' => $currentPath,
  173. 'perms' => '',
  174. );
  175. $directories[$currentPath]['menu'] = array('items' => $this->getListContextMenu($currentPath,$isDir,$directories[$currentPath]));
  176. } else {
  177. $url = rtrim($properties['url'],'/').'/'.$currentPath;
  178. $url = str_replace(' ','%20',$url);
  179. $page = '?a='.$editAction.'&file='.$currentPath.'&wctx='.$this->ctx->get('key').'&source='.$this->get('id');
  180. // $isBinary = $this->isBinary(rtrim($properties['url'],'/').'/'.$currentPath);
  181. // $cls = array();
  182. // $cls[] = 'icon-'.$ext;
  183. // if($isBinary) {
  184. // $cls[] = 'icon-lock';
  185. // }
  186. if ($this->hasPermission('file_remove')) $cls[] = 'premove';
  187. if ($this->hasPermission('file_update')) $cls[] = 'pupdate';
  188. $filenames[] = strtoupper($fileName);
  189. $files[$currentPath] = array(
  190. 'id' => $currentPath,
  191. 'text' => $fileName,
  192. 'cls' => implode(' ', $cls),
  193. 'iconCls' => 'icon icon-file icon-'.$ext,
  194. 'type' => 'file',
  195. 'leaf' => true,
  196. 'path' => $currentPath,
  197. 'page' => $this->isBinary($url) ? $page : null,
  198. 'pathRelative' => $currentPath,
  199. 'directory' => $currentPath,
  200. 'url' => $url,
  201. 'file' => $currentPath,
  202. );
  203. $files[$currentPath]['menu'] = array('items' => $this->getListContextMenu($currentPath,$isDir,$files[$currentPath]));
  204. if (!$hideTooltips) {
  205. $files[$currentPath]['qtip'] = '';
  206. if (in_array($ext, $imagesExts)) {
  207. $modAuth = $this->xpdo->user->getUserToken($this->xpdo->context->get('key'));
  208. $preview = true;
  209. $imageWidth = $this->ctx->getOption('filemanager_image_width', 400);
  210. $imageHeight = $this->ctx->getOption('filemanager_image_height', 300);
  211. $thumbnailType = $this->getOption('thumbnailType', $properties, 'png');
  212. $thumbnailQuality = $this->getOption('thumbnailQuality', $properties, 90);
  213. if ($ext == 'svg') {
  214. $svgString = @file_get_contents($bases['pathAbsoluteWithPath'].$url);
  215. preg_match('/(<svg[^>]*\swidth=")([\d\.]+)([a-z]*)"/si', $svgString, $svgWidth);
  216. preg_match('/(<svg[^>]*\sheight=")([\d\.]+)([a-z]*)"/si', $svgString, $svgHeight);
  217. preg_match('/(<svg[^>]*\sviewBox=")([\d\.]+(?:,|\s)[\d\.]+(?:,|\s)([\d\.]+)(?:,|\s)([\d\.]+))"/si', $svgString, $svgViewbox);
  218. if (!empty($svgViewbox)) {
  219. // get width and height from viewbox attribute
  220. $imageWidth = round($svgViewbox[3]);
  221. $imageHeight = round($svgViewbox[4]);
  222. } elseif (!empty($svgWidth) && !empty($svgHeight)) {
  223. // get width and height from width and height attributes
  224. $imageWidth = round($svgWidth[2]);
  225. $imageHeight = round($svgHeight[2]);
  226. }
  227. $image = $bases['urlAbsolute'] . urldecode($url);
  228. } else {
  229. $size = @getimagesize($url);
  230. if (is_array($size) && $size[0] > 0 && $size[1] > 0) {
  231. // get original image size for proportional scaling
  232. if ($size[0] > $size[1]) {
  233. // landscape
  234. $imageQueryWidth = $size[0] >= $imageWidth ? $imageWidth : $size[0];
  235. $imageQueryHeight = 0;
  236. $imageWidth = $imageQueryWidth;
  237. $imageHeight = round($size[1] * ($imageQueryWidth / $size[0]));
  238. } else {
  239. // portrait or square
  240. $imageQueryWidth = 0;
  241. $imageQueryHeight = $size[1] >= $imageHeight ? $imageHeight : $size[1];
  242. $imageWidth = round($size[0] * ($imageQueryHeight / $size[1]));
  243. $imageHeight = $imageQueryHeight;
  244. }
  245. $imageQuery = http_build_query(array(
  246. 'src' => $url,
  247. 'w' => $imageQueryWidth,
  248. 'h' => $imageQueryHeight,
  249. 'HTTP_MODAUTH' => $modAuth,
  250. 'f' => $thumbnailType,
  251. 'q' => $thumbnailQuality,
  252. 'wctx' => $this->ctx->get('key'),
  253. 'source' => $this->get('id'),
  254. ));
  255. $image = $this->ctx->getOption('connectors_url', MODX_CONNECTORS_URL).'system/phpthumb.php?'.urldecode($imageQuery);
  256. } else {
  257. $preview = false;
  258. $this->xpdo->log(modX::LOG_LEVEL_ERROR,'Thumbnail could not be created for file: '.$url);
  259. }
  260. }
  261. if ($preview) {
  262. $files[$currentPath]['qtip'] = '<img src="'.$image.'" width="'.$imageWidth.'" height="'.$imageHeight.'" alt="'.$fileName.'" />';
  263. }
  264. }
  265. }
  266. }
  267. }
  268. $ls = array();
  269. /* now sort files/directories */
  270. array_multisort($dirnames, SORT_ASC, SORT_STRING, $directories);
  271. // uksort($directories, 'strnatcasecmp');
  272. foreach ($directories as $dir) {
  273. $ls[] = $dir;
  274. }
  275. array_multisort($filenames, SORT_ASC, SORT_STRING, $files);
  276. // uksort($files, 'strnatcasecmp');
  277. foreach ($files as $file) {
  278. $ls[] = $file;
  279. }
  280. return $ls;
  281. }
  282. /**
  283. * Get the context menu for when viewing the source as a tree
  284. *
  285. * @param string $file
  286. * @param boolean $isDir
  287. * @param array $fileArray
  288. * @return array
  289. */
  290. public function getListContextMenu($file,$isDir,array $fileArray) {
  291. $menu = array();
  292. if (!$isDir) { /* files */
  293. if ($this->hasPermission('file_update')) {
  294. if ($fileArray['page'] != null) {
  295. $menu[] = array(
  296. 'text' => $this->xpdo->lexicon('file_edit'),
  297. 'handler' => 'this.editFile',
  298. );
  299. $menu[] = array(
  300. 'text' => $this->xpdo->lexicon('quick_update_file'),
  301. 'handler' => 'this.quickUpdateFile',
  302. );
  303. }
  304. $menu[] = array(
  305. 'text' => $this->xpdo->lexicon('rename'),
  306. 'handler' => 'this.renameFile',
  307. );
  308. }
  309. if ($this->hasPermission('file_view')) {
  310. $menu[] = array(
  311. 'text' => $this->xpdo->lexicon('file_download'),
  312. 'handler' => 'this.downloadFile',
  313. );
  314. }
  315. if ($this->hasPermission('file_remove')) {
  316. if (!empty($menu)) $menu[] = '-';
  317. $menu[] = array(
  318. 'text' => $this->xpdo->lexicon('file_remove'),
  319. 'handler' => 'this.removeFile',
  320. );
  321. }
  322. } else { /* directories */
  323. if ($this->hasPermission('directory_create')) {
  324. $menu[] = array(
  325. 'text' => $this->xpdo->lexicon('file_folder_create_here'),
  326. 'handler' => 'this.createDirectory',
  327. );
  328. }
  329. $menu[] = array(
  330. 'text' => $this->xpdo->lexicon('directory_refresh'),
  331. 'handler' => 'this.refreshActiveNode',
  332. );
  333. if ($this->hasPermission('file_upload')) {
  334. $menu[] = '-';
  335. $menu[] = array(
  336. 'text' => $this->xpdo->lexicon('upload_files'),
  337. 'handler' => 'this.uploadFiles',
  338. );
  339. }
  340. if ($this->hasPermission('file_create')) {
  341. $menu[] = array(
  342. 'text' => $this->xpdo->lexicon('file_create'),
  343. 'handler' => 'this.createFile',
  344. );
  345. $menu[] = array(
  346. 'text' => $this->xpdo->lexicon('quick_create_file'),
  347. 'handler' => 'this.quickCreateFile',
  348. );
  349. }
  350. if ($this->hasPermission('directory_remove')) {
  351. $menu[] = '-';
  352. $menu[] = array(
  353. 'text' => $this->xpdo->lexicon('file_folder_remove'),
  354. 'handler' => 'this.removeDirectory',
  355. );
  356. }
  357. }
  358. return $menu;
  359. }
  360. /**
  361. * Get all files in the directory and prepare thumbnail views
  362. *
  363. * @param string $path
  364. * @return array
  365. */
  366. public function getObjectsInContainer($path) {
  367. $properties = $this->getPropertyList();
  368. $list = $this->getS3ObjectList($path);
  369. $editAction = $this->getEditActionId();
  370. $modAuth = $this->xpdo->user->getUserToken($this->xpdo->context->get('key'));
  371. /* get default settings */
  372. $use_multibyte = $this->ctx->getOption('use_multibyte', false);
  373. $encoding = $this->ctx->getOption('modx_charset', 'UTF-8');
  374. $bucketUrl = rtrim($properties['url'],'/').'/';
  375. $allowedFileTypes = $this->getOption('allowedFileTypes',$this->properties,'');
  376. $allowedFileTypes = !empty($allowedFileTypes) && is_string($allowedFileTypes) ? array_map("trim",explode(',',$allowedFileTypes)) : $allowedFileTypes;
  377. $imageExtensions = $this->getOption('imageExtensions',$this->properties,'jpg,jpeg,png,gif,svg');
  378. $imageExtensions = explode(',',$imageExtensions);
  379. $thumbnailType = $this->getOption('thumbnailType',$this->properties,'png');
  380. $thumbnailQuality = $this->getOption('thumbnailQuality',$this->properties,90);
  381. $skipFiles = $this->getOption('skipFiles',$this->properties,'.svn,.git,_notes,nbproject,.idea,.DS_Store');
  382. $skipFiles = array_map("trim",explode(',',$skipFiles));
  383. $skipFiles[] = '.';
  384. $skipFiles[] = '..';
  385. /* iterate */
  386. $files = array();
  387. $filenames = array();
  388. foreach ($list as $idx => $currentPath) {
  389. $url = $bucketUrl.trim($currentPath,'/');
  390. $url = str_replace(' ','%20',$url);
  391. $fileName = basename($currentPath);
  392. $isDir = substr(strrev($currentPath),0,1) == '/' ? true : false;
  393. if (in_array($currentPath,$skipFiles)) continue;
  394. if (!$isDir) {
  395. $page = '?a='.$editAction.'&file='.$currentPath.'&wctx='.$this->ctx->get('key').'&source='.$this->get('id');
  396. // $isBinary = $this->isBinary(rtrim($properties['url'],'/').'/'.$currentPath);
  397. // get filesize from S3
  398. $filesize = 0;
  399. $ch = curl_init($url);
  400. curl_setopt($ch, CURLOPT_NOBODY, 1);
  401. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
  402. curl_setopt($ch, CURLOPT_HEADER, 0);
  403. if (curl_exec($ch) !== false) {
  404. $filesize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
  405. }
  406. curl_close($ch);
  407. $filenames[] = strtoupper($fileName);
  408. $fileArray = array(
  409. 'id' => $currentPath,
  410. 'name' => $fileName,
  411. 'url' => $url,
  412. 'relativeUrl' => $url,
  413. 'fullRelativeUrl' => $url,
  414. 'pathname' => $url,
  415. 'pathRelative' => $currentPath,
  416. 'size' => $filesize,
  417. 'page' => $this->isBinary($url) ? $page : null,
  418. 'leaf' => true,
  419. // 'menu' => array(
  420. // array('text' => $this->xpdo->lexicon('file_remove'),'handler' => 'this.removeFile'),
  421. // ),
  422. );
  423. $fileArray['ext'] = pathinfo($fileName,PATHINFO_EXTENSION);
  424. $fileArray['ext'] = $use_multibyte ? mb_strtolower($fileArray['ext'],$encoding) : strtolower($fileArray['ext']);
  425. $fileArray['cls'] = 'icon-'.$fileArray['ext'];
  426. if (!empty($allowedFileTypes) && !in_array($fileArray['ext'],$allowedFileTypes)) continue;
  427. /* get thumbnail */
  428. $preview = 0;
  429. if (in_array($fileArray['ext'],$imageExtensions)) {
  430. $preview = 1;
  431. $imageWidth = $this->ctx->getOption('filemanager_image_width', 800);
  432. $imageHeight = $this->ctx->getOption('filemanager_image_height', 600);
  433. $thumbWidth = $this->ctx->getOption('filemanager_thumb_width', 100);
  434. $thumbHeight = $this->ctx->getOption('filemanager_thumb_height', 80);
  435. $size = array($imageWidth, $imageHeight);
  436. if ($fileArray['ext'] == 'svg') {
  437. $svgString = @file_get_contents($url);
  438. preg_match('/(<svg[^>]*\swidth=")([\d\.]+)([a-z]*)"/si', $svgString, $svgWidth);
  439. preg_match('/(<svg[^>]*\sheight=")([\d\.]+)([a-z]*)"/si', $svgString, $svgHeight);
  440. preg_match('/(<svg[^>]*\sviewBox=")([\d\.]+(?:,|\s)[\d\.]+(?:,|\s)([\d\.]+)(?:,|\s)([\d\.]+))"/si', $svgString, $svgViewbox);
  441. if (!empty($svgViewbox)) {
  442. // get width and height from viewbox attribute
  443. $size[0] = round($svgViewbox[3]);
  444. $size[1] = round($svgViewbox[4]);
  445. } elseif (!empty($svgWidth) && !empty($svgHeight)) {
  446. // get width and height from width and height attributes
  447. $size[0] = round($svgWidth[2]);
  448. $size[1] = round($svgHeight[2]);
  449. }
  450. // proportional scaling of image and thumb
  451. if ($size[0] > $size[1]) {
  452. // landscape
  453. $imageWidth = $size[0] >= $imageWidth ? $imageWidth : $size[0];
  454. $imageHeight = round($size[1] * ($imageWidth / $size[0]));
  455. $thumbWidth = $size[0] >= $thumbWidth ? $thumbWidth : $size[0];
  456. $thumbHeight = round($size[1] * ($thumbWidth / $size[0]));
  457. } else {
  458. // portrait or square
  459. $imageHeight = $size[1] >= $imageHeight ? $imageHeight : $size[1];
  460. $imageWidth = round($size[0] * ($imageHeight / $size[1]));
  461. $thumbHeight = $size[1] >= $thumbHeight ? $thumbHeight : $size[1];
  462. $thumbWidth = round($size[0] * ($thumbHeight / $size[1]));
  463. }
  464. $fileArray['image'] = $fileArray['thumb'] = $url;
  465. } else {
  466. $size = @getimagesize($url);
  467. if (is_array($size) && $size[0] > 0 && $size[1] > 0) {
  468. // proportional scaling of image and thumb
  469. if ($size[0] > $size[1]) {
  470. // landscape
  471. $imageQueryWidth = $size[0] >= $imageWidth ? $imageWidth : $size[0];
  472. $imageQueryHeight = 0;
  473. $imageWidth = $imageQueryWidth;
  474. $imageHeight = round($size[1] * ($imageQueryWidth / $size[0]));
  475. $thumbQueryWidth = $size[0] >= $thumbWidth ? $thumbWidth : $size[0];
  476. $thumbQueryHeight = 0;
  477. $thumbWidth = $thumbQueryWidth;
  478. $thumbHeight = round($size[1] * ($thumbQueryWidth / $size[0]));
  479. } else {
  480. // portrait or square
  481. $imageQueryWidth = 0;
  482. $imageQueryHeight = $size[1] >= $imageHeight ? $imageHeight : $size[1];
  483. $imageWidth = round($size[0] * ($imageQueryHeight / $size[1]));
  484. $imageHeight = $imageQueryHeight;
  485. $thumbQueryWidth = 0;
  486. $thumbQueryHeight = $size[1] >= $thumbHeight ? $thumbHeight : $size[1];
  487. $thumbWidth = round($size[0] * ($thumbQueryHeight / $size[1]));
  488. $thumbHeight = $thumbQueryHeight;
  489. }
  490. $imageQuery = http_build_query(array(
  491. 'src' => $url,
  492. 'w' => $imageQueryWidth,
  493. 'h' => $imageQueryHeight,
  494. 'HTTP_MODAUTH' => $modAuth,
  495. 'f' => $thumbnailType,
  496. 'q' => $thumbnailQuality,
  497. 'wctx' => $this->ctx->get('key'),
  498. 'source' => $this->get('id'),
  499. ));
  500. $fileArray['image'] = $this->ctx->getOption('connectors_url', MODX_CONNECTORS_URL).'system/phpthumb.php?'.urldecode($imageQuery);
  501. $thumbQuery = http_build_query(array(
  502. 'src' => $url,
  503. 'w' => $thumbQueryWidth,
  504. 'h' => $thumbQueryHeight,
  505. 'HTTP_MODAUTH' => $modAuth,
  506. 'f' => $thumbnailType,
  507. 'q' => $thumbnailQuality,
  508. 'wctx' => $this->ctx->get('key'),
  509. 'source' => $this->get('id'),
  510. ));
  511. $fileArray['thumb'] = $this->ctx->getOption('connectors_url', MODX_CONNECTORS_URL).'system/phpthumb.php?'.urldecode($thumbQuery);
  512. } else {
  513. $this->xpdo->log(modX::LOG_LEVEL_ERROR,'Thumbnail could not be created for file: '.$url);
  514. $preview = 0;
  515. }
  516. }
  517. if ($preview) {
  518. $fileArray['thumb_width'] = $thumbWidth;
  519. $fileArray['thumb_height'] = $thumbHeight;
  520. $fileArray['image_width'] = is_array($size) && $size[0] > 0 ? $size[0] : $imageWidth;
  521. $fileArray['image_height'] = is_array($size) && $size[1] > 0 ? $size[1] : $imageHeight;
  522. }
  523. }
  524. if ($preview == 0) {
  525. $fileArray['thumb'] = $fileArray['image'] = $this->ctx->getOption('manager_url', MODX_MANAGER_URL).'templates/default/images/restyle/nopreview.jpg';
  526. $fileArray['thumb_width'] = $fileArray['image_width'] = $this->ctx->getOption('filemanager_thumb_width', 100);
  527. $fileArray['thumb_height'] = $fileArray['image_height'] = $this->ctx->getOption('filemanager_thumb_height', 80);
  528. $fileArray['preview'] = 0;
  529. }
  530. $files[$fileName] = $fileArray;
  531. $files[$fileName]['menu'] = $this->getListContextMenu($file, false, $files[$fileName]);
  532. }
  533. }
  534. // array_multisort($filenames, SORT_ASC, SORT_STRING, $files);
  535. $ls = array();
  536. array_multisort($filenames, SORT_ASC, SORT_STRING, $files);
  537. foreach ($files as $file) {
  538. $ls[] = $file;
  539. }
  540. return $ls;
  541. }
  542. /**
  543. * Create a Container
  544. *
  545. * @param string $name
  546. * @param string $parentContainer
  547. * @return boolean
  548. */
  549. public function createContainer($name,$parentContainer) {
  550. $newPath = ltrim($parentContainer.rtrim($name,'/').'/', '/');
  551. /* check to see if folder already exists */
  552. if ($this->driver->if_object_exists($this->bucket,$newPath)) {
  553. $this->addError('file',$this->xpdo->lexicon('file_folder_err_ae').': '.$newPath);
  554. return false;
  555. }
  556. /* create empty file that acts as folder */
  557. $created = $this->driver->create_object($this->bucket,$newPath,array(
  558. 'body' => '',
  559. 'acl' => AmazonS3::ACL_PUBLIC,
  560. 'length' => 0,
  561. ));
  562. if (!$created) {
  563. $this->addError('name',$this->xpdo->lexicon('file_folder_err_create').$newPath);
  564. return false;
  565. }
  566. $this->xpdo->logManagerAction('directory_create','',$newPath);
  567. return true;
  568. }
  569. /**
  570. * Remove an empty folder from s3
  571. *
  572. * @param $path
  573. * @return boolean
  574. */
  575. public function removeContainer($path) {
  576. if (!$this->driver->if_object_exists($this->bucket,$path)) {
  577. $this->addError('file',$this->xpdo->lexicon('file_folder_err_ns').': '.$path);
  578. return false;
  579. }
  580. /* remove file from s3 */
  581. $deleted = $this->driver->delete_object($this->bucket,$path);
  582. /* log manager action */
  583. $this->xpdo->logManagerAction('directory_remove','',$path);
  584. return !empty($deleted);
  585. }
  586. /**
  587. * Check that the filename has a file type extension that is allowed
  588. *
  589. * @param $filename
  590. * @return bool
  591. */
  592. public function checkFiletype($filename) {
  593. if ($this->getOption('allowedFileTypes')) {
  594. $allowedFileTypes = explode(',', $this->getOption('allowedFileTypes'));
  595. } else {
  596. $allowedFiles = $this->xpdo->getOption('upload_files') ? explode(',', $this->xpdo->getOption('upload_files')) : array();
  597. $allowedImages = $this->xpdo->getOption('upload_images') ? explode(',', $this->xpdo->getOption('upload_images')) : array();
  598. $allowedMedia = $this->xpdo->getOption('upload_media') ? explode(',', $this->xpdo->getOption('upload_media')) : array();
  599. $allowedFlash = $this->xpdo->getOption('upload_flash') ? explode(',', $this->xpdo->getOption('upload_flash')) : array();
  600. $allowedFileTypes = array_unique(array_merge($allowedFiles, $allowedImages, $allowedMedia, $allowedFlash));
  601. }
  602. $ext = pathinfo($filename, PATHINFO_EXTENSION);
  603. $ext = strtolower($ext);
  604. if (!empty($allowedFileTypes) && !in_array($ext, $allowedFileTypes)) {
  605. $this->addError('path', $this->xpdo->lexicon('file_err_ext_not_allowed', array(
  606. 'ext' => $ext,
  607. )));
  608. return false;
  609. }
  610. return true;
  611. }
  612. /**
  613. * Create a file
  614. *
  615. * @param string $objectPath
  616. * @param string $name
  617. * @param string $content
  618. * @return boolean|string
  619. */
  620. public function createObject($objectPath,$name,$content) {
  621. /* check to see if file already exists */
  622. if ($this->driver->if_object_exists($this->bucket,$objectPath.$name)) {
  623. $this->addError('file',sprintf($this->xpdo->lexicon('file_err_ae'),$objectPath.$name));
  624. return false;
  625. }
  626. if (!$this->checkFiletype($objectPath.$name)) {
  627. return false;
  628. }
  629. /* create empty file that acts as folder */
  630. $created = $this->driver->create_object($this->bucket,$objectPath.$name,array(
  631. 'body' => $content,
  632. 'acl' => AmazonS3::ACL_PUBLIC,
  633. 'length' => 0,
  634. ));
  635. if (!$created) {
  636. $this->addError('name',$this->xpdo->lexicon('file_err_create').$objectPath.$name);
  637. return false;
  638. }
  639. $this->xpdo->logManagerAction('file_create','',$objectPath.$name);
  640. return true;
  641. }
  642. /**
  643. * Update the contents of a file
  644. *
  645. * @param string $objectPath
  646. * @param string $content
  647. * @return boolean|string
  648. */
  649. public function updateObject($objectPath,$content) {
  650. /* create empty file that acts as folder */
  651. $created = $this->driver->create_object($this->bucket,$objectPath,array(
  652. 'body' => $content,
  653. 'acl' => AmazonS3::ACL_PUBLIC,
  654. 'length' => 0,
  655. ));
  656. if (!$created) {
  657. $this->addError('name',$this->xpdo->lexicon('file_err_create').$objectPath);
  658. return false;
  659. }
  660. $this->xpdo->logManagerAction('file_create','',$objectPath);
  661. return true;
  662. }
  663. /**
  664. * Delete a file from S3
  665. *
  666. * @param string $objectPath
  667. * @return boolean
  668. */
  669. public function removeObject($objectPath) {
  670. if (!$this->driver->if_object_exists($this->bucket,$objectPath)) {
  671. $this->addError('file',$this->xpdo->lexicon('file_folder_err_ns').': '.$objectPath);
  672. return false;
  673. }
  674. /* remove file from s3 */
  675. $deleted = $this->driver->delete_object($this->bucket,$objectPath);
  676. /* log manager action */
  677. $this->xpdo->logManagerAction('file_remove','',$objectPath);
  678. return !empty($deleted);
  679. }
  680. /**
  681. * Rename/move a file
  682. *
  683. * @param string $oldPath
  684. * @param string $newName
  685. * @return bool
  686. */
  687. public function renameObject($oldPath,$newName) {
  688. if (!$this->driver->if_object_exists($this->bucket,$oldPath)) {
  689. $this->addError('file',$this->xpdo->lexicon('file_folder_err_ns').': '.$oldPath);
  690. return false;
  691. }
  692. if (!$this->checkFiletype($newName)) {
  693. return false;
  694. }
  695. $dir = dirname($oldPath);
  696. $newPath = ($dir != '.' ? $dir.'/' : '').$newName;
  697. $copied = $this->driver->copy_object(array(
  698. 'bucket' => $this->bucket,
  699. 'filename' => $oldPath,
  700. ),array(
  701. 'bucket' => $this->bucket,
  702. 'filename' => $newPath,
  703. ),array(
  704. 'acl' => AmazonS3::ACL_PUBLIC,
  705. ));
  706. if (!$copied) {
  707. $this->addError('file',$this->xpdo->lexicon('file_folder_err_rename').': '.$oldPath);
  708. return false;
  709. }
  710. $this->driver->delete_object($this->bucket,$oldPath);
  711. $this->xpdo->logManagerAction('file_rename','',$oldPath);
  712. return true;
  713. }
  714. /**
  715. * Upload files to S3
  716. *
  717. * @param string $container
  718. * @param array $objects
  719. * @return bool
  720. */
  721. public function uploadObjectsToContainer($container,array $objects = array()) {
  722. if ($container == '/' || $container == '.') $container = '';
  723. $maxFileSize = $this->xpdo->getOption('upload_maxsize',null,1048576);
  724. /* loop through each file and upload */
  725. foreach ($objects as $file) {
  726. if ($file['error'] != 0) continue;
  727. if (empty($file['name'])) continue;
  728. $ext = pathinfo($file['name'],PATHINFO_EXTENSION);
  729. $ext = strtolower($ext);
  730. if (!$this->checkFiletype($file['name'])) {
  731. continue;
  732. }
  733. $size = filesize($file['tmp_name']);
  734. if ($size > $maxFileSize) {
  735. $this->addError('path',$this->xpdo->lexicon('file_err_too_large',array(
  736. 'size' => $size,
  737. 'allowed' => $maxFileSize,
  738. )));
  739. continue;
  740. }
  741. $newPath = $container.$file['name'];
  742. $contentType = $this->getContentType($ext);
  743. $uploaded = $this->driver->create_object($this->bucket,$newPath,array(
  744. 'fileUpload' => $file['tmp_name'],
  745. 'acl' => AmazonS3::ACL_PUBLIC,
  746. 'length' => $size,
  747. 'contentType' => $contentType,
  748. ));
  749. if (!$uploaded) {
  750. $this->addError('path',$this->xpdo->lexicon('file_err_upload'));
  751. }
  752. }
  753. /* invoke event */
  754. $this->xpdo->invokeEvent('OnFileManagerUpload',array(
  755. 'files' => &$objects,
  756. 'directory' => $container,
  757. 'source' => &$this,
  758. ));
  759. $this->xpdo->logManagerAction('file_upload','',$container);
  760. return !$this->hasErrors();
  761. }
  762. /**
  763. * Get the content type of the file based on extension
  764. * @param string $ext
  765. * @return string
  766. */
  767. protected function getContentType($ext) {
  768. $contentType = 'application/octet-stream';
  769. $mimeTypes = array(
  770. '323' => 'text/h323',
  771. 'acx' => 'application/internet-property-stream',
  772. 'ai' => 'application/postscript',
  773. 'aif' => 'audio/x-aiff',
  774. 'aifc' => 'audio/x-aiff',
  775. 'aiff' => 'audio/x-aiff',
  776. 'asf' => 'video/x-ms-asf',
  777. 'asr' => 'video/x-ms-asf',
  778. 'asx' => 'video/x-ms-asf',
  779. 'au' => 'audio/basic',
  780. 'avi' => 'video/x-msvideo',
  781. 'axs' => 'application/olescript',
  782. 'bas' => 'text/plain',
  783. 'bcpio' => 'application/x-bcpio',
  784. 'bin' => 'application/octet-stream',
  785. 'bmp' => 'image/bmp',
  786. 'c' => 'text/plain',
  787. 'cat' => 'application/vnd.ms-pkiseccat',
  788. 'cdf' => 'application/x-cdf',
  789. 'cer' => 'application/x-x509-ca-cert',
  790. 'class' => 'application/octet-stream',
  791. 'clp' => 'application/x-msclip',
  792. 'cmx' => 'image/x-cmx',
  793. 'cod' => 'image/cis-cod',
  794. 'cpio' => 'application/x-cpio',
  795. 'crd' => 'application/x-mscardfile',
  796. 'crl' => 'application/pkix-crl',
  797. 'crt' => 'application/x-x509-ca-cert',
  798. 'csh' => 'application/x-csh',
  799. 'css' => 'text/css',
  800. 'dcr' => 'application/x-director',
  801. 'der' => 'application/x-x509-ca-cert',
  802. 'dir' => 'application/x-director',
  803. 'dll' => 'application/x-msdownload',
  804. 'dms' => 'application/octet-stream',
  805. 'doc' => 'application/msword',
  806. 'dot' => 'application/msword',
  807. 'dvi' => 'application/x-dvi',
  808. 'dxr' => 'application/x-director',
  809. 'eps' => 'application/postscript',
  810. 'etx' => 'text/x-setext',
  811. 'evy' => 'application/envoy',
  812. 'exe' => 'application/octet-stream',
  813. 'fif' => 'application/fractals',
  814. 'flr' => 'x-world/x-vrml',
  815. 'gif' => 'image/gif',
  816. 'gtar' => 'application/x-gtar',
  817. 'gz' => 'application/x-gzip',
  818. 'h' => 'text/plain',
  819. 'hdf' => 'application/x-hdf',
  820. 'hlp' => 'application/winhlp',
  821. 'hqx' => 'application/mac-binhex40',
  822. 'hta' => 'application/hta',
  823. 'htc' => 'text/x-component',
  824. 'htm' => 'text/html',
  825. 'html' => 'text/html',
  826. 'htt' => 'text/webviewhtml',
  827. 'ico' => 'image/x-icon',
  828. 'ief' => 'image/ief',
  829. 'iii' => 'application/x-iphone',
  830. 'ins' => 'application/x-internet-signup',
  831. 'isp' => 'application/x-internet-signup',
  832. 'jfif' => 'image/pipeg',
  833. 'jpe' => 'image/jpeg',
  834. 'jpeg' => 'image/jpeg',
  835. 'jpg' => 'image/jpeg',
  836. 'js' => 'application/x-javascript',
  837. 'latex' => 'application/x-latex',
  838. 'lha' => 'application/octet-stream',
  839. 'lsf' => 'video/x-la-asf',
  840. 'lsx' => 'video/x-la-asf',
  841. 'lzh' => 'application/octet-stream',
  842. 'm13' => 'application/x-msmediaview',
  843. 'm14' => 'application/x-msmediaview',
  844. 'm3u' => 'audio/x-mpegurl',
  845. 'man' => 'application/x-troff-man',
  846. 'mdb' => 'application/x-msaccess',
  847. 'me' => 'application/x-troff-me',
  848. 'mht' => 'message/rfc822',
  849. 'mhtml' => 'message/rfc822',
  850. 'mid' => 'audio/mid',
  851. 'mny' => 'application/x-msmoney',
  852. 'mov' => 'video/quicktime',
  853. 'movie' => 'video/x-sgi-movie',
  854. 'mp2' => 'video/mpeg',
  855. 'mp3' => 'audio/mpeg',
  856. 'mpa' => 'video/mpeg',
  857. 'mpe' => 'video/mpeg',
  858. 'mpeg' => 'video/mpeg',
  859. 'mpg' => 'video/mpeg',
  860. 'mpp' => 'application/vnd.ms-project',
  861. 'mpv2' => 'video/mpeg',
  862. 'ms' => 'application/x-troff-ms',
  863. 'mvb' => 'application/x-msmediaview',
  864. 'nws' => 'message/rfc822',
  865. 'oda' => 'application/oda',
  866. 'p10' => 'application/pkcs10',
  867. 'p12' => 'application/x-pkcs12',
  868. 'p7b' => 'application/x-pkcs7-certificates',
  869. 'p7c' => 'application/x-pkcs7-mime',
  870. 'p7m' => 'application/x-pkcs7-mime',
  871. 'p7r' => 'application/x-pkcs7-certreqresp',
  872. 'p7s' => 'application/x-pkcs7-signature',
  873. 'pbm' => 'image/x-portable-bitmap',
  874. 'pdf' => 'application/pdf',
  875. 'pfx' => 'application/x-pkcs12',
  876. 'pgm' => 'image/x-portable-graymap',
  877. 'pko' => 'application/ynd.ms-pkipko',
  878. 'pma' => 'application/x-perfmon',
  879. 'pmc' => 'application/x-perfmon',
  880. 'pml' => 'application/x-perfmon',
  881. 'pmr' => 'application/x-perfmon',
  882. 'pmw' => 'application/x-perfmon',
  883. 'png' => 'image/png',
  884. 'pnm' => 'image/x-portable-anymap',
  885. 'pot' => 'application/vnd.ms-powerpoint',
  886. 'ppm' => 'image/x-portable-pixmap',
  887. 'pps' => 'application/vnd.ms-powerpoint',
  888. 'ppt' => 'application/vnd.ms-powerpoint',
  889. 'prf' => 'application/pics-rules',
  890. 'ps' => 'application/postscript',
  891. 'pub' => 'application/x-mspublisher',
  892. 'qt' => 'video/quicktime',
  893. 'ra' => 'audio/x-pn-realaudio',
  894. 'ram' => 'audio/x-pn-realaudio',
  895. 'ras' => 'image/x-cmu-raster',
  896. 'rgb' => 'image/x-rgb',
  897. 'rmi' => 'audio/mid',
  898. 'roff' => 'application/x-troff',
  899. 'rtf' => 'application/rtf',
  900. 'rtx' => 'text/richtext',
  901. 'scd' => 'application/x-msschedule',
  902. 'sct' => 'text/scriptlet',
  903. 'setpay' => 'application/set-payment-initiation',
  904. 'setreg' => 'application/set-registration-initiation',
  905. 'sh' => 'application/x-sh',
  906. 'shar' => 'application/x-shar',
  907. 'sit' => 'application/x-stuffit',
  908. 'snd' => 'audio/basic',
  909. 'spc' => 'application/x-pkcs7-certificates',
  910. 'spl' => 'application/futuresplash',
  911. 'src' => 'application/x-wais-source',
  912. 'sst' => 'application/vnd.ms-pkicertstore',
  913. 'stl' => 'application/vnd.ms-pkistl',
  914. 'stm' => 'text/html',
  915. 'svg' => 'image/svg+xml',
  916. 'sv4cpio' => 'application/x-sv4cpio',
  917. 'sv4crc' => 'application/x-sv4crc',
  918. 't' => 'application/x-troff',
  919. 'tar' => 'application/x-tar',
  920. 'tcl' => 'application/x-tcl',
  921. 'tex' => 'application/x-tex',
  922. 'texi' => 'application/x-texinfo',
  923. 'texinfo' => 'application/x-texinfo',
  924. 'tgz' => 'application/x-compressed',
  925. 'tif' => 'image/tiff',
  926. 'tiff' => 'image/tiff',
  927. 'tr' => 'application/x-troff',
  928. 'trm' => 'application/x-msterminal',
  929. 'tsv' => 'text/tab-separated-values',
  930. 'txt' => 'text/plain',
  931. 'uls' => 'text/iuls',
  932. 'ustar' => 'application/x-ustar',
  933. 'vcf' => 'text/x-vcard',
  934. 'vrml' => 'x-world/x-vrml',
  935. 'wav' => 'audio/x-wav',
  936. 'wcm' => 'application/vnd.ms-works',
  937. 'wdb' => 'application/vnd.ms-works',
  938. 'wks' => 'application/vnd.ms-works',
  939. 'wmf' => 'application/x-msmetafile',
  940. 'wps' => 'application/vnd.ms-works',
  941. 'wri' => 'application/x-mswrite',
  942. 'wrl' => 'x-world/x-vrml',
  943. 'wrz' => 'x-world/x-vrml',
  944. 'xaf' => 'x-world/x-vrml',
  945. 'xbm' => 'image/x-xbitmap',
  946. 'xla' => 'application/vnd.ms-excel',
  947. 'xlc' => 'application/vnd.ms-excel',
  948. 'xlm' => 'application/vnd.ms-excel',
  949. 'xls' => 'application/vnd.ms-excel',
  950. 'xlt' => 'application/vnd.ms-excel',
  951. 'xlw' => 'application/vnd.ms-excel',
  952. 'xof' => 'x-world/x-vrml',
  953. 'xpm' => 'image/x-xpixmap',
  954. 'xwd' => 'image/x-xwindowdump',
  955. 'z' => 'application/x-compress',
  956. 'zip' => 'application/zip'
  957. );
  958. if (isset($mimeTypes[strtolower($ext)])) {
  959. $contentType = $mimeTypes[strtolower($ext)];
  960. } else {
  961. $contentType = 'octet/application-stream';
  962. }
  963. return $contentType;
  964. }
  965. /**
  966. * Move a file or folder to a specific location
  967. *
  968. * @param string $from The location to move from
  969. * @param string $to The location to move to
  970. * @param string $point
  971. * @return boolean
  972. */
  973. public function moveObject($from,$to,$point = 'append') {
  974. $this->xpdo->lexicon->load('source');
  975. $success = false;
  976. if (substr(strrev($from),0,1) == '/') {
  977. $this->xpdo->error->message = $this->xpdo->lexicon('s3_no_move_folder',array(
  978. 'from' => $from
  979. ));
  980. return $success;
  981. }
  982. if (!$this->driver->if_object_exists($this->bucket,$from)) {
  983. $this->xpdo->error->message = $this->xpdo->lexicon('file_err_ns').': '.$from;
  984. return $success;
  985. }
  986. if ($to != '/') {
  987. if (!$this->driver->if_object_exists($this->bucket,$to)) {
  988. $this->xpdo->error->message = $this->xpdo->lexicon('file_err_ns').': '.$to;
  989. return $success;
  990. }
  991. $toPath = rtrim($to,'/').'/'.basename($from);
  992. } else {
  993. $toPath = basename($from);
  994. }
  995. $response = $this->driver->copy_object(array(
  996. 'bucket' => $this->bucket,
  997. 'filename' => $from,
  998. ),array(
  999. 'bucket' => $this->bucket,
  1000. 'filename' => $toPath,
  1001. ),array(
  1002. 'acl' => AmazonS3::ACL_PUBLIC,
  1003. ));
  1004. $success = $response->isOK();
  1005. if ($success) {
  1006. $deleteResponse = $this->driver->delete_object($this->bucket,$from);
  1007. $success = $deleteResponse->isOK();
  1008. } else {
  1009. $this->xpdo->error->message = $this->xpdo->lexicon('file_folder_err_rename').': '.$to.' -> '.$from;
  1010. }
  1011. return $success;
  1012. }
  1013. /**
  1014. * @return array
  1015. */
  1016. public function getDefaultProperties() {
  1017. return array(
  1018. 'url' => array(
  1019. 'name' => 'url',
  1020. 'desc' => 'prop_s3.url_desc',
  1021. 'type' => 'textfield',
  1022. 'options' => '',
  1023. 'value' => 'http://mysite.s3.amazonaws.com/',
  1024. 'lexicon' => 'core:source',
  1025. ),
  1026. 'bucket' => array(
  1027. 'name' => 'bucket',
  1028. 'desc' => 'prop_s3.bucket_desc',
  1029. 'type' => 'textfield',
  1030. 'options' => '',
  1031. 'value' => '',
  1032. 'lexicon' => 'core:source',
  1033. ),
  1034. 'key' => array(
  1035. 'name' => 'key',
  1036. 'desc' => 'prop_s3.key_desc',
  1037. 'type' => 'password',
  1038. 'options' => '',
  1039. 'value' => '',
  1040. 'lexicon' => 'core:source',
  1041. ),
  1042. 'secret_key' => array(
  1043. 'name' => 'secret_key',
  1044. 'desc' => 'prop_s3.secret_key_desc',
  1045. 'type' => 'password',
  1046. 'options' => '',
  1047. 'value' => '',
  1048. 'lexicon' => 'core:source',
  1049. ),
  1050. 'imageExtensions' => array(
  1051. 'name' => 'imageExtensions',
  1052. 'desc' => 'prop_s3.imageExtensions_desc',
  1053. 'type' => 'textfield',
  1054. 'value' => 'jpg,jpeg,png,gif,svg',
  1055. 'lexicon' => 'core:source',
  1056. ),
  1057. 'thumbnailType' => array(
  1058. 'name' => 'thumbnailType',
  1059. 'desc' => 'prop_s3.thumbnailType_desc',
  1060. 'type' => 'list',
  1061. 'options' => array(
  1062. array('name' => 'PNG','value' => 'png'),
  1063. array('name' => 'JPG','value' => 'jpg'),
  1064. array('name' => 'GIF','value' => 'gif'),
  1065. ),
  1066. 'value' => 'png',
  1067. 'lexicon' => 'core:source',
  1068. ),
  1069. 'thumbnailQuality' => array(
  1070. 'name' => 'thumbnailQuality',
  1071. 'desc' => 'prop_s3.thumbnailQuality_desc',
  1072. 'type' => 'textfield',
  1073. 'options' => '',
  1074. 'value' => 90,
  1075. 'lexicon' => 'core:source',
  1076. ),
  1077. 'skipFiles' => array(
  1078. 'name' => 'skipFiles',
  1079. 'desc' => 'prop_s3.skipFiles_desc',
  1080. 'type' => 'textfield',
  1081. 'options' => '',
  1082. 'value' => '.svn,.git,_notes,nbproject,.idea,.DS_Store',
  1083. 'lexicon' => 'core:source',
  1084. ),
  1085. 'region' => array(
  1086. 'name' => 'region',
  1087. 'desc' => 'prop_s3.region_desc',
  1088. 'type' => 'textfield',
  1089. 'options' => '',
  1090. 'value' => '',
  1091. 'lexicon' => 'core:source',
  1092. ),
  1093. );
  1094. }
  1095. /**
  1096. * Prepare a src parameter to be rendered with phpThumb
  1097. *
  1098. * @param string $src
  1099. * @return string
  1100. */
  1101. public function prepareSrcForThumb($src) {
  1102. $properties = $this->getPropertyList();
  1103. if (strpos($src,$properties['url']) === false) {
  1104. $src = $properties['url'].ltrim($src,'/');
  1105. }
  1106. return $src;
  1107. }
  1108. /**
  1109. * Get the base URL for this source. Only applicable to sources that are streams.
  1110. *
  1111. * @param string $object An optional object to find the base url of
  1112. * @return string
  1113. */
  1114. public function getBaseUrl($object = '') {
  1115. $properties = $this->getPropertyList();
  1116. return $properties['url'];
  1117. }
  1118. /**
  1119. * Get the absolute URL for a specified object. Only applicable to sources that are streams.
  1120. *
  1121. * @param string $object
  1122. * @return string
  1123. */
  1124. public function getObjectUrl($object = '') {
  1125. $properties = $this->getPropertyList();
  1126. return $properties['url'].$object;
  1127. }
  1128. public function getObjectFileSize($filename) {
  1129. return $this->driver->get_object_filesize($this->bucket, $filename);
  1130. }
  1131. /**
  1132. * Tells if a file is a binary file or not.
  1133. *
  1134. * @param string $file
  1135. * @param boolean If the passed string in $file is actual file content
  1136. * @return boolean True if a binary file.
  1137. */
  1138. public function isBinary($file, $isContent = false) {
  1139. if(!$isContent) {
  1140. // $file = file_get_contents($file, null, $stream, null, 512);
  1141. // this code is taken from modfilehandler.class.php method isBinary()
  1142. // the code above results in false positives (e.g. files marked as binary if the aren't)
  1143. $fh = @fopen($file, 'r');
  1144. $blk = @fread($fh, 512);
  1145. @fclose($fh);
  1146. @clearstatcache();
  1147. return (substr_count($blk, "^ -~" /*. "^\r\n"*/) / 512 > 0.3) || (substr_count($blk, "\x00") > 0) ? false : true;
  1148. }
  1149. $content = str_replace(array("\n", "\r", "\t"), '', $file);
  1150. return ctype_print($content) ? false : true;
  1151. }
  1152. /**
  1153. * Get the contents of a specified file
  1154. *
  1155. * @param string $objectPath
  1156. * @return array
  1157. */
  1158. public function getObjectContents($objectPath) {
  1159. $properties = $this->getPropertyList();
  1160. $objectUrl = $properties['url'].$objectPath;
  1161. $contents = @file_get_contents($objectUrl);
  1162. $imageExtensions = $this->getOption('imageExtensions',$this->properties,'jpg,jpeg,png,gif,svg');
  1163. $imageExtensions = explode(',',$imageExtensions);
  1164. $fileExtension = pathinfo($objectPath,PATHINFO_EXTENSION);
  1165. return array(
  1166. 'name' => $objectPath,
  1167. 'basename' => basename($objectPath),
  1168. 'path' => $objectPath,
  1169. 'size' => $this->getObjectFileSize($objectPath),
  1170. 'last_accessed' => '',
  1171. 'last_modified' => '',
  1172. 'content' => $contents,
  1173. 'image' => in_array($fileExtension,$imageExtensions) ? true : false,
  1174. 'is_writable' => !$this->isBinary($contents, true),
  1175. 'is_readable' => true,
  1176. );
  1177. }
  1178. }