Completed
Push — release-2.1 ( b61572...c939ca )
by Mathias
15s
created

Packages.template.php ➔ template_downloaded()   B

Complexity

Conditions 3
Paths 1

Size

Total Lines 25
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 1
nop 0
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Simple Machines Forum (SMF)
4
 *
5
 * @package SMF
6
 * @author Simple Machines http://www.simplemachines.org
7
 * @copyright 2017 Simple Machines and individual contributors
8
 * @license http://www.simplemachines.org/about/smf/license.php BSD
9
 *
10
 * @version 2.1 Beta 4
11
 */
12
13
/**
14
 * The main template
15
 */
16
function template_main()
0 ignored issues
show
Best Practice introduced by
The function template_main() has been defined more than once; this definition is ignored, only the first definition in Themes/default/BoardIndex.template.php (L56-134) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
17
{
18
}
19
20
/**
21
 * View package details when installing/uninstalling
22
 */
23
function template_view_package()
24
{
25
	global $context, $settings, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
26
27
	echo '
28
	<div id="admincenter">
29
		<div class="cat_bar">
30
			<h3 class="catbg">', $txt[($context['uninstalling'] ? 'un' : '') . 'install_mod'], '</h3>
31
		</div>
32
		<div class="information">';
33
34
	if ($context['is_installed'])
35
		echo '
36
			<strong>', $txt['package_installed_warning1'], '</strong><br>
37
			<br>
38
			', $txt['package_installed_warning2'], '<br>
39
			<br>';
40
41
	echo $txt['package_installed_warning3'], '
42
		</div>
43
		<br>';
44
45
	// Do errors exist in the install? If so light them up like a christmas tree.
46
	if ($context['has_failure'])
47
	{
48
		echo '
49
		<div class="errorbox">
50
			', sprintf($txt['package_will_fail_title'], $txt['package_' . ($context['uninstalling'] ? 'uninstall' : 'install')]), '<br>
51
			', sprintf($txt['package_will_fail_warning'], $txt['package_' . ($context['uninstalling'] ? 'uninstall' : 'install')]),
52
			!empty($context['failure_details']) ? '<br><br><strong>' . $context['failure_details'] . '</strong>' : '', '
53
		</div>';
54
	}
55
56
	// Display the package readme if one exists
57
	if (isset($context['package_readme']))
58
	{
59
		echo '
60
		<div class="cat_bar">
61
			<h3 class="catbg">', $txt['package_' . ($context['uninstalling'] ? 'un' : '') . 'install_readme'], '</h3>
62
		</div>
63
		<div class="windowbg2">
64
			', $context['package_readme'], '
65
			<span class="floatright">', $txt['package_available_readme_language'], '
66
				<select name="readme_language" id="readme_language" onchange="if (this.options[this.selectedIndex].value) window.location.href = smf_prepareScriptUrl(smf_scripturl + \'', '?action=admin;area=packages;sa=', $context['uninstalling'] ? 'uninstall' : 'install', ';package=', $context['filename'], ';readme=\' + this.options[this.selectedIndex].value + \';license=\' + get_selected(\'license_language\'));">';
67
68 View Code Duplication
		foreach ($context['readmes'] as $a => $b)
69
			echo '
70
					<option value="', $b, '"', $a === 'selected' ? ' selected' : '', '>', $b == 'default' ? $txt['package_readme_default'] : ucfirst($b), '</option>';
71
72
		echo '
73
				</select>
74
			</span>
75
		</div>
76
		<br>';
77
	}
78
79
	// Did they specify a license to display?
80
	if (isset($context['package_license']))
81
	{
82
		echo '
83
		<div class="cat_bar">
84
			<h3 class="catbg">', $txt['package_install_license'], '</h3>
85
		</div>
86
		<div class="windowbg2">
87
			', $context['package_license'], '
88
			<span class="floatright">', $txt['package_available_license_language'], '
89
				<select name="license_language" id="license_language" onchange="if (this.options[this.selectedIndex].value) window.location.href = smf_prepareScriptUrl(smf_scripturl + \'', '?action=admin;area=packages;sa=install', ';package=', $context['filename'], ';license=\' + this.options[this.selectedIndex].value + \';readme=\' + get_selected(\'readme_language\'));">';
90
91 View Code Duplication
		foreach ($context['licenses'] as $a => $b)
92
			echo '
93
					<option value="', $b, '"', $a === 'selected' ? ' selected' : '', '>', $b == 'default' ? $txt['package_license_default'] : ucfirst($b), '</option>';
94
		echo '
95
				</select>
96
			</span>
97
		</div>
98
		<br>';
99
	}
100
101
	echo '
102
		<form action="', !empty($context['post_url']) ? $context['post_url'] : '#', '" onsubmit="submitonce(this);" method="post" accept-charset="', $context['character_set'], '">
103
			<div class="cat_bar">
104
				<h3 class="catbg">
105
					', $context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['package_install_actions'], ' &quot;', $context['package_name'], '&quot;
106
				</h3>
107
			</div>';
108
109
	// Are there data changes to be removed?
110
	if ($context['uninstalling'] && !empty($context['database_changes']))
111
	{
112
		// This is really a special case so we're adding style inline
113
		echo '
114
			<div class="windowbg2" style="margin: 0; border-radius: 0;">
115
				<label for="do_db_changes"><input type="checkbox" name="do_db_changes" id="do_db_changes">', $txt['package_db_uninstall'], '</label> [<a href="#" onclick="return swap_database_changes();">', $txt['package_db_uninstall_details'], '</a>]
116
				<div id="db_changes_div">
117
					', $txt['package_db_uninstall_actions'], ':
118
					<ul>';
119
120
		foreach ($context['database_changes'] as $change)
121
			echo '
122
						<li>', $change, '</li>';
123
		echo '
124
					</ul>
125
				</div>
126
			</div>';
127
	}
128
129
	echo '
130
			<div class="information">';
131
132
	if (empty($context['actions']) && empty($context['database_changes']))
133
		echo '
134
				<br>
135
				<div class="errorbox">
136
					', $txt['corrupt_compatible'], '
137
				</div>
138
			</div><!-- .information -->';
139
	else
140
	{
141
		echo '
142
				', $txt['perform_actions'], '
143
			</div><!-- .information -->
144
			<br>
145
			<table class="table_grid">
146
				<thead>
147
					<tr class="title_bar">
148
						<th scope="col" width="20"></th>
149
						<th scope="col" width="30"></th>
150
						<th scope="col" class="lefttext">', $txt['package_install_type'], '</th>
151
						<th scope="col" class="lefttext" width="50%">', $txt['package_install_action'], '</th>
152
						<th class="lefttext" scope="col" width="20%">', $txt['package_install_desc'], '</th>
153
					</tr>
154
				</thead>
155
				<tbody>';
156
157
		$i = 1;
158
		$action_num = 1;
159
		$js_operations = array();
160
		foreach ($context['actions'] as $packageaction)
161
		{
162
			// Did we pass or fail?  Need to now for later on.
163
			$js_operations[$action_num] = isset($packageaction['failed']) ? $packageaction['failed'] : 0;
164
165
			echo '
166
					<tr class="windowbg">
167
						<td style="width: 5%;">', isset($packageaction['operations']) ? '<img id="operation_img_' . $action_num . '" src="' . $settings['images_url'] . '/selected_open.png" alt="*" style="display: none;">' : '', '</td>
168
						<td style="width: 5%;">', $i++, '.</td>
169
						<td style="width: 20%;">', $packageaction['type'], '</td>
170
						<td style="width: 50%;">', $packageaction['action'], '</td>
171
						<td style="width: 20%";>', $packageaction['description'], '</td>
172
					</tr>';
173
174
			// Is there water on the knee? Operation!
175 View Code Duplication
			if (isset($packageaction['operations']))
176
			{
177
				echo '
178
					<tr id="operation_', $action_num, '">
179
						<td colspan="5" class="windowbg2">
180
							<table class="table_grid">';
181
182
				// Show the operations.
183
				$operation_num = 1;
184
				foreach ($packageaction['operations'] as $operation)
185
				{
186
					// Determine the position text.
187
					$operation_text = $operation['position'] == 'replace' ? 'operation_replace' : ($operation['position'] == 'before' ? 'operation_after' : 'operation_before');
188
189
					echo '
190
								<tr class="windowbg">
191
									<td class="smalltext" style="width: 5%;">
192
										<a href="' . $scripturl . '?action=admin;area=packages;sa=showoperations;operation_key=', $operation['operation_key'], !empty($context['install_id']) ? ';install_id=' . $context['install_id'] : '', ';package=', $_REQUEST['package'], ';filename=', $operation['filename'], ($operation['is_boardmod'] ? ';boardmod' : ''), (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'uninstall' ? ';reverse' : ''), '" onclick="return reqWin(this.href, 680, 400, false);"><span class="generic_icons package_ops"></span></a>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$_REQUEST['package'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST
    in Themes/default/Packages.template.php on line 192

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
193
									</td>
194
									<td class="smalltext" style="width: 5%;">', $operation_num, '.</td>
195
									<td class="smalltext" style="width: 20%;">', $txt[$operation_text], '</td>
196
									<td class="smalltext" style="width: 50%;">', $operation['action'], '</td>
197
									<td class="smalltext" style="width: 20%;">', $operation['description'], !empty($operation['ignore_failure']) ? ' (' . $txt['operation_ignore'] . ')' : '', '</td>
198
								</tr>';
199
200
					$operation_num++;
201
				}
202
203
				echo '
204
							</table>
205
						</td>
206
					</tr>';
207
208
				// Increase it.
209
				$action_num++;
210
			}
211
		}
212
		echo '
213
				</tbody>
214
			</table>';
215
216
		// What if we have custom themes we can install into? List them too!
217
		if (!empty($context['theme_actions']))
218
		{
219
			echo '
220
			<br>
221
			<div class="cat_bar">
222
				<h3 class="catbg">
223
					', $context['uninstalling'] ? $txt['package_other_themes_uninstall'] : $txt['package_other_themes'], '
224
				</h3>
225
			</div>
226
			<div id="custom_changes">
227
				<div class="information">
228
					', $txt['package_other_themes_desc'], '
229
				</div>
230
				<table class="table_grid">';
231
232
			// Loop through each theme and display it's name, and then it's details.
233
			foreach ($context['theme_actions'] as $id => $theme)
234
			{
235
				// Pass?
236
				$js_operations[$action_num] = !empty($theme['has_failure']);
237
238
				echo '
239
					<tr class="title_bar">
240
						<td></td>
241
						<td>';
242
243
				if (!empty($context['themes_locked']))
244
					echo '
245
							<input type="hidden" name="custom_theme[]" value="', $id, '">';
246
				echo '
247
							<input type="checkbox" name="custom_theme[]" id="custom_theme_', $id, '" value="', $id, '" onclick="', (!empty($theme['has_failure']) ? 'if (this.form.custom_theme_' . $id . '.checked && !confirm(\'' . $txt['package_theme_failure_warning'] . '\')) return false;' : ''), 'invertAll(this, this.form, \'dummy_theme_', $id, '\', true);"', !empty($context['themes_locked']) ? ' disabled checked' : '', '>
248
						</td>
249
						<td colspan="3">
250
							', $theme['name'], '
251
						</td>
252
					</tr>';
253
254
				foreach ($theme['actions'] as $action)
255
				{
256
					echo '
257
					<tr class="windowbg">
258
						<td>', isset($packageaction['operations']) ? '<img id="operation_img_' . $action_num . '" src="' . $settings['images_url'] . '/selected_open.png" alt="*" style="display: none;">' : '', '</td>
259
						<td width="30">
260
							<input type="checkbox" name="theme_changes[]" value="', !empty($action['value']) ? $action['value'] : '', '" id="dummy_theme_', $id, '"', (!empty($action['not_mod']) ? '' : ' disabled'), !empty($context['themes_locked']) ? ' checked' : '', '>
261
						</td>
262
						<td>', $action['type'], '</td>
263
						<td width="50%">', $action['action'], '</td>
264
						<td width="20%"><strong>', $action['description'], '</strong></td>
265
					</tr>';
266
267
					// Is there water on the knee? Operation!
268 View Code Duplication
					if (isset($action['operations']))
269
					{
270
						echo '
271
					<tr id="operation_', $action_num, '">
272
						<td colspan="5" class="windowbg2">
273
							<table width="100%">';
274
275
						$operation_num = 1;
276
						foreach ($action['operations'] as $operation)
277
						{
278
							// Determine the possition text.
279
							$operation_text = $operation['position'] == 'replace' ? 'operation_replace' : ($operation['position'] == 'before' ? 'operation_after' : 'operation_before');
280
281
							echo '
282
								<tr class="windowbg">
283
									<td width="0"></td>
284
									<td width="30" class="smalltext"><a href="' . $scripturl . '?action=admin;area=packages;sa=showoperations;operation_key=', $operation['operation_key'], !empty($context['install_id']) ? ';install_id=' . $context['install_id'] : '', ';package=', $_REQUEST['package'], ';filename=', $operation['filename'], ($operation['is_boardmod'] ? ';boardmod' : ''), (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'uninstall' ? ';reverse' : ''), '" onclick="return reqWin(this.href, 600, 400, false);"><span class="generic_icons package_ops"></span></a></td>
0 ignored issues
show
Security Cross-Site Scripting introduced by
$_REQUEST['package'] can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST
    in Themes/default/Packages.template.php on line 284

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
285
									<td width="30" class="smalltext">', $operation_num, '.</td>
286
									<td width="23%" class="smalltext">', $txt[$operation_text], '</td>
287
									<td width="50%" class="smalltext">', $operation['action'], '</td>
288
									<td width="20%" class="smalltext">', $operation['description'], !empty($operation['ignore_failure']) ? ' (' . $txt['operation_ignore'] . ')' : '', '</td>
289
								</tr>';
290
							$operation_num++;
291
						}
292
293
						echo '
294
							</table>
295
						</td>
296
					</tr>';
297
298
						// Increase it.
299
						$action_num++;
300
					}
301
				}
302
			}
303
304
			echo '
305
				</table>
306
			</div><!-- #custom_changes -->';
307
		}
308
	}
309
310
	// Are we effectively ready to install?
311
	if (!$context['ftp_needed'] && (!empty($context['actions']) || !empty($context['database_changes'])))
312
	{
313
		echo '
314
			<div class="righttext padding">
315
				<input type="submit" value="', $context['uninstalling'] ? $txt['package_uninstall_now'] : $txt['package_install_now'], '" onclick="return ', !empty($context['has_failure']) ? '(submitThisOnce(this) &amp;&amp; confirm(\'' . ($context['uninstalling'] ? $txt['package_will_fail_popup_uninstall'] : $txt['package_will_fail_popup']) . '\'))' : 'submitThisOnce(this)', ';" class="button">
316
			</div>';
317
	}
318
	// If we need ftp information then demand it!
319
	elseif ($context['ftp_needed'])
320
	{
321
		echo '
322
			<div class="cat_bar">
323
				<h3 class="catbg">', $txt['package_ftp_necessary'], '</h3>
324
			</div>
325
			<div>
326
				', template_control_chmod(), '
327
			</div>';
328
	}
329
330
	echo '
331
332
			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">', (isset($context['form_sequence_number']) && !$context['ftp_needed']) ? '
333
			<input type="hidden" name="seqnum" value="' . $context['form_sequence_number'] . '">' : '', '
334
		</form>
335
	</div><!-- #admincenter -->';
336
337
	// Toggle options.
338
	echo '
339
	<script>
340
		var aOperationElements = new Array();';
341
342
	// Operations.
343
	if (!empty($js_operations))
344
	{
345
		foreach ($js_operations as $key => $operation)
346
		{
347
			echo '
348
		aOperationElements[', $key, '] = new smc_Toggle({
349
			bToggleEnabled: true,
350
			bNoAnimate: true,
351
			bCurrentlyCollapsed: ', $operation ? 'false' : 'true', ',
352
			aSwappableContainers: [
353
				\'operation_', $key, '\'
354
			],
355
			aSwapImages: [
356
				{
357
					sId: \'operation_img_', $key, '\',
358
					srcExpanded: smf_images_url + \'/selected_open.png\',
359
					altExpanded: \'*\',
360
					srcCollapsed: smf_images_url + \'/selected.png\',
361
					altCollapsed: \'*\'
362
				}
363
			]
364
		});';
365
		}
366
	}
367
368
	echo '
369
	</script>';
370
371
	// Get the currently selected item from a select list
372
	echo '
373
	<script>
374
		function get_selected(id)
375
		{
376
			var aSelected = document.getElementById(id);
377
			for (var i = 0; i < aSelected.options.length; i++)
378
			{
379
				if (aSelected.options[i].selected == true)
380
					return aSelected.options[i].value;
381
			}
382
			return aSelected.options[0];
383
		}
384
	</script>';
385
386
	// And a bit more for database changes.
387
	if (!empty($context['database_changes']))
388
		echo '
389
	<script>
390
		var database_changes_area = document.getElementById(\'db_changes_div\');
391
		var db_vis = false;
392
		database_changes_area.style.display = "none";
393
	</script>';
394
}
395
396
/**
397
 * Extract package contents
398
 */
399
function template_extract_package()
400
{
401
	global $context, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
402
403
	if (!empty($context['redirect_url']))
404
	{
405
		echo '
406
	<script>
407
		setTimeout("doRedirect();", ', empty($context['redirect_timeout']) ? '5000' : $context['redirect_timeout'], ');
408
409
		function doRedirect()
410
		{
411
			window.location = "', $context['redirect_url'], '";
412
		}
413
	</script>';
414
	}
415
416
	echo '
417
	<div id="admincenter">';
418
419
	if (empty($context['redirect_url']))
420
	{
421
		echo '
422
		<div class="cat_bar">
423
			<h3 class="catbg">', $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting'], '</h3>
424
		</div>
425
		<div class="information">', $txt['package_installed_extract'], '</div>';
426
	}
427
	else
428
		echo '
429
		<div class="cat_bar">
430
			<h3 class="catbg">', $txt['package_installed_redirecting'], '</h3>
431
		</div>';
432
433
	echo '
434
		<div class="windowbg">';
435
436
	// If we are going to redirect we have a slightly different agenda.
437
	if (!empty($context['redirect_url']))
438
	{
439
		echo '
440
			', $context['redirect_text'], '<br><br>
441
			<a href="', $context['redirect_url'], '">', $txt['package_installed_redirect_go_now'], '</a> | <a href="', $scripturl, '?action=admin;area=packages;sa=browse">', $txt['package_installed_redirect_cancel'], '</a>';
442
	}
443
	elseif ($context['uninstalling'])
444
		echo '
445
			', $txt['package_uninstall_done'];
446
447
	elseif ($context['install_finished'])
448
	{
449
		if ($context['extract_type'] == 'avatar')
450
			echo '
451
			', $txt['avatars_extracted'];
452
453
		elseif ($context['extract_type'] == 'language')
454
			echo '
455
			', $txt['language_extracted'];
456
457
		else
458
			echo '
459
			', $txt['package_installed_done'];
460
	}
461
	else
462
		echo '
463
			', $txt['corrupt_compatible'];
464
465
	echo '
466
		</div><!-- .windowbg -->';
467
468
	// Show the "restore permissions" screen?
469
	if (function_exists('template_show_list') && !empty($context['restore_file_permissions']['rows']))
470
	{
471
		echo '<br>';
472
		template_show_list('restore_file_permissions');
473
	}
474
475
	echo '
476
	</div><!-- #admincenter -->';
477
}
478
479
/**
480
 * List files in a package
481
 */
482
function template_list()
483
{
484
	global $context, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
485
486
	echo '
487
	<div id="admincenter">
488
		<div class="cat_bar">
489
			<h3 class="catbg">', $txt['list_file'], '</h3>
490
		</div>
491
		<div class="cat_bar">
492
			<h3 class="catbg">', $txt['files_archive'], ' ', $context['filename'], ':</h3>
493
		</div>
494
		<div class="windowbg">
495
			<ol>';
496
497
	foreach ($context['files'] as $fileinfo)
498
		echo '
499
				<li><a href="', $scripturl, '?action=admin;area=packages;sa=examine;package=', $context['filename'], ';file=', $fileinfo['filename'], '" title="', $txt['view'], '">', $fileinfo['filename'], '</a> (', $fileinfo['size'], ' ', $txt['package_bytes'], ')</li>';
500
501
	echo '
502
			</ol>
503
			<br>
504
			<a href="', $scripturl, '?action=admin;area=packages">[ ', $txt['back'], ' ]</a>
505
		</div>
506
	</div><!-- #admincenter -->';
507
}
508
509
/**
510
 * Examine a single file within a package
511
 */
512
function template_examine()
513
{
514
	global $context, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
515
516
	echo '
517
	<div id="admincenter">
518
		<div class="cat_bar">
519
			<h3 class="catbg">', $txt['package_examine_file'], '</h3>
520
		</div>
521
		<div class="cat_bar">
522
			<h3 class="catbg">', $txt['package_file_contents'], ' ', $context['filename'], ':</h3>
523
		</div>
524
		<div class="windowbg">
525
			<pre class="file_content">', $context['filedata'], '</pre>
526
			<a href="', $scripturl, '?action=admin;area=packages;sa=list;package=', $context['package'], '">[ ', $txt['list_files'], ' ]</a>
527
		</div>
528
	</div><!-- #admincenter -->';
529
}
530
531
/**
532
 * List all packages
533
 */
534
function template_browse()
0 ignored issues
show
Best Practice introduced by
The function template_browse() has been defined more than once; this definition is ignored, only the first definition in Themes/default/ManageMail.template.php (L17-41) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
535
{
536
	global $context, $txt, $scripturl, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
537
538
	echo '
539
	<div id="admincenter">
540
		<div id="update_section"></div>
541
		<div id="admin_form_wrapper">
542
			<div class="cat_bar">
543
				<h3 class="catbg">
544
					', $txt['packages_adding_title'], '
545
				</h3>
546
			</div>
547
			<div class="information">
548
				', $txt['packages_adding'], '
549
			</div>
550
551
			<script>
552
				window.smfForum_scripturl = smf_scripturl;
553
				window.smfForum_sessionid = smf_session_id;
554
				window.smfForum_sessionvar = smf_session_var;';
555
556
	// Make a list of already installed mods so nothing is listed twice ;).
557
	echo '
558
				window.smfInstalledPackages = ["', implode('", "', $context['installed_mods']), '"];
559
				window.smfVersion = "', $context['forum_version'], '";
560
			</script>
561
			<div id="yourVersion" style="display:none">', $context['forum_version'], '</div>';
562
563
	if (empty($modSettings['disable_smf_js']))
564
		echo '
565
			<script src="', $scripturl, '?action=viewsmfile;filename=latest-news.js"></script>';
566
567
	// This sets the announcements and current versions themselves ;).
568
	echo '
569
			<script>
570
				var oAdminIndex = new smf_AdminIndex({
571
					sSelf: \'oAdminCenter\',
572
					bLoadAnnouncements: false,
573
					bLoadVersions: false,
574
					bLoadUpdateNotification: true,
575
					sUpdateNotificationContainerId: \'update_section\',
576
					sUpdateNotificationDefaultTitle: ', JavaScriptEscape($txt['update_available']), ',
577
					sUpdateNotificationDefaultMessage: ', JavaScriptEscape($txt['update_message']), ',
578
					sUpdateNotificationTemplate: ', JavaScriptEscape('
579
						<h3 id="update_title">
580
							%title%
581
						</h3>
582
						<div id="update_message" class="smalltext">
583
							%message%
584
						</div>
585
					'), ',
586
					sUpdateNotificationLink: smf_scripturl + ', JavaScriptEscape('?action=admin;area=packages;pgdownload;auto;package=%package%;' . $context['session_var'] . '=' . $context['session_id']), '
587
588
				});
589
			</script>';
590
591
	echo '
592
		</div><!-- #admin_form_wrapper -->';
593
594
	$mods_available = false;
595
	foreach ($context['modification_types'] as $type)
596
	{
597
		if (!empty($context['available_' . $type]))
598
		{
599
			template_show_list('packages_lists_' . $type);
600
			$mods_available = true;
601
		}
602
	}
603
604
	if (!$mods_available)
605
		echo '
606
		<div class="noticebox">', $txt['no_packages'], '</div>';
607
	else
608
		echo '
609
		<br>';
610
611
	// the advanced (emulation) box, collapsed by default
612
	echo '
613
		<form action="', $scripturl, '?action=admin;area=packages;sa=browse" method="get">
614
			<div id="advanced_box">
615
				<div class="cat_bar">
616
					<h3 class="catbg">
617
						<span id="advanced_panel_toggle" class="floatright" style="display: none;"></span>
618
						<a href="#" id="advanced_panel_link">', $txt['package_advanced_button'], '</a>
619
					</h3>
620
				</div>
621
				<div id="advanced_panel_div" class="windowbg2 noup">
622
					<p>
623
						', $txt['package_emulate_desc'], '
624
					</p>
625
					<dl class="settings">
626
						<dt>
627
							<strong>', $txt['package_emulate'], ':</strong><br>
628
							<span class="smalltext">
629
								<a href="#" onclick="return revert();">', $txt['package_emulate_revert'], '</a>
630
							</span>
631
						</dt>
632
						<dd>
633
							<a id="revert" name="revert"></a>
634
							<select name="version_emulate" id="ve">';
635
636
	foreach ($context['emulation_versions'] as $version)
637
	{
638
		echo '
639
								<option value="', $version, '"', ($version == $context['selected_version'] ? ' selected="selected"' : ''), '>', $version, '</option>';
640
	}
641
642
	echo '
643
							</select>
644
						</dd>
645
					</dl>
646
					<div class="righttext padding">
647
						<input type="submit" value="', $txt['package_apply'], '" class="button">
648
					</div>
649
				</div><!-- #advanced_panel_div -->
650
			</div><!-- #advanced_box -->
651
			<input type="hidden" name="action" value="admin">
652
			<input type="hidden" name="area" value="packages">
653
			<input type="hidden" name="sa" value="browse">
654
		</form>
655
	</div><!-- #admincenter -->
656
	<script>
657
		var oAdvancedPanelToggle = new smc_Toggle({
658
			bToggleEnabled: true,
659
			bCurrentlyCollapsed: true,
660
			aSwappableContainers: [
661
				\'advanced_panel_div\'
662
			],
663
			aSwapImages: [
664
				{
665
					sId: \'advanced_panel_toggle\',
666
					altExpanded: ', JavaScriptEscape($txt['hide']), ',
667
					altCollapsed: ', JavaScriptEscape($txt['show']), '
668
				}
669
			],
670
			aSwapLinks: [
671
				{
672
					sId: \'advanced_panel_link\',
673
					msgExpanded: ', JavaScriptEscape($txt['package_advanced_button']), ',
674
					msgCollapsed: ', JavaScriptEscape($txt['package_advanced_button']), '
675
				}
676
			]
677
		});
678
		function revert()
679
		{
680
			var default_version = "', $context['default_version'], '";
681
			$("#ve").find("option").filter(function(index) {
682
    			return default_version === $(this).text();
683
			}).attr("selected", "selected");
684
			return false;
685
		}
686
	</script>';
687
}
688
689
/**
690
 * List package servers
691
 */
692
function template_servers()
693
{
694
	global $context, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
695
696 View Code Duplication
	if (!empty($context['package_ftp']['error']))
697
		echo '
698
	<div class="errorbox">
699
		<pre>', $context['package_ftp']['error'], '</pre>
700
	</div>';
701
702
	echo '
703
	<div id="admin_form_wrapper">
704
		<div class="cat_bar">
705
			<h3 class="catbg">' . $txt['package_upload_title'] . '</h3>
706
		</div>
707
		<div class="windowbg2 noup">
708
			<form action="' . $scripturl . '?action=admin;area=packages;get;sa=upload" method="post" accept-charset="', $context['character_set'], '" enctype="multipart/form-data" style="margin-bottom: 0;">
709
				<dl class="settings">
710
					<dt>
711
						<strong>' . $txt['package_upload_select'] . ':</strong>
712
					</dt>
713
					<dd>
714
						<input type="file" name="package" size="38">
715
					</dd>
716
				</dl>
717
				<input type="submit" value="' . $txt['upload'] . '" class="button">
718
				<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
719
			</form>
720
		</div>
721
		<div class="cat_bar">
722
			<h3 class="catbg">
723
				<a class="download_new_package">
724
					<span class="toggle_down floatright" alt="*" title="', $txt['show'], '"></span>
725
					', $txt['download_new_package'], '
726
				</a>
727
			</h3>
728
		</div>
729
		<div class="new_package_content">';
730
731
	if ($context['package_download_broken'])
732
	{
733
		echo '
734
			<div class="cat_bar">
735
				<h3 class="catbg">', $txt['package_ftp_necessary'], '</h3>
736
			</div>
737
			<div class="windowbg">
738
				<p>
739
					', $txt['package_ftp_why_download'], '
740
				</p>
741
				<form action="', $scripturl, '?action=admin;area=packages;get" method="post" accept-charset="', $context['character_set'], '">
742
					<dl class="settings">
743
						<dt>
744
							<label for="ftp_server">', $txt['package_ftp_server'], ':</label>
745
						</dt>
746
						<dd>
747
							<input type="text" size="30" name="ftp_server" id="ftp_server" value="', $context['package_ftp']['server'], '">
748
							<label for="ftp_port">', $txt['package_ftp_port'], ':&nbsp;</label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', $context['package_ftp']['port'], '">
749
						</dd>
750
						<dt>
751
							<label for="ftp_username">', $txt['package_ftp_username'], ':</label>
752
						</dt>
753
						<dd>
754
							<input type="text" size="50" name="ftp_username" id="ftp_username" value="', $context['package_ftp']['username'], '" style="width: 99%;">
755
						</dd>
756
						<dt>
757
							<label for="ftp_password">', $txt['package_ftp_password'], ':</label>
758
						</dt>
759
						<dd>
760
							<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;">
761
						</dd>
762
						<dt>
763
							<label for="ftp_path">', $txt['package_ftp_path'], ':</label>
764
						</dt>
765
						<dd>
766
							<input type="text" size="50" name="ftp_path" id="ftp_path" value="', $context['package_ftp']['path'], '" style="width: 99%;">
767
						</dd>
768
					</dl>
769
					<div class="righttext">
770
						<input type="submit" value="', $txt['package_proceed'], '" class="button">
771
					</div>
772
				</form>
773
			</div><!-- .windowbg -->';
774
	}
775
776
	echo '
777
			<div class="windowbg2 noup">
778
				<fieldset>
779
					<legend>' . $txt['package_servers'] . '</legend>
780
					<ul class="package_servers">';
781
782
	foreach ($context['servers'] as $server)
783
		echo '
784
						<li class="flow_auto">
785
							<span class="floatleft">' . $server['name'] . '</span>
786
							<span class="package_server floatright"><a href="' . $scripturl . '?action=admin;area=packages;get;sa=remove;server=' . $server['id'] . ';', $context['session_var'], '=', $context['session_id'], '">[ ' . $txt['delete'] . ' ]</a></span>
787
							<span class="package_server floatright"><a href="' . $scripturl . '?action=admin;area=packages;get;sa=browse;server=' . $server['id'] . '">[ ' . $txt['package_browse'] . ' ]</a></span>
788
						</li>';
789
	echo '
790
					</ul>
791
				</fieldset>
792
				<fieldset>
793
					<legend>' . $txt['add_server'] . '</legend>
794
					<form action="' . $scripturl . '?action=admin;area=packages;get;sa=add" method="post" accept-charset="', $context['character_set'], '">
795
						<dl class="settings">
796
							<dt>
797
								<strong>' . $txt['server_name'] . ':</strong>
798
							</dt>
799
							<dd>
800
								<input type="text" name="servername" size="44" value="SMF">
801
							</dd>
802
							<dt>
803
								<strong>' . $txt['serverurl'] . ':</strong>
804
							</dt>
805
							<dd>
806
								<input type="text" name="serverurl" size="44" value="https://">
807
							</dd>
808
						</dl>
809
						<div class="righttext">
810
							<input type="submit" value="' . $txt['add_server'] . '" class="button">
811
							<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
812
						</div>
813
					</form>
814
				</fieldset>
815
				<fieldset>
816
					<legend>', $txt['package_download_by_url'], '</legend>
817
					<form action="', $scripturl, '?action=admin;area=packages;get;sa=download;byurl;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
818
						<dl class="settings">
819
							<dt>
820
								<strong>' . $txt['serverurl'] . ':</strong>
821
							</dt>
822
							<dd>
823
								<input type="text" name="package" size="44" value="https://">
824
							</dd>
825
							<dt>
826
								<strong>', $txt['package_download_filename'], ':</strong>
827
							</dt>
828
							<dd>
829
								<input type="text" name="filename" size="44"><br>
830
								<span class="smalltext">', $txt['package_download_filename_info'], '</span>
831
							</dd>
832
						</dl>
833
						<input type="submit" value="', $txt['download'], '" class="button">
834
					</form>
835
				</fieldset>
836
			</div><!-- .windowbg2 -->
837
		</div><!-- .new_package_content -->
838
	</div><!-- #admin_form_wrapper -->';
839
}
840
841
/**
842
 * Confirm package operation
843
 */
844
function template_package_confirm()
845
{
846
	global $context, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
847
848
	echo '
849
	<div id="admincenter">
850
		<div class="cat_bar">
851
			<h3 class="catbg">', $context['page_title'], '</h3>
852
		</div>
853
		<div class="windowbg2 noup">
854
			<p>', $context['confirm_message'], '</p>
855
			<a href="', $context['proceed_href'], '">[ ', $txt['package_confirm_proceed'], ' ]</a> <a href="JavaScript:history.go(-1);">[ ', $txt['package_confirm_go_back'], ' ]</a>
856
		</div>
857
	</div>';
858
}
859
860
/**
861
 * List packages.
862
 */
863
function template_package_list()
864
{
865
	global $context, $txt, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
866
867
	echo '
868
	<div id="admincenter">
869
		<div class="cat_bar">
870
			<h3 class="catbg">' . $context['page_title'] . '</h3>
871
		</div>
872
		<div class="windowbg2">';
873
874
	// No packages, as yet.
875
	if (empty($context['package_list']))
876
		echo '
877
			<ul>
878
				<li>', $txt['no_packages'], '</li>
879
			</ul>';
880
881
	// List out the packages...
882
	else
883
	{
884
		echo '
885
			<ul id="package_list">';
886
		foreach ($context['package_list'] as $i => $packageSection)
887
		{
888
			echo '
889
				<li>
890
					<strong><span id="ps_img_', $i, '" class="toggle_up" alt="*" style="display: none;"></span> ', $packageSection['title'], '</strong>';
891
892
			if (!empty($packageSection['text']))
893
				echo '
894
					<div class="sub_bar">
895
						<h3 class="subbg">', $packageSection['text'], '</h3>
896
					</div>';
897
898
			echo '
899
					<', $context['list_type'], ' id="package_section_', $i, '" class="packages">';
900
901
			foreach ($packageSection['items'] as $id => $package)
902
			{
903
				echo '
904
						<li>';
905
				// Textual message. Could be empty just for a blank line...
906
				if ($package['is_text'])
907
					echo '
908
							', empty($package['name']) ? '&nbsp;' : $package['name'];
909
				// This is supposed to be a rule..
910
				elseif ($package['is_line'])
911
					echo '
912
						<hr>';
913
				// A remote link.
914
				elseif ($package['is_remote'])
915
				{
916
					echo '
917
						<strong>', $package['link'], '</strong>';
918
				}
919
				// A title?
920
				elseif ($package['is_heading'] || $package['is_title'])
921
				{
922
					echo '
923
						<strong>', $package['name'], '</strong>';
924
				}
925
				// Otherwise, it's a package.
926
				else
927
				{
928
					// 1. Some mod [ Download ].
929
					echo '
930
						<strong><span id="ps_img_', $i, '_pkg_', $id, '" class="toggle_up" alt="*" style="display: none;"></span> ', $package['can_install'] || !empty($package['can_emulate_install']) ? '<strong>' . $package['name'] . '</strong> <a href="' . $package['download']['href'] . '">[ ' . $txt['download'] . ' ]</a>' : $package['name'], '</strong>
931
						<ul id="package_section_', $i, '_pkg_', $id, '" class="package_section">';
932
933
					// Show the mod type?
934
					if ($package['type'] != '')
935
						echo '
936
							<li class="package_section">', $txt['package_type'], ':&nbsp; ', $smcFunc['ucwords']($smcFunc['strtolower']($package['type'])), '</li>';
937
938
					// Show the version number?
939
					if ($package['version'] != '')
940
						echo '
941
							<li class="package_section">', $txt['mod_version'], ':&nbsp; ', $package['version'], '</li>';
942
943
					// How 'bout the author?
944
					if (!empty($package['author']) && $package['author']['name'] != '' && isset($package['author']['link']))
945
						echo '
946
							<li class="package_section">', $txt['mod_author'], ':&nbsp; ', $package['author']['link'], '</li>';
947
948
					// The homepage....
949
					if ($package['author']['website']['link'] != '')
950
						echo '
951
							<li class="package_section">', $txt['author_website'], ':&nbsp; ', $package['author']['website']['link'], '</li>';
952
953
					// Description: bleh bleh!
954
					// Location of file: http://someplace/.
955
					echo '
956
							<li class="package_section">', $txt['file_location'], ':&nbsp; <a href="', $package['href'], '">', $package['href'], '</a></li>
957
							<li class="package_section"><div class="information">', $txt['package_description'], ':&nbsp; ', $package['description'], '</div></li>
958
						</ul>';
959
				}
960
961
				echo '
962
					</li>';
963
			}
964
			echo '
965
				</', $context['list_type'], '>
966
				</li>';
967
		}
968
		echo '
969
			</ul>';
970
971
	}
972
973
	echo '
974
		</div><!-- .windowbg2 -->
975
	</div><!-- #admincenter -->';
976
977
	// Now go through and turn off all the sections.
978
	if (!empty($context['package_list']))
979
	{
980
		$section_count = count($context['package_list']);
981
982
		echo '
983
	<script>';
984
985
		foreach ($context['package_list'] as $section => $ps)
986
		{
987
			echo '
988
		var oPackageServerToggle_', $section, ' = new smc_Toggle({
989
			bToggleEnabled: true,
990
			bCurrentlyCollapsed: ', count($ps['items']) == 1 || $section_count == 1 ? 'false' : 'true', ',
991
			aSwappableContainers: [
992
				\'package_section_', $section, '\'
993
			],
994
			aSwapImages: [
995
				{
996
					sId: \'ps_img_', $section, '\',
997
					altExpanded: \'*\',
998
					altCollapsed: \'*\'
999
				}
1000
			]
1001
		});';
1002
1003
			foreach ($ps['items'] as $id => $package)
1004
			{
1005
				if (!$package['is_text'] && !$package['is_line'] && !$package['is_remote'])
1006
					echo '
1007
		var oPackageToggle_', $section, '_pkg_', $id, ' = new smc_Toggle({
1008
			bToggleEnabled: true,
1009
			bCurrentlyCollapsed: true,
1010
			aSwappableContainers: [
1011
				\'package_section_', $section, '_pkg_', $id, '\'
1012
			],
1013
			aSwapImages: [
1014
				{
1015
					sId: \'ps_img_', $section, '_pkg_', $id, '\',
1016
					altExpanded: \'*\',
1017
					altCollapsed: \'*\'
1018
				}
1019
			]
1020
		});';
1021
			}
1022
		}
1023
1024
		echo '
1025
	</script>';
1026
	}
1027
}
1028
1029
/**
1030
 * Confirmation page showing a package was uploaded/downloaded successfully.
1031
 */
1032
function template_downloaded()
1033
{
1034
	global $context, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1035
1036
	echo '
1037
	<div id="admincenter">
1038
		<div class="cat_bar">
1039
			<h3 class="catbg">', $context['page_title'], '</h3>
1040
		</div>
1041
		<div class="windowbg2 noup">
1042
			<p>
1043
				', (empty($context['package_server']) ? $txt['package_uploaded_successfully'] : $txt['package_downloaded_successfully']), '
1044
			</p>
1045
			<ul>
1046
				<li>
1047
					<span class="floatleft"><strong>', $context['package']['name'], '</strong></span>
1048
					<span class="package_server floatright">', $context['package']['list_files']['link'], '</span>
1049
					<span class="package_server floatright">', $context['package']['install']['link'], '</span>
1050
				</li>
1051
			</ul>
1052
			<br><br>
1053
			<p><a href="', $scripturl, '?action=admin;area=packages;get', (isset($context['package_server']) ? ';sa=browse;server=' . $context['package_server'] : ''), '">[ ', $txt['back'], ' ]</a></p>
1054
		</div>
1055
	</div><!-- #admincenter -->';
1056
}
1057
1058
/**
1059
 * Installation options - FTP info and backup settings
1060
 */
1061
function template_install_options()
1062
{
1063
	global $context, $txt, $scripturl;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1064
1065
	if (!empty($context['saved_successful']))
1066
		echo '
1067
	<div class="infobox">', $txt['settings_saved'], '</div>';
1068
1069
	echo '
1070
	<div id="admincenter">
1071
		<div class="cat_bar">
1072
			<h3 class="catbg">', $txt['package_install_options'], '</h3>
1073
		</div>
1074
		<div class="information noup">
1075
			', $txt['package_install_options_ftp_why'], '
1076
		</div>
1077
		<div class="windowbg2 noup">
1078
			<form action="', $scripturl, '?action=admin;area=packages;sa=options" method="post" accept-charset="', $context['character_set'], '">
1079
				<dl class="settings">
1080
					<dt>
1081
						<label for="pack_server"><strong>', $txt['package_install_options_ftp_server'], ':</strong></label>
1082
					</dt>
1083
					<dd>
1084
						<input type="text" name="pack_server" id="pack_server" value="', $context['package_ftp_server'], '" size="30">
1085
					</dd>
1086
					<dt>
1087
						<label for="pack_port"><strong>', $txt['package_install_options_ftp_port'], ':</strong></label>
1088
					</dt>
1089
					<dd>
1090
						<input type="text" name="pack_port" id="pack_port" size="3" value="', $context['package_ftp_port'], '">
1091
					</dd>
1092
					<dt>
1093
						<label for="pack_user"><strong>', $txt['package_install_options_ftp_user'], ':</strong></label>
1094
					</dt>
1095
					<dd>
1096
						<input type="text" name="pack_user" id="pack_user" value="', $context['package_ftp_username'], '" size="30">
1097
					</dd>
1098
					<dt>
1099
						<label for="package_make_backups">', $txt['package_install_options_make_backups'], '</label>
1100
					</dt>
1101
					<dd>
1102
						<input type="checkbox" name="package_make_backups" id="package_make_backups" value="1"', $context['package_make_backups'] ? ' checked' : '', '>
1103
					</dd>
1104
					<dt>
1105
						<label for="package_make_full_backups">', $txt['package_install_options_make_full_backups'], '</label>
1106
					</dt>
1107
					<dd>
1108
						<input type="checkbox" name="package_make_full_backups" id="package_make_full_backups" value="1"', $context['package_make_full_backups'] ? ' checked' : '', '>
1109
					</dd>
1110
				</dl>
1111
1112
				<input type="submit" name="save" value="', $txt['save'], '" class="button">
1113
				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
1114
			</form>
1115
		</div><!-- .windowbg2 -->
1116
	</div><!-- #admincenter -->';
1117
}
1118
1119
/**
1120
 * CHMOD control form
1121
 *
1122
 * @return bool False if nothing to do.
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1123
 */
1124
function template_control_chmod()
1125
{
1126
	global $context, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1127
1128
	// Nothing to do? Brilliant!
1129
	if (empty($context['package_ftp']))
1130
		return false;
1131
1132
	if (empty($context['package_ftp']['form_elements_only']))
1133
	{
1134
		echo '
1135
				', sprintf($txt['package_ftp_why'], 'document.getElementById(\'need_writable_list\').style.display = \'\'; return false;'), '<br>
1136
				<div id="need_writable_list" class="smalltext">
1137
					', $txt['package_ftp_why_file_list'], '
1138
					<ul style="display: inline;">';
1139
1140
		if (!empty($context['notwritable_files']))
1141
			foreach ($context['notwritable_files'] as $file)
1142
				echo '
1143
						<li>', $file, '</li>';
1144
1145
		echo '
1146
					</ul>';
1147
1148
		if (!$context['server']['is_windows'])
1149
			echo '
1150
					<hr>
1151
					', $txt['package_chmod_linux'], '<br>
1152
					<tt># chmod a+w ', implode(' ', $context['notwritable_files']), '</tt>';
1153
1154
		echo '
1155
				</div><!-- #need_writable_list -->';
1156
	}
1157
1158
	echo '
1159
				<div class="bordercolor" id="ftp_error_div" style="', (!empty($context['package_ftp']['error']) ? '' : 'display:none;'), 'padding: 1px; margin: 1ex;">
1160
					<div class="windowbg2" id="ftp_error_innerdiv" style="padding: 1ex;">
1161
						<tt id="ftp_error_message">', !empty($context['package_ftp']['error']) ? $context['package_ftp']['error'] : '', '</tt>
1162
					</div>
1163
				</div>';
1164
1165
	if (!empty($context['package_ftp']['destination']))
1166
		echo '
1167
				<form action="', $context['package_ftp']['destination'], '" method="post" accept-charset="', $context['character_set'], '" style="margin: 0;">';
1168
1169
	echo '
1170
					<fieldset>
1171
					<dl class="settings">
1172
						<dt>
1173
							<label for="ftp_server">', $txt['package_ftp_server'], ':</label>
1174
						</dt>
1175
						<dd>
1176
							<input type="text" size="30" name="ftp_server" id="ftp_server" value="', $context['package_ftp']['server'], '">
1177
							<label for="ftp_port">', $txt['package_ftp_port'], ':&nbsp;</label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', $context['package_ftp']['port'], '">
1178
						</dd>
1179
						<dt>
1180
							<label for="ftp_username">', $txt['package_ftp_username'], ':</label>
1181
						</dt>
1182
						<dd>
1183
							<input type="text" size="50" name="ftp_username" id="ftp_username" value="', $context['package_ftp']['username'], '" style="width: 98%;">
1184
						</dd>
1185
						<dt>
1186
							<label for="ftp_password">', $txt['package_ftp_password'], ':</label>
1187
						</dt>
1188
						<dd>
1189
							<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 98%;">
1190
						</dd>
1191
						<dt>
1192
							<label for="ftp_path">', $txt['package_ftp_path'], ':</label>
1193
						</dt>
1194
						<dd>
1195
							<input type="text" size="50" name="ftp_path" id="ftp_path" value="', $context['package_ftp']['path'], '" style="width: 98%;">
1196
						</dd>
1197
					</dl>
1198
					</fieldset>';
1199
1200
	if (empty($context['package_ftp']['form_elements_only']))
1201
		echo '
1202
					<div class="righttext" style="margin: 1ex;">
1203
						<span id="test_ftp_placeholder_full"></span>
1204
						<input type="submit" value="', $txt['package_proceed'], '" class="button">
1205
					</div>';
1206
1207
	if (!empty($context['package_ftp']['destination']))
1208
		echo '
1209
					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '">
1210
				</form>';
1211
1212
	// Hide the details of the list.
1213
	if (empty($context['package_ftp']['form_elements_only']))
1214
		echo '
1215
				<script>
1216
					document.getElementById(\'need_writable_list\').style.display = \'none\';
1217
				</script>';
1218
1219
	// Quick generate the test button.
1220
	echo '
1221
				<script>
1222
					// Generate a "test ftp" button.
1223
					var generatedButton = false;
1224
					function generateFTPTest()
1225
					{
1226
						// Don\'t ever call this twice!
1227
						if (generatedButton)
1228
							return false;
1229
						generatedButton = true;
1230
1231
						// No XML?
1232
						if (!window.XMLHttpRequest || (!document.getElementById("test_ftp_placeholder") && !document.getElementById("test_ftp_placeholder_full")))
1233
							return false;
1234
1235
						var ftpTest = document.createElement("input");
1236
						ftpTest.type = "button";
1237
						ftpTest.onclick = testFTP;
1238
1239
						if (document.getElementById("test_ftp_placeholder"))
1240
						{
1241
							ftpTest.value = "', $txt['package_ftp_test'], '";
1242
							document.getElementById("test_ftp_placeholder").appendChild(ftpTest);
1243
						}
1244
						else
1245
						{
1246
							ftpTest.value = "', $txt['package_ftp_test_connection'], '";
1247
							document.getElementById("test_ftp_placeholder_full").appendChild(ftpTest);
1248
						}
1249
					}
1250
					function testFTPResults(oXMLDoc)
1251
					{
1252
						ajax_indicator(false);
1253
1254
						// This assumes it went wrong!
1255
						var wasSuccess = false;
1256
						var message = "', addcslashes($txt['package_ftp_test_failed'], "'"), '";
1257
1258
						var results = oXMLDoc.getElementsByTagName(\'results\')[0].getElementsByTagName(\'result\');
1259
						if (results.length > 0)
1260
						{
1261
							if (results[0].getAttribute(\'success\') == 1)
1262
								wasSuccess = true;
1263
							message = results[0].firstChild.nodeValue;
1264
						}
1265
1266
						document.getElementById("ftp_error_div").style.display = "";
1267
						document.getElementById("ftp_error_div").style.backgroundColor = wasSuccess ? "green" : "red";
1268
						document.getElementById("ftp_error_innerdiv").style.backgroundColor = wasSuccess ? "#DBFDC7" : "#FDBDBD";
1269
1270
						setInnerHTML(document.getElementById("ftp_error_message"), message);
1271
					}
1272
					generateFTPTest();
1273
				</script>';
1274
1275
	// Make sure the button gets generated last.
1276
	$context['insert_after_template'] .= '
1277
				<script>
1278
					generateFTPTest();
1279
				</script>';
1280
}
1281
1282
/**
1283
 * Wrapper for the above template function showing that FTP is required
1284
 */
1285
function template_ftp_required()
1286
{
1287
	global $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1288
1289
	echo '
1290
		<fieldset>
1291
			<legend>
1292
				', $txt['package_ftp_necessary'], '
1293
			</legend>
1294
			<div class="ftp_details">
1295
				', template_control_chmod(), '
1296
			</div>
1297
		</fieldset>';
1298
}
1299
1300
/**
1301
 * View operation details.
1302
 */
1303
function template_view_operations()
1304
{
1305
	global $context, $txt, $settings, $modSettings;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1306
1307
	echo '<!DOCTYPE html>
1308
<html', $context['right_to_left'] ? ' dir="rtl"' : '', '>
1309
	<head>
1310
		<meta charset="', $context['character_set'], '">
1311
		<title>', $txt['operation_title'], '</title>
1312
		<link rel="stylesheet" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css', $modSettings['browser_cache'], '">
1313
		<link rel="stylesheet" href="', $settings['theme_url'], '/css/admin.css', $modSettings['browser_cache'], '">
1314
		<script src="', $settings['default_theme_url'], '/scripts/script.js', $modSettings['browser_cache'], '"></script>
1315
		<script src="', $settings['default_theme_url'], '/scripts/theme.js', $modSettings['browser_cache'], '"></script>
1316
	</head>
1317
	<body>
1318
		<div class="padding windowbg">
1319
			<div class="padding">
1320
				', $context['operations']['search'], '
1321
			</div>
1322
			<div class="padding">
1323
				', $context['operations']['replace'], '
1324
			</div>
1325
		</div>
1326
	</body>
1327
</html>';
1328
}
1329
1330
/**
1331
 * The file permissions page.
1332
 */
1333
function template_file_permissions()
1334
{
1335
	global $txt, $scripturl, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1336
1337
	// This will handle expanding the selection.
1338
	echo '
1339
	<script>
1340
		var oRadioValues = {
1341
			0: "read",
1342
			1: "writable",
1343
			2: "execute",
1344
			3: "custom",
1345
			4: "no_change"
1346
		}
1347
		function dynamicAddMore()
1348
		{
1349
			ajax_indicator(true);
1350
1351
			getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + \'action=admin;area=packages;fileoffset=\' + (parseInt(this.offset) + ', $context['file_limit'], ') + \';onlyfind=\' + escape(this.path) + \';sa=perms;xml;', $context['session_var'], '=', $context['session_id'], '\', onNewFolderReceived);
1352
		}
1353
1354
		// Getting something back?
1355
		function onNewFolderReceived(oXMLDoc)
1356
		{
1357
			ajax_indicator(false);
1358
1359
			var fileItems = oXMLDoc.getElementsByTagName(\'folders\')[0].getElementsByTagName(\'folder\');
1360
1361
			// No folders, no longer worth going further.
1362
			if (fileItems.length < 1)
1363
			{
1364
				if (oXMLDoc.getElementsByTagName(\'roots\')[0].getElementsByTagName(\'root\')[0])
1365
				{
1366
					var rootName = oXMLDoc.getElementsByTagName(\'roots\')[0].getElementsByTagName(\'root\')[0].firstChild.nodeValue;
1367
					var itemLink = document.getElementById(\'link_\' + rootName);
1368
1369
					// Move the children up.
1370
					for (i = 0; i <= itemLink.childNodes.length; i++)
1371
						itemLink.parentNode.insertBefore(itemLink.childNodes[0], itemLink);
1372
1373
					// And remove the link.
1374
					itemLink.parentNode.removeChild(itemLink);
1375
				}
1376
				return false;
1377
			}
1378
			var tableHandle = false;
1379
			var isMore = false;
1380
			var ident = "";
1381
			var my_ident = "";
1382
			var curLevel = 0;
1383
1384
			for (var i = 0; i < fileItems.length; i++)
1385
			{
1386
				if (fileItems[i].getAttribute(\'more\') == 1)
1387
				{
1388
					isMore = true;
1389
					var curOffset = fileItems[i].getAttribute(\'offset\');
1390
				}
1391
1392
				if (fileItems[i].getAttribute(\'more\') != 1 && document.getElementById("insert_div_loc_" + fileItems[i].getAttribute(\'ident\')))
1393
				{
1394
					ident = fileItems[i].getAttribute(\'ident\');
1395
					my_ident = fileItems[i].getAttribute(\'my_ident\');
1396
					curLevel = fileItems[i].getAttribute(\'level\') * 5;
1397
					curPath = fileItems[i].getAttribute(\'path\');
1398
1399
					// Get where we\'re putting it next to.
1400
					tableHandle = document.getElementById("insert_div_loc_" + fileItems[i].getAttribute(\'ident\'));
1401
1402
					var curRow = document.createElement("tr");
1403
					curRow.className = "windowbg";
1404
					curRow.id = "content_" + my_ident;
1405
					curRow.style.display = "";
1406
					var curCol = document.createElement("td");
1407
					curCol.className = "smalltext";
1408
					curCol.width = "40%";
1409
1410
					// This is the name.
1411
					var fileName = document.createTextNode(fileItems[i].firstChild.nodeValue);
1412
1413
					// Start by wacking in the spaces.
1414
					setInnerHTML(curCol, repeatString("&nbsp;", curLevel));
1415
1416
					// Create the actual text.
1417
					if (fileItems[i].getAttribute(\'folder\') == 1)
1418
					{
1419
						var linkData = document.createElement("a");
1420
						linkData.name = "fol_" + my_ident;
1421
						linkData.id = "link_" + my_ident;
1422
						linkData.href = \'#\';
1423
						linkData.path = curPath + "/" + fileItems[i].firstChild.nodeValue;
1424
						linkData.ident = my_ident;
1425
						linkData.onclick = dynamicExpandFolder;
1426
1427
						var folderImage = document.createElement("span");
1428
						folderImage.className = "generic_icons folder";
1429
						linkData.appendChild(folderImage);
1430
1431
						linkData.appendChild(fileName);
1432
						curCol.appendChild(linkData);
1433
					}
1434
					else
1435
						curCol.appendChild(fileName);
1436
1437
					curRow.appendChild(curCol);
1438
1439
					// Right, the permissions.
1440
					curCol = document.createElement("td");
1441
					curCol.className = "smalltext";
1442
1443
					var writeSpan = document.createElement("span");
1444
					writeSpan.className = fileItems[i].getAttribute(\'writable\') ? "green" : "red";
1445
					setInnerHTML(writeSpan, fileItems[i].getAttribute(\'writable\') ? \'', $txt['package_file_perms_writable'], '\' : \'', $txt['package_file_perms_not_writable'], '\');
1446
					curCol.appendChild(writeSpan);
1447
1448
					if (fileItems[i].getAttribute(\'permissions\'))
1449
					{
1450
						var permData = document.createTextNode("\u00a0(', $txt['package_file_perms_chmod'], ': " + fileItems[i].getAttribute(\'permissions\') + ")");
1451
						curCol.appendChild(permData);
1452
					}
1453
1454
					curRow.appendChild(curCol);
1455
1456
					// Now add the five radio buttons.
1457
					for (j = 0; j < 5; j++)
1458
					{
1459
						curCol = document.createElement("td");
1460
						curCol.className = "centertext perm_" + oRadioValues[j];
1461
						curCol.align = "center";
1462
1463
						var curInput = createNamedElement("input", "permStatus[" + curPath + "/" + fileItems[i].firstChild.nodeValue + "]", j == 4 ? "checked" : "");
1464
						curInput.type = "radio";
1465
						curInput.checked = "checked";
1466
						curInput.value = oRadioValues[j];
1467
1468
						curCol.appendChild(curInput);
1469
						curRow.appendChild(curCol);
1470
					}
1471
1472
					// Put the row in.
1473
					tableHandle.parentNode.insertBefore(curRow, tableHandle);
1474
1475
					// Put in a new dummy section?
1476
					if (fileItems[i].getAttribute(\'folder\') == 1)
1477
					{
1478
						var newRow = document.createElement("tr");
1479
						newRow.id = "insert_div_loc_" + my_ident;
1480
						newRow.style.display = "none";
1481
						tableHandle.parentNode.insertBefore(newRow, tableHandle);
1482
						var newCol = document.createElement("td");
1483
						newCol.colspan = 2;
1484
						newRow.appendChild(newCol);
1485
					}
1486
				}
1487
			}
1488
1489
			// Is there some more to remove?
1490
			if (document.getElementById("content_" + ident + "_more"))
1491
			{
1492
				document.getElementById("content_" + ident + "_more").parentNode.removeChild(document.getElementById("content_" + ident + "_more"));
1493
			}
1494
1495
			// Add more?
1496
			if (isMore && tableHandle)
1497
			{
1498
				// Create the actual link.
1499
				var linkData = document.createElement("a");
1500
				linkData.href = \'#fol_\' + my_ident;
1501
				linkData.path = curPath;
1502
				linkData.offset = curOffset;
1503
				linkData.onclick = dynamicAddMore;
1504
1505
				linkData.appendChild(document.createTextNode(\'', $txt['package_file_perms_more_files'], '\'));
1506
1507
				curRow = document.createElement("tr");
1508
				curRow.className = "windowbg";
1509
				curRow.id = "content_" + ident + "_more";
1510
				tableHandle.parentNode.insertBefore(curRow, tableHandle);
1511
				curCol = document.createElement("td");
1512
				curCol.className = "smalltext";
1513
				curCol.width = "40%";
1514
1515
				setInnerHTML(curCol, repeatString("&nbsp;", curLevel));
1516
				curCol.appendChild(document.createTextNode(\'\\u00ab \'));
1517
				curCol.appendChild(linkData);
1518
				curCol.appendChild(document.createTextNode(\' \\u00bb\'));
1519
1520
				curRow.appendChild(curCol);
1521
				curCol = document.createElement("td");
1522
				curCol.className = "smalltext";
1523
				curRow.appendChild(curCol);
1524
			}
1525
1526
			// Keep track of it.
1527
			var curInput = createNamedElement("input", "back_look[]");
1528
			curInput.type = "hidden";
1529
			curInput.value = curPath;
1530
1531
			curCol.appendChild(curInput);
1532
		}
1533
	</script>';
1534
1535
	echo '
1536
	<div class="noticebox">
1537
		<div>
1538
			<strong>', $txt['package_file_perms_warning'], ':</strong>
1539
			<div class="smalltext">
1540
				<ol style="margin-top: 2px; margin-bottom: 2px">
1541
					', $txt['package_file_perms_warning_desc'], '
1542
				</ol>
1543
			</div>
1544
		</div>
1545
	</div>
1546
1547
	<form action="', $scripturl, '?action=admin;area=packages;sa=perms;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
1548
		<div class="cat_bar">
1549
			<h3 class="catbg">
1550
				<span class="floatleft">', $txt['package_file_perms'], '</span><span class="fperm floatright">', $txt['package_file_perms_new_status'], '</span>
1551
			</h3>
1552
		</div>
1553
		<table class="table_grid">
1554
			<thead>
1555
				<tr class="title_bar">
1556
					<th class="lefttext" width="30%">&nbsp;', $txt['package_file_perms_name'], '&nbsp;</th>
1557
					<th width="30%" class="lefttext">', $txt['package_file_perms_status'], '</th>
1558
					<th width="8%"><span class="filepermissions">', $txt['package_file_perms_status_read'], '</span></th>
1559
					<th width="8%"><span class="filepermissions">', $txt['package_file_perms_status_write'], '</span></th>
1560
					<th width="8%"><span class="filepermissions">', $txt['package_file_perms_status_execute'], '</span></th>
1561
					<th width="8%"><span class="filepermissions">', $txt['package_file_perms_status_custom'], '</span></th>
1562
					<th width="8%"><span class="filepermissions">', $txt['package_file_perms_status_no_change'], '</span></th>
1563
				</tr>
1564
			</thead>
1565
			<tbody>';
1566
1567
	foreach ($context['file_tree'] as $name => $dir)
1568
	{
1569
		echo '
1570
				<tr class="windowbg">
1571
					<td width="30%"><strong>';
1572
1573 View Code Duplication
				if (!empty($dir['type']) && ($dir['type'] == 'dir' || $dir['type'] == 'dir_recursive'))
1574
					echo '
1575
						<span class="generic_icons folder"></span>';
1576
1577
				echo '
1578
						', $name, '
1579
					</strong></td>
1580
					<td width="30%">
1581
						<span style="color: ', ($dir['perms']['chmod'] ? 'green' : 'red'), '">', ($dir['perms']['chmod'] ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']), '</span>
1582
						', ($dir['perms']['perms'] ? '&nbsp;(' . $txt['package_file_perms_chmod'] . ': ' . substr(sprintf('%o', $dir['perms']['perms']), -4) . ')' : ''), '
1583
					</td>
1584
					<td class="centertext perm_read"><input type="radio" name="permStatus[', $name, ']" value="read" class="centertext"></td>
1585
					<td class="centertext perm_writable"><input type="radio" name="permStatus[', $name, ']" value="writable" class="centertext"></td>
1586
					<td class="centertext perm_execute"><input type="radio" name="permStatus[', $name, ']" value="execute" class="centertext"></td>
1587
					<td class="centertext perm_custom"><input type="radio" name="permStatus[', $name, ']" value="custom" class="centertext"></td>
1588
					<td class="centertext perm_no_change"><input type="radio" name="permStatus[', $name, ']" value="no_change" checked class="centertext"></td>
1589
				</tr>';
1590
1591
		if (!empty($dir['contents']))
1592
			template_permission_show_contents($name, $dir['contents'], 1);
1593
	}
1594
1595
	echo '
1596
			</tbody>
1597
		</table>
1598
		<br>
1599
		<div class="cat_bar">
1600
			<h3 class="catbg">', $txt['package_file_perms_change'], '</h3>
1601
		</div>
1602
		<div class="windowbg2 noup">
1603
			<fieldset>
1604
				<dl>
1605
					<dt>
1606
						<input type="radio" name="method" value="individual" checked id="method_individual">
1607
						<label for="method_individual"><strong>', $txt['package_file_perms_apply'], '</strong></label>
1608
					</dt>
1609
					<dd>
1610
						<em class="smalltext">', $txt['package_file_perms_custom'], ': <input type="text" name="custom_value" value="0755" maxlength="4" size="5">&nbsp;<a href="', $scripturl, '?action=helpadmin;help=chmod_flags" onclick="return reqOverlayDiv(this.href);" class="help">(?)</a></em>
1611
					</dd>
1612
					<dt>
1613
						<input type="radio" name="method" value="predefined" id="method_predefined">
1614
						<label for="method_predefined"><strong>', $txt['package_file_perms_predefined'], ':</strong></label>
1615
						<select name="predefined" onchange="document.getElementById(\'method_predefined\').checked = \'checked\';">
1616
							<option value="restricted" selected>', $txt['package_file_perms_pre_restricted'], '</option>
1617
							<option value="standard">', $txt['package_file_perms_pre_standard'], '</option>
1618
							<option value="free">', $txt['package_file_perms_pre_free'], '</option>
1619
						</select>
1620
					</dt>
1621
					<dd>
1622
						<em class="smalltext">', $txt['package_file_perms_predefined_note'], '</em>
1623
					</dd>
1624
				</dl>
1625
			</fieldset>';
1626
1627
	// Likely to need FTP?
1628
	if (empty($context['ftp_connected']))
1629
		echo '
1630
			<p>
1631
				', $txt['package_file_perms_ftp_details'], ':
1632
			</p>
1633
			', template_control_chmod(), '
1634
			<div class="noticebox">', $txt['package_file_perms_ftp_retain'], '</div>';
1635
1636
	echo '
1637
			<span id="test_ftp_placeholder_full"></span>
1638
			<input type="hidden" name="action_changes" value="1">
1639
			<input type="submit" value="', $txt['package_file_perms_go'], '" name="go" class="button">
1640
		</div><!-- .windowbg2 -->';
1641
1642
	// Any looks fors we've already done?
1643
	foreach ($context['look_for'] as $path)
1644
		echo '
1645
			<input type="hidden" name="back_look[]" value="', $path, '">';
1646
1647
	echo '
1648
	</form>
1649
	<br>';
1650
}
1651
1652
/**
1653
 * Shows permissions for items within a directory (called from template_file_permissions)
1654
 *
1655
 * @param string $ident A unique ID - typically the directory name
1656
 * @param array $contents An array of items within the directory
1657
 * @param int $level How far to go inside the directory
1658
 * @param bool $has_more Whether there are more files to display besides what's in $contents
1659
 */
1660
function template_permission_show_contents($ident, $contents, $level, $has_more = false)
1661
{
1662
	global $txt, $scripturl, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1663
	$js_ident = preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident);
1664
1665
	// Have we actually done something?
1666
	$drawn_div = false;
1667
1668
	foreach ($contents as $name => $dir)
1669
	{
1670
		if (isset($dir['perms']))
1671
		{
1672
			if (!$drawn_div)
1673
			{
1674
				$drawn_div = true;
1675
				echo '
1676
			</tbody>
1677
			<tbody class="table_grid" id="', $js_ident, '">';
1678
			}
1679
1680
			$cur_ident = preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident . '/' . $name);
1681
1682
			echo '
1683
				<tr class="windowbg" id="content_', $cur_ident, '">
1684
					<td class="smalltext" width="30%">' . str_repeat('&nbsp;', $level * 5), '
1685
					', (!empty($dir['type']) && $dir['type'] == 'dir_recursive') || !empty($dir['list_contents']) ? '<a id="link_' . $cur_ident . '" href="' . $scripturl . '?action=admin;area=packages;sa=perms;find=' . base64_encode($ident . '/' . $name) . ';back_look=' . $context['back_look_data'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '#fol_' . $cur_ident . '" onclick="return expandFolder(\'' . $cur_ident . '\', \'' . addcslashes($ident . '/' . $name, "'\\") . '\');">' : '';
1686
1687 View Code Duplication
			if (!empty($dir['type']) && ($dir['type'] == 'dir' || $dir['type'] == 'dir_recursive'))
1688
				echo '
1689
						<span class="generic_icons folder"></span>';
1690
1691
			echo '
1692
						', $name, '
1693
						', (!empty($dir['type']) && $dir['type'] == 'dir_recursive') || !empty($dir['list_contents']) ? '</a>' : '', '
1694
					</td>
1695
					<td class="smalltext">
1696
						<span class="', ($dir['perms']['chmod'] ? 'success' : 'error'), '">', ($dir['perms']['chmod'] ? $txt['package_file_perms_writable'] : $txt['package_file_perms_not_writable']), '</span>
1697
						', ($dir['perms']['perms'] ? '&nbsp;(' . $txt['package_file_perms_chmod'] . ': ' . substr(sprintf('%o', $dir['perms']['perms']), -4) . ')' : ''), '
1698
					</td>
1699
					<td class="centertext perm_read"><input type="radio" name="permStatus[', $ident . '/' . $name, ']" value="read"></td>
1700
					<td class="centertext perm_writable"><input type="radio" name="permStatus[', $ident . '/' . $name, ']" value="writable"></td>
1701
					<td class="centertext perm_execute"><input type="radio" name="permStatus[', $ident . '/' . $name, ']" value="execute"></td>
1702
					<td class="centertext perm_custom"><input type="radio" name="permStatus[', $ident . '/' . $name, ']" value="custom"></td>
1703
					<td class="centertext perm_no_change"><input type="radio" name="permStatus[', $ident . '/' . $name, ']" value="no_change" checked></td>
1704
				</tr>
1705
				<tr id="insert_div_loc_' . $cur_ident . '" style="display: none;"><td></td></tr>';
1706
1707
			if (!empty($dir['contents']))
1708
			{
1709
				template_permission_show_contents($ident . '/' . $name, $dir['contents'], $level + 1, !empty($dir['more_files']));
1710
			}
1711
		}
1712
	}
1713
1714
	// We have more files to show?
1715
	if ($has_more)
1716
		echo '
1717
				<tr class="windowbg" id="content_', $js_ident, '_more">
1718
					<td class="smalltext" width="40%">' . str_repeat('&nbsp;', $level * 5), '
1719
						&#171; <a href="' . $scripturl . '?action=admin;area=packages;sa=perms;find=' . base64_encode($ident) . ';fileoffset=', ($context['file_offset'] + $context['file_limit']), ';' . $context['session_var'] . '=' . $context['session_id'] . '#fol_' . preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $ident) . '">', $txt['package_file_perms_more_files'], '</a> &#187;
1720
					</td>
1721
					<td colspan="6"></td>
1722
				</tr>';
1723
1724
	if ($drawn_div)
1725
	{
1726
		// Hide anything too far down the tree.
1727
		$isFound = false;
1728
		foreach ($context['look_for'] as $tree)
1729
		{
1730
			if (substr($tree, 0, strlen($ident)) == $ident)
1731
				$isFound = true;
1732
		}
1733
1734
		if ($level > 1 && !$isFound)
1735
			echo '
1736
		<script>
1737
			expandFolder(\'', $js_ident, '\', \'\');
1738
		</script>';
1739
	}
1740
}
1741
1742
/**
1743
 * A progress page showing what permissions changes are being applied
1744
 */
1745
function template_action_permissions()
1746
{
1747
	global $txt, $scripturl, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1748
1749
	$countDown = 3;
1750
1751
	echo '
1752
	<div id="admincenter">
1753
		<form action="', $scripturl, '?action=admin;area=packages;sa=perms;', $context['session_var'], '=', $context['session_id'], '" id="perm_submit" method="post" accept-charset="', $context['character_set'], '">
1754
			<div class="cat_bar">
1755
				<h3 class="catbg">', $txt['package_file_perms_applying'], '</h3>
1756
			</div>';
1757
1758
	if (!empty($context['skip_ftp']))
1759
		echo '
1760
			<div class="errorbox">
1761
				', $txt['package_file_perms_skipping_ftp'], '
1762
			</div>';
1763
1764
	// How many have we done?
1765
	$remaining_items = count($context['method'] == 'individual' ? $context['to_process'] : $context['directory_list']);
1766
	$progress_message = sprintf($context['method'] == 'individual' ? $txt['package_file_perms_items_done'] : $txt['package_file_perms_dirs_done'], $context['total_items'] - $remaining_items, $context['total_items']);
1767
	$progress_percent = round(($context['total_items'] - $remaining_items) / $context['total_items'] * 100, 1);
1768
1769
	echo '
1770
			<div class="windowbg">
1771
				<div>
1772
					<strong>', $progress_message, '</strong><br>
1773
					<br>
1774
					<div class="progress_bar">
1775
						<div class="full_bar">', $progress_percent, '%</div>
1776
						<div class="blue_percent" style="width: ', $progress_percent, '%;">&nbsp;</div>
1777
					</div>
1778
				</div>';
1779
1780
	// Second progress bar for a specific directory?
1781
	if ($context['method'] != 'individual' && !empty($context['total_files']))
1782
	{
1783
		$file_progress_message = sprintf($txt['package_file_perms_files_done'], $context['file_offset'], $context['total_files']);
1784
		$file_progress_percent = round($context['file_offset'] / $context['total_files'] * 100, 1);
1785
1786
		echo '
1787
				<br>
1788
				<div>
1789
					<strong>', $file_progress_message, '</strong><br><br>
1790
					<div class="progress_bar">
1791
						<div class="full_bar">', $file_progress_percent, '%</div>
1792
						<div class="green_percent" style="width: ', $file_progress_percent, '%;">&nbsp;</div>
1793
					</div>
1794
				</div>';
1795
	}
1796
1797
	echo '
1798
				<br>';
1799
1800
	// Put out the right hidden data.
1801
	if ($context['method'] == 'individual')
1802
		echo '
1803
				<input type="hidden" name="custom_value" value="', $context['custom_value'], '">
1804
				<input type="hidden" name="totalItems" value="', $context['total_items'], '">
1805
				<input type="hidden" name="toProcess" value="', $context['to_process_encode'], '">';
1806
	else
1807
		echo '
1808
				<input type="hidden" name="predefined" value="', $context['predefined_type'], '">
1809
				<input type="hidden" name="fileOffset" value="', $context['file_offset'], '">
1810
				<input type="hidden" name="totalItems" value="', $context['total_items'], '">
1811
				<input type="hidden" name="dirList" value="', $context['directory_list_encode'], '">
1812
				<input type="hidden" name="specialFiles" value="', $context['special_files_encode'], '">';
1813
1814
	// Are we not using FTP for whatever reason.
1815
	if (!empty($context['skip_ftp']))
1816
		echo '
1817
				<input type="hidden" name="skip_ftp" value="1">';
1818
1819
	// Retain state.
1820
	foreach ($context['back_look_data'] as $path)
1821
		echo '
1822
				<input type="hidden" name="back_look[]" value="', $path, '">';
1823
1824
	echo '
1825
				<input type="hidden" name="method" value="', $context['method'], '">
1826
				<input type="hidden" name="action_changes" value="1">
1827
				<div class="righttext padding">
1828
					<input type="submit" name="go" id="cont" value="', $txt['not_done_continue'], '" class="button">
1829
				</div>
1830
			</div><!-- .windowbg -->
1831
		</form>
1832
	</div><!-- #admincenter -->';
1833
1834
	// Just the countdown stuff
1835
	echo '
1836
	<script>
1837
		var countdown = ', $countDown, ';
1838
		doAutoSubmit();
1839
1840
		function doAutoSubmit()
1841
		{
1842
			if (countdown == 0)
1843
				document.forms.perm_submit.submit();
1844
			else if (countdown == -1)
1845
				return;
1846
1847
			document.getElementById(\'cont\').value = "', $txt['not_done_continue'], ' (" + countdown + ")";
1848
			countdown--;
1849
1850
			setTimeout("doAutoSubmit();", 1000);
1851
		}
1852
	</script>';
1853
1854
}
1855
1856
?>