Completed
Push — master ( 1614fa...13be2d )
by Armando
02:26 queued 42s
created

WP2D_Options::__wakeup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 1
rs 10
cc 1
eloc 1
nc 1
nop 0
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;
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;
59
60
	/** Singleton, keep private. */
61
	final private function __clone() { }
62
63
	/** Singleton, keep private. */
64
	final private function __construct() { }
65
66
	/**
67
	 * Create / Get the instance of this class.
68
	 *
69
	 * @return WP2D_Options Instance of this class.
70
	 */
71 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...
72
		if ( ! isset( self::$_instance ) ) {
73
			self::$_instance = new self();
74
			self::$_instance->_setup();
75
		}
76
		return self::$_instance;
77
	}
78
79
	/**
80
	 * Set up the options menu.
81
	 */
82
	private function _setup() {
83
84
		// Populate options array.
85
		$this->get_option();
86
87
		// Setup Options page and Contextual Help.
88
		add_action( 'admin_menu', array( $this, 'setup_wpadmin_pages' ) );
89
90
		// Register all settings.
91
		add_action( 'admin_init', array( $this, 'register_settings' ) );
92
	}
93
94
95
	/**
96
	 * Get the currently selected tab.
97
	 *
98
	 * @todo Multi-level if statement to make it look prettier.
99
	 *
100
	 * @param string $default Tab to select if the current selection is invalid.
101
	 * @return string Return the currently selected tab.
102
	 */
103
	private function _current_tab( $default = 'defaults' ) {
104
		$tab = ( isset ( $_GET['tab'] ) ? $_GET['tab'] : $default );
105
106
		// If the pod settings aren't configured yet, open the 'Setup' tab.
107
		if ( ! $this->is_pod_set_up() ) {
108
			$tab = 'setup';
109
		}
110
111
		return $tab;
112
	}
113
114
	/**
115
	 * Output all options tabs and return an array of them all, if requested by $return.
116
	 *
117
	 * @param bool $return Define if the options tabs should be returned.
118
	 * @return array (If requested) An array of the outputted options tabs.
119
	 */
120
	private function _options_page_tabs( $return = false ) {
121
		// The array defining all options sections to be shown as tabs.
122
		$tabs = array();
123
		if ( $this->is_pod_set_up() ) {
124
			$tabs['defaults'] = __( 'Defaults', 'wp-to-diaspora' );
125
		}
126
127
		// Add the 'Setup' tab to the end of the list.
128
		$tabs['setup'] = __( 'Setup', 'wp-to-diaspora' ) . '<span id="pod-connection-status" class="dashicons-before hidden"></span><span class="spinner"></span>';
129
130
		// Container for all options tabs.
131
		$out = '<h2 id="options-tabs" class="nav-tab-wrapper">';
132
		foreach ( $tabs as $tab => $name ) {
133
			// The tab link.
134
			$out .= '<a class="nav-tab' . ( ( $tab === $this->_current_tab() ) ? ' nav-tab-active' : '' ) . '" href="?page=wp_to_diaspora&tab=' . $tab . '">' . $name . '</a>';
135
		}
136
		$out .= '</h2>';
137
138
		// Output the container with all tabs.
139
		echo $out;
140
141
		// Check if the tabs should be returned.
142
		if ( $return ) {
143
			return $tabs;
144
		}
145
	}
146
147
148
	/**
149
	 * Set up admin options page.
150
	 */
151
	public function admin_options_page() {
152
		?>
153
		<div class="wrap">
154
			<h2>WP to diaspora*</h2>
155
156
			<div id="wp2d-message" class="notice hidden" <?php echo ( defined( 'WP2D_DEBUGGING' ) ) ? ' data-debugging' : ''; ?>></div>
157
158
			<?php
159
			// Check the connection status to diaspora.
160
			if ( ! $this->is_pod_set_up() ) {
161
				add_settings_error(
162
					'wp_to_diaspora_settings',
163
					'wp_to_diaspora_connected',
164
					__( 'First of all, set up the connection to your pod below.', 'wp-to-diaspora' ),
165
					'updated'
166
				);
167
			} else {
168
				// Get initial aspects list and connected services.
169
				// DON'T check for empty services list here!!
170
				// It could always be empty, resulting in this code being run every time the page is loaded.
171
				// The aspects will at least have a "Public" entry after the initial fetch.
172
				$aspects_list = $this->get_option( 'aspects_list' );
173
				if ( ( $force = get_transient( 'wp2d_no_js_force_refetch' ) ) || empty( $aspects_list ) ) {
174
175
					// Set up the connection to diaspora*.
176
					$api = WP2D_Helpers::api_quick_connect();
177
					if ( ! $api->has_last_error() ) {
178
						// Get the loaded aspects.
179
						if ( is_array( $aspects = $api->get_aspects() ) ) {
180
							// Save the new list of aspects.
181
							$this->set_option( 'aspects_list', $aspects );
182
						}
183
184
						// Get the loaded services.
185
						if ( is_array( $services = $api->get_services() ) ) {
186
							// Save the new list of services.
187
							$this->set_option( 'services_list', $services );
188
						}
189
190
						$this->save();
191
					}
192
193
					if ( $force ) {
194
						delete_transient( 'wp2d_no_js_force_refetch' );
195
						$message = ( ! $api->has_last_error() ) ? __( 'Connection successful.', 'wp-to-diaspora' ) : $api->get_last_error();
196
						add_settings_error(
197
							'wp_to_diaspora_settings',
198
							'wp_to_diaspora_connected',
199
							$message,
200
							( ! $api->has_last_error() ) ? 'updated' : 'error'
201
						);
202
					}
203
				}
204
			}
205
206
			// Output success or error message.
207
			settings_errors( 'wp_to_diaspora_settings' );
208
			?>
209
210
			<?php $page_tabs = array_keys( $this->_options_page_tabs( true ) ); ?>
211
212
			<form action="options.php" method="post">
213
				<input id="wp2d_no_js" type="hidden" name="wp_to_diaspora_settings[no_js]" value="1">
214
				<?php
215
				// Load the settings fields.
216
				settings_fields( 'wp_to_diaspora_settings' );
217
				do_settings_sections( 'wp_to_diaspora_settings' );
218
219
				// Get the name of the current tab, if set, else take the first one from the list.
220
				$tab = $this->_current_tab( $page_tabs[0] );
221
222
				// Add Save and Reset buttons.
223
				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;';
224
				if ( 'setup' !== $tab ) {
225
					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' ) . '" />';
226
				}
227
				?>
228
229
			</form>
230
		</div>
231
232
		<?php
233
	}
234
235
	/**
236
	 * Return if the settings for the pod setup have been entered.
237
	 *
238
	 * @return boolean If the setup for the pod has been done.
239
	 */
240
	public function is_pod_set_up() {
241
		return ( $this->get_option( 'pod' ) && $this->get_option( 'username' ) && $this->get_option( 'password' ) );
242
	}
243
244
	/**
245
	 * Setup Contextual Help and Options pages.
246
	 */
247
	public function setup_wpadmin_pages() {
248
		// Add options page.
249
		$hook = add_options_page( 'WP to diaspora*', 'WP to diaspora*', 'manage_options', 'wp_to_diaspora', array( $this, 'admin_options_page' ) );
250
251
		// Setup the contextual help menu after the options page has been loaded.
252
		add_action( 'load-' . $hook, array( 'WP2D_Contextual_Help', 'instance' ) );
253
254
		// Setup the contextual help menu tab for post types. Checks are made there!
255
		add_action( 'load-post.php', array( 'WP2D_Contextual_Help', 'instance' ) );
256
		add_action( 'load-post-new.php', array( 'WP2D_Contextual_Help', 'instance' ) );
257
	}
258
259
	/**
260
	 * Initialise the settings sections and fields of the currently selected tab.
261
	 */
262
	public function register_settings() {
263
		// Register the settings with validation callback.
264
		register_setting( 'wp_to_diaspora_settings', 'wp_to_diaspora_settings', array( $this, 'validate_settings' ) );
265
266
		// Load only the sections of the selected tab.
267
		switch ( $this->_current_tab() ) {
268
			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...
269
				// Add a "Defaults" section that contains all posting settings to be used by default.
270
				add_settings_section( 'wp_to_diaspora_defaults_section', __( 'Posting Defaults', 'wp-to-diaspora' ), array( $this, 'defaults_section' ), 'wp_to_diaspora_settings' );
271
				break;
272
			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...
273
				// Add a "Setup" section that contains the Pod domain, Username and Password.
274
				add_settings_section( 'wp_to_diaspora_setup_section', __( 'diaspora* Setup', 'wp-to-diaspora' ), array( $this, 'setup_section' ), 'wp_to_diaspora_settings' );
275
				break;
276
		}
277
	}
278
279
280
	/**
281
	 * Callback for the "Setup" section.
282
	 */
283
	public function setup_section() {
284
		esc_html_e( 'Set up the connection to your diaspora* account.', 'wp-to-diaspora' );
285
286
		// Pod entry field.
287
		add_settings_field( 'pod', __( 'Diaspora* Pod', 'wp-to-diaspora' ), array( $this, 'pod_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
288
289
		// Username entry field.
290
		add_settings_field( 'username', __( 'Username' ), array( $this, 'username_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
291
292
		// Password entry field.
293
		add_settings_field( 'password', __( 'Password' ), array( $this, 'password_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
294
	}
295
296
	/**
297
	 * Render the "Pod" field.
298
	 */
299
	public function pod_render() {
300
		// Update entries: curl 'https://podupti.me/api.php?key=4r45tg&format=json' | jq '.pods[].domain'
301
		$pod_list = array(
302
			'berdaguermontes.eu',
303
			'berlinspora.de',
304
			'bip.tw',
305
			'canfly.org',
306
			'community.kanalinseln.de',
307
			'confetticake.club',
308
			'cryptospora.net',
309
			'd.consumium.org',
310
			'despora.de',
311
			'dia.manuelbichler.at',
312
			'dia.myocastor.de',
313
			'diapod.net',
314
			'diasp.ca',
315
			'diasp.cz',
316
			'diasp.de',
317
			'diasp.eu',
318
			'diasp.in',
319
			'diasp.nl',
320
			'diasp.org',
321
			'diaspod.de',
322
			'diaspora.alfter.us',
323
			'diaspora.bohramt.de',
324
			'diaspora.com.ar',
325
			'diaspora.deadhexagon.com',
326
			'diaspora.digi-merc.org',
327
			'diaspora.espiritolivre.org',
328
			'diaspora.hofud.com',
329
			'diaspora.hzsogood.net',
330
			'diaspora.kapper.net',
331
			'diaspora.koehn.com',
332
			'diaspora.kolosowscy.pl',
333
			'diaspora.lebarjack.com',
334
			'diaspora.microdata.co.uk',
335
			'diaspora.moosje.nl',
336
			'diaspora.net.gr',
337
			'diaspora.permutationsofchaos.com',
338
			'diaspora.podzimek.org',
339
			'diaspora.poleni.com',
340
			'diaspora.psyco.fr',
341
			'diaspora.punkbeer.me',
342
			'diaspora.raven-ip.com',
343
			'diaspora.sceal.ie',
344
			'diaspora.softwarelivre.org',
345
			'diaspora.soh.re',
346
			'diaspora.subsignal.org',
347
			'diaspora.trash-talk.de',
348
			'diaspora.u4u.org',
349
			'diaspora.zone',
350
			'diasporabr.com.br',
351
			'diasporabrazil.org',
352
			'diasporacaribe.org',
353
			'diasporapr.tk',
354
			'diasporing.ch',
355
			'diaspote.org',
356
			'distributed.chat',
357
			'ege.land',
358
			'espora.com.es',
359
			'espora.social',
360
			'flokk.no',
361
			'framasphere.org',
362
			'freehuman.fr',
363
			'friendica.tk',
364
			'gesichtsbu.ch',
365
			'iliketoast.net',
366
			'ingtech.net',
367
			'joindiaspora.com',
368
			'jons.gr',
369
			'kapok.se',
370
			'karmasphe.re',
371
			'kosmospora.net',
372
			'laba.mba',
373
			'libdi.net',
374
			'liberdade.digital',
375
			'librenet.co.za',
376
			'librenet.gr',
377
			'misamigos.online',
378
			'mmkr.co',
379
			'mondiaspora.net',
380
			'nerdpol.ch',
381
			'networkwizard.de',
382
			'nx-pod.de',
383
			'pe.spbstu.ru',
384
			'pod.4ray.co',
385
			'pod.8n1.org',
386
			'pod.afox.me',
387
			'pod.alterworld.info',
388
			'pod.cyberdungeon.de',
389
			'pod.dapor.net',
390
			'pod.datenknoten.me',
391
			'pod.diaspora.software',
392
			'pod.dirkomatik.de',
393
			'pod.disroot.org',
394
			'pod.geraspora.de',
395
			'pod.gleisnetze.de',
396
			'pod.goodsharing.at',
397
			'pod.hashtagueule.fr',
398
			'pod.hoizi.net',
399
			'pod.itabs.nl',
400
			'pod.jpope.org',
401
			'pod.kakise.xyz',
402
			'pod.kneedrag.org',
403
			'pod.liebeleu.de',
404
			'pod.mausdompteur.de',
405
			'pod.mew.cat',
406
			'pod.nomorestars.com',
407
			'pod.orkz.net',
408
			'pod.phantasie.cc',
409
			'pod.ponk.pink',
410
			'pod.promedol.com',
411
			'pod.psynet.su',
412
			'pod.sccn.club',
413
			'pod.sd.vc',
414
			'pod.sertelon.fr',
415
			'pod.storel.li',
416
			'pod.tchncs.de',
417
			'pod.thomasdalichow.de',
418
			'pod.undernet.uy',
419
			'pod.userzap.de',
420
			'pod.volt.io',
421
			'pod.wampe.de',
422
			'podbay.net',
423
			'poddery.com',
424
			'pubpod.alqualonde.org',
425
			'revreso.de',
426
			'ruhrspora.de',
427
			'russiandiaspora.org',
428
			'sechat.org',
429
			'shrekislove.us',
430
			'social.acclaro.digital',
431
			'social.baldr.io',
432
			'social.cigliola.com',
433
			'social.daxbau.net',
434
			'social.elaon.de',
435
			'social.mbuto.me',
436
			'socializer.cc',
437
			'socialpod.us',
438
			'spyurk.am',
439
			'subvillage.de',
440
			'sysad.org',
441
			'therealtalk.org',
442
			'thinkopen.net',
443
			'tippentappen.de',
444
			'translator.group',
445
			'whatsnewz.com',
446
			'wk3.org',
447
			'www.datataffel.dk',
448
			'www.diasporaix.de',
449
		);
450
		?>
451
		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>
452
		<datalist id="pod-list">
453
		<?php foreach ( $pod_list as $pod ) : ?>
454
			<option value="<?php echo esc_attr( $pod ); ?>"></option>
455
		<?php endforeach; ?>
456
		</datalist>
457
		<?php
458
	}
459
460
	/**
461
	 * Render the "Username" field.
462
	 */
463
	public function username_render() {
464
		?>
465
		<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>
466
		<?php
467
	}
468
469
	/**
470
	 * Render the "Password" field.
471
	 */
472
	public function password_render() {
473
		// Special case if we already have a password.
474
		$has_password = ( '' !== $this->get_option( 'password', '' ) );
475
		$placeholder  = ( $has_password ) ? __( 'Password already set.', 'wp-to-diaspora' ) : __( 'Password' );
476
		$required     = ( $has_password ) ? '' : ' required';
477
		?>
478
		<input type="password" name="wp_to_diaspora_settings[password]" value="" placeholder="<?php echo esc_attr( $placeholder ); ?>"<?php echo esc_attr( $required ); ?>>
479
		<?php if ( $has_password ) : ?>
480
			<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>
481
		<?php endif;
482
	}
483
484
485
	/**
486
	 * Callback for the "Defaults" section.
487
	 */
488
	public function defaults_section() {
489
		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' );
490
491
		// Post types field.
492
		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' );
493
494
		 // Post to diaspora* checkbox.
495
		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' ) );
496
497
		// Full entry link checkbox.
498
		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' ) );
499
500
		// Full text or excerpt radio buttons.
501
		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' ) );
502
503
		// Tags to post dropdown.
504
		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' ) );
505
506
		// Global tags field.
507
		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' ) );
508
509
		// Aspects checkboxes.
510
		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' ) ) );
511
512
		// Services checkboxes.
513
		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' ) ) );
514
	}
515
516
	/**
517
	 * Render the "Post types" checkboxes.
518
	 */
519
	public function post_types_render() {
520
		$post_types = get_post_types( array( 'public' => true ), 'objects' );
521
522
		// Remove excluded post types from the list.
523
		$excluded_post_types = array( 'attachment', 'nav_menu_item', 'revision' );
524
		foreach ( $excluded_post_types as $excluded ) {
525
			unset( $post_types[ $excluded ] );
526
		}
527
		?>
528
529
		<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][]">
530
		<?php foreach ( $post_types as $post_type ) : ?>
531
			<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>
532
		<?php endforeach; ?>
533
		</select>
534
535
		<p class="description"><?php esc_html_e( 'Choose which post types can be posted to diaspora*.', 'wp-to-diaspora' ); ?></p>
536
537
		<?php
538
	}
539
540
	/**
541
	 * Render the "Post to diaspora*" checkbox.
542
	 *
543
	 * @param bool $post_to_diaspora If this checkbox is checked or not.
544
	 */
545
	public function post_to_diaspora_render( $post_to_diaspora ) {
546
		$label = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id ) ? __( 'Yes' ) : __( 'Post to diaspora*', 'wp-to-diaspora' );
547
		?>
548
		<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>
549
		<?php
550
	}
551
552
	/**
553
	 * Render the "Show 'Posted at' link" checkbox.
554
	 *
555
	 * @param bool $show_link If the checkbox is checked or not.
556
	 */
557
	public function fullentrylink_render( $show_link ) {
558
		$description = __( 'Include a link back to your original post.', 'wp-to-diaspora' );
559
		$checkbox = '<input type="checkbox" id="fullentrylink" name="wp_to_diaspora_settings[fullentrylink]" value="1"' . checked( $show_link, true, false ) . '>';
560
561
		if ( 'settings_page_wp_to_diaspora' === get_current_screen()->id ) : ?>
562
			<label><?php echo $checkbox; ?><?php esc_html_e( 'Yes' ); ?></label>
563
			<p class="description"><?php echo esc_html( $description ); ?></p>
564
		<?php else : ?>
565
			<label title="<?php echo esc_attr( $description ); ?>"><?php echo $checkbox; ?><?php esc_html_e( 'Show "Posted at" link?', 'wp-to-diaspora' ); ?></label>
566
		<?php endif;
567
	}
568
569
	/**
570
	 * Render the "Display" radio buttons.
571
	 *
572
	 * @param string $display The selected radio button.
573
	 */
574
	public function display_render( $display ) {
575
		?>
576
		<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 />
577
		<label><input type="radio" name="wp_to_diaspora_settings[display]" value="excerpt" <?php checked( $display, 'excerpt' ); ?>><?php esc_html_e( 'Excerpt' ); ?></label>
578
		<?php
579
	}
580
581
	/**
582
	 * Render the "Tags to post" field.
583
	 *
584
	 * @param array $tags_to_post The types of tags to be posted.
585
	 */
586
	public function tags_to_post_render( $tags_to_post ) {
587
		$on_settings_page = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id );
588
		$description = esc_html__( 'Choose which tags should be posted to diaspora*.', 'wp-to-diaspora' );
589
590
		if ( ! $on_settings_page ) {
591
			echo '<label>' . esc_html( $description );
592
		}
593
594
		?>
595
		<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][]">
596
			<option value="global" <?php selected( in_array( 'global', $tags_to_post ) ); ?>><?php esc_html_e( 'Global tags', 'wp-to-diaspora' ); ?></option>
597
			<option value="custom" <?php selected( in_array( 'custom', $tags_to_post ) ); ?>><?php esc_html_e( 'Custom tags', 'wp-to-diaspora' ); ?></option>
598
			<option value="post"   <?php selected( in_array( 'post',   $tags_to_post ) ); ?>><?php esc_html_e( 'Post tags',   'wp-to-diaspora' ); ?></option>
599
		</select>
600
601
		<?php if ( $on_settings_page ) : ?>
602
			<p class="description"><?php echo esc_html( $description ); ?></p>
603
		<?php else : ?>
604
			</label>
605
		<?php endif;
606
	}
607
608
	/**
609
	 * Render the "Global tags" field.
610
	 *
611
	 * @param array $tags The global tags to be posted.
612
	 */
613 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...
614
		WP2D_Helpers::arr_to_str( $tags );
615
		?>
616
		<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">
617
		<p class="description"><?php esc_html_e( 'Custom tags to add to all posts being posted to diaspora*.', 'wp-to-diaspora' ); ?></p>
618
		<?php
619
	}
620
621
	/**
622
	 * Render the "Custom tags" field.
623
	 *
624
	 * @param array $tags The custom tags to be posted.
625
	 */
626 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...
627
		WP2D_Helpers::arr_to_str( $tags );
628
		?>
629
		<label title="<?php esc_attr_e( 'Custom tags to add to this post when it\'s posted to diaspora*.', 'wp-to-diaspora' ); ?>">
630
			<?php esc_html_e( 'Custom tags', 'wp-to-diaspora' ); ?>
631
			<input type="text" class="wp2dtags" name="wp_to_diaspora_settings[custom_tags]" value="<?php echo esc_attr( $tags ); ?>" class="widefat">
632
		</label>
633
		<p class="description"><?php esc_html_e( 'Separate tags with commas' ); ?></p>
634
		<?php
635
	}
636
637
	/**
638
	 * Render the "Aspects" and "Services" checkboxes.
639
	 *
640
	 * @param array $args Array containing the type and items to output as checkboxes.
641
	 */
642
	public function aspects_services_render( $args ) {
643
		list( $type, $items ) = $args;
644
645
		$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...
646
		$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...
647
		$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...
648
649
		// This is where the 2 types show their differences.
650
		switch ( $type ) {
651
			case 'aspects':
652
				$refresh_button = __( 'Refresh Aspects', 'wp-to-diaspora' );
653
				$description    = esc_html__( 'Choose which aspects to share to.', 'wp-to-diaspora' );
654
				$empty_label    = '<input type="checkbox" name="wp_to_diaspora_settings[aspects][]" value="public" checked="checked">' . esc_html__( 'Public' );
655
				break;
656
657
			case 'services':
658
				$refresh_button = __( 'Refresh Services', 'wp-to-diaspora' );
659
				$description    = sprintf( '%1$s<br><a href="%2$s" target="_blank">%3$s</a>',
660
					esc_html__( 'Choose which services to share to.', 'wp-to-diaspora' ),
661
					esc_url( 'https://' . $this->get_option( 'pod' ) . '/services' ),
662
					esc_html__( 'Show available services on my pod.', 'wp-to-diaspora' )
663
				);
664
				$empty_label    = esc_html__( 'No services connected yet.', 'wp-to-diaspora' );
665
				break;
666
667
			default:
668
				return;
669
		}
670
671
		$items = array_filter( (array) $items ) ?: array();
672
673
		// Special case for this field if it's displayed on the settings page.
674
		$on_settings_page = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id );
675
676
		if ( ! $on_settings_page ) {
677
			echo $description;
678
			$description = '';
679
		}
680
681
		?>
682
		<div id="<?php echo esc_attr( $type ); ?>-container" data-<?php echo esc_attr( $type ); ?>-selected="<?php echo esc_attr( implode( ',', $items ) ); ?>">
683
			<?php if ( $list = (array) $this->get_option( $type . '_list' ) ) : ?>
684
				<?php foreach ( $list as $id => $name ) : ?>
685
					<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>
686
				<?php endforeach; ?>
687
			<?php else : ?>
688
				<label><?php echo $empty_label; ?></label>
689
			<?php endif; ?>
690
		</div>
691
		<p class="description">
692
			<?php echo $description; ?>
693
			<a id="refresh-<?php echo esc_attr( $type ); ?>-list" class="button hide-if-no-js"><?php echo esc_html( $refresh_button ); ?></a>
694
			<span class="spinner"></span>
695
			<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>
696
		</p>
697
		<?php
698
	}
699
700
701
	/**
702
	 * Get a specific option.
703
	 *
704
	 * @param string       $option  ID of option to get.
705
	 * @param array|string $default Override default value if option not found.
706
	 * @return array|string Requested option value.
707
	 */
708
	public function get_option( $option = null, $default = null ) {
709
		if ( ! isset( self::$_options ) ) {
710
			self::$_options = get_option( 'wp_to_diaspora_settings', self::$_default_options );
711
		}
712
		if ( isset( $option ) ) {
713
			if ( isset( self::$_options[ $option ] ) ) {
714
				// Return found option value.
715
				return self::$_options[ $option ];
716
			} elseif ( isset( $default ) ) {
717
				// Return overridden default value.
718
				return $default;
719
			} elseif ( isset( self::$_default_options[ $option ] ) ) {
720
				// Return default option value.
721
				return self::$_default_options[ $option ];
722
			}
723
		}
724
	}
725
726
	/**
727
	 * Get all options.
728
	 *
729
	 * @return array All the options.
730
	 */
731
	public function get_options() {
732
		return self::$_options;
733
	}
734
735
	/**
736
	 * Set a certain option.
737
	 *
738
	 * @param string       $option ID of option to get.
739
	 * @param array|string $value  Value to be set for the passed option.
740
	 * @param boolean      $save   Save the options immediately after setting them.
741
	 */
742
	public function set_option( $option, $value, $save = false ) {
743
		if ( isset( $option ) ) {
744
			if ( isset( $value ) ) {
745
				self::$_options[ $option ] = $value;
746
			} else {
747
				unset( self::$_options[ $option ] );
748
			}
749
		}
750
		if ( $save ) {
751
			self::save();
752
		}
753
	}
754
755
	/**
756
	 * Save the options.
757
	 */
758
	public function save() {
759
		update_option( 'wp_to_diaspora_settings', self::$_options );
760
	}
761
762
	/**
763
	 * Get all valid input values for the passed field.
764
	 *
765
	 * @param string $field Field to get the valid values for.
766
	 * @return array List of valid values.
767
	 */
768
	public function get_valid_values( $field ) {
769
		if ( array_key_exists( $field, self::$_valid_values ) ) {
770
			return self::$_valid_values[ $field ];
771
		}
772
	}
773
774
	/**
775
	 * Check if a value is valid for the passed field.
776
	 *
777
	 * @param string $field Field to check the valid value for.
778
	 * @param object $value Value to check validity.
779
	 * @return boolean If the passed value is valid.
780
	 */
781
	public function is_valid_value( $field, $value ) {
782
		if ( $valids = self::get_valid_values( $field ) ) {
783
			return ( in_array( $value, $valids ) );
784
		}
785
		return false;
786
	}
787
788
	/**
789
	 * Validate all settings.
790
	 *
791
	 * @param array $input RAW input values.
792
	 * @return array Validated input values.
793
	 */
794
	public function validate_settings( $input ) {
795
		/* Validate all settings before saving to the database. */
796
797
		// Saving the pod setup details.
798
		if ( isset( $input['submit_setup'] ) ) {
799
			$input['pod']      = trim( sanitize_text_field( $input['pod'] ), ' /' );
800
			$input['username'] = sanitize_text_field( $input['username'] );
801
			$input['password'] = sanitize_text_field( $input['password'] );
802
803
			// If password is blank, it hasn't been changed.
804
			// 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.
805
			if ( '' === $input['password'] || $this->get_option( 'password' ) === $input['password'] ) {
806
				$input['password'] = $this->get_option( 'password' );
807
			} else {
808
				$input['password'] = WP2D_Helpers::encrypt( $input['password'] );
809
			}
810
811
			// This is for when JS in not enabled, to make sure that the aspects and services
812
			// are refetched when displaying the options page after saving.
813
			if ( isset( $input['no_js'] ) ) {
814
				set_transient( 'wp2d_no_js_force_refetch', true );
815
			}
816
		}
817
818
		// Saving the default options.
819
		if ( isset( $input['submit_defaults'] ) ) {
820
			if ( ! isset( $input['enabled_post_types'] ) ) {
821
				$input['enabled_post_types'] = array();
822
			}
823
824
			// Checkboxes.
825
			$this->validate_checkboxes( array( 'post_to_diaspora', 'fullentrylink' ), $input );
826
827
			// Single Selects.
828
			$this->validate_single_selects( 'display', $input );
829
830
			// Multiple Selects.
831
			$this->validate_multi_selects( 'tags_to_post', $input );
832
833
			// Get unique, non-empty, trimmed tags and clean them up.
834
			$this->validate_tags( $input['global_tags'] );
835
836
			// Clean up the list of aspects. If the list is empty, only use the 'Public' aspect.
837
			$this->validate_aspects_services( $input['aspects'], array( 'public' ) );
838
839
			// Clean up the list of services.
840
			$this->validate_aspects_services( $input['services'] );
841
		}
842
843
		// Reset to defaults.
844
		if ( isset( $input['reset_defaults'] ) ) {
845
			// Set the input to the default options.
846
			$input = self::$_default_options;
847
848
			// Don't reset the fetched lists of aspects and services.
849
			unset( $input['aspects_list'] );
850
			unset( $input['services_list'] );
851
		}
852
853
		// Unset all unused input fields.
854
		unset( $input['submit_defaults'] );
855
		unset( $input['reset_defaults'] );
856
		unset( $input['submit_setup'] );
857
858
		// Parse inputs with default options and return.
859
		return wp_parse_args( $input, array_merge( self::$_default_options, self::$_options ) );
860
	}
861
862
	/**
863
	 * Validate checkboxes, make them either true or false.
864
	 *
865
	 * @param string|array $checkboxes Checkboxes to validate.
866
	 * @param array        $options    Options values themselves.
867
	 * @return array The validated options.
868
	 */
869
	public function validate_checkboxes( $checkboxes, &$options ) {
870
		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...
871
			$options[ $checkbox ] = isset( $options[ $checkbox ] );
872
		}
873
		return $options;
874
	}
875
876
	/**
877
	 * Validate single-select fields and make sure their selected value are valid.
878
	 *
879
	 * @param string|array $selects Name(s) of the select fields.
880
	 * @param array        $options Options values themselves.
881
	 * @return array The validated options.
882
	 */
883
	public function validate_single_selects( $selects, &$options ) {
884
		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...
885
			if ( isset( $options[ $select ] ) && ! $this->is_valid_value( $select, $options[ $select ] ) ) {
886
				unset( $options[ $select ] );
887
			}
888
		}
889
		return $options;
890
	}
891
892
	/**
893
	 * Validate multi-select fields and make sure their selected values are valid.
894
	 *
895
	 * @param string|array $selects Name(s) of the select fields.
896
	 * @param array        $options Options values themselves.
897
	 * @return array The validated options.
898
	 */
899
	public function validate_multi_selects( $selects, &$options ) {
900
		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...
901
			if ( isset( $options[ $select ] ) ) {
902
				foreach ( (array) $options[ $select ] as $option_value ) {
903
					if ( ! $this->is_valid_value( $select, $option_value ) ) {
904
						unset( $options[ $select ] );
905
						break;
906
					}
907
				}
908
			} else {
909
				$options[ $select ] = array();
910
			}
911
		}
912
		return $options;
913
	}
914
915
	/**
916
	 * Clean up the passed tags. Keep only alphanumeric, hyphen and underscore characters.
917
	 *
918
	 * @param array|string $tags Tags to be cleaned as array or comma seperated values.
919
	 * @return array The cleaned tags.
920
	 */
921
	public function validate_tags( &$tags ) {
922
		WP2D_Helpers::str_to_arr( $tags );
923
924
		$tags = array_map( array( $this, 'validate_tag' ),
925
			array_unique(
926
				array_filter( $tags, 'trim' )
927
			)
928
		);
929
		return $tags;
930
	}
931
932
	/**
933
	 * Clean up the passed tag. Keep only alphanumeric, hyphen and underscore characters.
934
	 *
935
	 * @todo What about eastern characters? (chinese, indian, etc.)
936
	 *
937
	 * @param string $tag Tag to be cleaned.
938
	 * @return string The clean tag.
939
	 */
940
	public function validate_tag( &$tag ) {
941
		$tag = preg_replace( '/[^\w $\-]/u', '', str_replace( ' ', '-', trim( $tag ) ) );
942
		return $tag;
943
	}
944
945
	/**
946
	 * Validate the passed aspects or services.
947
	 *
948
	 * @param array $aspects_services List of aspects or services that need to be validated.
949
	 * @param array $default          Default value if not valid.
950
	 * @return array The validated list of aspects or services.
951
	 */
952
	public function validate_aspects_services( &$aspects_services, $default = array() ) {
953
		if ( empty( $aspects_services ) || ! is_array( $aspects_services ) ) {
954
			$aspects_services = $default;
955
		} else {
956
			array_walk( $aspects_services, 'sanitize_text_field' );
957
		}
958
		return $aspects_services;
959
	}
960
}
961