tiny_mce_gzip.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <?php
  2. /**
  3. * tiny_mce_gzip.php
  4. *
  5. * Copyright 2010, Moxiecode Systems AB
  6. * Released under LGPL License.
  7. *
  8. * License: http://tinymce.moxiecode.com/license
  9. * Contributing: http://tinymce.moxiecode.com/contributing
  10. */
  11. // Handle incoming request if it's a script call
  12. if (TinyMCE_Compressor::getParam("js")) {
  13. // Default settings
  14. $tinyMCECompressor = new TinyMCE_Compressor(array(
  15. /*
  16. * Add any site-specific defaults here that you may wish to implement. For example:
  17. *
  18. * "languages" => "en",
  19. * "cache_dir" => realpath(dirname(__FILE__) . "/../../_cache"),
  20. * "files" => "somescript,anotherscript",
  21. * "expires" => "1m",
  22. */
  23. "cache_dir" => realpath(dirname(__FILE__) . "/../../cache_public"),
  24. ));
  25. // Handle request, compress and stream to client
  26. $tinyMCECompressor->handleRequest();
  27. }
  28. /**
  29. * This class combines and compresses the TinyMCE core, plugins, themes and
  30. * language packs into one disk cached gzipped request. It improves the loading speed of TinyMCE dramatically but
  31. * still provides dynamic initialization.
  32. *
  33. * Example of direct usage:
  34. * require_once("../jscripts/tiny_mce/tiny_mce_gzip.php");
  35. *
  36. * // Renders script tag with compressed scripts
  37. * TinyMCE_Compressor::renderTag(array(
  38. * "url" => "../jscripts/tiny_mce/tiny_mce_gzip.php",
  39. * "plugins" => "pagebreak,style",
  40. * "themes" => "advanced",
  41. * "languages" => "en"
  42. * ));
  43. */
  44. class TinyMCE_Compressor {
  45. private $files, $settings;
  46. private static $defaultSettings = array(
  47. "plugins" => "",
  48. "themes" => "",
  49. "languages" => "",
  50. "disk_cache" => true,
  51. "expires" => "30d",
  52. "cache_dir" => "",
  53. "compress" => true,
  54. "suffix" => "",
  55. "files" => "",
  56. "source" => false,
  57. );
  58. /**
  59. * Constructs a new compressor instance.
  60. *
  61. * @param Array $settings Name/value array with non-default settings for the compressor instance.
  62. */
  63. public function __construct($settings = array()) {
  64. $this->settings = array_merge(self::$defaultSettings, $settings);
  65. if (empty($this->settings["cache_dir"]))
  66. $this->settings["cache_dir"] = dirname(__FILE__);
  67. }
  68. /**
  69. * Adds a file to the concatenation/compression process.
  70. *
  71. * @param String $path Path to the file to include in the compressed package/output.
  72. */
  73. public function &addFile($file) {
  74. $this->files .= ($this->files ? "," : "") . $file;
  75. return $this;
  76. }
  77. /**
  78. * Handles the incoming HTTP request and sends back a compressed script depending on settings and client support.
  79. */
  80. public function handleRequest() {
  81. $files = array();
  82. $supportsGzip = false;
  83. $expiresOffset = $this->parseTime($this->settings["expires"]);
  84. $tinymceDir = dirname(__FILE__);
  85. // Override settings with querystring params
  86. $plugins = self::getParam("plugins");
  87. if ($plugins)
  88. $this->settings["plugins"] = $plugins;
  89. $plugins = explode(',', $this->settings["plugins"]);
  90. $themes = self::getParam("themes");
  91. if ($themes)
  92. $this->settings["themes"] = $themes;
  93. $themes = explode(',', $this->settings["themes"]);
  94. $languages = self::getParam("languages");
  95. if ($languages)
  96. $this->settings["languages"] = $languages;
  97. $languages = explode(',', $this->settings["languages"]);
  98. $tagFiles = self::getParam("files");
  99. if ($tagFiles)
  100. $this->settings["files"] = $tagFiles;
  101. $diskCache = self::getParam("diskcache");
  102. if ($diskCache)
  103. $this->settings["disk_cache"] = ($diskCache === "true");
  104. $src = self::getParam("src");
  105. if ($src)
  106. $this->settings["source"] = ($src === "true");
  107. // Add core
  108. $files[] = "tiny_mce";
  109. foreach ($languages as $language)
  110. $files[] = "langs/$language";
  111. // Add plugins
  112. foreach ($plugins as $plugin) {
  113. $files[] = "plugins/$plugin/editor_plugin";
  114. foreach ($languages as $language)
  115. $files[] = "plugins/$plugin/langs/$language";
  116. }
  117. // Add themes
  118. foreach ($themes as $theme) {
  119. $files[] = "themes/$theme/editor_template";
  120. foreach ($languages as $language)
  121. $files[] = "themes/$theme/langs/$language";
  122. }
  123. // Add any specified files.
  124. $allFiles = array_merge($files, explode(',', $this->settings['files']));
  125. // Process source files
  126. for ($i = 0; $i < count($allFiles); $i++) {
  127. $file = $allFiles[$i];
  128. if ($this->settings["source"] && file_exists($file . "_src.js")) {
  129. $file .= "_src.js";
  130. } else if (file_exists($file . ".js")) {
  131. $file .= ".js";
  132. } else {
  133. $file = "";
  134. }
  135. $allFiles[$i] = $file;
  136. }
  137. // Generate hash for all files
  138. $hash = md5(implode('', $allFiles));
  139. // Check if it supports gzip
  140. $zlibOn = ini_get('zlib.output_compression') || (ini_set('zlib.output_compression', 0) === false);
  141. $encodings = (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) ? strtolower($_SERVER['HTTP_ACCEPT_ENCODING']) : "";
  142. $encoding = preg_match( '/\b(x-gzip|gzip)\b/', $encodings, $match) ? $match[1] : "";
  143. // Is northon antivirus header
  144. if (isset($_SERVER['---------------']))
  145. $encoding = "x-gzip";
  146. $supportsGzip = $this->settings['compress'] && !empty($encoding) && !$zlibOn && function_exists('gzencode');
  147. // Set cache file name
  148. $cacheFile = $this->settings["cache_dir"] . "/" . $hash . ($supportsGzip ? ".gz" : ".js");
  149. // Set headers
  150. header("Content-type: text/javascript");
  151. header("Vary: Accept-Encoding"); // Handle proxies
  152. header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expiresOffset) . " GMT");
  153. header("Cache-Control: public, max-age=" . $expiresOffset);
  154. if ($supportsGzip)
  155. header("Content-Encoding: " . $encoding);
  156. // Use cached file
  157. if ($this->settings['disk_cache'] && file_exists($cacheFile)) {
  158. readfile($cacheFile);
  159. return;
  160. }
  161. // Set base URL for where tinymce is loaded from
  162. $buffer = "var tinyMCEPreInit={base:'" . dirname($_SERVER["SCRIPT_NAME"]) . "',suffix:''};";
  163. // Load all tinymce script files into buffer
  164. foreach ($allFiles as $file) {
  165. if ($file) {
  166. $fileContents = $this->getFileContents($tinymceDir . "/" . $file);
  167. // $buffer .= "\n//-FILE-$tinymceDir/$file (". strlen($fileContents) . " bytes)\n";
  168. $buffer .= $fileContents;
  169. }
  170. }
  171. // Mark all themes, plugins and languages as done
  172. $buffer .= 'tinymce.each("' . implode(',', $files) . '".split(","),function(f){tinymce.ScriptLoader.markDone(tinyMCE.baseURL+"/"+f+".js");});';
  173. // Compress data
  174. if ($supportsGzip)
  175. $buffer = gzencode($buffer, 9, FORCE_GZIP);
  176. // Write cached file
  177. if ($this->settings["disk_cache"])
  178. @file_put_contents($cacheFile, $buffer);
  179. // Stream contents to client
  180. echo $buffer;
  181. }
  182. /**
  183. * Renders a script tag that loads the TinyMCE script.
  184. *
  185. * @param Array $settings Name/value array with settings for the script tag.
  186. * @param Bool $return The script tag is return instead of being output if true
  187. * @return String the tag is returned if $return is true
  188. */
  189. public static function renderTag($tagSettings, $return = false) {
  190. $settings = array_merge(self::$defaultSettings, $tagSettings);
  191. if (empty($settings["cache_dir"]))
  192. $settings["cache_dir"] = dirname(__FILE__);
  193. $scriptSrc = $settings["url"] . "?js=1";
  194. // Add plugins
  195. if (isset($settings["plugins"]))
  196. $scriptSrc .= "&plugins=" . (is_array($settings["plugins"]) ? implode(',', $settings["plugins"]) : $settings["plugins"]);
  197. // Add themes
  198. if (isset($settings["themes"]))
  199. $scriptSrc .= "&themes=" . (is_array($settings["themes"]) ? implode(',', $settings["themes"]) : $settings["themes"]);
  200. // Add languages
  201. if (isset($settings["languages"]))
  202. $scriptSrc .= "&languages=" . (is_array($settings["languages"]) ? implode(',', $settings["languages"]) : $settings["languages"]);
  203. // Add disk_cache
  204. if (isset($settings["disk_cache"]))
  205. $scriptSrc .= "&diskcache=" . ($settings["disk_cache"] === true ? "true" : "false");
  206. // Add any explicitly specified files if the default settings have been overriden by the tag ones
  207. /*
  208. * Specifying tag files will override (rather than merge with) any site-specific ones set in the
  209. * TinyMCE_Compressor object creation. Note that since the parameter parser limits content to alphanumeric
  210. * only base filenames can be specified. The file extension is assumed to be ".js" and the directory is
  211. * the TinyMCE root directory. A typical use of this is to include a script which initiates the TinyMCE object.
  212. */
  213. if (isset($tagSettings["files"]))
  214. $scriptSrc .= "&files=" .(is_array($settings["files"]) ? implode(',', $settings["files"]) : $settings["files"]);
  215. // Add src flag
  216. if (isset($settings["source"]))
  217. $scriptSrc .= "&src=" . ($settings["source"] === true ? "true" : "false");
  218. $scriptTag = '<script type="text/javascript" src="' . htmlspecialchars($scriptSrc) . '"></script>';
  219. if ($return) {
  220. return $scriptTag;
  221. } else {
  222. echo $scriptTag;
  223. }
  224. }
  225. /**
  226. * Returns a sanitized query string parameter.
  227. *
  228. * @param String $name Name of the query string param to get.
  229. * @param String $default Default value if the query string item shouldn't exist.
  230. * @return String Sanitized query string parameter value.
  231. */
  232. public static function getParam($name, $default = "") {
  233. if (!isset($_GET[$name]))
  234. return $default;
  235. return preg_replace("/[^0-9a-z\-_,]+/i", "", $_GET[$name]); // Sanatize for security, remove anything but 0-9,a-z,-_,
  236. }
  237. /**
  238. * Parses the specified time format into seconds. Supports formats like 10h, 10d, 10m.
  239. *
  240. * @param String $time Time format to convert into seconds.
  241. * @return Int Number of seconds for the specified format.
  242. */
  243. private function parseTime($time) {
  244. $multipel = 1;
  245. // Hours
  246. if (strpos($time, "h") > 0)
  247. $multipel = 3600;
  248. // Days
  249. if (strpos($time, "d") > 0)
  250. $multipel = 86400;
  251. // Months
  252. if (strpos($time, "m") > 0)
  253. $multipel = 2592000;
  254. // Trim string
  255. return intval($time) * $multipel;
  256. }
  257. /**
  258. * Returns the contents of the script file if it exists and removes the UTF-8 BOM header if it exists.
  259. *
  260. * @param String $file File to load.
  261. * @return String File contents or empty string if it doesn't exist.
  262. */
  263. private function getFileContents($file) {
  264. $content = file_get_contents($file);
  265. // Remove UTF-8 BOM
  266. if (substr($content, 0, 3) === pack("CCC", 0xef, 0xbb, 0xbf))
  267. $content = substr($content, 3);
  268. return $content;
  269. }
  270. }
  271. ?>