Completed
Push — new-committers ( 29cb6f...bcba16 )
by Sam
12:18 queued 33s
created

HtmlEditorConfig   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 366
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 3
Metric Value
wmc 44
lcom 4
cbo 3
dl 0
loc 366
rs 8.3396

18 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 4 2
A set_active() 0 3 1
A get_active_identifier() 0 4 2
A get_active() 0 4 1
A getOption() 0 3 2
A setOption() 0 4 1
A disablePlugins() 0 11 4
A getPlugins() 0 3 1
A modifyButtons() 0 10 4
A get_available_configs_map() 0 9 2
A setOptions() 0 6 2
B enablePlugins() 0 13 5
A setButtonsForLine() 0 11 3
A addButtonsToLine() 0 10 3
A insertButtonsBefore() 0 5 1
A insertButtonsAfter() 0 5 1
A removeButtons() 0 6 2
C require_js() 0 73 7

How to fix   Complexity   

Complex Class

Complex classes like HtmlEditorConfig often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HtmlEditorConfig, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * A PHP version of TinyMCE's configuration, to allow various parameters to be configured on a site or section basis
5
 *
6
 * There can be multiple HtmlEditorConfig's, which should always be created / accessed using HtmlEditorConfig::get.
7
 * You can then set the currently active config using set_active. Whichever config is active when
8
 * HtmlEditorField#Field is called wins.
9
 *
10
 * @author "Hamish Friedlander" <[email protected]>
11
 * @package forms
12
 * @subpackage fields-formattedinput
13
 */
14
class HtmlEditorConfig {
15
16
	private static $configs = array();
17
18
	private static $current = null;
19
20
	/**
21
	 * Get the HtmlEditorConfig object for the given identifier. This is a correct way to get an HtmlEditorConfig
22
	 * instance - do not call 'new'
23
	 *
24
	 * @param $identifier string - the identifier for the config set
25
	 * @return HtmlEditorConfig - the configuration object. This will be created if it does not yet exist for that
26
	 *                            identifier
27
	 */
28
	public static function get($identifier = 'default') {
29
		if (!array_key_exists($identifier, self::$configs)) self::$configs[$identifier] = new HtmlEditorConfig();
30
		return self::$configs[$identifier];
31
	}
32
33
	/**
34
	 * Set the currently active configuration object
35
	 * @param $identifier string - the identifier for the config set
36
	 * @return null
37
	 */
38
	public static function set_active($identifier = null) {
39
		self::$current = $identifier;
40
	}
41
42
	/**
43
	 * Get the currently active configuration identifier
44
	 * @return String - the active configuration identifier
45
	 */
46
	public static function get_active_identifier() {
47
		$identifier = self::$current ? self::$current : 'default';
48
		return $identifier;
49
	}
50
	/**
51
	 * Get the currently active configuration object
52
	 * @return HtmlEditorConfig - the active configuration object
53
	 */
54
	public static function get_active() {
55
		$identifier = self::get_active_identifier();
56
		return self::get($identifier);
57
	}
58
59
	/**
60
	 * Get the available configurations as a map of friendly_name to
61
	 * configuration name.
62
	 * @return array
63
	 */
64
	public static function get_available_configs_map() {
65
		$configs = array();
66
67
		foreach(self::$configs as $identifier => $config) {
68
			$configs[$identifier] = $config->getOption('friendly_name');
69
		}
70
71
		return $configs;
72
	}
73
74
	/**
75
	 * Holder for all TinyMCE settings _except_ plugins and buttons
76
	 */
77
	protected $settings = array(
78
		'friendly_name' => '(Please set a friendly name for this config)',
79
		'priority' => 0,
80
		'mode' => "none", // initialized through HtmlEditorField.js redraw() logic
81
		'editor_selector' => "htmleditor",
82
		'width' => "100%",
83
		'auto_resize' => false,
84
		'update_interval' => 5000, // Ensure update of this data every 5 seconds to the underlying textarea
85
		'theme' => "advanced",
86
87
		'theme_advanced_layout_manager' => "SimpleLayout",
88
		'theme_advanced_toolbar_location' => "top",
89
		'theme_advanced_toolbar_align' => "left",
90
		'theme_advanced_toolbar_parent' => "right",
91
92
		'blockquote_clear_tag' => "p",
93
		'table_inline_editing' => true,
94
95
		'safari_warning' => false,
96
		'relative_urls' => true,
97
		'verify_html' => true,
98
		'browser_spellcheck' => true,
99
	);
100
101
	/**
102
	 * Holder list of enabled plugins
103
	 */
104
	protected $plugins = array(
105
		'contextmenu' => null,
106
		'table' => null,
107
		'emotions' => null,
108
		'paste' => null,
109
	);
110
111
	/**
112
	 * Holder list of buttons, organised by line
113
	 */
114
	protected $buttons = array(
115
		1 => array('bold','italic','underline','strikethrough','separator',
116
			'justifyleft','justifycenter','justifyright','justifyfull','formatselect','separator',
117
			'bullist','numlist','outdent','indent','blockquote','hr','charmap'),
118
		2 => array('undo','redo','separator','cut','copy','paste','pastetext','pasteword','separator',
119
			'advcode','search','replace','selectall','visualaid','separator','tablecontrols'),
120
		3 => array()
121
	);
122
123
	/**
124
	 * Get the current value of an option
125
	 * @param $k string - The key of the option to get
126
	 * @return mixed - The value of the specified option
127
	 */
128
	public function getOption($k) {
129
		if(isset($this->settings[$k])) return $this->settings[$k];
130
	}
131
132
	/**
133
	 * Set the value of one option
134
	 * @param $k string - The key of the option to set
135
	 * @param $v mixed - The value of the option to set
136
	 * @return HtmlEditorConfig
137
	 */
138
	public function setOption($k,$v) {
139
		$this->settings[$k] = $v;
140
		return $this;
141
	}
142
143
	/**
144
	 * Set multiple options
145
	 * @param $a array - The options to set, as keys and values of the array
146
	 * @return HtmlEditorConfig
147
	 */
148
	public function setOptions($a) {
149
		foreach ($a as $k=>$v) {
150
			$this->settings[$k] = $v;
151
		}
152
		return $this;
153
	}
154
155
	/**
156
	 * Enable one or several plugins. Will maintain unique list if already
157
	 * enabled plugin is re-passed. If passed in as a map of plugin-name to path,
158
	 * the plugin will be loaded by tinymce.PluginManager.load() instead of through tinyMCE.init().
159
	 * Keep in mind that these externals plugins require a dash-prefix in their name.
160
	 *
161
	 * @see http://wiki.moxiecode.com/index.php/TinyMCE:API/tinymce.PluginManager/load
162
	 *
163
	 * @param String [0..] a string, or several strings, or a single array of strings - The plugins to enable
164
	 * @return HtmlEditorConfig
165
	 */
166
	public function enablePlugins() {
167
		$plugins = func_get_args();
168
		if (is_array(current($plugins))) $plugins = current($plugins);
169
		foreach ($plugins as $plugin => $path) {
170
			// if plugins are passed without a path
171
			if(is_numeric($plugin)) {
172
				$plugin = $path;
173
				$path = null;
174
			}
175
			if (!array_key_exists($plugin, $this->plugins)) $this->plugins[$plugin] = $path;
176
		}
177
		return $this;
178
	}
179
180
	/**
181
	 * Enable one or several plugins. Will properly handle being passed a plugin that is already disabled
182
	 * @param String [0..] a string, or several strings, or a single array of strings - The plugins to disable
183
	 * @return HtmlEditorConfig
184
	 */
185
	public function disablePlugins() {
186
		$plugins = func_get_args();
187
		if (is_array(current($plugins))) $plugins = current($plugins);
188
189
		foreach ($plugins as $plugin) {
190
			if(array_key_exists($plugin, $this->plugins)) {
191
				unset($this->plugins[$plugin]);
192
			}
193
		}
194
		return $this;
195
	}
196
197
	/**
198
	 * @return Array
199
	 */
200
	public function getPlugins() {
201
		return $this->plugins;
202
	}
203
204
	/**
205
	 * Totally re-set the buttons on a given line
206
	 *
207
	 * @param integer from 1..3 - The line number to redefine
208
	 * @param string  a string or several strings, or a single array of strings - The button names to make this line
209
	 *                contain
210
	 * @return HtmlEditorConfig
211
	 */
212
	public function setButtonsForLine() {
213
		if (func_num_args() == 2) {
214
			list($line, $buttons) = func_get_args();
215
		}
216
		else {
217
			$buttons = func_get_args();
218
			$line = array_shift($buttons);
219
		}
220
		$this->buttons[$line] = is_array($buttons) ? $buttons : array($buttons);
221
		return $this;
222
	}
223
224
	/**
225
	 * Add buttons to the end of a line
226
	 * @param integer from 1..3
227
	 * @param string a string, or several strings, or a single array of strings - The button names to add to the end
228
	 *               of this line
229
	 * @return HtmlEditorConfig
230
	 */
231
	public function addButtonsToLine() {
232
		$inserts = func_get_args();
233
		$line = array_shift($inserts);
234
		if (is_array($inserts[0])) $inserts = $inserts[0];
235
236
		foreach ($inserts as $button) {
237
			$this->buttons[$line][] = $button;
238
		}
239
		return $this;
240
	}
241
242
	/**
243
	 * Internal function for adding and removing buttons related to another button
244
	 * @param $name string - the name of the button to modify
245
	 * @param $offset integer - the offset relative to that button to perform an array_splice at - 0 for before $name,
246
	 *                          1 for after
247
	 * @param $del integer - the number of buttons to remove at the position given by index(string) + offset
248
	 * @param $add mixed - an array or single item to insert at the position given by index(string) + offset,
249
	 *                     or null for no insertion
250
	 * @return boolean - true if $name matched a button, false otherwise
251
	 */
252
	protected function modifyButtons($name, $offset, $del=0, $add=null) {
253
		foreach ($this->buttons as &$buttons) {
254
			if (($idx = array_search($name, $buttons)) !== false) {
255
				if ($add) array_splice($buttons, $idx+$offset, $del, $add);
256
				else     array_splice($buttons, $idx+$offset, $del, $add);
257
				return true;
258
			}
259
		}
260
		return false;
261
	}
262
263
	/**
264
	 * Insert buttons before the first occurance of another button
265
	 * @param string - the name of the button to insert other buttons before
266
	 * @param string a string, or several strings, or a single array of strings - the button names to insert before
267
	 *               that button
268
	 * @return boolean - true if insertion occured, false if it did not (because the given button name was not found)
269
	 */
270
	public function insertButtonsBefore() {
271
		$inserts = func_get_args();
272
		$before = array_shift($inserts);
273
		return $this->modifyButtons($before, 0, 0, $inserts);
274
	}
275
276
	/**
277
	 * Insert buttons after the first occurance of another button
278
	 * @param string - the name of the button to insert other buttons after
279
	 * @param string a string, or several strings, or a single array of strings - the button names to insert after
280
	 *               that button
281
	 * @return boolean - true if insertion occured, false if it did not (because the given button name was not found)
282
	 */
283
	public function insertButtonsAfter() {
284
		$inserts = func_get_args();
285
		$after = array_shift($inserts);
286
		return $this->modifyButtons($after, 1, 0, $inserts);
287
	}
288
289
	/**
290
	 * Remove the first occurance of buttons
291
	 * @param string one or more strings - the name of the buttons to remove
292
	 * @return null
293
	 */
294
	public function removeButtons() {
295
		$removes = func_get_args();
296
		foreach ($removes as $button) {
297
			$this->modifyButtons($button, 0, 1);
298
		}
299
	}
300
301
	/**
302
	 * Generate the JavaScript that will set TinyMCE's configuration:
303
	 * - Parse all configurations into JSON objects to be used in JavaScript
304
	 * - Includes TinyMCE and configurations using the {@link Requirements} system
305
	 */
306
	public static function require_js() {
307
		require_once 'tinymce/tiny_mce_gzip.php';
308
		$useGzip = Config::inst()->get('HtmlEditorField', 'use_gzip');
309
310
		$configs = array();
311
		$externalPlugins = array();
312
		$internalPlugins = array();
313
		$languages = array();
314
315
		foreach (self::$configs as $configID => $config) {
316
			$settings = $config->settings;
317
			// parse plugins
318
			$configPlugins = array();
319
			foreach($config->plugins as $plugin => $path) {
320
				if(!$path) {
321
					$configPlugins[] = $plugin;
322
					$internalPlugins[] = $plugin;
323
				} else {
324
					$configPlugins[] = '-' . $plugin;
325
					if ( !array_key_exists($plugin, $externalPlugins) )
326
					{
327
						$externalPlugins[$plugin] = sprintf(
328
							'tinymce.PluginManager.load("%s", "%s");',
329
							$plugin,
330
							$path
331
						);
332
					}
333
				}
334
			}
335
336
			// save config plugins settings
337
			$settings['plugins'] = implode(',', $configPlugins);
338
339
			// buttons
340
			foreach ($config->buttons as $i=>$buttons) {
341
				$settings['theme_advanced_buttons'.$i] = implode(',', $buttons);
342
			}
343
344
			// languages
345
			$languages[] = $config->getOption('language');
346
347
			// save this config settings
348
			$configs[$configID] = $settings;
349
		}
350
351
		// tinyMCE JS requirement
352
		if ( $useGzip )
353
		{
354
			$tag = TinyMCE_Compressor::renderTag(array(
355
				'url' => THIRDPARTY_DIR . '/tinymce/tiny_mce_gzip.php',
356
				'plugins' => implode(',', $internalPlugins),
357
				'themes' => 'advanced',
358
				'languages' => implode(",", array_filter($languages))
359
			), true);
360
			preg_match('/src="([^"]*)"/', $tag, $matches);
361
			Requirements::javascript(html_entity_decode($matches[1]));
362
		}
363
		else{
364
			Requirements::javascript(MCE_ROOT . 'tiny_mce_src.js');
365
		}
366
367
		// prepare external plugins js string
368
		$externalPlugins = array_values($externalPlugins);
369
		$externalPlugins = implode("\n	", $externalPlugins);
370
371
		// tinyMCE config object and external plugings
372
		$configsJS = "
373
if((typeof tinyMCE != 'undefined')) {
374
	$externalPlugins
375
	var ssTinyMceConfig = " . Convert::raw2json($configs) . ";
376
}";
377
		Requirements::customScript($configsJS, 'htmlEditorConfig');
378
	}
379
}
380