WP2D_Options::defaults_section()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 27
rs 8.8571
cc 1
eloc 10
nc 1
nop 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 15 and the first side effect is on line 10.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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
		'pod_list'           => array(),
31
		'aspects_list'       => array(),
32
		'services_list'      => array(),
33
		'post_to_diaspora'   => true,
34
		'enabled_post_types' => array( 'post' ),
35
		'fullentrylink'      => true,
36
		'display'            => 'full',
37
		'tags_to_post'       => array( 'global', 'custom', 'post' ),
38
		'global_tags'        => '',
39
		'aspects'            => array( 'public' ),
40
		'services'           => array(),
41
		'version'            => WP2D_VERSION,
42
	);
43
44
	/**
45
	 * Valid values for select fields.
46
	 *
47
	 * @var array
48
	 */
49
	private static $_valid_values = array(
50
		'display'      => array( 'full', 'excerpt' ),
51
		'tags_to_post' => array( 'global', 'custom', 'post' ),
52
	);
53
54
	/**
55
	 * All plugin options.
56
	 *
57
	 * @var array
58
	 */
59
	private static $_options = null;
60
61
	/** Singleton, keep private. */
62
	final private function __clone() { }
63
64
	/** Singleton, keep private. */
65
	final private function __construct() { }
66
67
	/** Singleton, keep private. */
68
	final private function __wakeup() { }
69
70
	/**
71
	 * Create / Get the instance of this class.
72
	 *
73
	 * @return WP2D_Options Instance of this class.
74
	 */
75 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...
76
		if ( ! isset( self::$_instance ) ) {
77
			self::$_instance = new self();
78
			self::$_instance->_setup();
79
		}
80
		return self::$_instance;
81
	}
82
83
	/**
84
	 * Set up the options menu.
85
	 */
86
	private function _setup() {
87
88
		// Populate options array.
89
		$this->get_option();
90
91
		// Setup Options page and Contextual Help.
92
		add_action( 'admin_menu', array( $this, 'setup_wpadmin_pages' ) );
93
94
		// Register all settings.
95
		add_action( 'admin_init', array( $this, 'register_settings' ) );
96
	}
97
98
99
	/**
100
	 * Get the currently selected tab.
101
	 *
102
	 * @todo Multi-level if statement to make it look prettier.
103
	 *
104
	 * @param string $default Tab to select if the current selection is invalid.
105
	 * @return string Return the currently selected tab.
106
	 */
107
	private function _current_tab( $default = 'defaults' ) {
0 ignored issues
show
Coding Style introduced by
_current_tab uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
108
		$tab = ( isset ( $_GET['tab'] ) ? $_GET['tab'] : $default );
109
110
		// If the pod settings aren't configured yet, open the 'Setup' tab.
111
		if ( ! $this->is_pod_set_up() ) {
112
			$tab = 'setup';
113
		}
114
115
		return $tab;
116
	}
117
118
	/**
119
	 * Output all options tabs and return an array of them all, if requested by $return.
120
	 *
121
	 * @param bool $return Define if the options tabs should be returned.
122
	 * @return array (If requested) An array of the outputted options tabs.
123
	 */
124
	private function _options_page_tabs( $return = false ) {
125
		// The array defining all options sections to be shown as tabs.
126
		$tabs = array();
127
		if ( $this->is_pod_set_up() ) {
128
			$tabs['defaults'] = __( 'Defaults', 'wp-to-diaspora' );
129
		}
130
131
		// Add the 'Setup' tab to the end of the list.
132
		$tabs['setup'] = __( 'Setup', 'wp-to-diaspora' ) . '<span id="pod-connection-status" class="dashicons-before hidden"></span><span class="spinner"></span>';
133
134
		// Container for all options tabs.
135
		$out = '<h2 id="options-tabs" class="nav-tab-wrapper">';
136
		foreach ( $tabs as $tab => $name ) {
137
			// The tab link.
138
			$out .= '<a class="nav-tab' . ( ( $tab === $this->_current_tab() ) ? ' nav-tab-active' : '' ) . '" href="?page=wp_to_diaspora&tab=' . $tab . '">' . $name . '</a>';
139
		}
140
		$out .= '</h2>';
141
142
		// Output the container with all tabs.
143
		echo $out;
144
145
		// Check if the tabs should be returned.
146
		if ( $return ) {
147
			return $tabs;
148
		}
149
	}
150
151
152
	/**
153
	 * Set up admin options page.
154
	 */
155
	public function admin_options_page() {
0 ignored issues
show
Coding Style introduced by
admin_options_page uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
156
		?>
157
		<div class="wrap">
158
			<h2>WP to diaspora*</h2>
159
160
			<div id="wp2d-message" class="notice hidden" <?php echo ( defined( 'WP2D_DEBUGGING' ) ) ? ' data-debugging' : ''; ?>></div>
161
162
			<?php
163
			// Check the connection status to diaspora.
164
			if ( ! $this->is_pod_set_up() ) {
165
				add_settings_error(
166
					'wp_to_diaspora_settings',
167
					'wp_to_diaspora_connected',
168
					__( 'First of all, set up the connection to your pod below.', 'wp-to-diaspora' ),
169
					'updated'
170
				);
171
			} else {
172
				// Get initial aspects list and connected services.
173
				// DON'T check for empty services list here!!
174
				// It could always be empty, resulting in this code being run every time the page is loaded.
175
				// The aspects will at least have a "Public" entry after the initial fetch.
176
				$aspects_list = $this->get_option( 'aspects_list' );
177
				if ( ( $force = get_transient( 'wp2d_no_js_force_refetch' ) ) || empty( $aspects_list ) ) {
178
179
					// Set up the connection to diaspora*.
180
					$api = WP2D_Helpers::api_quick_connect();
181
					if ( ! $api->has_last_error() ) {
182
						// Get the loaded aspects.
183
						if ( is_array( $aspects = $api->get_aspects() ) ) {
184
							// Save the new list of aspects.
185
							$this->set_option( 'aspects_list', $aspects );
186
						}
187
188
						// Get the loaded services.
189
						if ( is_array( $services = $api->get_services() ) ) {
190
							// Save the new list of services.
191
							$this->set_option( 'services_list', $services );
192
						}
193
194
						$this->save();
195
					}
196
197
					if ( $force ) {
198
						delete_transient( 'wp2d_no_js_force_refetch' );
199
						$message = ( ! $api->has_last_error() ) ? __( 'Connection successful.', 'wp-to-diaspora' ) : $api->get_last_error();
200
						add_settings_error(
201
							'wp_to_diaspora_settings',
202
							'wp_to_diaspora_connected',
203
							$message,
204
							( ! $api->has_last_error() ) ? 'updated' : 'error'
205
						);
206
					}
207
				}
208
209
				// Attempt to get the cacert.pem file and save it to the plugin's root directory.
210
				if ( isset( $_GET['wp2d_temp_ssl_fix'] ) ) {
211
					$cacert_file = file_get_contents( 'http://curl.haxx.se/ca/cacert.pem' );
212
					if ( $cacert_file && file_put_contents( WP2D_DIR . '/cacert.pem', $cacert_file ) ) {
213
						add_settings_error(
214
							'wp_to_diaspora_settings',
215
							'wp_to_diaspora_temp_ssl_fix',
216
							__( 'Successfully saved cacert.pem!', 'wp-to-diaspora' ),
217
							'updated'
218
						);
219
					} else {
220
						add_settings_error(
221
							'wp_to_diaspora_settings',
222
							'wp_to_diaspora_temp_ssl_fix',
223
							__( 'Failed to save cacert.pem!', 'wp-to-diaspora' ),
224
							'error'
225
						);
226
					}
227
				}
228
			}
229
230
			// Output success or error message.
231
			settings_errors( 'wp_to_diaspora_settings' );
232
			?>
233
234
			<?php $page_tabs = array_keys( $this->_options_page_tabs( true ) ); ?>
235
236
			<form action="options.php" method="post">
237
				<input id="wp2d_no_js" type="hidden" name="wp_to_diaspora_settings[no_js]" value="1">
238
				<?php
239
				// Load the settings fields.
240
				settings_fields( 'wp_to_diaspora_settings' );
241
				do_settings_sections( 'wp_to_diaspora_settings' );
242
243
				// Get the name of the current tab, if set, else take the first one from the list.
244
				$tab = $this->_current_tab( $page_tabs[0] );
245
246
				// Add Save and Reset buttons.
247
				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;';
248
				if ( 'setup' !== $tab ) {
249
					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' ) . '" />';
250
				}
251
				?>
252
253
			</form>
254
		</div>
255
256
		<?php
257
	}
258
259
	/**
260
	 * Return if the settings for the pod setup have been entered.
261
	 *
262
	 * @return boolean If the setup for the pod has been done.
263
	 */
264
	public function is_pod_set_up() {
265
		return ( $this->get_option( 'pod' ) && $this->get_option( 'username' ) && $this->get_option( 'password' ) );
266
	}
267
268
	/**
269
	 * Setup Contextual Help and Options pages.
270
	 */
271
	public function setup_wpadmin_pages() {
272
		// Add options page.
273
		$hook = add_options_page( 'WP to diaspora*', 'WP to diaspora*', 'manage_options', 'wp_to_diaspora', array( $this, 'admin_options_page' ) );
274
275
		// Setup the contextual help menu after the options page has been loaded.
276
		add_action( 'load-' . $hook, array( 'WP2D_Contextual_Help', 'instance' ) );
277
278
		// Setup the contextual help menu tab for post types. Checks are made there!
279
		add_action( 'load-post.php', array( 'WP2D_Contextual_Help', 'instance' ) );
280
		add_action( 'load-post-new.php', array( 'WP2D_Contextual_Help', 'instance' ) );
281
	}
282
283
	/**
284
	 * Initialise the settings sections and fields of the currently selected tab.
285
	 */
286
	public function register_settings() {
287
		// Register the settings with validation callback.
288
		register_setting( 'wp_to_diaspora_settings', 'wp_to_diaspora_settings', array( $this, 'validate_settings' ) );
289
290
		// Load only the sections of the selected tab.
291
		switch ( $this->_current_tab() ) {
292
			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...
293
				// Add a "Defaults" section that contains all posting settings to be used by default.
294
				add_settings_section( 'wp_to_diaspora_defaults_section', __( 'Posting Defaults', 'wp-to-diaspora' ), array( $this, 'defaults_section' ), 'wp_to_diaspora_settings' );
295
				break;
296
			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...
297
				// Add a "Setup" section that contains the Pod domain, Username and Password.
298
				add_settings_section( 'wp_to_diaspora_setup_section', __( 'diaspora* Setup', 'wp-to-diaspora' ), array( $this, 'setup_section' ), 'wp_to_diaspora_settings' );
299
				break;
300
		}
301
	}
302
303
304
	/**
305
	 * Callback for the "Setup" section.
306
	 */
307
	public function setup_section() {
308
		esc_html_e( 'Set up the connection to your diaspora* account.', 'wp-to-diaspora' );
309
310
		// Pod entry field.
311
		add_settings_field( 'pod', __( 'Diaspora* Pod', 'wp-to-diaspora' ), array( $this, 'pod_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
312
313
		// Username entry field.
314
		add_settings_field( 'username', __( 'Username' ), array( $this, 'username_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
315
316
		// Password entry field.
317
		add_settings_field( 'password', __( 'Password' ), array( $this, 'password_render' ), 'wp_to_diaspora_settings', 'wp_to_diaspora_setup_section' );
318
	}
319
320
	/**
321
	 * Render the "Pod" field.
322
	 */
323
	public function pod_render() {
324
		?>
325
		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> <a id="refresh-pod-list" class="button hide-if-no-js"><?php esc_html_e( 'Refresh pod list', 'wp-to-diaspora' ); ?></a><span class="spinner"></span>
326
		<datalist id="pod-list">
327
		<?php foreach ( (array) $this->get_option( 'pod_list' ) as $pod ) : ?>
328
			<option data-secure="<?php echo esc_attr( $pod['secure'] ); ?>" value="<?php echo esc_attr( $pod['domain'] ); ?>"></option>
329
		<?php endforeach; ?>
330
		</datalist>
331
		<?php
332
	}
333
334
	/**
335
	 * Render the "Username" field.
336
	 */
337
	public function username_render() {
338
		?>
339
		<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>
340
		<?php
341
	}
342
343
	/**
344
	 * Render the "Password" field.
345
	 */
346
	public function password_render() {
347
		// Special case if we already have a password.
348
		$has_password = ( '' !== $this->get_option( 'password', '' ) );
349
		$placeholder  = ( $has_password ) ? __( 'Password already set.', 'wp-to-diaspora' ) : __( 'Password' );
350
		$required     = ( $has_password ) ? '' : ' required';
351
		?>
352
		<input type="password" name="wp_to_diaspora_settings[password]" value="" placeholder="<?php echo esc_attr( $placeholder ); ?>"<?php echo esc_attr( $required ); ?>>
353
		<?php if ( $has_password ) : ?>
354
			<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>
355
		<?php endif;
356
	}
357
358
359
	/**
360
	 * Callback for the "Defaults" section.
361
	 */
362
	public function defaults_section() {
363
		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' );
364
365
		// Post types field.
366
		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' );
367
368
		 // Post to diaspora* checkbox.
369
		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' ) );
370
371
		// Full entry link checkbox.
372
		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' ) );
373
374
		// Full text or excerpt radio buttons.
375
		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' ) );
376
377
		// Tags to post dropdown.
378
		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' ) );
379
380
		// Global tags field.
381
		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' ) );
382
383
		// Aspects checkboxes.
384
		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' ) ) );
385
386
		// Services checkboxes.
387
		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' ) ) );
388
	}
389
390
	/**
391
	 * Render the "Post types" checkboxes.
392
	 */
393
	public function post_types_render() {
394
		$post_types = get_post_types( array( 'public' => true ), 'objects' );
395
396
		// Remove excluded post types from the list.
397
		$excluded_post_types = array( 'attachment', 'nav_menu_item', 'revision' );
398
		foreach ( $excluded_post_types as $excluded ) {
399
			unset( $post_types[ $excluded ] );
400
		}
401
		?>
402
403
		<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][]">
404
		<?php foreach ( $post_types as $post_type ) : ?>
405
			<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>
406
		<?php endforeach; ?>
407
		</select>
408
409
		<p class="description"><?php esc_html_e( 'Choose which post types can be posted to diaspora*.', 'wp-to-diaspora' ); ?></p>
410
411
		<?php
412
	}
413
414
	/**
415
	 * Render the "Post to diaspora*" checkbox.
416
	 *
417
	 * @param bool $post_to_diaspora If this checkbox is checked or not.
418
	 */
419
	public function post_to_diaspora_render( $post_to_diaspora ) {
420
		$label = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id ) ? __( 'Yes' ) : __( 'Post to diaspora*', 'wp-to-diaspora' );
421
		?>
422
		<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>
423
		<?php
424
	}
425
426
	/**
427
	 * Render the "Show 'Posted at' link" checkbox.
428
	 *
429
	 * @param bool $show_link If the checkbox is checked or not.
430
	 */
431
	public function fullentrylink_render( $show_link ) {
432
		$description = __( 'Include a link back to your original post.', 'wp-to-diaspora' );
433
		$checkbox = '<input type="checkbox" id="fullentrylink" name="wp_to_diaspora_settings[fullentrylink]" value="1"' . checked( $show_link, true, false ) . '>';
434
435
		if ( 'settings_page_wp_to_diaspora' === get_current_screen()->id ) : ?>
436
			<label><?php echo $checkbox; ?><?php esc_html_e( 'Yes' ); ?></label>
437
			<p class="description"><?php echo esc_html( $description ); ?></p>
438
		<?php else : ?>
439
			<label title="<?php echo esc_attr( $description ); ?>"><?php echo $checkbox; ?><?php esc_html_e( 'Show "Posted at" link?', 'wp-to-diaspora' ); ?></label>
440
		<?php endif;
441
	}
442
443
	/**
444
	 * Render the "Display" radio buttons.
445
	 *
446
	 * @param string $display The selected radio button.
447
	 */
448
	public function display_render( $display ) {
449
		?>
450
		<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 />
451
		<label><input type="radio" name="wp_to_diaspora_settings[display]" value="excerpt" <?php checked( $display, 'excerpt' ); ?>><?php esc_html_e( 'Excerpt' ); ?></label>
452
		<?php
453
	}
454
455
	/**
456
	 * Render the "Tags to post" field.
457
	 *
458
	 * @param array $tags_to_post The types of tags to be posted.
459
	 */
460
	public function tags_to_post_render( $tags_to_post ) {
461
		$on_settings_page = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id );
462
		$description = esc_html__( 'Choose which tags should be posted to diaspora*.', 'wp-to-diaspora' );
463
464
		if ( ! $on_settings_page ) {
465
			echo '<label>' . esc_html( $description );
466
		}
467
468
		?>
469
		<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][]">
470
			<option value="global" <?php selected( in_array( 'global', $tags_to_post ) ); ?>><?php esc_html_e( 'Global tags', 'wp-to-diaspora' ); ?></option>
471
			<option value="custom" <?php selected( in_array( 'custom', $tags_to_post ) ); ?>><?php esc_html_e( 'Custom tags', 'wp-to-diaspora' ); ?></option>
472
			<option value="post"   <?php selected( in_array( 'post',   $tags_to_post ) ); ?>><?php esc_html_e( 'Post tags',   'wp-to-diaspora' ); ?></option>
473
		</select>
474
475
		<?php if ( $on_settings_page ) : ?>
476
			<p class="description"><?php echo esc_html( $description ); ?></p>
477
		<?php else : ?>
478
			</label>
479
		<?php endif;
480
	}
481
482
	/**
483
	 * Render the "Global tags" field.
484
	 *
485
	 * @param array $tags The global tags to be posted.
486
	 */
487 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...
488
		WP2D_Helpers::arr_to_str( $tags );
489
		?>
490
		<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">
491
		<p class="description"><?php esc_html_e( 'Custom tags to add to all posts being posted to diaspora*.', 'wp-to-diaspora' ); ?></p>
492
		<?php
493
	}
494
495
	/**
496
	 * Render the "Custom tags" field.
497
	 *
498
	 * @param array $tags The custom tags to be posted.
499
	 */
500 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...
501
		WP2D_Helpers::arr_to_str( $tags );
502
		?>
503
		<label title="<?php esc_attr_e( 'Custom tags to add to this post when it\'s posted to diaspora*.', 'wp-to-diaspora' ); ?>">
504
			<?php esc_html_e( 'Custom tags', 'wp-to-diaspora' ); ?>
505
			<input type="text" class="wp2dtags" name="wp_to_diaspora_settings[custom_tags]" value="<?php echo esc_attr( $tags ); ?>" class="widefat">
506
		</label>
507
		<p class="description"><?php esc_html_e( 'Separate tags with commas' ); ?></p>
508
		<?php
509
	}
510
511
	/**
512
	 * Render the "Aspects" and "Services" checkboxes.
513
	 *
514
	 * @param array $args Array containing the type and items to output as checkboxes.
515
	 */
516
	public function aspects_services_render( $args ) {
517
		list( $type, $items ) = $args;
518
519
		$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...
520
		$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...
521
		$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...
522
523
		// This is where the 2 types show their differences.
524
		switch ( $type ) {
525
			case 'aspects':
526
				$refresh_button = __( 'Refresh Aspects', 'wp-to-diaspora' );
527
				$description    = esc_html__( 'Choose which aspects to share to.', 'wp-to-diaspora' );
528
				$empty_label    = '<input type="checkbox" name="wp_to_diaspora_settings[aspects][]" value="public" checked="checked">' . esc_html__( 'Public' );
529
				break;
530
531
			case 'services':
532
				$refresh_button = __( 'Refresh Services', 'wp-to-diaspora' );
533
				$description    = sprintf( '%1$s<br><a href="%2$s" target="_blank">%3$s</a>',
534
					esc_html__( 'Choose which services to share to.', 'wp-to-diaspora' ),
535
					esc_url( 'https://' . $this->get_option( 'pod' ) . '/services' ),
536
					esc_html__( 'Show available services on my pod.', 'wp-to-diaspora' )
537
				);
538
				$empty_label    = esc_html__( 'No services connected yet.', 'wp-to-diaspora' );
539
				break;
540
541
			default:
542
				return;
543
		}
544
545
		$items = array_filter( (array) $items ) ?: array();
546
547
		// Special case for this field if it's displayed on the settings page.
548
		$on_settings_page = ( 'settings_page_wp_to_diaspora' === get_current_screen()->id );
549
550
		if ( ! $on_settings_page ) {
551
			echo $description;
552
			$description = '';
553
		}
554
555
		?>
556
		<div id="<?php echo esc_attr( $type ); ?>-container" data-<?php echo esc_attr( $type ); ?>-selected="<?php echo esc_attr( implode( ',', $items ) ); ?>">
557
			<?php if ( $list = (array) $this->get_option( $type . '_list' ) ) : ?>
558
				<?php foreach ( $list as $id => $name ) : ?>
559
					<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>
560
				<?php endforeach; ?>
561
			<?php else : ?>
562
				<label><?php echo $empty_label; ?></label>
563
			<?php endif; ?>
564
		</div>
565
		<p class="description">
566
			<?php echo $description; ?>
567
			<a id="refresh-<?php echo esc_attr( $type ); ?>-list" class="button hide-if-no-js"><?php echo esc_html( $refresh_button ); ?></a>
568
			<span class="spinner"></span>
569
			<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>
570
		</p>
571
		<?php
572
	}
573
574
575
	/**
576
	 * Get a specific option.
577
	 *
578
	 * @param string       $option  ID of option to get.
579
	 * @param array|string $default Override default value if option not found.
580
	 * @return array|string Requested option value.
581
	 */
582
	public function get_option( $option = null, $default = null ) {
583
		if ( ! isset( self::$_options ) ) {
584
			self::$_options = get_option( 'wp_to_diaspora_settings', self::$_default_options );
585
		}
586
		if ( isset( $option ) ) {
587
			if ( isset( self::$_options[ $option ] ) ) {
588
				// Return found option value.
589
				return self::$_options[ $option ];
590
			} elseif ( isset( $default ) ) {
591
				// Return overridden default value.
592
				return $default;
593
			} elseif ( isset( self::$_default_options[ $option ] ) ) {
594
				// Return default option value.
595
				return self::$_default_options[ $option ];
596
			}
597
		}
598
	}
599
600
	/**
601
	 * Get all options.
602
	 *
603
	 * @return array All the options.
604
	 */
605
	public function get_options() {
606
		return self::$_options;
607
	}
608
609
	/**
610
	 * Set a certain option.
611
	 *
612
	 * @param string       $option ID of option to get.
613
	 * @param array|string $value  Value to be set for the passed option.
614
	 * @param boolean      $save   Save the options immediately after setting them.
615
	 */
616
	public function set_option( $option, $value, $save = false ) {
617
		if ( isset( $option ) ) {
618
			if ( isset( $value ) ) {
619
				self::$_options[ $option ] = $value;
620
			} else {
621
				unset( self::$_options[ $option ] );
622
			}
623
		}
624
		if ( $save ) {
625
			self::save();
626
		}
627
	}
628
629
	/**
630
	 * Save the options.
631
	 */
632
	public function save() {
633
		update_option( 'wp_to_diaspora_settings', self::$_options );
634
	}
635
636
	/**
637
	 * Get all valid input values for the passed field.
638
	 *
639
	 * @param string $field Field to get the valid values for.
640
	 * @return array List of valid values.
641
	 */
642
	public function get_valid_values( $field ) {
643
		if ( array_key_exists( $field, self::$_valid_values ) ) {
644
			return self::$_valid_values[ $field ];
645
		}
646
	}
647
648
	/**
649
	 * Check if a value is valid for the passed field.
650
	 *
651
	 * @param string $field Field to check the valid value for.
652
	 * @param object $value Value to check validity.
653
	 * @return boolean If the passed value is valid.
654
	 */
655
	public function is_valid_value( $field, $value ) {
656
		if ( $valids = self::get_valid_values( $field ) ) {
657
			return ( in_array( $value, $valids ) );
658
		}
659
		return false;
660
	}
661
662
	/**
663
	 * Validate all settings.
664
	 *
665
	 * @param array $input RAW input values.
666
	 * @return array Validated input values.
667
	 */
668
	public function validate_settings( $input ) {
669
		/* Validate all settings before saving to the database. */
670
671
		// Saving the pod setup details.
672
		if ( isset( $input['submit_setup'] ) ) {
673
			$input['pod']      = trim( sanitize_text_field( $input['pod'] ), ' /' );
674
			$input['username'] = sanitize_text_field( $input['username'] );
675
			$input['password'] = sanitize_text_field( $input['password'] );
676
677
			// If password is blank, it hasn't been changed.
678
			// 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.
679
			if ( '' === $input['password'] || $this->get_option( 'password' ) === $input['password'] ) {
680
				$input['password'] = $this->get_option( 'password' );
681
			} else {
682
				$input['password'] = WP2D_Helpers::encrypt( $input['password'] );
683
			}
684
685
			// This is for when JS in not enabled, to make sure that the aspects and services
686
			// are refetched when displaying the options page after saving.
687
			if ( isset( $input['no_js'] ) ) {
688
				set_transient( 'wp2d_no_js_force_refetch', true );
689
			}
690
		}
691
692
		// Saving the default options.
693
		if ( isset( $input['submit_defaults'] ) ) {
694
			if ( ! isset( $input['enabled_post_types'] ) ) {
695
				$input['enabled_post_types'] = array();
696
			}
697
698
			// Checkboxes.
699
			$this->validate_checkboxes( array( 'post_to_diaspora', 'fullentrylink' ), $input );
700
701
			// Single Selects.
702
			$this->validate_single_selects( 'display', $input );
703
704
			// Multiple Selects.
705
			$this->validate_multi_selects( 'tags_to_post', $input );
706
707
			// Get unique, non-empty, trimmed tags and clean them up.
708
			$this->validate_tags( $input['global_tags'] );
709
710
			// Clean up the list of aspects. If the list is empty, only use the 'Public' aspect.
711
			$this->validate_aspects_services( $input['aspects'], array( 'public' ) );
712
713
			// Clean up the list of services.
714
			$this->validate_aspects_services( $input['services'] );
715
		}
716
717
		// Reset to defaults.
718
		if ( isset( $input['reset_defaults'] ) ) {
719
			// Set the input to the default options.
720
			$input = self::$_default_options;
721
722
			// Don't reset the fetched lists of pods, aspects and services.
723
			unset( $input['pod_list'] );
724
			unset( $input['aspects_list'] );
725
			unset( $input['services_list'] );
726
		}
727
728
		// Unset all unused input fields.
729
		unset( $input['submit_defaults'] );
730
		unset( $input['reset_defaults'] );
731
		unset( $input['submit_setup'] );
732
733
		// Parse inputs with default options and return.
734
		return wp_parse_args( $input, array_merge( self::$_default_options, self::$_options ) );
735
	}
736
737
	/**
738
	 * Validate checkboxes, make them either true or false.
739
	 *
740
	 * @param string|array $checkboxes Checkboxes to validate.
741
	 * @param array        $options    Options values themselves.
742
	 * @return array The validated options.
743
	 */
744
	public function validate_checkboxes( $checkboxes, &$options ) {
745
		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...
746
			$options[ $checkbox ] = isset( $options[ $checkbox ] );
747
		}
748
		return $options;
749
	}
750
751
	/**
752
	 * Validate single-select fields and make sure their selected value are valid.
753
	 *
754
	 * @param string|array $selects Name(s) of the select fields.
755
	 * @param array        $options Options values themselves.
756
	 * @return array The validated options.
757
	 */
758
	public function validate_single_selects( $selects, &$options ) {
759
		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...
760
			if ( isset( $options[ $select ] ) && ! $this->is_valid_value( $select, $options[ $select ] ) ) {
761
				unset( $options[ $select ] );
762
			}
763
		}
764
		return $options;
765
	}
766
767
	/**
768
	 * Validate multi-select fields and make sure their selected values are valid.
769
	 *
770
	 * @param string|array $selects Name(s) of the select fields.
771
	 * @param array        $options Options values themselves.
772
	 * @return array The validated options.
773
	 */
774
	public function validate_multi_selects( $selects, &$options ) {
775
		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...
776
			if ( isset( $options[ $select ] ) ) {
777
				foreach ( (array) $options[ $select ] as $option_value ) {
778
					if ( ! $this->is_valid_value( $select, $option_value ) ) {
779
						unset( $options[ $select ] );
780
						break;
781
					}
782
				}
783
			} else {
784
				$options[ $select ] = array();
785
			}
786
		}
787
		return $options;
788
	}
789
790
	/**
791
	 * Clean up the passed tags. Keep only alphanumeric, hyphen and underscore characters.
792
	 *
793
	 * @param array|string $tags Tags to be cleaned as array or comma seperated values.
794
	 * @return array The cleaned tags.
795
	 */
796
	public function validate_tags( &$tags ) {
797
		WP2D_Helpers::str_to_arr( $tags );
798
799
		$tags = array_map( array( $this, 'validate_tag' ),
800
			array_unique(
801
				array_filter( $tags, 'trim' )
802
			)
803
		);
804
		return $tags;
805
	}
806
807
	/**
808
	 * Clean up the passed tag. Keep only alphanumeric, hyphen and underscore characters.
809
	 *
810
	 * @todo What about eastern characters? (chinese, indian, etc.)
811
	 *
812
	 * @param string $tag Tag to be cleaned.
813
	 * @return string The clean tag.
814
	 */
815
	public function validate_tag( &$tag ) {
816
		$tag = preg_replace( '/[^\w $\-]/u', '', str_replace( ' ', '-', trim( $tag ) ) );
817
		return $tag;
818
	}
819
820
	/**
821
	 * Validate the passed aspects or services.
822
	 *
823
	 * @param array $aspects_services List of aspects or services that need to be validated.
824
	 * @param array $default          Default value if not valid.
825
	 * @return array The validated list of aspects or services.
826
	 */
827
	public function validate_aspects_services( &$aspects_services, $default = array() ) {
828
		if ( empty( $aspects_services ) || ! is_array( $aspects_services ) ) {
829
			$aspects_services = $default;
830
		} else {
831
			array_walk( $aspects_services, 'sanitize_text_field' );
832
		}
833
		return $aspects_services;
834
	}
835
}
836