Passed
Push — master ( 655649...07d5b2 )
by Spuds
06:48 queued 05:03
created

Template::renderForm()   B

Complexity

Conditions 11
Paths 16

Size

Total Lines 94
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 41
c 0
b 0
f 0
nc 16
nop 1
dl 0
loc 94
ccs 0
cts 87
cp 0
crap 132
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @name      OpenImporter
4
 * @copyright OpenImporter contributors
5
 * @license   BSD https://opensource.org/licenses/BSD-3-Clause
6
 *
7
 * @version 1.0
8
 */
9
10
namespace OpenImporter;
11
12
/**
13
 * Class Template
14
 * This is our UI
15
 *
16
 * @package OpenImporter
17
 */
18
class Template
19
{
20
	/** @var \OpenImporter\HttpResponse */
21
	protected $response;
22
23
	/** @var \OpenImporter\DummyLang */
24
	protected $language;
25
26
	/**
27
	 * Template constructor.
28
	 *
29
	 * @param $language
30
	 */
31
	public function __construct($language)
32
	{
33
		// If nothing is found, use a stub
34
		if ($language === null)
35
		{
36
			$language = new DummyLang();
37
		}
38
39
		$this->language = $language;
40
	}
41
42
	/**
43
	 * Display a specific error message.
44
	 *
45
	 * @param string $error_message
46
	 * @param int|bool $trace
47
	 * @param int|bool $line
48
	 * @param string|bool $file
49
	 */
50
	public function error($error_message, $trace = false, $line = false, $file = false)
51
	{
52
		echo '
53
			<div class="error_message">
54
				<div class="error_text">
55
					', !empty($trace) ? $this->language->get(array('error_message', $error_message)) : $error_message, '
56
				</div>';
57
58
		if (!empty($trace))
59
		{
60
			echo '
61
				<div class="error_text">', $this->language->get(array('error_trace', $trace)), '</div>';
62
		}
63
64
		if (!empty($line))
65
		{
66
			echo '
67
				<div class="error_text">', $this->language->get(array('error_line', $line)), '</div>';
68
		}
69
70
		if (!empty($file))
71
		{
72
			echo '
73
				<div class="error_text">', $this->language->get(array('error_file', $file)), '</div>';
74
		}
75
76
		echo '
77
			</div>';
78
	}
79
80
	/**
81
	 * Sets the response for the template to use
82
	 *
83
	 * @param \OpenImporter\HttpResponse $response
84
	 */
85
	public function setResponse($response)
86
	{
87
		$this->response = $response;
88
	}
89
90
	/**
91
	 * Renders the template
92
	 */
93
	public function render()
94
	{
95
		// No text? ... so sad. :(
96
		if ($this->response->no_template)
97
		{
98
			return;
99
		}
100
101
		// Set http headers as needed
102
		$this->response->sendHeaders();
103
104
		// XML ajax feedback? We can just skip everything else
105
		if ($this->response->is_xml)
106
		{
107
			$this->xml();
108
		}
109
		// Maybe showing a new page
110
		elseif ($this->response->is_page)
111
		{
112
			// Header
113
			$this->header(!$this->response->template_error);
114
115
			// Body
116
			if ($this->response->template_error)
117
			{
118
				foreach ($this->response->getErrors() as $msg)
119
				{
120
					$this->error($msg);
121
				}
122
			}
123
124
			call_user_func_array(array($this, $this->response->use_template), $this->response->params_template);
125
126
			// Footer
127
			$this->footer(!$this->response->template_error);
128
		}
129
		else
130
		{
131
			call_user_func_array(array($this, $this->response->use_template), $this->response->params_template);
132
		}
133
	}
134
135
	/**
136
	 * Show the footer.
137
	 *
138
	 * @param bool $inner
139
	 */
140
	public function footer($inner = true)
141
	{
142
		if (($this->response->step === 1 || $this->response->step === 2) && $inner === true)
143
		{
144
			echo '
145
				</p>
146
			</div>';
147
		}
148
		echo '
149
		</div>
150
	</body>
151
</html>';
152
	}
153
154
	/**
155
	 * Show the header.
156
	 *
157
	 * @param bool $inner
158
	 */
159
	public function header($inner = true)
160
	{
161
		echo '<!DOCTYPE html>
162
<html lang="', $this->language->get('locale'), '">
163
	<head>
164
		<meta charset="UTF-8" />
165
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
166
		<title>', $this->response->page_title, '</title>
167
		<script>
168
			function AJAXCall(url, callback, string)
169
			{
170
				let req = new XMLHttpRequest();
171
172
				req.onreadystatechange = processRequest;
173
174
				function processRequest()
175
				{
176
					// ReadyState of 4 signifies request is complete
177
					if (req.readyState === 4)
178
					{
179
						// Status of 200 signifies successful HTTP call
180
						if (req.status === 200)
181
							if (callback) callback(req.responseXML, string);
182
					}
183
				}
184
185
				// Make an HTTP GET request to the URL asynchronously
186
				this.doGet = function () {
187
					req.open("GET", url, true);
188
					req.send(null);
189
				};
190
			}
191
192
			function validateField(string)
193
			{
194
				let target = document.getElementById(string),
195
					url = "import.php?action=validate&xml=true&" + string + "=" + target.value.replace(/\/+$/g, "") + "&import_script=' . addslashes($this->response->script) . '",
196
					ajax = new AJAXCall(url, validateCallback, string);
197
198
				ajax.doGet();
199
			}
200
201
			function validateCallback(responseXML, string)
202
			{
203
				let msg = responseXML.getElementsByTagName("valid")[0].firstChild.nodeValue,
204
					field = document.getElementById(string),
205
					validate = document.getElementById(\'validate_\' + string),
206
					submitBtn = document.getElementById("submit_button");
207
208
				if (msg === "false")
209
				{
210
					field.className = "invalid_field";
211
					validate.innerHTML = "' . $this->language->get('invalid') . '";
212
213
					// Set the style on the div to invalid
214
					submitBtn.disabled = true;
215
				}
216
				else
217
				{
218
					field.className = "valid_field";
219
					validate.innerHTML = "' . $this->language->get('validated') . '";
220
221
					submitBtn.disabled = false;
222
				}
223
			}
224
225
			window.onload = function() {
226
				validateField(\'path_to\');
227
				validateField(\'path_from\');
228
			}
229
		</script>
230
		<style>
231
			body {
232
				background-color: #cbd9e7;
233
				margin: 0;
234
				padding: 0;
235
			}
236
			body, td {
237
				color: #000;
238
				font-size: small;
239
				font-family: arial;
240
			}
241
			a {
242
				color: #2a4259;
243
				text-decoration: none;
244
				border-bottom: 1px dashed #789;
245
			}
246
			#header {
247
				background-color: #809ab3;
248
				padding: 22px 4% 12px 4%;
249
				color: #fff;
250
				text-shadow: 0 0 8px #333;
251
				border-bottom: 1px solid #fff;
252
				height: 40px;
253
			}
254
			#main {
255
				padding: 20px 30px;
256
				background-color: #fff;
257
				border-radius: 5px;
258
				margin: 7px;
259
				border: 1px solid #abadb3;
260
			}
261
			#path_from, #path_to {
262
				width: 480px;
263
			}
264
			.error_message, blockquote, .error {
265
				border: 1px dashed red;
266
				border-radius: 5px;
267
				background-color: #fee;
268
				padding: 1.5ex;
269
			}
270
			.error_text {
271
				color: red;
272
			}
273
			.content {
274
				border-radius: 3px;
275
				background-color: #eee;
276
				color: #444;
277
				margin: 1ex 0;
278
				padding: 1.2ex;
279
				border: 1px solid #abadb3;
280
			}
281
			.button {
282
				margin: 0 0.8em 0.8em 0.8em;
283
			}
284
			#submit_button {
285
				cursor: pointer;
286
			}
287
			h1 {
288
				margin: 0;
289
				padding: 0;
290
				font-size: 2.5em;
291
			}
292
			h2 {
293
				font-size: 1.5em;
294
				color: #809ab3;
295
				font-weight: bold;
296
			}
297
			form {
298
				margin: 0;
299
			}
300
			.textbox {
301
				padding-top: 2px;
302
				white-space: nowrap;
303
				padding-right: 1ex;
304
			}
305
			.bp_invalid {
306
				color:red;
307
				font-weight: bold;
308
			}
309
			.bp_valid {
310
				color:green;
311
			}
312
			.validate {
313
				font-style: italic;
314
				font-size: smaller;
315
			}
316
			.valid_field {
317
				background-color: #DEFEDD;
318
				border: 1px solid green;
319
			}
320
			.invalid_field {
321
				background-color: #fee;;
322
				border: 1px solid red;
323
			}
324
			#progressbar {
325
				position: relative;
326
				top: -28px;
327
				left: 255px;
328
			}
329
			progress {
330
				width: 300px;
331
			}
332
			#advanced_options {
333
  				columns: 2;
334
  				margin-left: 20%;
335
			}
336
			#advanced_options dt {
337
				break-after: avoid;
338
				width: 50%;
339
				float: none;
340
			}
341
			#advanced_options dd {
342
				break-before: avoid;
343
				float: none;
344
			}
345
			dl {
346
				clear: right;
347
				overflow: auto;
348
				margin: 0 0 0 0;
349
				padding: 0;
350
			}
351
			dt {
352
				width: 20%;
353
				float: left;
354
				margin: 6px 5px 10px 0;
355
				padding: 0;
356
				clear: both;
357
			}
358
			dd {
359
				width: 78%;
360
				float: right;
361
				margin: 6px 0 3px 0;
362
				padding: 0;
363
			}
364
			#arrow_up {
365
				display: none;
366
			}
367
			#toggle_button {
368
				display: block;
369
				color: #2a4259;
370
				margin-bottom: 4px;
371
				cursor: pointer;
372
			}
373
			.arrow {
374
				font-size: 8pt;
375
			}
376
			#destinations ul, #source {
377
				padding: 0 1em;
378
			}
379
			#destinations ul li a {
380
				display: block;
381
				margin-bottom: 3px;
382
				padding-bottom: 3px;
383
				border-bottom: medium none;
384
				line-height: 4em;
385
			}
386
			#destinations ul li, #source li {
387
				cursor: pointer;
388
				float: left;
389
				list-style: none;
390
				padding: 0.5em;
391
				margin: 0 0.5em;
392
				border: 1px solid #abadb3;
393
				border-radius: 3px;
394
			}
395
			#destinations ul li {
396
				width: 20%;
397
				float: none;
398
				display: inline-block;
399
				height: 4em;
400
				cursor: default;
401
				vertical-align: middle;
402
				margin-top: 1em;
403
			}
404
			#destinations ul li.active, #source li.active {
405
				background-color: #fff;
406
			}
407
			#destinations ul:after, #source:after {
408
				content: "";
409
				display: block;
410
				clear: both;
411
			}
412
		</style>
413
	</head>
414
	<body>
415
		<div id="header">
416
			<h1>', isset($this->response->importer->xml->general->{'name'}) ? $this->response->importer->xml->general->{'name'} . ' to ' : '', 'OpenImporter</h1>
0 ignored issues
show
Bug Best Practice introduced by
The property importer does not exist on OpenImporter\HttpResponse. Since you implemented __get, consider adding a @property annotation.
Loading history...
417
		</div>
418
		<div id="main">';
419
420
		if (!empty($_GET['step'])
421
			&& ((int) $_GET['step'] === 1 || (int) $_GET['step'] === 2) && $inner === true)
422
		{
423
			echo '
424
			<h2 style="margin-top: 2ex">', $this->language->get('importing'), '...</h2>
425
			<div class="content"><p>';
426
		}
427
	}
428
429
	/**
430
	 * This is the template part for selecting the importer script.
431
	 *
432
	 * @param array $scripts The available "From" forums
433
	 * @param string[] $destination_names The available "To" forums
434
	 */
435
	public function select_script($scripts, $destination_names)
436
	{
437
		echo '
438
			<h2>', $this->language->get('to_what'), '</h2>
439
			<div class="content">
440
				<p><label for="source">', $this->language->get('locate_source'), '</label></p>
441
				<ul id="source">';
442
443
		// Who can we import into?
444
		foreach ($destination_names as $key => $values)
445
		{
446
			echo '
447
					<li onclick="toggle_to(this);" data-value="', preg_replace('~[^\w]~', '_', $key), '">', $values, '</li>';
448
		}
449
450
		echo '
451
				</ul>
452
			</div>';
453
454
		echo '
455
			<h2>', $this->language->get('which_software'), '</h2>
456
			<div id="destinations" class="content">';
457
458
		// We found at least one?
459
		if (!empty($scripts))
460
		{
461
			echo '
462
				<p>', $this->language->get('multiple_files'), '</p>';
463
464
			foreach ($scripts as $key => $value)
465
			{
466
				echo '
467
				<ul id="', preg_replace('~[^\w]~', '_', $key), '">';
468
469
				// Let's loop and output all the found scripts.
470
				foreach ($value as $script)
471
				{
472
					echo '
473
					<li>
474
						<a href="', $_SERVER['PHP_SELF'], '?import_script=', $script['path'], '">', $script['name'], '</a>
475
					</li>';
476
				}
477
478
				echo '
479
				</ul>';
480
			}
481
482
			echo '
483
			</div>
484
			<h2>', $this->language->get('not_here'), '</h2>
485
			<div class="content">
486
				<p>', $this->language->get('check_more'), '</p>
487
				<p>', $this->language->get('having_problems'), '</p>';
488
		}
489
		else
490
		{
491
			echo '
492
				<p>', $this->language->get('not_found'), '</p>
493
				<p>', $this->language->get('not_found_download'), '</p>
494
				<a href="', $_SERVER['PHP_SELF'], '?import_script=">', $this->language->get('try_again'), '</a>';
495
		}
496
497
		echo '
498
			<script>
499
				function toggle_to(e)
500
				{
501
					let dest_container = document.getElementById(\'destinations\'),
502
						dests = dest_container.getElementsByTagName(\'ul\'),
503
						sources = document.getElementById(\'source\').getElementsByTagName(\'li\'),
504
						i;
505
506
					for (i = 0; i < dests.length; i++)
507
						dests[i].style.display = \'none\';
508
509
					if (typeof e === \'undefined\')
510
						e = sources[0];
511
512
					for (i = 0; i < sources.length; i++)
513
						sources[i].removeAttribute("class");
514
515
					e.setAttribute("class", "active");
516
					document.getElementById(e.getAttribute(\'data-value\')).style.display = \'block\';
517
				}
518
519
				toggle_to();
520
			</script>
521
			</div>';
522
	}
523
524
	/**
525
	 * Everyone has to start somewhere, we start and 0,0,0,0
526
	 *
527
	 * Called from doStep0 from the ImportManager
528
	 *
529
	 * @param \OpenImporter\ImportManager $object
530
	 * @param \OpenImporter\Form $form
531
	 */
532
	public function step0($object, $form)
533
	{
534
		echo '
535
			<h2>', $this->language->get('before_continue'), '</h2>
536
			<div class="content">
537
				<p>', sprintf($this->language->get('before_details'), (string) $object->importer->xml->general->name), '</p>
538
			</div>';
539
540
		$form->title = $this->language->get('where');
541
		$form->description = $this->language->get('locate_destination');
542
		$form->submit = array(
543
			'name' => 'submit_button',
544
			'value' => $this->language->get('continue'),
545
		);
546
547
		$this->renderForm($form);
548
549
		echo '
550
			<h2>', $this->language->get('not_this'), '</h2>
551
			<div class="content">
552
				<p>', sprintf($this->language->get('pick_different'), $_SERVER['PHP_SELF']), '</p>
553
			</div>';
554
	}
555
556
	/**
557
	 * Display notification with the given status
558
	 *
559
	 * @param int $substep
560
	 * @param int $status
561
	 * @param string $title
562
	 * @param bool $hide = false
563
	 */
564
	public function status($substep, $status, $title, $hide = false)
565
	{
566
		$status = (int) $status;
567
		if (isset($title) && $hide === false)
568
		{
569
			echo '<span style="width: 250px; display: inline-block">' . $title . '...</span> ';
570
		}
571
572
		if ($status === 1)
573
		{
574
			echo '<span style="color: green">&#x2714</span>';
575
		}
576
577
		if ($status === 2)
578
		{
579
			echo '<span style="color: grey">&#x2714</span> (', $this->language->get('skipped'), ')';
580
		}
581
582
		if ($status === 3)
583
		{
584
			echo '<span style="color: red">&#x2718</span> (', $this->language->get('not_found_skipped'), ')';
585
		}
586
587
		if ($status !== 0)
588
		{
589
			echo '<br />';
590
		}
591
	}
592
593
	/**
594
	 * Display information related to step2
595
	 */
596
	public function step2()
597
	{
598
		echo '
599
				<span style="width: 250px; display: inline-block">', $this->language->get('recalculate'), '...</span> ';
600
	}
601
602
	/**
603
	 * Display last step UI, completion status and allow eventually
604
	 * to delete the scripts
605
	 *
606
	 * @param string $name
607
	 * @param string $boardurl
608
	 * @param bool $writable if the files are writable, the UI will allow deletion
609
	 */
610
	public function step3($name, $boardurl, $writable)
611
	{
612
		echo '
613
			</div>
614
			<h2 style="margin-top: 2ex">', $this->language->get('complete'), '</h2>
615
			<div class="content">
616
			<p>', $this->language->get('congrats'), '</p>';
617
618
		if ($writable)
619
		{
620
			echo '
621
				<div style="margin: 1ex; font-weight: bold">
622
					<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete()" />', $this->language->get('check_box'), '</label>
623
				</div>
624
				<script>
625
					function doTheDelete()
626
					{
627
						new Image().src = "', $_SERVER['PHP_SELF'], '?delete=1&" + (+Date());
628
						(document.getElementById ? document.getElementById("delete_self") : document.all.delete_self).disabled = true;
629
					}
630
				</script>';
631
		}
632
633
		echo '
634
				<p>', sprintf($this->language->get('all_imported'), $name), '</p>
635
				<p>', $this->language->get('smooth_transition'), '</p>';
636
	}
637
638
	/**
639
	 * Display the progress bar,
640
	 * and inform the user about when the script is paused and re-run.
641
	 *
642
	 * @param int $bar
643
	 * @param int $value
644
	 * @param int $max
645
	 */
646
	public function time_limit($bar, $value, $max)
647
	{
648
		if (!empty($bar))
649
		{
650
			echo '
651
			<div id="progressbar">
652
				<progress value="', $bar, '" max="100">', $bar, '%</progress>
653
			</div>';
654
		}
655
656
		echo '
657
		</div>
658
		<h2 style="margin-top: 2ex">', $this->language->get('not_done'), '</h2>
659
		<div class="content">
660
			<div style="margin-bottom: 15px; margin-top: 10px;"><span style="width: 250px; display: inline-block">', $this->language->get('overall_progress'), '</span><progress value="', $value, '" max="', $max, '"></progress></div>
661
			<p>', $this->language->get('importer_paused'), '</p>
662
663
			<form action="', $_SERVER['PHP_SELF'], '?step=', $_GET['step'], isset($_GET['substep']) ? '&amp;substep=' . $_GET['substep'] : '', '&amp;start=', $_REQUEST['start'], '" method="post" name="autoSubmit">
664
				<div align="right" style="margin: 1ex"><input name="b" type="submit" value="', $this->language->get('continue'), '" /></div>
665
			</form>
666
667
			<script>
668
				let countdown = 3;
669
				
670
				window.onload = doAutoSubmit;
671
672
				function doAutoSubmit()
673
				{
674
					if (countdown === 0)
675
						document.autoSubmit.submit();
676
					else if (countdown === -1)
677
						return;
678
679
					document.autoSubmit.b.value = "', $this->language->get('continue'), ' (" + countdown + ")";
680
					countdown--;
681
682
					setTimeout("doAutoSubmit();", 1000);
683
				}
684
			</script>';
685
	}
686
687
	/**
688
	 * Ajax's response. Whether the paths to the source and destination
689
	 * software are correctly set.
690
	 */
691
	public function xml()
692
	{
693
		echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
694
	<valid>', $this->response->valid ? 'true' : 'false', '</valid>';
695
	}
696
697
	/**
698
	 * Function to generate a form from a set of form options
699
	 * @param $form
700
	 */
701
	public function renderForm($form)
702
	{
703
		$toggle = false;
704
705
		echo '
706
			<h2>', $form->title, '</h2>
707
			<div class="content">
708
				<form action="', $form->action_url, '" method="post">
709
					<p>', $form->description, '</p>
710
					<dl>';
711
712
		foreach ($form->options as $option)
713
		{
714
			if (empty($option))
715
			{
716
				$toggle = true;
717
				echo '
718
					</dl>
719
					<div id="toggle_button">', $this->language->get('advanced_options'), ' <span id="arrow_down" class="arrow">&#9660</span><span id="arrow_up" class="arrow">&#9650</span></div>
720
					<dl id="advanced_options" style="display: none; margin-top: 5px">';
721
				continue;
722
			}
723
724
			switch ($option['type'])
725
			{
726
				case 'text':
727
				{
728
					echo '
729
						<dt>
730
							<label for="', $option['id'], '">', $option['label'], ':</label>
731
						</dt>
732
						<dd>
733
							<input type="text" name="', $option['id'], '" id="', $option['id'], '" value="', $option['value'], '" ', !empty($option['validate']) ? 'onblur="validateField(\'' . $option['id'] . '\')"' : '', ' class="text" />
734
							<div id="validate_', $option['id'], '" class="validate">', $option['correct'], '</div>
735
						</dd>';
736
					break;
737
				}
738
				case 'checkbox':
739
				{
740
					echo '
741
						<dt></dt>
742
						<dd>
743
							<label for="', $option['id'], '">', $option['label'], ':
744
								<input type="checkbox" name="', $option['id'], '" id="', $option['id'], '" value="', $option['value'], '" ', $option['attributes'], '/>
745
							</label>
746
						</dd>';
747
					break;
748
				}
749
				case 'password':
750
				{
751
					echo '
752
						<dt>
753
							<label for="', $option['id'], '">', $option['label'], ':</label>
754
						</dt>
755
						<dd>
756
							<input type="password" name="', $option['id'], '" id="', $option['id'], '" class="text" />
757
							<div style="font-style: italic; font-size: smaller">', $option['correct'], '</div>
758
						</dd>';
759
					break;
760
				}
761
				case 'steps':
762
				{
763
					echo '
764
						<dt>
765
							<label for="', $option['id'], '">', $option['label'], ':</label>
766
						</dt>
767
						<dd>';
768
769
					foreach ($option['value'] as $key => $step)
770
					{
771
						echo '
772
							<label>
773
								<input type="checkbox" name="do_steps[', $key, ']" id="do_steps[', $key, ']" value="', $step['count'], '"', $step['mandatory'] ? 'readonly="readonly" ' : ' ', $step['checked'], '" /> ', $step['label'], '
774
							</label><br />';
775
					}
776
777
					echo '
778
						</dd>';
779
					break;
780
				}
781
			}
782
		}
783
784
		echo '
785
					</dl>
786
					<div class="button">
787
						<input id="submit_button" name="', $form->submit['name'], '" type="submit" value="', $form->submit['value'], '" class="submit" />
788
					</div>
789
				</form>
790
			</div>';
791
792
		if ($toggle)
793
		{
794
			echo '
795
			<script>
796
				document.getElementById(\'toggle_button\').onclick = function ()
797
				{
798
					let elem = document.getElementById(\'advanced_options\'),
799
						arrow_up = document.getElementById(\'arrow_up\'),
800
						arrow_down = document.getElementById(\'arrow_down\');
801
802
					if (!elem)
803
						return true;
804
805
					if (elem.style.display === \'none\')
806
					{
807
						elem.style.display = \'block\';
808
						arrow_down.style.display = \'none\';
809
						arrow_up.style.display = \'inline\';
810
					}
811
					else
812
					{
813
						elem.style.display = \'none\';
814
						arrow_down.style.display = \'inline\';
815
						arrow_up.style.display = \'none\';
816
					}
817
818
					return true;
819
				}
820
			</script>';
821
		}
822
	}
823
}
824