modstaticresource.class.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. /**
  11. * A derivative of modResource that stores content on the filesystem.
  12. *
  13. * {@inheritdoc}
  14. *
  15. * @package modx
  16. */
  17. class modStaticResource extends modResource implements modResourceInterface {
  18. /**
  19. * @var string Path of the file containing the source content, relative to
  20. * the {@link modStaticResource::$_sourcePath}.
  21. */
  22. protected $_sourceFile= '';
  23. /**
  24. * @var integer Size of the source file content in bytes.
  25. */
  26. protected $_sourceFileSize= 0;
  27. /**
  28. * @var string An absolute base filesystem path where the source file
  29. * exists.
  30. */
  31. protected $_sourcePath= '';
  32. /**
  33. * Overrides modResource::__construct to set the class key for this Resource type
  34. * @param xPDO $xpdo A reference to the xPDO|modX instance
  35. */
  36. function __construct(& $xpdo) {
  37. parent :: __construct($xpdo);
  38. $this->set('class_key','modStaticResource');
  39. $this->showInContextMenu = true;
  40. }
  41. /**
  42. * Get the absolute path to the static source file represented by this instance.
  43. *
  44. * @param array $options An array of options.
  45. * @return string The absolute path to the static source file.
  46. */
  47. public function getSourceFile(array $options = array()) {
  48. if (empty($this->_sourceFile)) {
  49. $filename = parent :: getContent($options);
  50. if (!empty($filename)) {
  51. $array = array();
  52. if ($this->xpdo->getParser() && $this->xpdo->parser->collectElementTags($filename, $array)) {
  53. $this->xpdo->parser->processElementTags('', $filename);
  54. }
  55. }
  56. if (!file_exists($filename)) {
  57. $this->_sourcePath= $this->xpdo->getOption('resource_static_path', $options, $this->xpdo->getOption('base_path'));
  58. if ($this->xpdo->getParser() && $this->xpdo->parser->collectElementTags($this->_sourcePath, $array)) {
  59. $this->xpdo->parser->processElementTags('', $this->_sourcePath);
  60. }
  61. $this->_sourceFile= $this->_sourcePath . $filename;
  62. } else {
  63. $this->_sourceFile= $filename;
  64. }
  65. }
  66. return $this->_sourceFile;
  67. }
  68. /**
  69. * Get the filesize of the static source file represented by this instance.
  70. *
  71. * @param array $options An array of options.
  72. * @return integer The filesize of the source file in bytes.
  73. */
  74. public function getSourceFileSize(array $options = array()) {
  75. if (empty($this->_sourceFileSize)) {
  76. $this->getSourceFile($options);
  77. if (file_exists($this->_sourceFile)) {
  78. $this->_sourceFileSize = filesize($this->_sourceFile);
  79. }
  80. }
  81. return $this->_sourceFileSize;
  82. }
  83. /**
  84. * Treats the local content as a filename to load the raw content from.
  85. *
  86. * {@inheritdoc}
  87. */
  88. public function getContent(array $options = array()) {
  89. $content = false; // Going to sendErrorPage() if couldn't populate the $content
  90. $this->getSourceFile($options);
  91. if (!empty ($this->_sourceFile)) {
  92. if (file_exists($this->_sourceFile)) {
  93. $content= $this->getFileContent($this->_sourceFile);
  94. if ($content === false) {
  95. $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "No content could be retrieved from source file: {$this->_sourceFile}");
  96. }
  97. } else {
  98. $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not locate source file: {$this->_sourceFile}");
  99. }
  100. } else {
  101. $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "No source file specified.");
  102. }
  103. if($content===false) {
  104. $this->xpdo->sendErrorPage();
  105. }
  106. return $content;
  107. }
  108. /**
  109. * Retrieve the resource content stored in a physical file.
  110. *
  111. * @param string $file A path to the file representing the resource content.
  112. * @param array $options
  113. * @return string The content of the file, of false if it could not be
  114. * retrieved.
  115. */
  116. public function getFileContent($file, array $options = array()) {
  117. $content= false;
  118. $memory_limit= ini_get('memory_limit');
  119. if (!$memory_limit) $memory_limit= '8M';
  120. $byte_limit= $this->_bytes($memory_limit) * .5;
  121. $filesize= $this->getSourceFileSize($options);
  122. if ($this->getOne('ContentType')) {
  123. $type= $this->ContentType->get('mime_type') ? $this->ContentType->get('mime_type') : 'text/html';
  124. if ($this->ContentType->get('binary') || $filesize > $byte_limit) {
  125. if ($alias= $this->get('uri')) {
  126. $name= basename($alias);
  127. } elseif ($this->get('alias')) {
  128. $name= $this->get('alias');
  129. if ($ext= $this->ContentType->getExtension()) {
  130. $name .= "{$ext}";
  131. }
  132. } elseif ($name= $this->get('pagetitle')) {
  133. $name= $this->cleanAlias($name);
  134. if ($ext= $this->ContentType->getExtension()) {
  135. $name .= "{$ext}";
  136. }
  137. } else {
  138. $name= 'download';
  139. if ($ext= $this->ContentType->getExtension()) {
  140. $name .= "{$ext}";
  141. }
  142. }
  143. $header= 'Content-Type: ' . $type;
  144. if (!$this->ContentType->get('binary')) {
  145. $charset= $this->xpdo->getOption('modx_charset',null,'UTF-8');
  146. $header .= '; charset=' . $charset;
  147. }
  148. header($header);
  149. if ($this->ContentType->get('binary')) {
  150. header('Content-Transfer-Encoding: binary');
  151. }
  152. if ($filesize > 0) {
  153. $header= 'Content-Length: ' . $filesize;
  154. header($header);
  155. }
  156. $header= 'Cache-Control: public';
  157. header($header);
  158. $header= 'Content-Disposition: ' . ($this->get('content_dispo') ? 'attachment; filename=' . $name : 'inline');
  159. header($header);
  160. $header= 'Vary: User-Agent';
  161. header($header);
  162. if ($customHeaders= $this->ContentType->get('headers')) {
  163. foreach ($customHeaders as $headerKey => $headerString) {
  164. header($headerString);
  165. }
  166. }
  167. @session_write_close();
  168. while (ob_get_level() && @ob_end_clean()) {}
  169. readfile($file);
  170. die();
  171. }
  172. else {
  173. $content = file_get_contents($file);
  174. }
  175. if (!is_string($content)) {
  176. $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "modStaticResource->getFileContent({$file}): Could not get content from file.");
  177. }
  178. }
  179. return $content;
  180. }
  181. /**
  182. * Converts to bytes from PHP ini_get() format.
  183. *
  184. * PHP ini modifiers for byte values:
  185. * <ul>
  186. * <li>G = gigabytes</li>
  187. * <li>M = megabytes</li>
  188. * <li>K = kilobytes</li>
  189. * </ul>
  190. *
  191. * @access protected
  192. * @param string $value Number of bytes represented in PHP ini value format.
  193. * @return integer The value converted to bytes.
  194. */
  195. protected function _bytes($value) {
  196. $value = trim($value);
  197. $modifier = strtolower($value{strlen($value)-1});
  198. switch($modifier) {
  199. case 'g':
  200. $value *= 1024;
  201. case 'm':
  202. $value *= 1024;
  203. case 'k':
  204. $value *= 1024;
  205. }
  206. return $value;
  207. }
  208. /**
  209. * Sets the path to the Static Resource manager controller
  210. * @static
  211. * @param xPDO $modx A reference to the modX instance
  212. * @return string
  213. */
  214. public static function getControllerPath(xPDO &$modx) {
  215. $path = modResource::getControllerPath($modx);
  216. return $path.'staticresource/';
  217. }
  218. /**
  219. * Use this in your extended Resource class to display the text for the context menu item, if showInContextMenu is
  220. * set to true.
  221. * @return array
  222. */
  223. public function getContextMenuText() {
  224. return array(
  225. 'text_create' => $this->xpdo->lexicon('static_resource'),
  226. 'text_create_here' => $this->xpdo->lexicon('static_resource_create_here'),
  227. );
  228. }
  229. /**
  230. * Use this in your extended Resource class to return a translatable name for the Resource Type.
  231. * @return string
  232. */
  233. public function getResourceTypeName() {
  234. return $this->xpdo->lexicon('static_resource');
  235. }
  236. }