Passed
Push — development ( 2bb3b4...48f606 )
by Emanuele
53s
created

Template   C

Complexity

Total Complexity 69

Size/Duplication

Total Lines 581
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 18
Bugs 1 Features 1
Metric Value
wmc 69
lcom 1
cbo 5
dl 0
loc 581
rs 5.6445
c 18
b 1
f 1

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B render() 0 24 3
A setResponse() 0 4 1
B initReplaces() 0 20 7
A fetchStyles() 0 11 2
A fetchScripts() 0 13 2
A sendHead() 0 14 3
B header() 0 30 3
A sendBody() 0 14 4
A renderErrors() 0 14 3
B error() 0 46 6
A sendFooter() 0 7 2
A footer() 0 14 3
B selectScript() 0 66 4
A step0() 0 21 1
C renderForm() 0 71 10
A step3() 0 20 2
B timeLimit() 0 26 2
A validate() 0 5 2
A renderStatuses() 0 13 2
B status() 0 27 6

How to fix   Complexity   

Complex Class

Complex classes like Template 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 Template, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @name      OpenImporter
4
 * @copyright OpenImporter contributors
5
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
6
 *
7
 * @version 2.0 Alpha
8
 */
9
10
namespace OpenImporter\Core;
11
12
/**
13
 * Class Template
14
 * This is our UI
15
 *
16
 * @package OpenImporter\Core
17
 */
18
class Template
19
{
20
	/**
21
	 * @var HttpResponse
22
	 */
23
	protected $response;
24
	protected $replaces = array();
25
	protected $lng = null;
26
	protected $config = null;
27
	protected $header_rendered = false;
28
29
	/**
30
	 * Template constructor.
31
	 *
32
	 * @param Lang $lng
33
	 * @param Configurator $config
34
	 */
35
	public function __construct(Lang $lng, Configurator $config)
36
	{
37
		$this->lng = $lng;
38
		$this->config = $config;
39
	}
40
41
	/**
42
	 * Render a page to show
43
	 *
44
	 * @param null|HttpResponse $response
45
	 */
46
	public function render($response = null)
47
	{
48
		if ($response !== null)
49
		{
50
			$this->setResponse($response);
51
		}
52
53
		// No text? ... so sad. :(
54
		if ($this->response->no_template)
55
		{
56
			return;
57
		}
58
59
		$this->initReplaces();
60
61
		$this->response->styles = $this->fetchStyles();
62
		$this->response->scripts = $this->fetchScripts();
63
64
		$this->sendHead();
65
66
		$this->sendBody();
67
68
		$this->sendFooter();
69
	}
70
71
	/**
72
	 * Set the instance of the HttpResponse
73
	 *
74
	 * @param HttpResponse $response
75
	 */
76
	public function setResponse($response)
77
	{
78
		$this->response = $response;
79
	}
80
81
	protected function initReplaces()
82
	{
83
		$this->replaces = array();
84
85
		foreach ($this->response->getAll() as $key => $val)
86
		{
87
			if (!is_object($val) && !is_array($val))
88
			{
89
				$this->replaces['{{response->' . $key . '}}'] = (string) $val;
90
			}
91
		}
92
93
		foreach ($this->lng->getAll() as $key => $val)
94
		{
95
			if (!is_object($val) && !is_array($val))
96
			{
97
				$this->replaces['{{language->' . $key . '}}'] = $val;
98
			}
99
		}
100
	}
101
102
	/**
103
	 * Return any style sheets that need to be loaded with the template
104
	 *
105
	 * @return string
106
	 */
107
	protected function fetchStyles()
108
	{
109
		if (file_exists($this->response->assets_dir . '/index.css'))
110
		{
111
			return file_get_contents($this->response->assets_dir . '/index.css');
112
		}
113
		else
114
		{
115
			return '';
116
		}
117
	}
118
119
	/**
120
	 * Return any scripts that need to be loaded in the template
121
	 *
122
	 * @return string
123
	 */
124
	protected function fetchScripts()
125
	{
126
		if (file_exists($this->response->assets_dir . '/scripts.js'))
127
		{
128
			$file = file_get_contents($this->response->assets_dir . '/scripts.js');
129
130
			return strtr($file, $this->replaces);
131
		}
132
		else
133
		{
134
			return '';
135
		}
136
	}
137
138
	protected function sendHead()
139
	{
140
		if ($this->header_rendered === false)
141
		{
142
			$this->response->sendHeaders();
143
144
			if ($this->response->is_page)
145
			{
146
				$this->header();
147
			}
148
149
			$this->header_rendered = true;
150
		}
151
	}
152
153
	/**
154
	 * Show the header.
155
	 */
156
	public function header()
157
	{
158
		echo '<!DOCTYPE html>
159
<html lang="', $this->lng->get('locale'), '">
160
	<head>
161
		<meta charset="UTF-8" />
162
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
163
		<title>', $this->response->page_title, '</title>
164
165
		<script src="//code.jquery.com/jquery-2.2.0.min.js"></script>
166
		<script>
167
', $this->response->scripts, '
168
		</script>
169
		<style type="text/css">
170
', $this->response->styles, '
171
		</style>
172
	</head>
173
	<body>
174
		<div id="header">
175
			<h1>', $this->response->page_title, '</h1>
176
		</div>
177
		<div id="main">';
178
179
		if ($this->config->progress->current_step == 1 || $this->config->progress->current_step == 2)
180
		{
181
			echo '
182
			<h2>', $this->lng->get('importing'), '...</h2>
183
			<div class="content"><p>';
184
		}
185
	}
186
187
	/**
188
	 * Show the main body of the template
189
	 */
190
	protected function sendBody()
191
	{
192
		// Any errors that we should let them know about
193
		if ($this->response->is_page && $this->response->template_error)
194
		{
195
			$this->renderErrors();
196
		}
197
198
		$templates = $this->response->getTemplates();
199
		foreach ($templates as $template)
200
		{
201
			call_user_func_array(array($this, $template['name']), $template['params']);
202
		}
203
	}
204
205
	/**
206
	 * If we have errors, this will show them
207
	 */
208
	protected function renderErrors()
209
	{
210
		foreach ($this->response->getErrors() as $msg)
211
		{
212
			if (is_array($msg))
213
			{
214
				call_user_func_array(array($this, 'error'), $msg);
215
			}
216
			else
217
			{
218
				$this->error($msg);
219
			}
220
		}
221
	}
222
223
	/**
224
	 * Display a specific error message.
225
	 *
226
	 * @param string $error_message
227
	 * @param int|bool $trace
228
	 * @param int|bool $line
229
	 * @param string|bool $file
230
	 */
231
	public function error($error_message, $trace = false, $line = false, $file = false, $query = false)
232
	{
233
		echo '
234
			<div class="error_message">
235
				<div class="error_text">';
236
		if ($query)
237
		{
238
			echo '
239
				<b>', $this->lng->get('db_unsuccessful'), '</b><br />
240
				', $this->lng->get(array('db_query_failed', '<blockquote>' . $query['query'] . '</blockquote>')), '
241
				', $this->lng->get(array('db_error_caused', '<br />
242
				<blockquote>' . $query['error'] . '</blockquote>')), '
243
				<form action="' . $query['action_url'] . '" method="post">
244
					<input type="submit" value="', $this->lng->get('try_again'), '" />
245
				</form>';
246
		}
247
		else
248
		{
249
			echo '
250
					', !empty($trace) ? $this->lng->get(array('error_message', $error_message)) : $error_message;
251
		}
252
253
		echo '
254
				</div>';
255
256
		if (!empty($trace))
257
		{
258
			echo '
259
				<div class="error_text">', $this->lng->get(array('error_trace', $trace)), '</div>';
260
		}
261
262
		if (!empty($line))
263
		{
264
			echo '
265
				<div class="error_text">', $this->lng->get(array('error_line', $line)), '</div>';
266
		}
267
268
		if (!empty($file))
269
		{
270
			echo '
271
				<div class="error_text">', $this->lng->get(array('error_file', $file)), '</div>';
272
		}
273
274
		echo '
275
			</div>';
276
	}
277
278
	protected function sendFooter()
279
	{
280
		if ($this->response->is_page)
281
		{
282
			$this->footer();
283
		}
284
	}
285
286
	/**
287
	 * Show the footer.
288
	 */
289
	public function footer()
290
	{
291
		if ($this->response->step == 1 || $this->response->step == 2)
292
		{
293
			echo '
294
				</p>
295
			</div>';
296
		}
297
298
		echo '
299
		</div>
300
	</body>
301
</html>';
302
	}
303
304
	/**
305
	 * This is the template part for selecting the source and destination systems
306
	 *
307
	 * @param array $scripts
308
	 * @param array $destination_names
309
	 */
310
	public function selectScript($scripts, $destination_names)
311
	{
312
		echo '
313
			<h2>', $this->lng->get('to_what'), '</h2>
314
			<form id="conversion" class="conversion" action="', $this->response->scripturl, '" method="post">
315
				<div class="content">
316
					<p><label for="source">', $this->lng->get('locate_source'), '</label></p>
317
					<ul id="source">';
318
319
		foreach ($destination_names as $key => $value)
320
		{
321
			$id = preg_replace('~[^\w\d]~', '_', $key);
322
			echo '
323
						<li>
324
							<input class="input_select" data-type="destination" type="radio" value="', $key, '" id="destination_', $id, '" name="destination" />
325
							<label for="destination_', $id, '">', $value, '</label>
326
						</li>';
327
		}
328
329
		echo '
330
					</ul>
331
				</div>';
332
333
		echo '
334
				<h2>', $this->lng->get('which_software'), '</h2>
335
				<div class="content">';
336
337
		// We found at least one?
338
		if (!empty($scripts))
339
		{
340
			echo '
341
					<p>', $this->lng->get('multiple_files'), '</p>
342
					<ul id="destinations">';
343
344
			// Let's loop and output all the found scripts.
345
			foreach ($scripts as $key => $script)
346
			{
347
				$id = preg_replace('~[^\w\d]~', '_', $key);
348
				echo '
349
						<li>
350
							<input class="input_select" data-type="source" type="radio" value="', $script['path'], '" id="source_', $id, '" name="source" />
351
							<label for="source_', $id, '">', $script['name'], '</label>
352
						</li>';
353
			}
354
355
			echo '
356
					</ul>
357
				</div>
358
				<input class="start_conversion" type="submit" value="', $this->lng->get('start_conversion'), '" />
359
			</form>
360
			<h2>', $this->lng->get('not_here'), '</h2>
361
			<div class="content">
362
				<p>', $this->lng->get('check_more'), '</p>
363
				<p>', $this->lng->get('having_problems'), '</p>';
364
		}
365
		else
366
		{
367
			echo '
368
				<p>', $this->lng->get('not_found'), '</p>
369
				<p>', $this->lng->get('not_found_download'), '</p>
370
				<a href="', $this->response->scripturl, '?action=reset">', $this->lng->get('try_again'), '</a>';
371
		}
372
373
		echo '
374
			</div>';
375
	}
376
377
	/**
378
	 * @param Form $form
379
	 */
380
	public function step0(Form $form)
381
	{
382
		echo '
383
			<h2>', $this->lng->get('before_continue'), '</h2>
384
			<div class="content">
385
				<p>', sprintf($this->lng->get('before_details'), $this->response->source_name, $this->response->destination_name), '</p>
386
			</div>';
387
		$form->title = $this->lng->get('where');
388
		$form->description = $this->lng->get('locate_destination');
389
		$form->submit = array(
390
			'name' => 'submit_button',
391
			'value' => $this->lng->get('continue'),
392
		);
393
		$this->renderForm($form);
394
395
		echo '
396
			<div class="content">
397
				<h3>', $this->lng->get('not_this'), '</h3>
398
				<p>', $this->lng->get(array('pick_different', $this->response->scripturl . '?action=reset')), '</p>
399
			</div>';
400
	}
401
402
	public function renderForm(Form $form)
403
	{
404
		echo '
405
			<h2>', $form->title, '</h2>
406
			<div class="content">
407
				<form action="', $form->action_url, '" method="post">
408
					<p>', $form->description, '</p>
409
					<dl>';
410
411
		foreach ($form->options as $option)
412
		{
413
			if (empty($option))
414
			{
415
				echo '
416
					</dl>
417
					<div id="toggle_button" class="open">', $this->lng->get('advanced_options'), '</div>
418
					<dl id="advanced_options">';
419
				continue;
420
			}
421
422
			switch ($option['type'])
423
			{
424
				case 'text':
425
					echo '
426
						<dt><label for="', $option['id'], '">', $option['label'], ':</label></dt>
427
						<dd>
428
							<input type="text" name="', $option['id'], '" id="', $option['id'], '" value="', $option['value'], '" class="text', !empty($option['validate']) ? ' dovalidation' : '', '" />
429
							<div id="validate_', $option['id'], '" class="validate">', $option['correct'], '</div>
430
						</dd>';
431
					break;
432
				case 'checkbox':
433
					echo '
434
						<dt></dt>
435
						<dd>
436
							<label for="', $option['id'], '">', $option['label'], ':
437
								<input type="checkbox" name="', $option['id'], '" id="', $option['id'], '" value="', $option['value'], '" ', $option['attributes'], '/>
438
							</label>
439
						</dd>';
440
					break;
441
				case 'password':
442
					echo '
443
						<dt><label for="', $option['id'], '">', $option['label'], ':</label></dt>
444
						<dd>
445
							<input type="password" name="', $option['id'], '" id="', $option['id'], '" class="text" />
446
							<div class="passwdcheck">', $option['correct'], '</div>
447
						</dd>';
448
					break;
449
				case 'steps':
450
					echo '
451
						<dt><label for="', $option['id'], '">', $option['label'], ':</label></dt>
452
						<dd>';
453
					foreach ($option['value'] as $key => $step)
454
					{
455
						echo '
456
							<label><input type="checkbox" name="do_steps[', $key, ']" id="do_steps[', $key, ']" value="', $step['count'], '"', $step['mandatory'] ? ' readonly="readonly" ' : ' ', $step['checked'], ' /> ', $step['label'], '</label><br />';
457
					}
458
459
					echo '
460
						</dd>';
461
					break;
462
			}
463
		}
464
465
		echo '
466
					</dl>
467
					<div class="button">
468
						<input id="submit_button" name="', $form->submit['name'], '" type="submit" value="', $form->submit['value'], '" class="submit" />
469
					</div>
470
				</form>
471
			</div>';
472
	}
473
474
	/**
475
	 * Display last step UI, completion status and allow eventually
476
	 * to delete the scripts
477
	 *
478
	 * @param string $name
479
	 * @param bool $writable if the files are writable, the UI will allow deletion
480
	 */
481
	public function step3($name, $writable)
482
	{
483
		echo '
484
			</div>
485
			<h2>', $this->lng->get('complete'), '</h2>
486
			<div class="content">
487
			<p>', $this->lng->get('congrats'), '</p>';
488
489
		if ($writable)
490
		{
491
			echo '
492
				<div class="notice">
493
					<label for="delete_self"><input type="checkbox" id="delete_self" />', $this->lng->get('check_box'), '</label>
494
				</div>';
495
		}
496
497
		echo '
498
				<p>', sprintf($this->lng->get('all_imported'), $name), '</p>
499
				<p>', $this->lng->get('smooth_transition'), '</p>';
500
	}
501
502
	/**
503
	 * Display the progress bar,
504
	 * and inform the user about when the script is paused and re-run.
505
	 *
506
	 * @todo the url should be built in the PasttimeException, not here
507
	 *
508
	 * @param int $bar
509
	 * @param int $value
510
	 * @param int $max
511
	 * @param int $substep
512
	 * @param int $start
513
	 */
514
	public function timeLimit($bar, $value, $max, $substep, $start)
515
	{
516
		if (!empty($bar))
517
		{
518
			echo '
519
			<div id="progressbar">
520
				<progress value="', $bar, '" max="100">', $bar, '%</progress>
521
			</div>';
522
		}
523
524
		echo '
525
		</div>
526
		<h2>', $this->lng->get('not_done'), '</h2>
527
		<div class="content">
528
			<div class="progress"><span>', $this->lng->get('overall_progress'), '</span><progress value="', $value, '" max="', $max, '"></progress></div>
529
			<p>', $this->lng->get('importer_paused'), '</p>
530
531
			<form action="', $this->response->scripturl, '?step=', $this->response->step, '&amp;substep=', $substep, '&amp;start=', $start, '" method="post" name="autoSubmit">
532
				<div class="continue"><input name="b" type="submit" value="', $this->lng->get('continue'), '" /></div>
533
			</form>
534
535
			<script>
536
				var countdown = 3;
537
				window.onload = doAutoSubmit;
538
			</script>';
539
	}
540
541
	/**
542
	 * ajax response, whether the paths to the source and destination
543
	 * software are correctly set.
544
	 */
545
	public function validate()
546
	{
547
		echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
548
	<valid>', $this->response->valid ? 'true' : 'false', '</valid>';
549
	}
550
551
	protected function renderStatuses()
552
	{
553
		echo '
554
		<span class="statuses">';
555
556
		foreach ($this->response->getStatuses() as $status)
557
		{
558
			$this->status($status['status'], $status['title']);
559
		}
560
561
		echo '
562
		</span>';
563
	}
564
565
	/**
566
	 * Display notification with the given status
567
	 *
568
	 * @param int $status
569
	 * @param string $title
570
	 */
571
	public function status($status, $title)
572
	{
573
		if (!empty($title))
574
		{
575
			echo '<span class="text">' . $title . '...</span> ';
576
		}
577
578
		if ($status == 1)
579
		{
580
			echo '<span class="success">&#x2714</span>';
581
		}
582
583
		if ($status == 2)
584
		{
585
			echo '<span class="disabled">&#x2714</span> (', $this->lng->get('skipped'), ')';
586
		}
587
588
		if ($status == 3)
589
		{
590
			echo '<span class="failure">&#x2718</span> (', $this->lng->get('not_found_skipped'), ')';
591
		}
592
593
		if ($status != 0)
594
		{
595
			echo '<br />';
596
		}
597
	}
598
}