openrat-cms

# OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs

ckeditor_php5.php (16038B)


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