ckeditor_php5.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <?php
  2. /*
  3. * Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
  4. * For licensing, see LICENSE.html or http://ckeditor.com/license
  5. */
  6. /**
  7. * \brief CKEditor class that can be used to create editor
  8. * instances in PHP pages on server side.
  9. * @see http://ckeditor.com
  10. *
  11. * Sample usage:
  12. * @code
  13. * $CKEditor = new CKEditor();
  14. * $CKEditor->editor("editor1", "<p>Initial value.</p>");
  15. * @endcode
  16. */
  17. class CKEditor
  18. {
  19. /**
  20. * The version of %CKEditor.
  21. */
  22. const version = '3.6.2';
  23. /**
  24. * A constant string unique for each release of %CKEditor.
  25. */
  26. const timestamp = 'B8DJ5M3';
  27. /**
  28. * URL to the %CKEditor installation directory (absolute or relative to document root).
  29. * If not set, CKEditor will try to guess it's path.
  30. *
  31. * Example usage:
  32. * @code
  33. * $CKEditor->basePath = '/ckeditor/';
  34. * @endcode
  35. */
  36. public $basePath;
  37. /**
  38. * An array that holds the global %CKEditor configuration.
  39. * For the list of available options, see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
  40. *
  41. * Example usage:
  42. * @code
  43. * $CKEditor->config['height'] = 400;
  44. * // Use @@ at the beggining of a string to ouput it without surrounding quotes.
  45. * $CKEditor->config['width'] = '@@screen.width * 0.8';
  46. * @endcode
  47. */
  48. public $config = array();
  49. /**
  50. * A boolean variable indicating whether CKEditor has been initialized.
  51. * Set it to true only if you have already included
  52. * &lt;script&gt; tag loading ckeditor.js in your website.
  53. */
  54. public $initialized = false;
  55. /**
  56. * Boolean variable indicating whether created code should be printed out or returned by a function.
  57. *
  58. * Example 1: get the code creating %CKEditor instance and print it on a page with the "echo" function.
  59. * @code
  60. * $CKEditor = new CKEditor();
  61. * $CKEditor->returnOutput = true;
  62. * $code = $CKEditor->editor("editor1", "<p>Initial value.</p>");
  63. * echo "<p>Editor 1:</p>";
  64. * echo $code;
  65. * @endcode
  66. */
  67. public $returnOutput = false;
  68. /**
  69. * An array with textarea attributes.
  70. *
  71. * When %CKEditor is created with the editor() method, a HTML &lt;textarea&gt; element is created,
  72. * it will be displayed to anyone with JavaScript disabled or with incompatible browser.
  73. */
  74. public $textareaAttributes = array( "rows" => 8, "cols" => 60 );
  75. /**
  76. * A string indicating the creation date of %CKEditor.
  77. * Do not change it unless you want to force browsers to not use previously cached version of %CKEditor.
  78. */
  79. public $timestamp = "B8DJ5M3";
  80. /**
  81. * An array that holds event listeners.
  82. */
  83. private $events = array();
  84. /**
  85. * An array that holds global event listeners.
  86. */
  87. private $globalEvents = array();
  88. /**
  89. * Main Constructor.
  90. *
  91. * @param $basePath (string) URL to the %CKEditor installation directory (optional).
  92. */
  93. function __construct($basePath = null) {
  94. if (!empty($basePath)) {
  95. $this->basePath = $basePath;
  96. }
  97. }
  98. /**
  99. * Creates a %CKEditor instance.
  100. * In incompatible browsers %CKEditor will downgrade to plain HTML &lt;textarea&gt; element.
  101. *
  102. * @param $name (string) Name of the %CKEditor instance (this will be also the "name" attribute of textarea element).
  103. * @param $value (string) Initial value (optional).
  104. * @param $config (array) The specific configurations to apply to this editor instance (optional).
  105. * @param $events (array) Event listeners for this editor instance (optional).
  106. *
  107. * Example usage:
  108. * @code
  109. * $CKEditor = new CKEditor();
  110. * $CKEditor->editor("field1", "<p>Initial value.</p>");
  111. * @endcode
  112. *
  113. * Advanced example:
  114. * @code
  115. * $CKEditor = new CKEditor();
  116. * $config = array();
  117. * $config['toolbar'] = array(
  118. * array( 'Source', '-', 'Bold', 'Italic', 'Underline', 'Strike' ),
  119. * array( 'Image', 'Link', 'Unlink', 'Anchor' )
  120. * );
  121. * $events['instanceReady'] = 'function (ev) {
  122. * alert("Loaded: " + ev.editor.name);
  123. * }';
  124. * $CKEditor->editor("field1", "<p>Initial value.</p>", $config, $events);
  125. * @endcode
  126. */
  127. public function editor($name, $value = "", $config = array(), $events = array())
  128. {
  129. $attr = "";
  130. foreach ($this->textareaAttributes as $key => $val) {
  131. $attr.= " " . $key . '="' . str_replace('"', '&quot;', $val) . '"';
  132. }
  133. $out = "<textarea name=\"" . $name . "\"" . $attr . ">" . htmlspecialchars($value) . "</textarea>\n";
  134. if (!$this->initialized) {
  135. $out .= $this->init();
  136. }
  137. $_config = $this->configSettings($config, $events);
  138. $js = $this->returnGlobalEvents();
  139. if (!empty($_config))
  140. $js .= "CKEDITOR.replace('".$name."', ".$this->jsEncode($_config).");";
  141. else
  142. $js .= "CKEDITOR.replace('".$name."');";
  143. $out .= $this->script($js);
  144. if (!$this->returnOutput) {
  145. print $out;
  146. $out = "";
  147. }
  148. return $out;
  149. }
  150. /**
  151. * Replaces a &lt;textarea&gt; with a %CKEditor instance.
  152. *
  153. * @param $id (string) The id or name of textarea element.
  154. * @param $config (array) The specific configurations to apply to this editor instance (optional).
  155. * @param $events (array) Event listeners for this editor instance (optional).
  156. *
  157. * Example 1: adding %CKEditor to &lt;textarea name="article"&gt;&lt;/textarea&gt; element:
  158. * @code
  159. * $CKEditor = new CKEditor();
  160. * $CKEditor->replace("article");
  161. * @endcode
  162. */
  163. public function replace($id, $config = array(), $events = array())
  164. {
  165. $out = "";
  166. if (!$this->initialized) {
  167. $out .= $this->init();
  168. }
  169. $_config = $this->configSettings($config, $events);
  170. $js = $this->returnGlobalEvents();
  171. if (!empty($_config)) {
  172. $js .= "CKEDITOR.replace('".$id."', ".$this->jsEncode($_config).");";
  173. }
  174. else {
  175. $js .= "CKEDITOR.replace('".$id."');";
  176. }
  177. $out .= $this->script($js);
  178. if (!$this->returnOutput) {
  179. print $out;
  180. $out = "";
  181. }
  182. return $out;
  183. }
  184. /**
  185. * Replace all &lt;textarea&gt; elements available in the document with editor instances.
  186. *
  187. * @param $className (string) If set, replace all textareas with class className in the page.
  188. *
  189. * Example 1: replace all &lt;textarea&gt; elements in the page.
  190. * @code
  191. * $CKEditor = new CKEditor();
  192. * $CKEditor->replaceAll();
  193. * @endcode
  194. *
  195. * Example 2: replace all &lt;textarea class="myClassName"&gt; elements in the page.
  196. * @code
  197. * $CKEditor = new CKEditor();
  198. * $CKEditor->replaceAll( 'myClassName' );
  199. * @endcode
  200. */
  201. public function replaceAll($className = null)
  202. {
  203. $out = "";
  204. if (!$this->initialized) {
  205. $out .= $this->init();
  206. }
  207. $_config = $this->configSettings();
  208. $js = $this->returnGlobalEvents();
  209. if (empty($_config)) {
  210. if (empty($className)) {
  211. $js .= "CKEDITOR.replaceAll();";
  212. }
  213. else {
  214. $js .= "CKEDITOR.replaceAll('".$className."');";
  215. }
  216. }
  217. else {
  218. $classDetection = "";
  219. $js .= "CKEDITOR.replaceAll( function(textarea, config) {\n";
  220. if (!empty($className)) {
  221. $js .= " var classRegex = new RegExp('(?:^| )' + '". $className ."' + '(?:$| )');\n";
  222. $js .= " if (!classRegex.test(textarea.className))\n";
  223. $js .= " return false;\n";
  224. }
  225. $js .= " CKEDITOR.tools.extend(config, ". $this->jsEncode($_config) .", true);";
  226. $js .= "} );";
  227. }
  228. $out .= $this->script($js);
  229. if (!$this->returnOutput) {
  230. print $out;
  231. $out = "";
  232. }
  233. return $out;
  234. }
  235. /**
  236. * Adds event listener.
  237. * Events are fired by %CKEditor in various situations.
  238. *
  239. * @param $event (string) Event name.
  240. * @param $javascriptCode (string) Javascript anonymous function or function name.
  241. *
  242. * Example usage:
  243. * @code
  244. * $CKEditor->addEventHandler('instanceReady', 'function (ev) {
  245. * alert("Loaded: " + ev.editor.name);
  246. * }');
  247. * @endcode
  248. */
  249. public function addEventHandler($event, $javascriptCode)
  250. {
  251. if (!isset($this->events[$event])) {
  252. $this->events[$event] = array();
  253. }
  254. // Avoid duplicates.
  255. if (!in_array($javascriptCode, $this->events[$event])) {
  256. $this->events[$event][] = $javascriptCode;
  257. }
  258. }
  259. /**
  260. * Clear registered event handlers.
  261. * Note: this function will have no effect on already created editor instances.
  262. *
  263. * @param $event (string) Event name, if not set all event handlers will be removed (optional).
  264. */
  265. public function clearEventHandlers($event = null)
  266. {
  267. if (!empty($event)) {
  268. $this->events[$event] = array();
  269. }
  270. else {
  271. $this->events = array();
  272. }
  273. }
  274. /**
  275. * Adds global event listener.
  276. *
  277. * @param $event (string) Event name.
  278. * @param $javascriptCode (string) Javascript anonymous function or function name.
  279. *
  280. * Example usage:
  281. * @code
  282. * $CKEditor->addGlobalEventHandler('dialogDefinition', 'function (ev) {
  283. * alert("Loading dialog: " + ev.data.name);
  284. * }');
  285. * @endcode
  286. */
  287. public function addGlobalEventHandler($event, $javascriptCode)
  288. {
  289. if (!isset($this->globalEvents[$event])) {
  290. $this->globalEvents[$event] = array();
  291. }
  292. // Avoid duplicates.
  293. if (!in_array($javascriptCode, $this->globalEvents[$event])) {
  294. $this->globalEvents[$event][] = $javascriptCode;
  295. }
  296. }
  297. /**
  298. * Clear registered global event handlers.
  299. * Note: this function will have no effect if the event handler has been already printed/returned.
  300. *
  301. * @param $event (string) Event name, if not set all event handlers will be removed (optional).
  302. */
  303. public function clearGlobalEventHandlers($event = null)
  304. {
  305. if (!empty($event)) {
  306. $this->globalEvents[$event] = array();
  307. }
  308. else {
  309. $this->globalEvents = array();
  310. }
  311. }
  312. /**
  313. * Prints javascript code.
  314. *
  315. * @param string $js
  316. */
  317. private function script($js)
  318. {
  319. $out = "<script type=\"text/javascript\">";
  320. $out .= "//<![CDATA[\n";
  321. $out .= $js;
  322. $out .= "\n//]]>";
  323. $out .= "</script>\n";
  324. return $out;
  325. }
  326. /**
  327. * Returns the configuration array (global and instance specific settings are merged into one array).
  328. *
  329. * @param $config (array) The specific configurations to apply to editor instance.
  330. * @param $events (array) Event listeners for editor instance.
  331. */
  332. private function configSettings($config = array(), $events = array())
  333. {
  334. $_config = $this->config;
  335. $_events = $this->events;
  336. if (is_array($config) && !empty($config)) {
  337. $_config = array_merge($_config, $config);
  338. }
  339. if (is_array($events) && !empty($events)) {
  340. foreach ($events as $eventName => $code) {
  341. if (!isset($_events[$eventName])) {
  342. $_events[$eventName] = array();
  343. }
  344. if (!in_array($code, $_events[$eventName])) {
  345. $_events[$eventName][] = $code;
  346. }
  347. }
  348. }
  349. if (!empty($_events)) {
  350. foreach($_events as $eventName => $handlers) {
  351. if (empty($handlers)) {
  352. continue;
  353. }
  354. else if (count($handlers) == 1) {
  355. $_config['on'][$eventName] = '@@'.$handlers[0];
  356. }
  357. else {
  358. $_config['on'][$eventName] = '@@function (ev){';
  359. foreach ($handlers as $handler => $code) {
  360. $_config['on'][$eventName] .= '('.$code.')(ev);';
  361. }
  362. $_config['on'][$eventName] .= '}';
  363. }
  364. }
  365. }
  366. return $_config;
  367. }
  368. /**
  369. * Return global event handlers.
  370. */
  371. private function returnGlobalEvents()
  372. {
  373. static $returnedEvents;
  374. $out = "";
  375. if (!isset($returnedEvents)) {
  376. $returnedEvents = array();
  377. }
  378. if (!empty($this->globalEvents)) {
  379. foreach ($this->globalEvents as $eventName => $handlers) {
  380. foreach ($handlers as $handler => $code) {
  381. if (!isset($returnedEvents[$eventName])) {
  382. $returnedEvents[$eventName] = array();
  383. }
  384. // Return only new events
  385. if (!in_array($code, $returnedEvents[$eventName])) {
  386. $out .= ($code ? "\n" : "") . "CKEDITOR.on('". $eventName ."', $code);";
  387. $returnedEvents[$eventName][] = $code;
  388. }
  389. }
  390. }
  391. }
  392. return $out;
  393. }
  394. /**
  395. * Initializes CKEditor (executed only once).
  396. */
  397. private function init()
  398. {
  399. static $initComplete;
  400. $out = "";
  401. if (!empty($initComplete)) {
  402. return "";
  403. }
  404. if ($this->initialized) {
  405. $initComplete = true;
  406. return "";
  407. }
  408. $args = "";
  409. $ckeditorPath = $this->ckeditorPath();
  410. if (!empty($this->timestamp) && $this->timestamp != "%"."TIMESTAMP%") {
  411. $args = '?t=' . $this->timestamp;
  412. }
  413. // Skip relative paths...
  414. if (strpos($ckeditorPath, '..') !== 0) {
  415. $out .= $this->script("window.CKEDITOR_BASEPATH='". $ckeditorPath ."';");
  416. }
  417. $out .= "<script type=\"text/javascript\" src=\"" . $ckeditorPath . 'ckeditor.js' . $args . "\"></script>\n";
  418. $extraCode = "";
  419. if ($this->timestamp != self::timestamp) {
  420. $extraCode .= ($extraCode ? "\n" : "") . "CKEDITOR.timestamp = '". $this->timestamp ."';";
  421. }
  422. if ($extraCode) {
  423. $out .= $this->script($extraCode);
  424. }
  425. $initComplete = $this->initialized = true;
  426. return $out;
  427. }
  428. /**
  429. * Return path to ckeditor.js.
  430. */
  431. private function ckeditorPath()
  432. {
  433. if (!empty($this->basePath)) {
  434. return $this->basePath;
  435. }
  436. /**
  437. * The absolute pathname of the currently executing script.
  438. * Note: If a script is executed with the CLI, as a relative path, such as file.php or ../file.php,
  439. * $_SERVER['SCRIPT_FILENAME'] will contain the relative path specified by the user.
  440. */
  441. if (isset($_SERVER['SCRIPT_FILENAME'])) {
  442. $realPath = dirname($_SERVER['SCRIPT_FILENAME']);
  443. }
  444. else {
  445. /**
  446. * realpath - Returns canonicalized absolute pathname
  447. */
  448. $realPath = realpath( './' ) ;
  449. }
  450. /**
  451. * The filename of the currently executing script, relative to the document root.
  452. * For instance, $_SERVER['PHP_SELF'] in a script at the address http://example.com/test.php/foo.bar
  453. * would be /test.php/foo.bar.
  454. */
  455. $selfPath = dirname($_SERVER['PHP_SELF']);
  456. $file = str_replace("\\", "/", __FILE__);
  457. if (!$selfPath || !$realPath || !$file) {
  458. return "/ckeditor/";
  459. }
  460. $documentRoot = substr($realPath, 0, strlen($realPath) - strlen($selfPath));
  461. $fileUrl = substr($file, strlen($documentRoot));
  462. $ckeditorUrl = str_replace("ckeditor_php5.php", "", $fileUrl);
  463. return $ckeditorUrl;
  464. }
  465. /**
  466. * This little function provides a basic JSON support.
  467. *
  468. * @param mixed $val
  469. * @return string
  470. */
  471. private function jsEncode($val)
  472. {
  473. if (is_null($val)) {
  474. return 'null';
  475. }
  476. if (is_bool($val)) {
  477. return $val ? 'true' : 'false';
  478. }
  479. if (is_int($val)) {
  480. return $val;
  481. }
  482. if (is_float($val)) {
  483. return str_replace(',', '.', $val);
  484. }
  485. if (is_array($val) || is_object($val)) {
  486. if (is_array($val) && (array_keys($val) === range(0,count($val)-1))) {
  487. return '[' . implode(',', array_map(array($this, 'jsEncode'), $val)) . ']';
  488. }
  489. $temp = array();
  490. foreach ($val as $k => $v){
  491. $temp[] = $this->jsEncode("{$k}") . ':' . $this->jsEncode($v);
  492. }
  493. return '{' . implode(',', $temp) . '}';
  494. }
  495. // String otherwise
  496. if (strpos($val, '@@') === 0)
  497. return substr($val, 2);
  498. if (strtoupper(substr($val, 0, 9)) == 'CKEDITOR.')
  499. return $val;
  500. return '"' . str_replace(array("\\", "/", "\n", "\t", "\r", "\x08", "\x0c", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'), $val) . '"';
  501. }
  502. }