Completed
Push — 145-remove_bulk ( 0dcbcf )
by Armando
02:12
created

WP2D_Options::admin_options_page()   C

Complexity

Conditions 12
Paths 34

Size

Total Lines 83
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 83
rs 5.034
c 0
b 0
f 0
cc 12
eloc 44
nc 34
nop 0

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
 * Plugin Options.
4
 *
5
 * @package WP_To_Diaspora\Options
6
 * @since 1.3.0
7
 */
8
9
// Exit if accessed directly.
10
defined( 'ABSPATH' ) || exit;
11
12
/**
13
 * Class to manage the settings using the Settings API.
14
 */
15
class WP2D_Options {
16
17
	/**
18
	 * Only instance of this class.
19
	 *
20
	 * @var WP2D_Options
21
	 */
22
	private static $_instance = null;
23
24
	/**
25
	 * All default plugin options.
26
	 *
27
	 * @var array
28
	 */
29
	private static $_default_options = array(
30
		'aspects_list'       => array(),
31
		'services_list'      => array(),
32
		'post_to_diaspora'   => true,
33
		'enabled_post_types' => array( 'post' ),
34
		'fullentrylink'      => true,
35
		'display'            => 'full',
36
		'tags_to_post'       => array( 'global', 'custom', 'post' ),
37
		'global_tags'        => '',
38
		'aspects'            => array( 'public' ),
39
		'services'           => array(),
40
		'version'            => WP2D_VERSION,
41
	);
42
43
	/**
44
	 * Valid values for select fields.
45
	 *
46
	 * @var array
47
	 */
48
	private static $_valid_values = array(
49
		'display'      => array( 'full', 'excerpt' ),
50
		'tags_to_post' => array( 'global', 'custom', 'post' ),
51
	);
52
53
	/**
54
	 * All plugin options.
55
	 *
56
	 * @var array
57
	 */
58
	private static $_options = null;
59
60
	/** Singleton, keep private. */
61
	final private function __clone() { }
62
63
	/** Singleton, keep private. */
64
	final private function __construct() { }
65
66
	/** Singleton, keep private. */
67
	final private function __wakeup() { }
68
69
	/**
70
	 * Create / Get the instance of this class.
71
	 *
72
	 * @return WP2D_Options Instance of this class.
73
	 */
74 View Code Duplication
	public static function instance() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
75
		if ( ! isset( self::$_instance ) ) {
76
			self::$_instance = new self();
77
			self::$_instance->_setup();
78
		}
79
		return self::$_instance;
80
	}
81
82
	/**
83
	 * Set up the options menu.
84
	 */
85
	private function _setup() {
86
87
		// Populate options array.
88
		$this->get_option();
89
90
		// Setup Options page and Contextual Help.
91
		add_action( 'admin_menu', array( $this, 'setup_wpadmin_pages' ) );
92
93
		// Register all settings.
94
		add_action( 'admin_init', array( $this, 'register_settings' ) );
95
	}
96
97
98
	/**
99
	 * Get the currently selected tab.
100
	 *
101
	 * @todo Multi-level if statement to make it look prettier.
102
	 *
103
	 * @param string $default Tab to select if the current selection is invalid.
104
	 * @return string Return the currently selected tab.
105
	 */
106
	private function _current_tab( $default = 'defaults' ) {
107
		$tab = ( isset ( $_GET['tab'] ) ? $_GET['tab'] : $default );
108
109
		// If the pod settings aren't configured yet, open the 'Setup' tab.
110
		if ( ! $this->is_pod_set_up() ) {
111
			$tab = 'setup';
112
		}
113
114
		return $tab;
115
	}
116
117
	/**
118
	 * Output all options tabs and return an array of them all, if requested by $return.
119
	 *
120
	 * @param bool $return Define if the options tabs should be returned.
121
	 * @return array (If requested) An array of the outputted options tabs.
122
	 */
123
	private function _options_page_tabs( $return = false ) {
124
		// The array defining all options sections to be shown as tabs.
125
		$tabs = array();
126
		if ( $this->is_pod_set_up() ) {
127
			$tabs['defaults'] = __( 'Defaults', 'wp-to-diaspora' );
128
		}
129
130
		// Add the 'Setup' tab to the end of the list.
131
		$tabs['setup'] = __( 'Setup', 'wp-to-diaspora' ) . '<span id="pod-connection-status" class="dashicons-before hidden"></span><span class="spinner"></span>';
132
133
		// Container for all options tabs.
134
		$out = '<h2 id="options-tabs" class="nav-tab-wrapper">';
135
		foreach ( $tabs as $tab => $name ) {
136
			// The tab link.
137
			$out .= '<a class="nav-tab' . ( ( $tab === $this->_current_tab() ) ? ' nav-tab-active' : '' ) . '" href="?page=wp_to_diaspora&tab=' . $tab . '">' . $name . '</a>';
138
		}
139
		$out .= '</h2>';
140
141
		// Output the container with all tabs.
142
		echo $out;
143
144
		// Check if the tabs should be returned.
145
		if ( $return ) {
146
			return $tabs;
147
		}
148
	}
149
150
151
	/**
152
	 * Set up admin options page.
153
	 */
154
	public function admin_options_page() {
155
		?>
156
		<div class="wrap">
157
			<h2>WP to diaspora*</h2>
158
159
			<div id="wp2d-message" class="notice hidden" <?php echo ( defined( 'WP2D_DEBUGGING' ) ) ? ' data-debugging' : ''; ?>></div>
160
161
			<?php
162
			// Check the connection status to diaspora.
163
			if ( ! $this->is_pod_set_up() ) {
164
				add_settings_error(
165
					'wp_to_diaspora_settings',
166
					'wp_to_diaspora_connected',
167
					__( 'First of all, set up the connection to your pod below.', 'wp-to-diaspora' ),
168
					'updated'
169
				);
170
			} else {
171
				// Get initial aspects list and connected services.
172
				// DON'T check for empty services list here!!
173
				// It could always be empty, resulting in this code being run every time the page is loaded.
174
				// The aspects will at least have a "Public" entry after the initial fetch.
175
				$aspects_list = $this->get_option( 'aspects_list' );
176
				if ( ( $force = get_transient( 'wp2d_no_js_force_refetch' ) ) || empty( $aspects_list ) ) {
177
178
					// Set up the connection to diaspora*.
179
					$api = WP2D_Helpers::api_quick_connect();
180
					if ( ! $api->has_last_error() ) {
181
						// Get the loaded aspects.
182
						if ( is_array( $aspects = $api->get_aspects() ) ) {
183
							// Save the new list of aspects.
184
							$this->set_option( 'aspects_list', $aspects );
185
						}
186
187
						// Get the loaded services.
188
						if ( is_array( $services = $api->get_services() ) ) {
189
							// Save the new list of services.
190
							$this->set_option( 'services_list', $services );
191
						}
192
193
						$this->save();
194
					}
195
196
					if ( $force ) {
197
						delete_transient( 'wp2d_no_js_force_refetch' );
198
						$message = ( ! $api->has_last_error() ) ? __( 'Connection successful.', 'wp-to-diaspora' ) : $api->get_last_error();
199
						add_settings_error(
200
							'wp_to_diaspora_settings',
201
							'wp_to_diaspora_connected',
202
							$message,
203
							( ! $api->has_last_error() ) ? 'updated' : 'error'
204
						);
205
					}
206
				}
207
			}
208
209
			// Output success or error message.
210
			settings_errors( 'wp_to_diaspora_settings' );
211
			?>
212
213
			<?php $page_tabs = array_keys( $this->_options_page_tabs( true ) ); ?>
214
215
			<form action="options.php" method="post">
216
				<input id="wp2d_no_js" type="hidden" name="wp_to_diaspora_settings[no_js]" value="1">
217
				<?php
218
				// Load the settings fields.
219
				settings_fields( 'wp_to_diaspora_settings' );
220
				do_settings_sections( 'wp_to_diaspora_settings' );
221
222
				// Get the name of the current tab, if set, else take the first one from the list.
223
				$tab = $this->_current_tab( $page_tabs[0] );
224
225
				// Add Save and Reset buttons.
226
				echo '<input id="submit-' . esc_attr( $tab ) . '" name="wp_to_diaspora_settings[submit_' . esc_attr( $tab ) . ']" type="submit" class="button-primary" value="' . esc_attr__( 'Save Changes' ) . '" />&nbsp;';
227
				if ( 'setup' !== $tab ) {
228
					echo '<input id="reset-' . esc_attr( $tab ) . '" name="wp_to_diaspora_settings[reset_' . esc_attr( $tab ) . ']" type="submit" class="button-secondary" value="' . esc_attr__( 'Reset Defaults', 'wp-to-diaspora' ) . '" />';
229
				}
230
				?>
231
232
			</form>
233
		</div>
234
235
		<?php
236
	}
237
238
	/**
239
	 * Return if the settings for the pod setup have been entered.
240
	 *
241
	 * @return boolean If the setup for the pod has been done.
242
	 */
243
	public function is_pod_set_up() {
244
		return ( $this->get_option( 'pod' ) && $this->get_option( 'username' ) && $this->get_option( 'password' ) );
245
	}
246
247
	/**
248
	 * Setup Contextual Help and Options pages.
249
	 */
250
	public function setup_wpadmin_pages() {
251
		// Add options page.
252
		$hook = add_options_page( 'WP to diaspora*', 'WP to diaspora*', 'manage_options', 'wp_to_diaspora', array( $this, 'admin_options_page' ) );
253
254
		// Setup the contextual help menu after the options page has been loaded.
255
		add_action( 'load-' . $hook, array( 'WP2D_Contextual_Help', 'instance' ) );
256
257
		// Setup the contextual help menu tab for post types. Checks are made there!
258
		add_action( 'load-post.php', array( 'WP2D_Contextual_Help', 'instance' ) );
259
		add_action( 'load-post-new.php', array( 'WP2D_Contextual_Help', 'instance' ) );
260
	}
261
262
	/**
263
	 * Initialise the settings sections and fields of the currently selected tab.
264
	 */
265
	public function register_settings() {
266
		// Register the settings with validation callback.
267
		register_setting( 'wp_to_diaspora_settings', 'wp_to_diaspora_settings', array( $this, 'validate_settings' ) );
268
269
		// Load only the sections of the selected tab.
270
		switch ( $this->_current_tab() ) {
271
			case 'defaults' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
272
				// Add a "Defaults" section that contains all posting settings to be used by default.
273
				add_settings_section( 'wp_to_diaspora_defaults_section', __( 'Posting Defaults', 'wp-to-diaspora' ), array( $this, 'defaults_section' ), 'wp_to_diaspora_settings' );
274
				break;
275
			case 'setup' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
276
				// Add a "Setup" section that contains the Pod domain, Username and Password.
277
				add_settings_section( 'wp_to_diaspora_setup_section', __( 'diaspora* Setup', 'wp-to-diaspora' ), array( $this, 'setup_section' ), 'wp_to_diaspora_settings' );
278
				break;
279
		}
280
	}
281
282
283
	/**
284
	 * Callback for the "Setup" section.
285
	 */
286
	public function setup_section() {
287
		esc_html_e( 'Set up the connection to your diaspora* account.', 'wp-to-diaspora' );
288
289
		// Pod entry field.
290
		add_settings_field( 'pod', __( 'Diaspora* Pod', 'wp-to-diaspora' ), array( $this, 'pod_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
291
292
		// Username entry field.
293
		add_settings_field( 'username', __( 'Username' ), array( $this, 'username_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
294
295
		// Password entry field.
296
		add_settings_field( 'password', __( 'Password' ), array( $this, 'password_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
297
	}
298
299
	/**
300
	 * Render the "Pod" field.
301
	 */
302
	public function pod_render() {
303
		// Update entries: curl 'https://podupti.me/api.php?key=4r45tg&format=json' | jq '.pods[].domain'
304
		$pod_list = array(
305
			'berdaguermontes.eu',
306
			'berlinspora.de',
307
			'bip.tw',
308
			'canfly.org',
309
			'community.kanalinseln.de',
310
			'confetticake.club',
311
			'cryptospora.net',
312
			'd.consumium.org',
313
			'despora.de',
314
			'dia.manuelbichler.at',
315
			'dia.myocastor.de',
316
			'diapod.net',
317
			'diasp.ca',
318
			'diasp.cz',
319
			'diasp.de',
320
			'diasp.eu',
321
			'diasp.in',
322
			'diasp.nl',
323
			'diasp.org',
324
			'diaspod.de',
325
			'diaspora.alfter.us',
326
			'diaspora.bohramt.de',
327
			'diaspora.com.ar',
328
			'diaspora.deadhexagon.com',
329
			'diaspora.digi-merc.org',
330
			'diaspora.espiritolivre.org',
331
			'diaspora.hofud.com',
332
			'diaspora.hzsogood.net',
333
			'diaspora.kapper.net',
334
			'diaspora.koehn.com',
335
			'diaspora.kolosowscy.pl',
336
			'diaspora.lebarjack.com',
337
			'diaspora.microdata.co.uk',
338
			'diaspora.moosje.nl',
339
			'diaspora.net.gr',
340
			'diaspora.permutationsofchaos.com',
341
			'diaspora.podzimek.org',
342
			'diaspora.poleni.com',
343
			'diaspora.psyco.fr',
344
			'diaspora.punkbeer.me',
345
			'diaspora.raven-ip.com',
346
			'diaspora.sceal.ie',
347
			'diaspora.softwarelivre.org',
348
			'diaspora.soh.re',
349
			'diaspora.subsignal.org',
350
			'diaspora.trash-talk.de',
351
			'diaspora.u4u.org',
352
			'diaspora.zone',
353
			'diasporabr.com.br',
354
			'diasporabrazil.org',
355
			'diasporacaribe.org',
356
			'diasporapr.tk',
357
			'diasporing.ch',
358
			'diaspote.org',
359
			'distributed.chat',
360
			'ege.land',
361
			'espora.com.es',
362
			'espora.social',
363
			'flokk.no',
364
			'framasphere.org',
365
			'freehuman.fr',
366
			'friendica.tk',
367
			'gesichtsbu.ch',
368
			'iliketoast.net',
369
			'ingtech.net',
370
			'joindiaspora.com',
371
			'jons.gr',
372
			'kapok.se',
373
			'karmasphe.re',
374
			'kosmospora.net',
375
			'laba.mba',
376
			'libdi.net',
377
			'liberdade.digital',
378
			'librenet.co.za',
379
			'librenet.gr',
380
			'misamigos.online',
381
			'mmkr.co',
382
			'mondiaspora.net',
383
			'nerdpol.ch',
384
			'networkwizard.de',
385
			'nx-pod.de',
386
			'pe.spbstu.ru',
387
			'pod.4ray.co',
388
			'pod.8n1.org',
389
			'pod.afox.me',
390
			'pod.alterworld.info',
391
			'pod.cyberdungeon.de',
392
			'pod.dapor.net',
393
			'pod.datenknoten.me',
394
			'pod.diaspora.software',
395
			'pod.dirkomatik.de',
396
			'pod.disroot.org',
397
			'pod.geraspora.de',
398
			'pod.gleisnetze.de',
399
			'pod.goodsharing.at',
400
			'pod.hashtagueule.fr',
401
			'pod.hoizi.net',
402
			'pod.itabs.nl',
403
			'pod.jpope.org',
404
			'pod.kakise.xyz',
405
			'pod.kneedrag.org',
406
			'pod.liebeleu.de',
407
			'pod.mausdompteur.de',
408
			'pod.mew.cat',
409
			'pod.nomorestars.com',
410
			'pod.orkz.net',
411
			'pod.phantasie.cc',
412
			'pod.ponk.pink',
413
			'pod.promedol.com',
414
			'pod.psynet.su',
415
			'pod.sccn.club',
416
			'pod.sd.vc',
417
			'pod.sertelon.fr',
418
			'pod.storel.li',
419
			'pod.tchncs.de',
420
			'pod.thomasdalichow.de',
421
			'pod.undernet.uy',
422
			'pod.userzap.de',
423
			'pod.volt.io',
424
			'pod.wampe.de',
425
			'podbay.net',
426
			'poddery.com',
427
			'pubpod.alqualonde.org',
428
			'revreso.de',
429
			'ruhrspora.de',
430
			'russiandiaspora.org',
431
			'sechat.org',
432
			'shrekislove.us',
433
			'social.acclaro.digital',
434
			'social.baldr.io',
435
			'social.cigliola.com',
436
			'social.daxbau.net',
437
			'social.elaon.de',
438
			'social.mbuto.me',
439
			'socializer.cc',
440
			'socialpod.us',
441
			'spyurk.am',
442
			'subvillage.de',
443
			'sysad.org',
444
			'therealtalk.org',
445
			'thinkopen.net',
446
			'tippentappen.de',
447
			'translator.group',
448
			'whatsnewz.com',
449
			'wk3.org',
450
			'www.datataffel.dk',
451
			'www.diasporaix.de',
452
		);
453
		?>
454
		https://<input type="text" name="wp_to_diaspora_settings[pod]" value="<?php echo esc_attr( $this->get_option( 'pod' ) ); ?>" placeholder="e.g. joindiaspora.com" autocomplete="on" list="pod-list" required>
455
		<datalist id="pod-list">
456
		<?php foreach ( $pod_list as $pod ) : ?>
457
			<option value="<?php echo esc_attr( $pod ); ?>"></option>
458
		<?php endforeach; ?>
459
		</datalist>
460
		<?php
461
	}
462
463
	/**
464
	 * Render the "Username" field.
465
	 */
466
	public function username_render() {
467
		?>
468
		<input type="text" name="wp_to_diaspora_settings[username]" value="<?php echo esc_attr( $this->get_option( 'username' ) ); ?>" placeholder="<?php esc_attr_e( 'Username' ); ?>" required>
469
		<?php
470
	}
471
472
	/**
473
	 * Render the "Password" field.
474
	 */
475
	public function password_render() {
476
		// Special case if we already have a password.
477
		$has_password = ( '' !== $this->get_option( 'password', '' ) );
478
		$placeholder  = ( $has_password ) ? __( 'Password already set.', 'wp-to-diaspora' ) : __( 'Password' );
479
		$required     = ( $has_password ) ? '' : ' required';
480
		?>
481
		<input type="password" name="wp_to_diaspora_settings[password]" value="" placeholder="<?php echo esc_attr( $placeholder ); ?>"<?php echo esc_attr( $required ); ?>>
482
		<?php if ( $has_password ) : ?>
483
			<p class="description"><?php esc_html_e( 'If you would like to change the password type a new one. Otherwise leave this blank.', 'wp-to-diaspora' ); ?></p>
484
		<?php endif;
485
	}
486
487
488
	/**
489
	 * Callback for the "Defaults" section.
490
	 */
491
	public function defaults_section() {
492
		esc_html_e( 'Define the default posting behaviour for all posts here. These settings can be modified for each post individually, by changing the values in the "WP to diaspora*" meta box, which gets displayed in your post edit screen.', 'wp-to-diaspora' );
493
494
		// Post types field.
495
		add_settings_field( 'enabled_post_types', __( 'Post types', 'wp-to-diaspora' ), array( $this, 'post_types_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section' );
496
497
		 // Post to diaspora* checkbox.
498
		add_settings_field( 'post_to_diaspora', __( 'Post to diaspora*', 'wp-to-diaspora' ), array( $this, 'post_to_diaspora_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', $this->get_option( 'post_to_diaspora' ) );
499
500
		// Full entry link checkbox.
501
		add_settings_field( 'fullentrylink', __( 'Show "Posted at" link?', 'wp-to-diaspora' ), array( $this, 'fullentrylink_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', $this->get_option( 'fullentrylink' ) );
502
503
		// Full text or excerpt radio buttons.
504
		add_settings_field( 'display', __( 'Display', 'wp-to-diaspora' ), array( $this, 'display_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', $this->get_option( 'display' ) );
505
506
		// Tags to post dropdown.
507
		add_settings_field( 'tags_to_post', __( 'Tags to post', 'wp-to-diaspora' ), array( $this, 'tags_to_post_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', $this->get_option( 'tags_to_post', 'gc' ) );
508
509
		// Global tags field.
510
		add_settings_field( 'global_tags', __( 'Global tags', 'wp-to-diaspora' ), array( $this, 'global_tags_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', $this->get_option( 'global_tags' ) );
511
512
		// Aspects checkboxes.
513
		add_settings_field( 'aspects', __( 'Aspects', 'wp-to-diaspora' ), array( $this, 'aspects_services_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', array( 'aspects', $this->get_option( 'aspects' ) ) );
514
515
		// Services checkboxes.
516
		add_settings_field( 'services', __( 'Services', 'wp-to-diaspora' ), array( $this, 'aspects_services_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_defaults_section', array( 'services', $this->get_option( 'services' ) ) );
517
	}
518
519
	/**
520
	 * Render the "Post types" checkboxes.
521
	 */
522
	public function post_types_render() {
523
		$post_types = get_post_types( array( 'public' => true ), 'objects' );
524
525
		// Remove excluded post types from the list.
526
		$excluded_post_types = array( 'attachment', 'nav_menu_item', 'revision' );
527
		foreach ( $excluded_post_types as $excluded ) {
528
			unset( $post_types[ $excluded ] );
529
		}
530
		?>
531
532
		<select id="enabled-post-types" multiple data-placeholder="<?php esc_attr_e( 'None', 'wp-to-diaspora' ); ?>" class="chosen" name="wp_to_diaspora_settings[enabled_post_types][]">
533
		<?php foreach ( $post_types as $post_type ) : ?>
534
			<option value="<?php echo esc_attr( $post_type->name ); ?>" <?php selected( in_array( $post_type->name, $this->get_option( 'enabled_post_types' ) ) ); ?>><?php echo esc_html( $post_type->label ); ?></option>
535
		<?php endforeach; ?>
536
		</select>
537
538
		<p class="description"><?php esc_html_e( 'Choose which post types can be posted to diaspora*.', 'wp-to-diaspora' ); ?></p>
539
540
		<?php
541
	}
542
543
	/**
544
	 * Render the "Post to diaspora*" checkbox.
545
	 *
546
	 * @param bool $post_to_diaspora If this checkbox is checked or not.
547
	 */
548
	public function post_to_diaspora_render( $post_to_diaspora ) {
549
		$label = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id ) ? __( 'Yes' ) : __( 'Post to diaspora*', 'wp-to-diaspora' );
550
		?>
551
		<label><input type="checkbox" id="post-to-diaspora" name="wp_to_diaspora_settings[post_to_diaspora]" value="1" <?php checked( $post_to_diaspora ); ?>><?php echo esc_html( $label ); ?></label>
552
		<?php
553
	}
554
555
	/**
556
	 * Render the "Show 'Posted at' link" checkbox.
557
	 *
558
	 * @param bool $show_link If the checkbox is checked or not.
559
	 */
560
	public function fullentrylink_render( $show_link ) {
561
		$description = __( 'Include a link back to your original post.', 'wp-to-diaspora' );
562
		$checkbox = '<input type="checkbox" id="fullentrylink" name="wp_to_diaspora_settings[fullentrylink]" value="1"' . checked( $show_link, true, false ) . '>';
563
564
		if ( 'settings_page_wp_to_diaspora' === get_current_screen()->id ) : ?>
565
			<label><?php echo $checkbox; ?><?php esc_html_e( 'Yes' ); ?></label>
566
			<p class="description"><?php echo esc_html( $description ); ?></p>
567
		<?php else : ?>
568
			<label title="<?php echo esc_attr( $description ); ?>"><?php echo $checkbox; ?><?php esc_html_e( 'Show "Posted at" link?', 'wp-to-diaspora' ); ?></label>
569
		<?php endif;
570
	}
571
572
	/**
573
	 * Render the "Display" radio buttons.
574
	 *
575
	 * @param string $display The selected radio button.
576
	 */
577
	public function display_render( $display ) {
578
		?>
579
		<label><input type="radio" name="wp_to_diaspora_settings[display]" value="full" <?php checked( $display, 'full' ); ?>><?php esc_html_e( 'Full Post', 'wp-to-diaspora' ); ?></label><br />
580
		<label><input type="radio" name="wp_to_diaspora_settings[display]" value="excerpt" <?php checked( $display, 'excerpt' ); ?>><?php esc_html_e( 'Excerpt' ); ?></label>
581
		<?php
582
	}
583
584
	/**
585
	 * Render the "Tags to post" field.
586
	 *
587
	 * @param array $tags_to_post The types of tags to be posted.
588
	 */
589
	public function tags_to_post_render( $tags_to_post ) {
590
		$on_settings_page = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id );
591
		$description = esc_html__( 'Choose which tags should be posted to diaspora*.', 'wp-to-diaspora' );
592
593
		if ( ! $on_settings_page ) {
594
			echo '<label>' . esc_html( $description );
595
		}
596
597
		?>
598
		<select id="tags-to-post" multiple data-placeholder="<?php esc_attr_e( 'No tags', 'wp-to-diaspora' ); ?>" class="chosen" name="wp_to_diaspora_settings[tags_to_post][]">
599
			<option value="global" <?php selected( in_array( 'global', $tags_to_post ) ); ?>><?php esc_html_e( 'Global tags', 'wp-to-diaspora' ); ?></option>
600
			<option value="custom" <?php selected( in_array( 'custom', $tags_to_post ) ); ?>><?php esc_html_e( 'Custom tags', 'wp-to-diaspora' ); ?></option>
601
			<option value="post"   <?php selected( in_array( 'post',   $tags_to_post ) ); ?>><?php esc_html_e( 'Post tags',   'wp-to-diaspora' ); ?></option>
602
		</select>
603
604
		<?php if ( $on_settings_page ) : ?>
605
			<p class="description"><?php echo esc_html( $description ); ?></p>
606
		<?php else : ?>
607
			</label>
608
		<?php endif;
609
	}
610
611
	/**
612
	 * Render the "Global tags" field.
613
	 *
614
	 * @param array $tags The global tags to be posted.
615
	 */
616 View Code Duplication
	public function global_tags_render( $tags ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
617
		WP2D_Helpers::arr_to_str( $tags );
618
		?>
619
		<input type="text" class="wp2dtags" name="wp_to_diaspora_settings[global_tags]" value="<?php echo esc_attr( $tags ); ?>" placeholder="<?php esc_attr_e( 'Global tags', 'wp-to-diaspora' ); ?>" class="regular-text">
620
		<p class="description"><?php esc_html_e( 'Custom tags to add to all posts being posted to diaspora*.', 'wp-to-diaspora' ); ?></p>
621
		<?php
622
	}
623
624
	/**
625
	 * Render the "Custom tags" field.
626
	 *
627
	 * @param array $tags The custom tags to be posted.
628
	 */
629 View Code Duplication
	public function custom_tags_render( $tags ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
630
		WP2D_Helpers::arr_to_str( $tags );
631
		?>
632
		<label title="<?php esc_attr_e( 'Custom tags to add to this post when it\'s posted to diaspora*.', 'wp-to-diaspora' ); ?>">
633
			<?php esc_html_e( 'Custom tags', 'wp-to-diaspora' ); ?>
634
			<input type="text" class="wp2dtags" name="wp_to_diaspora_settings[custom_tags]" value="<?php echo esc_attr( $tags ); ?>" class="widefat">
635
		</label>
636
		<p class="description"><?php esc_html_e( 'Separate tags with commas' ); ?></p>
637
		<?php
638
	}
639
640
	/**
641
	 * Render the "Aspects" and "Services" checkboxes.
642
	 *
643
	 * @param array $args Array containing the type and items to output as checkboxes.
644
	 */
645
	public function aspects_services_render( $args ) {
646
		list( $type, $items ) = $args;
647
648
		$refresh_button = '';
0 ignored issues
show
Unused Code introduced by
$refresh_button is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
649
		$description    = '';
0 ignored issues
show
Unused Code introduced by
$description is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
650
		$empty_label    = '';
0 ignored issues
show
Unused Code introduced by
$empty_label is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
651
652
		// This is where the 2 types show their differences.
653
		switch ( $type ) {
654
			case 'aspects':
655
				$refresh_button = __( 'Refresh Aspects', 'wp-to-diaspora' );
656
				$description    = esc_html__( 'Choose which aspects to share to.', 'wp-to-diaspora' );
657
				$empty_label    = '<input type="checkbox" name="wp_to_diaspora_settings[aspects][]" value="public" checked="checked">' . esc_html__( 'Public' );
658
				break;
659
660
			case 'services':
661
				$refresh_button = __( 'Refresh Services', 'wp-to-diaspora' );
662
				$description    = sprintf( '%1$s<br><a href="%2$s" target="_blank">%3$s</a>',
663
					esc_html__( 'Choose which services to share to.', 'wp-to-diaspora' ),
664
					esc_url( 'https://' . $this->get_option( 'pod' ) . '/services' ),
665
					esc_html__( 'Show available services on my pod.', 'wp-to-diaspora' )
666
				);
667
				$empty_label    = esc_html__( 'No services connected yet.', 'wp-to-diaspora' );
668
				break;
669
670
			default:
671
				return;
672
		}
673
674
		$items = array_filter( (array) $items ) ?: array();
675
676
		// Special case for this field if it's displayed on the settings page.
677
		$on_settings_page = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id );
678
679
		if ( ! $on_settings_page ) {
680
			echo $description;
681
			$description = '';
682
		}
683
684
		?>
685
		<div id="<?php echo esc_attr( $type ); ?>-container" data-<?php echo esc_attr( $type ); ?>-selected="<?php echo esc_attr( implode( ',', $items ) ); ?>">
686
			<?php if ( $list = (array) $this->get_option( $type . '_list' ) ) : ?>
687
				<?php foreach ( $list as $id => $name ) : ?>
688
					<label><input type="checkbox" name="wp_to_diaspora_settings[<?php echo esc_attr( $type ); ?>][]" value="<?php echo esc_attr( $id ); ?>" <?php checked( in_array( $id, $items ) ); ?>><?php echo esc_html( $name ); ?></label>
689
				<?php endforeach; ?>
690
			<?php else : ?>
691
				<label><?php echo $empty_label; ?></label>
692
			<?php endif; ?>
693
		</div>
694
		<p class="description">
695
			<?php echo $description; ?>
696
			<a id="refresh-<?php echo esc_attr( $type ); ?>-list" class="button hide-if-no-js"><?php echo esc_html( $refresh_button ); ?></a>
697
			<span class="spinner"></span>
698
			<span class="hide-if-js"><?php printf( esc_html_x( 'To update this list, %sre-save your login info%s.', 'placeholders are link tags to the settings page.', 'wp-to-diaspora' ), '<a href="' . admin_url( 'options-general.php?page=wp_to_diaspora' ) . '&amp;tab=setup" target="_blank">', '</a>' ); ?></span>
699
		</p>
700
		<?php
701
	}
702
703
704
	/**
705
	 * Get a specific option.
706
	 *
707
	 * @param string       $option  ID of option to get.
708
	 * @param array|string $default Override default value if option not found.
709
	 * @return array|string Requested option value.
710
	 */
711
	public function get_option( $option = null, $default = null ) {
712
		if ( ! isset( self::$_options ) ) {
713
			self::$_options = get_option( 'wp_to_diaspora_settings', self::$_default_options );
714
		}
715
		if ( isset( $option ) ) {
716
			if ( isset( self::$_options[ $option ] ) ) {
717
				// Return found option value.
718
				return self::$_options[ $option ];
719
			} elseif ( isset( $default ) ) {
720
				// Return overridden default value.
721
				return $default;
722
			} elseif ( isset( self::$_default_options[ $option ] ) ) {
723
				// Return default option value.
724
				return self::$_default_options[ $option ];
725
			}
726
		}
727
	}
728
729
	/**
730
	 * Get all options.
731
	 *
732
	 * @return array All the options.
733
	 */
734
	public function get_options() {
735
		return self::$_options;
736
	}
737
738
	/**
739
	 * Set a certain option.
740
	 *
741
	 * @param string       $option ID of option to get.
742
	 * @param array|string $value  Value to be set for the passed option.
743
	 * @param boolean      $save   Save the options immediately after setting them.
744
	 */
745
	public function set_option( $option, $value, $save = false ) {
746
		if ( isset( $option ) ) {
747
			if ( isset( $value ) ) {
748
				self::$_options[ $option ] = $value;
749
			} else {
750
				unset( self::$_options[ $option ] );
751
			}
752
		}
753
		if ( $save ) {
754
			self::save();
755
		}
756
	}
757
758
	/**
759
	 * Save the options.
760
	 */
761
	public function save() {
762
		update_option( 'wp_to_diaspora_settings', self::$_options );
763
	}
764
765
	/**
766
	 * Get all valid input values for the passed field.
767
	 *
768
	 * @param string $field Field to get the valid values for.
769
	 * @return array List of valid values.
770
	 */
771
	public function get_valid_values( $field ) {
772
		if ( array_key_exists( $field, self::$_valid_values ) ) {
773
			return self::$_valid_values[ $field ];
774
		}
775
	}
776
777
	/**
778
	 * Check if a value is valid for the passed field.
779
	 *
780
	 * @param string $field Field to check the valid value for.
781
	 * @param object $value Value to check validity.
782
	 * @return boolean If the passed value is valid.
783
	 */
784
	public function is_valid_value( $field, $value ) {
785
		if ( $valids = self::get_valid_values( $field ) ) {
786
			return ( in_array( $value, $valids ) );
787
		}
788
		return false;
789
	}
790
791
	/**
792
	 * Validate all settings.
793
	 *
794
	 * @param array $input RAW input values.
795
	 * @return array Validated input values.
796
	 */
797
	public function validate_settings( $input ) {
798
		/* Validate all settings before saving to the database. */
799
800
		// Saving the pod setup details.
801
		if ( isset( $input['submit_setup'] ) ) {
802
			$input['pod']      = trim( sanitize_text_field( $input['pod'] ), ' /' );
803
			$input['username'] = sanitize_text_field( $input['username'] );
804
			$input['password'] = sanitize_text_field( $input['password'] );
805
806
			// If password is blank, it hasn't been changed.
807
			// If new password is equal to the encrypted password already saved, it was just passed again. It happens everytime update_option('wp_to_diaspora_settings') is called.
808
			if ( '' === $input['password'] || $this->get_option( 'password' ) === $input['password'] ) {
809
				$input['password'] = $this->get_option( 'password' );
810
			} else {
811
				$input['password'] = WP2D_Helpers::encrypt( $input['password'] );
812
			}
813
814
			// This is for when JS in not enabled, to make sure that the aspects and services
815
			// are refetched when displaying the options page after saving.
816
			if ( isset( $input['no_js'] ) ) {
817
				set_transient( 'wp2d_no_js_force_refetch', true );
818
			}
819
		}
820
821
		// Saving the default options.
822
		if ( isset( $input['submit_defaults'] ) ) {
823
			if ( ! isset( $input['enabled_post_types'] ) ) {
824
				$input['enabled_post_types'] = array();
825
			}
826
827
			// Checkboxes.
828
			$this->validate_checkboxes( array( 'post_to_diaspora', 'fullentrylink' ), $input );
829
830
			// Single Selects.
831
			$this->validate_single_selects( 'display', $input );
832
833
			// Multiple Selects.
834
			$this->validate_multi_selects( 'tags_to_post', $input );
835
836
			// Get unique, non-empty, trimmed tags and clean them up.
837
			$this->validate_tags( $input['global_tags'] );
838
839
			// Clean up the list of aspects. If the list is empty, only use the 'Public' aspect.
840
			$this->validate_aspects_services( $input['aspects'], array( 'public' ) );
841
842
			// Clean up the list of services.
843
			$this->validate_aspects_services( $input['services'] );
844
		}
845
846
		// Reset to defaults.
847
		if ( isset( $input['reset_defaults'] ) ) {
848
			// Set the input to the default options.
849
			$input = self::$_default_options;
850
851
			// Don't reset the fetched lists of aspects and services.
852
			unset( $input['aspects_list'] );
853
			unset( $input['services_list'] );
854
		}
855
856
		// Unset all unused input fields.
857
		unset( $input['submit_defaults'] );
858
		unset( $input['reset_defaults'] );
859
		unset( $input['submit_setup'] );
860
861
		// Parse inputs with default options and return.
862
		return wp_parse_args( $input, array_merge( self::$_default_options, self::$_options ) );
863
	}
864
865
	/**
866
	 * Validate checkboxes, make them either true or false.
867
	 *
868
	 * @param string|array $checkboxes Checkboxes to validate.
869
	 * @param array        $options    Options values themselves.
870
	 * @return array The validated options.
871
	 */
872
	public function validate_checkboxes( $checkboxes, &$options ) {
873
		foreach ( WP2D_Helpers::str_to_arr( $checkboxes ) as $checkbox ) {
0 ignored issues
show
Bug introduced by
The expression \WP2D_Helpers::str_to_arr($checkboxes) of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
874
			$options[ $checkbox ] = isset( $options[ $checkbox ] );
875
		}
876
		return $options;
877
	}
878
879
	/**
880
	 * Validate single-select fields and make sure their selected value are valid.
881
	 *
882
	 * @param string|array $selects Name(s) of the select fields.
883
	 * @param array        $options Options values themselves.
884
	 * @return array The validated options.
885
	 */
886
	public function validate_single_selects( $selects, &$options ) {
887
		foreach ( WP2D_Helpers::str_to_arr( $selects ) as $select ) {
0 ignored issues
show
Bug introduced by
The expression \WP2D_Helpers::str_to_arr($selects) of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
888
			if ( isset( $options[ $select ] ) && ! $this->is_valid_value( $select, $options[ $select ] ) ) {
889
				unset( $options[ $select ] );
890
			}
891
		}
892
		return $options;
893
	}
894
895
	/**
896
	 * Validate multi-select fields and make sure their selected values are valid.
897
	 *
898
	 * @param string|array $selects Name(s) of the select fields.
899
	 * @param array        $options Options values themselves.
900
	 * @return array The validated options.
901
	 */
902
	public function validate_multi_selects( $selects, &$options ) {
903
		foreach ( WP2D_Helpers::str_to_arr( $selects ) as $select ) {
0 ignored issues
show
Bug introduced by
The expression \WP2D_Helpers::str_to_arr($selects) of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
904
			if ( isset( $options[ $select ] ) ) {
905
				foreach ( (array) $options[ $select ] as $option_value ) {
906
					if ( ! $this->is_valid_value( $select, $option_value ) ) {
907
						unset( $options[ $select ] );
908
						break;
909
					}
910
				}
911
			} else {
912
				$options[ $select ] = array();
913
			}
914
		}
915
		return $options;
916
	}
917
918
	/**
919
	 * Clean up the passed tags. Keep only alphanumeric, hyphen and underscore characters.
920
	 *
921
	 * @param array|string $tags Tags to be cleaned as array or comma seperated values.
922
	 * @return array The cleaned tags.
923
	 */
924
	public function validate_tags( &$tags ) {
925
		WP2D_Helpers::str_to_arr( $tags );
926
927
		$tags = array_map( array( $this, 'validate_tag' ),
928
			array_unique(
929
				array_filter( $tags, 'trim' )
930
			)
931
		);
932
		return $tags;
933
	}
934
935
	/**
936
	 * Clean up the passed tag. Keep only alphanumeric, hyphen and underscore characters.
937
	 *
938
	 * @todo What about eastern characters? (chinese, indian, etc.)
939
	 *
940
	 * @param string $tag Tag to be cleaned.
941
	 * @return string The clean tag.
942
	 */
943
	public function validate_tag( &$tag ) {
944
		$tag = preg_replace( '/[^\w $\-]/u', '', str_replace( ' ', '-', trim( $tag ) ) );
945
		return $tag;
946
	}
947
948
	/**
949
	 * Validate the passed aspects or services.
950
	 *
951
	 * @param array $aspects_services List of aspects or services that need to be validated.
952
	 * @param array $default          Default value if not valid.
953
	 * @return array The validated list of aspects or services.
954
	 */
955
	public function validate_aspects_services( &$aspects_services, $default = array() ) {
956
		if ( empty( $aspects_services ) || ! is_array( $aspects_services ) ) {
957
			$aspects_services = $default;
958
		} else {
959
			array_walk( $aspects_services, 'sanitize_text_field' );
960
		}
961
		return $aspects_services;
962
	}
963
}
964