Loader.php 9.46 KB
Newer Older
sgalinsk's avatar
sgalinsk committed
1
<?php
2

3 4
namespace SGalinski\Tinymce;

sgalinsk's avatar
sgalinsk committed
5
/***************************************************************
6
 *  Copyright notice
sgalinsk's avatar
sgalinsk committed
7
 *
8 9
 *  (c) sgalinski Internet Services (http://www.sgalinski.de)
 *
10
 *  All rights reserved
sgalinsk's avatar
sgalinsk committed
11
 *
12 13 14
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
15
 *  the Free Software Foundation; either version 3 of the License, or
16 17 18 19 20 21 22
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 25 26 27
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/
sgalinsk's avatar
sgalinsk committed
28

29 30 31 32 33 34
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Lang\LanguageService;

sgalinsk's avatar
sgalinsk committed
35 36 37
/**
 * tinyMCE initialisation class
 *
38
 * Usage:
39 40 41
 * $tinyMceLoader = GeneralUtility::makeInstance('\SGalinski\Tinymce\Loader');
 * $tinyMceLoader->loadConfiguration($configurationFile);
 * $javascript = $tinyMceLoader->loadJsViaPageRenderer();
42 43 44 45 46 47
 *
 * Basic Configuration:
 *
 * tinymce.init({
 *    selector: 'textarea'
 * });
sgalinsk's avatar
sgalinsk committed
48
 */
49
class Loader {
50
	/**
51
	 * TinyMCE configuration
52
	 *
53 54
	 * @var array
	 */
55
	protected $tinymceConfiguration = array();
56 57

	/**
58 59 60
	 * Initialization flag
	 *
	 * @var bool
61
	 */
62
	static protected $init = FALSE;
sgalinsk's avatar
sgalinsk committed
63

64 65 66 67 68 69 70 71 72 73 74 75
	/**
	 * @param string $configuration file reference or configuration string (defaults to basic configuration)
	 * @param boolean $forceLanguage set this to true if you want to force your language set by the configuration
	 * @return void
	 */
	public function loadConfiguration($configuration = '', $forceLanguage = FALSE) {
		self::$init = FALSE;
		$this->tinymceConfiguration = $this->prepareTinyMCEConfiguration($configuration);
		if (!$forceLanguage) {
			$this->setLanguage();
		}
	}
sgalinsk's avatar
sgalinsk committed
76

77
	/**
78 79
	 * Calculates and sets the current language
	 *
80 81 82
	 * @return void
	 */
	protected function setLanguage() {
83
		/** @var $languageInstance LanguageService */
sgalinsk's avatar
sgalinsk committed
84
		$languageInstance = (TYPO3_MODE === 'FE' ? $GLOBALS['TSFE'] : $GLOBALS['LANG']);
85 86
		$languageKey = $languageInstance->lang;

87
		if (TYPO3_MODE === 'BE') {
88
			$groupOrUserProps = BackendUtility::getModTSconfig('', 'tx_tinyMCE');
89 90 91
			if (trim($groupOrUserProps['properties']['prefLang']) !== '') {
				$languageKey = $groupOrUserProps['properties']['prefLang'];
			}
sgalinsk's avatar
sgalinsk committed
92 93
		}

94
		// language conversion from TLD to iso631
95 96 97 98
		/** @var $locales \TYPO3\CMS\Core\Localization\Locales */
		$locales = GeneralUtility::makeInstance('\TYPO3\CMS\Core\Localization\Locales');
		$locales->initialize();
		$isoArray = $locales->getIsoMapping();
sgalinsk's avatar
sgalinsk committed
99 100 101

		if (array_key_exists($languageKey, $isoArray)) {
			$languageKey = $isoArray[$languageKey];
102
		}
103

104
		$languageFile = PATH_site . ExtensionManagementUtility::siteRelPath('tinymce') .
105
			'Contrib/tinymce/langs/' . $languageKey . '.js';
106 107 108 109
		if (!is_file($languageFile)) {
			$languageKey = 'en';
		}

110
		$this->addConfigurationOption('language', $languageKey);
sgalinsk's avatar
sgalinsk committed
111 112 113
	}

	/**
114
	 * Returns a file that contains the tinyMCE configuration
sgalinsk's avatar
sgalinsk committed
115
	 *
116 117 118 119
	 * Note: The load dom event cannot be used, because e.g. IRRE adds the javascript
	 * later on. This leads to code that is never executed. The interval timer hack fixes this
	 * issue.
	 *
120
	 * @return string
sgalinsk's avatar
sgalinsk committed
121
	 */
122
	protected function getConfiguration() {
123 124
		$configuration = $this->tinymceConfiguration['preJS'];
		$configuration .= '
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
			var SG = SG || {};
			SG.initTinyMceLoadFunction = function() {
				if (SG.initializedTinyMceLoaderInstance) {
					return;
				}

				if (SG.TinyMceLoader && window.tinymce && window.tinymce.init) {
					SG.initializedTinyMceLoaderInstance = new SG.TinyMceLoader(window.tinymce, {
						' . $this->replaceTypo3Paths($this->tinymceConfiguration['configurationData']) . '
					});
					clearInterval(SG.initTinyMceLoadInterval);
				}
			};
			SG.initTinyMceLoadFunction();
			SG.initTinyMceLoadInterval = window.setInterval(SG.initTinyMceLoadFunction, 1000);
140
		';
141
		$configuration .= $this->tinymceConfiguration['postJS'];
sgalinsk's avatar
sgalinsk committed
142

143 144 145 146
		$filename = 'tinymceConfiguration' . sha1($configuration) . '.js';
		$file = PATH_site . 'typo3temp/' . $filename;
		if (!is_file($file)) {
			file_put_contents($file, $configuration);
147
			GeneralUtility::fixPermissions($file);
148 149 150
		}

		return $this->getPath($file, TRUE);
sgalinsk's avatar
sgalinsk committed
151 152 153
	}

	/**
154
	 * Returns the needed javascript inclusion code
sgalinsk's avatar
sgalinsk committed
155
	 *
156
	 * Note: This function can only be called once.
sgalinsk's avatar
sgalinsk committed
157
	 *
158
	 * @return string
sgalinsk's avatar
sgalinsk committed
159
	 */
160
	public function getJS() {
161
		$output = '';
162 163
		if (!self::$init) {
			self::$init = TRUE;
164 165 166
			$pathToTinyMceExtension = ExtensionManagementUtility::extRelPath('tinymce');

			$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/tinymce/tinymce.min.js';
167 168
			$output = '<script type="text/javascript" src="' . $script . '"></script>';

169 170 171 172 173 174 175
			$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/MutationObserver/MutationObserver.js';
			$output .= '<script type="text/javascript" src="' . $script . '"></script>';

			$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Resources/Public/JavaScript/Loader.js';
			$output .= '<script type="text/javascript" src="' . $script . '"></script>';

			$script = $this->getConfiguration();
176 177 178 179 180 181 182 183 184
			$output .= '<script type="text/javascript" src="' . $script . '"></script>';
		}

		return $output;
	}

	/**
	 * Loads the required javascript via the given page renderer instance
	 *
185 186
	 * Note: This function can only be called once.
	 *
187
	 * @param PageRenderer $pageRenderer
188 189
	 * @return void
	 */
190
	public function loadJsViaPageRenderer(PageRenderer $pageRenderer) {
191
		if (self::$init) {
192
			return;
193
		}
194
		self::$init = TRUE;
sgalinsk's avatar
sgalinsk committed
195

196 197
		$pathToTinyMceExtension = ExtensionManagementUtility::extRelPath('tinymce');
		$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/tinymce/tinymce.min.js';
198
		$pageRenderer->addJsLibrary('tinymce', $script, 'text/javascript', FALSE, TRUE, '', TRUE);
199 200

		$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/MutationObserver/MutationObserver.js';
201
		$pageRenderer->addJsLibrary('MutationObserver', $script, 'text/javascript', FALSE, TRUE, '', TRUE);
202 203

		$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Resources/Public/JavaScript/Loader.js';
204
		$pageRenderer->addJsFile($script, 'text/javascript', FALSE, TRUE, '', TRUE);
sgalinsk's avatar
sgalinsk committed
205

206
		$script = $this->getConfiguration();
207
		$pageRenderer->addJsFile($script, 'text/javascript', FALSE, TRUE, '', TRUE);
sgalinsk's avatar
sgalinsk committed
208 209 210
	}

	/**
211
	 * Parses and processes the tinyMCE configuration
sgalinsk's avatar
sgalinsk committed
212
	 *
213 214
	 * @param string $configuration file reference or configuration string
	 * @return array
sgalinsk's avatar
sgalinsk committed
215
	 */
216 217
	protected function prepareTinyMCEConfiguration($configuration) {
		$configurationArray = array();
218 219

		// try to resolve a potential TYPO3 file path
220
		$configurationFile = GeneralUtility::getFileAbsFileName($configuration);
221 222
		if (is_file($configurationFile)) {
			$configuration = file_get_contents($configurationFile);
223
		}
sgalinsk's avatar
sgalinsk committed
224

225 226
		// split config into first and last javascript parts (applied later again into the config variables)
		// additionally the config part is matched to get the options
227
		$pattern = '/(.*)tinymce\.init\s*\(\s*\{(.*?)\}\s*\)\s*;?(.*)/is';
228
		preg_match($pattern, $configuration, $matches);
sgalinsk's avatar
sgalinsk committed
229

230
		// add preJS and postJS
231 232 233
		$configurationArray['preJS'] = trim($matches[1]);
		$configurationArray['configurationData'] = trim($matches[2]);
		$configurationArray['postJS'] = trim($matches[3]);
sgalinsk's avatar
sgalinsk committed
234

235
		return $configurationArray;
sgalinsk's avatar
sgalinsk committed
236
	}
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

	/**
	 * Adds a basic configuration value to the parsed configuration
	 *
	 * @param string $key
	 * @param mixed $value
	 */
	public function addConfigurationOption($key, $value) {
		if (is_numeric($value)) {
			if (strpos($value, '.')) {
				$value = (float) $value;
			} else {
				$value = (int) $value;
			}
		} elseif (strpos(trim($value), '[') === FALSE && strpos(trim($value), '{') === FALSE &&
			strpos(trim($value), 'function') === FALSE
		) {
			$value = '\'' . $value . '\'';
		}

257 258 259 260 261
		if ($this->tinymceConfiguration['configurationData'] !== '') {
			$this->tinymceConfiguration['configurationData'] .= "\n,";
		}

		$this->tinymceConfiguration['configurationData'] .= $key . ': ' . $value;
262 263 264 265 266 267 268 269 270 271 272
	}

	/**
	 * Replaces any TYPO3 extension path with the domain prefixed one.
	 *
	 * @param string $configuration
	 * @return string
	 */
	protected function replaceTypo3Paths($configuration) {
		$replacementFunction = function ($value) {
			// getPath should be used, but this causes a php exception with PHP 5.3 as $this isn't set there
273 274
			return '\'' . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') .
			str_replace(PATH_site, '', GeneralUtility::getFileAbsFileName($value[1])) . '\'';
275 276 277 278 279 280 281 282 283 284 285 286 287 288
		};

		return preg_replace_callback('/["\'](EXT:[^"\']*)["\']/is', $replacementFunction, $configuration);
	}

	/**
	 * Resolves a relative path like EXT:tinymce/... into an absolute one that contains either the
	 * current host or the path to the file in the file system.
	 *
	 * @param string $relativePath
	 * @param bool $returnWithDomain
	 * @return string
	 */
	protected function getPath($relativePath, $returnWithDomain = FALSE) {
289
		$finalPath = $absolutePath = GeneralUtility::getFileAbsFileName($relativePath);
290
		if ($returnWithDomain) {
291
			$finalPath = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . str_replace(PATH_site, '', $absolutePath);
292 293 294
		}
		return $finalPath;
	}
sgalinsk's avatar
sgalinsk committed
295 296
}

297
?>