Issues (2873)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

components/I18n/I18n.php (25 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 28 and the first side effect is on line 19.

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
 * Name: Translate Pods Admin
4
 *
5
 * Menu Name: Translate Pods
6
 *
7
 * Description: Allow UI of Pods and fields to be translated
8
 *
9
 * Version: 0.1
10
 *
11
 * Category: I18n
12
 *
13
 * Class: Pods_Component_I18n
14
 *
15
 * @package    Pods\Components
16
 * @subpackage i18n
17
 */
18
19
! defined( 'ABSPATH' ) and die();
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
20
21
if ( class_exists( 'Pods_Component_I18n' ) ) {
22
	return;
23
}
24
25
/**
26
 * Class Pods_Component_I18n
27
 */
28
class Pods_Component_I18n extends PodsComponent {
29
30
	public $settings             = array();
31
	public $locale               = null;
32
	public $languages            = array();
33
	public $languages_available  = array();
34
	public $languages_translated = array();
35
	public $cur_pod              = null;
36
	public $option_key           = 'pods_component_i18n_settings';
37
	public $admin_page           = 'pods-component-translate-pods-admin';
38
	public $capability           = 'pods_i18n_activate_lanuages';
39
	public $nonce                = 'pods_i18n_activate_lanuages';
40
41
	public $translatable_fields = array(
42
		'label',
43
		'description',
44
		'placeholder',
45
		'menu_name',
46
		'name_admin_bar',
47
		'pick_select_text',
48
		'file_add_button',
49
		'file_modal_title',
50
		'file_modal_add_button',
51
	);
52
53
	/**
54
	 * {@inheritdoc}
55
	 */
56
	public function init() {
57
58
		$this->settings = get_option( $this->option_key, array() );
59
60
		// Polylang
61
		if ( function_exists( 'PLL' ) && file_exists( plugin_dir_path( __FILE__ ) . 'I18n-polylang.php' ) ) {
62
			include_once plugin_dir_path( __FILE__ ) . 'I18n-polylang.php';
63
		}
64
		// WPML
65
		if ( did_action( 'wpml_loaded' ) && file_exists( plugin_dir_path( __FILE__ ) . 'I18n-wpml.php' ) ) {
66
			include_once plugin_dir_path( __FILE__ ) . 'I18n-wpml.php';
67
		}
68
69
		$active = false;
70
		// Are there active languages?
71
		if ( ! empty( $this->settings['enabled_languages'] ) ) {
72
			$this->languages = $this->settings['enabled_languages'];
73
			$this->locale    = get_locale();
74
			$active          = true;
75
		}
76
77
		$is_component_page = false;
78
		$is_pods_edit_page = false;
79
80
		if ( is_admin() && isset( $_GET['page'] ) ) {
81
82
			// Is the current page the admin page of this component or a Pods edit page?
83
			if ( $_GET['page'] === $this->admin_page ) {
84
				$is_component_page = true;
85
			} elseif ( $_GET['page'] === 'pods' ) {
0 ignored issues
show
Found "=== '". Use Yoda Condition checks, you must
Loading history...
86
				$is_pods_edit_page = true;
87
			}
88
89
			if ( $is_component_page || ( $is_pods_edit_page && $active ) ) {
90
				add_action( 'admin_enqueue_scripts', array( $this, 'admin_assets' ) );
91
			}
92
		}
93
94
		if ( $is_component_page ) {
95
			// Do save action here because otherwise the loading of post_types get done first and labels aren't translated
96
			if ( pods_is_admin( $this->capability ) && isset( $_POST['_nonce_i18n'] ) && wp_verify_nonce( $_POST['_nonce_i18n'], $this->nonce ) ) {
97
				$this->admin_save();
98
			}
99
		}
100
101
		if ( $active ) {
102
			// WP Object filters (post_type and taxonomy)
103
			add_filter( 'pods_register_post_type', array( $this, 'pods_register_wp_object_i18n' ), 10, 2 );
104
			add_filter( 'pods_register_taxonomy', array( $this, 'pods_register_wp_object_i18n' ), 10, 2 );
105
106
			// ACT's
107
			add_filter(
108
				'pods_advanced_content_type_pod_data', array(
109
					$this,
110
					'pods_filter_object_strings_i18n',
111
				), 10, 2
112
			);
113
114
			// Setting pages
115
			add_filter( 'pods_admin_menu_page_title', array( $this, 'admin_menu_page_title_i18n' ), 10, 2 );
116
			add_filter( 'pods_admin_menu_label', array( $this, 'admin_menu_label_i18n' ), 10, 2 );
117
118
			// Default filters for all fields
119
			add_filter( 'pods_form_ui_label_text', array( $this, 'fields_ui_label_text_i18n' ), 10, 4 );
120
			add_filter( 'pods_form_ui_comment_text', array( $this, 'fields_ui_comment_text_i18n' ), 10, 3 );
121
122
			foreach ( pods_form()->field_types() as $type => $data ) {
123
				add_filter(
124
					'pods_form_ui_field_' . $type . '_options', array(
125
						$this,
126
						'form_ui_field_options_i18n',
127
					), 10, 5
128
				);
129
			}
130
131
			// Field specific
132
			// add_filter( 'pods_field_pick_data', array( $this, 'field_pick_data_i18n' ), 10, 6 );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
133
			if ( $is_pods_edit_page ) {
134
135
				$pod = null;
136
				// Get the pod if available
137
				if ( isset( $_GET['id'] ) && is_numeric( $_GET['id'] ) ) {
138
					$pod = pods_api()->load_pod( array( 'id' => $_GET['id'] ) );
139
					// Append options to root array for pods_v function to work
140
					foreach ( $pod['options'] as $_option => $_value ) {
141
						$pod[ $_option ] = $_value;
142
					}
143
				}
144
				$this->cur_pod = $pod;
145
146
				// Add option tab for post types
147
				// add_filter( 'pods_admin_setup_edit_tabs_post_type', array( $this, 'pod_tab' ), 11, 3 );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
148
				// Add the same tab for taxonomies
149
				// add_filter( 'pods_admin_setup_edit_tabs_taxonomy', array( $this, 'pod_tab' ), 11, 3 );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
150
				// Add options to the new tab
151
				// add_filter( 'pods_admin_setup_edit_options', array( $this, 'pod_options' ), 12, 2 );
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
152
				// Add options metabox to the pod edit screens
153
				add_action( 'pods_add_meta_boxes', array( $this, 'admin_meta_box' ) );
154
155
				// Add the i18n input fields based on existing fields
156
				add_filter( 'pods_form_ui_field_text', array( $this, 'add_i18n_inputs' ), 10, 6 );
157
			}//end if
158
		}//end if
159
	}
160
161
	/**
162
	 * Load assets for this component
163
	 *
164
	 * @since 0.1
165
	 */
166
	public function admin_assets() {
167
168
		wp_enqueue_script(
169
			'pods-admin-i18n', PODS_URL . 'components/I18n/pods-admin-i18n.js', array(
170
				'jquery',
171
				'pods-i18n',
172
			), '1.0', true
173
		);
174
		$localize_script = array();
175
		if ( ! empty( $this->languages ) ) {
176
			foreach ( $this->languages as $lang => $lang_data ) {
177
				$lang_label = $this->create_lang_label( $lang_data );
178
				if ( ! empty( $lang_label ) ) {
179
					$lang_label = $lang . ' (' . $lang_label . ')';
180
				} else {
181
					$lang_label = $lang;
182
				}
183
				$localize_script[ $lang ] = $lang_label;
184
			}
185
		}
186
		wp_localize_script( 'pods-admin-i18n', 'pods_admin_i18n_strings', $localize_script );
187
188
		// Add strings to the i18n js object
189
		add_filter( 'pods_localized_strings', array( $this, 'localize_assets' ) );
190
	}
191
192
	/**
193
	 * Localize the assets
194
	 *
195
	 * @param  array $str Existing strings
196
	 *
197
	 * @return array
198
	 */
199
	public function localize_assets( $str ) {
200
201
		$str['Add translation']     = __( 'Add translation', 'pods' );
202
		$str['Toggle translations'] = __( 'Toggle translations', 'pods' );
203
		$str['Show translations']   = __( 'Show translations', 'pods' );
204
		$str['Hide translations']   = __( 'Hide translations', 'pods' );
205
		$str['Select']              = __( 'Select', 'pods' );
206
207
		return $str;
208
	}
209
210
	/**
211
	 * Check is a field name is set for translation
212
	 *
213
	 * @since 0.1
214
	 *
215
	 * @param string $name
216
	 *
217
	 * @return bool
218
	 */
219
	public function is_translatable_field( $name ) {
220
221
		$translatable_fields = $this->get_translatable_fields();
222
223
		// All fields that start with "label"
224
		if ( strpos( $name, 'label' ) === 0 ) {
0 ignored issues
show
Found "=== 0". Use Yoda Condition checks, you must
Loading history...
225
			return true;
226
		}
227
		// All translatable fields
228
		if ( in_array( $name, $translatable_fields, true ) ) {
229
			return true;
230
		}
231
		// Custom fields data, the name must begin with field_data[
232
		if ( strpos( $name, 'field_data[' ) === 0 ) {
0 ignored issues
show
Found "=== 0". Use Yoda Condition checks, you must
Loading history...
233
			$name = str_replace( 'field_data[', '', $name );
234
			$name = rtrim( $name, ']' );
235
			$name = explode( '][', $name );
236
			$name = end( $name );
237
			// All translatable fields from field_data[ (int) ][ $name ]
238
			if ( in_array( $name, $translatable_fields, true ) ) {
239
				return true;
240
			}
241
		}
242
243
		return false;
244
	}
245
246
	/**
247
	 * Get a translated option for a field key (if available)
248
	 *
249
	 * @since  0.1
250
	 *
251
	 * @param  string $current Current value
252
	 * @param  string $key     The key / opion name to search for
253
	 * @param  array  $data    Pod data (can also be an options array of a pod or field)
254
	 *
255
	 * @return string
256
	 */
257
	public function get_value_translation( $current, $key, $data ) {
258
259
		$locale = $this->locale;
260
		// Validate locale and pod
261
		if ( is_array( $data ) && array_key_exists( $locale, $this->languages ) && $this->obj_is_language_enabled( $locale, $data ) ) {
262
			// Add option keys to $data array
263
			if ( ! empty( $data['options'] ) ) {
264
				$data = array_merge( $data, $data['options'] );
265
			}
266
			// Check if the i18n option exists and isn't empty
267
			if ( ! empty( $data[ $key . '_' . $locale ] ) ) {
268
				return (string) $data[ $key . '_' . $locale ];
269
			}
270
		}
271
272
		return $current;
273
	}
274
275
	/**
276
	 * Page title for setting pages
277
	 *
278
	 * @since  0.1
279
	 * @see    PodsAdmin.php >> admin_menu()
280
	 * @see    PodsAdmin.php >> admin_content_settings()
281
	 *
282
	 * @param  string $page_title Current page title
283
	 * @param  array  $pod        Pod data
284
	 *
285
	 * @return string
286
	 */
287
	public function admin_menu_page_title_i18n( $page_title, $pod ) {
288
289
		return (string) $this->get_value_translation( $page_title, 'label', $pod );
290
	}
291
292
	/**
293
	 * Menu title for setting pages
294
	 *
295
	 * @since  0.1
296
	 * @see    PodsAdmin.php >> admin_menu()
297
	 *
298
	 * @param  string $menu_label Current menu label
299
	 * @param  array  $pod        Pod data
300
	 *
301
	 * @return string
302
	 */
303
	public function admin_menu_label_i18n( $menu_label, $pod ) {
304
305
		return (string) $this->get_value_translation( $menu_label, 'menu_name', $pod );
306
	}
307
308
	/**
309
	 * Returns the translated label if available
310
	 *
311
	 * @since  0.1
312
	 * @see    PodsForm.php >> 'pods_form_ui_label_text' (filter)
313
	 *
314
	 * @param  string $label   The default label
315
	 * @param  string $name    The field name
316
	 * @param  string $help    The help text
317
	 * @param  array  $options The field options
318
	 *
319
	 * @return string
320
	 */
321
	public function fields_ui_label_text_i18n( $label, $name, $help, $options ) {
322
323
		return (string) $this->get_value_translation( $label, 'label', $options );
324
	}
325
326
	/**
327
	 * Returns the translated description if available
328
	 *
329
	 * @since  0.1
330
	 * @see    PodsForm.php >> 'pods_form_ui_comment_text' (filter)
331
	 *
332
	 * @param  string $message The default description
333
	 * @param  string $name    The field name
334
	 * @param  array  $options The field options
335
	 *
336
	 * @return string
337
	 */
338
	public function fields_ui_comment_text_i18n( $message, $name, $options ) {
339
340
		return (string) $this->get_value_translation( $message, 'description', $options );
341
	}
342
343
	/**
344
	 * Replaces the default selected text with a translation if available
345
	 *
346
	 * @since  0.1
347
	 * @see    pick.php >> 'pods_field_pick_data' (filter)
348
	 *
349
	 * @param  array  $data    The default data of the field
350
	 * @param  string $name    The field name
351
	 * @param  string $value   The field value
352
	 * @param  array  $options The field options
353
	 * @param  array  $pod     The Pod
354
	 * @param  int    $id      The field ID
355
	 *
356
	 * @return array
357
	 */
358
	public function field_pick_data_i18n( $data, $name, $value, $options, $pod, $id ) {
359
360
		if ( isset( $data[''] ) && isset( $options['pick_select_text'] ) ) {
361
			$locale = $this->locale;
362
			if ( isset( $options[ 'pick_select_text_' . $locale ] ) && array_key_exists( $locale, $this->languages ) && $this->obj_is_language_enabled( $locale, $pod ) ) {
363
				$data[''] = $options[ 'pick_select_text_' . $locale ];
364
			}
365
		}
366
367
		return $data;
368
	}
369
370
	/**
371
	 * Replaces the default values with a translation if available
372
	 *
373
	 * @since  0.1
374
	 * @see    PodsForm.php >> 'pods_form_ui_field_' . $type . '_options' (filter)
375
	 *
376
	 * @param  array  $options The field options
377
	 * @param  string $name    The field name
378
	 * @param  string $value   The field value
379
	 * @param  array  $pod     The Pod
380
	 * @param  int    $id      The field ID
381
	 *
382
	 * @return array
383
	 */
384
	public function form_ui_field_options_i18n( $options, $name, $value, $pod, $id ) {
385
386
		foreach ( $this->get_translatable_fields() as $field ) {
387
			$locale = $this->locale;
388
			if ( isset( $options[ $field . '_' . $locale ] ) && array_key_exists( $locale, $this->languages ) && $this->obj_is_language_enabled( $locale, $pod ) ) {
389
				$options[ $field ] = $options[ $field . '_' . $locale ];
390
			}
391
		}
392
393
		return $options;
394
	}
395
396
	/**
397
	 * Filter hook function to overwrite the labels and description with translations (if available)
398
	 *
399
	 * @since  0.1
400
	 * @see    PodsInit.php >> setup_content_types()
401
	 *
402
	 * @param  array  $options The array of object options
403
	 * @param  string $object  The object type name/slug
404
	 *
405
	 * @return array
406
	 */
407
	public function pods_register_wp_object_i18n( $options, $object ) {
408
409
		$locale = $this->locale;
410
411
		$pod = pods_api()->load_pod( $object );
412
413
		if ( ! $this->obj_is_language_enabled( $locale, $pod ) ) {
414
			return $options;
415
		}
416
417
		// Load the pod
418
		foreach ( $pod['options'] as $_option => $_value ) {
419
			$pod[ $_option ] = $_value;
420
		}
421
422
		// Default
423
		$locale_labels                          = array();
424
		$locale_labels['name']                  = esc_html( pods_v( 'label_' . $locale, $pod, '', true ) );
425
		$locale_labels['singular_name']         = esc_html( pods_v( 'label_singular_' . $locale, $pod, '', true ) );
426
		$locale_labels['menu_name']             = pods_v( 'menu_name_' . $locale, $pod, '', true );
427
		$locale_labels['add_new_item']          = pods_v( 'label_add_new_item_' . $locale, $pod, '', true );
428
		$locale_labels['edit_item']             = pods_v( 'label_edit_item_' . $locale, $pod, '', true );
429
		$locale_labels['view_item']             = pods_v( 'label_view_item_' . $locale, $pod, '', true );
430
		$locale_labels['all_items']             = pods_v( 'label_all_items_' . $locale, $pod, '', true );
431
		$locale_labels['search_items']          = pods_v( 'label_search_items_' . $locale, $pod, '', true );
432
		$locale_labels['parent_item_colon']     = pods_v( 'label_parent_item_colon_' . $locale, $pod, '', true );
433
		$locale_labels['not_found']             = pods_v( 'label_not_found_' . $locale, $pod, '', true );
434
		$locale_labels['items_list_navigation'] = pods_v( 'label_items_list_navigation_' . $locale, $pod, '', true );
435
		$locale_labels['items_list']            = pods_v( 'label_items_list_' . $locale, $pod, '', true );
436
437
		// Post Types
438
		$locale_labels['name_admin_bar']        = pods_v( 'name_admin_bar_' . $locale, $pod, '', true );
439
		$locale_labels['add_new']               = pods_v( 'label_add_new_' . $locale, $pod, '', true );
440
		$locale_labels['new_item']              = pods_v( 'label_new_item_' . $locale, $pod, '', true );
441
		$locale_labels['edit']                  = pods_v( 'label_edit_' . $locale, $pod, '', true );
442
		$locale_labels['view']                  = pods_v( 'label_view_' . $locale, $pod, '', true );
443
		$locale_labels['view_items']            = pods_v( 'label_view_items_' . $locale, $pod, '', true );
444
		$locale_labels['parent']                = pods_v( 'label_parent_' . $locale, $pod, '', true );
445
		$locale_labels['not_found_in_trash']    = pods_v( 'label_not_found_in_trash_' . $locale, $pod, '', true );
446
		$locale_labels['archives']              = pods_v( 'label_archives_' . $locale, $pod, '', true );
447
		$locale_labels['attributes']            = pods_v( 'label_attributes_' . $locale, $pod, '', true );
448
		$locale_labels['insert_into_item']      = pods_v( 'label_insert_into_item_' . $locale, $pod, '', true );
449
		$locale_labels['uploaded_to_this_item'] = pods_v( 'label_uploaded_to_this_item_' . $locale, $pod, '', true );
450
		$locale_labels['featured_image']        = pods_v( 'label_featured_image_' . $locale, $pod, '', true );
451
		$locale_labels['set_featured_image']    = pods_v( 'label_set_featured_image_' . $locale, $pod, '', true );
452
		$locale_labels['remove_featured_image'] = pods_v( 'label_remove_featured_image_' . $locale, $pod, '', true );
453
		$locale_labels['use_featured_image']    = pods_v( 'label_use_featured_image_' . $locale, $pod, '', true );
454
		$locale_labels['filter_items_list']     = pods_v( 'label_filter_items_list_' . $locale, $pod, '', true );
455
456
		// Taxonomies
457
		$locale_labels['update_item']                = pods_v( 'label_update_item_' . $locale, $pod, '', true );
458
		$locale_labels['popular_items']              = pods_v( 'label_popular_items_' . $locale, $pod, '', true );
459
		$locale_labels['parent_item']                = pods_v( 'label_parent_item_' . $locale, $pod, '', true );
460
		$locale_labels['new_item_name']              = pods_v( 'label_new_item_name_' . $locale, $pod, '', true );
461
		$locale_labels['separate_items_with_commas'] = pods_v( 'label_separate_items_with_commas_' . $locale, $pod, '', true );
462
		$locale_labels['add_or_remove_items']        = pods_v( 'label_add_or_remove_items_' . $locale, $pod, '', true );
463
		$locale_labels['choose_from_most_used']      = pods_v( 'label_choose_from_the_most_used_' . $locale, $pod, '', true );
464
		$locale_labels['no_terms']                   = pods_v( 'label_no_terms_' . $locale, $pod, '', true );
465
466
		// Assign to label array
467
		if ( isset( $options['labels'] ) && is_array( $options['labels'] ) ) {
468
			foreach ( $options['labels'] as $key => $value ) {
469
				// @todo Currently I only overwrite, maybe also append even if the default locale isn't set?
470
				if ( isset( $locale_labels[ $key ] ) && ! empty( $locale_labels[ $key ] ) ) {
471
					$options['labels'][ $key ] = $locale_labels[ $key ];
472
				}
473
			}
474
		}
475
476
		return $options;
477
	}
478
479
	/**
480
	 * Filter hook function to overwrite the labels and description with translations (if available)
481
	 *
482
	 * @since  0.1
483
	 * @see    PodsInit.php >> admin_menu()
484
	 *
485
	 * @param  array  $options The array of object options
486
	 * @param  string $object  The object type name/slug
487
	 *
488
	 * @return array
489
	 */
490
	public function pods_filter_object_strings_i18n( $options, $object ) {
491
492
		/**
493
		 * @todo allow labels to be set even if the default language isn't
494
		 *
495
		 * - Find all keys that end with the current locale
496
		 * - Assign them to the keys without that locale
497
		 */
498
499
		foreach ( $options as $key => $option ) {
500
			if ( is_string( $option ) && $this->is_translatable_field( $key ) ) {
501
				$options[ $key ] = $this->get_value_translation( $option, $key, $options );
502
			}
503
		}
504
505
		if ( ! empty( $options['options'] ) ) {
506
			foreach ( $options['options'] as $key => $option ) {
507
				if ( is_string( $option ) && $this->is_translatable_field( $key ) ) {
508
					$options['options'][ $key ] = $this->get_value_translation( $option, $key, $options['options'] );
509
				}
510
			}
511
		}
512
513
		return $options;
514
	}
515
516
	/**
517
	 * Save component settings
518
	 *
519
	 * @since 0.1
520
	 */
521
	public function admin_save() {
522
523
		$this->languages_available = get_available_languages();
524
525
		/**
526
		 * format: array( language, version, updated, english_name, native_name, package, iso, strings )
527
		 */
528
		require_once ABSPATH . 'wp-admin/includes/translation-install.php';
529
		$this->languages_translated = wp_get_available_translations();
530
531
		$new_languages = array();
532
533
		if ( isset( $_POST['pods_i18n_enabled_languages'] ) && is_array( $_POST['pods_i18n_enabled_languages'] ) ) {
534
			foreach ( $_POST['pods_i18n_enabled_languages'] as $locale ) {
535
				$locale = sanitize_text_field( $locale );
536
537
				if ( in_array( $locale, $this->languages_available, true ) ) {
538
					$new_languages[ $locale ] = array();
539
540
					if ( isset( $this->languages_translated[ $locale ]['language'] ) ) {
541
						$new_languages[ $locale ]['language'] = $this->languages_translated[ $locale ]['language'];
542
					}
543
544
					if ( isset( $this->languages_translated[ $locale ]['english_name'] ) ) {
545
						$new_languages[ $locale ]['english_name'] = $this->languages_translated[ $locale ]['english_name'];
546
					}
547
548
					if ( isset( $this->languages_translated[ $locale ]['native_name'] ) ) {
549
						$new_languages[ $locale ]['native_name'] = $this->languages_translated[ $locale ]['native_name'];
550
					}
551
				}
552
			}
553
		}//end if
554
555
		$this->languages                     = $new_languages;
556
		$this->settings['enabled_languages'] = $new_languages;
557
558
		update_option( $this->option_key, $this->settings );
559
560
	}
561
562
	/**
563
	 * Build admin area
564
	 *
565
	 * @since  0.1
566
	 *
567
	 * @param  $options
568
	 * @param  $component
569
	 *
570
	 * @return void
571
	 */
572
	public function admin( $options, $component ) {
573
574
		$this->languages_available = get_available_languages();
575
576
		/**
577
		 * format: array( language, version, updated, english_name, native_name, package, iso, strings )
578
		 */
579
		require_once ABSPATH . 'wp-admin/includes/translation-install.php';
580
581
		$this->languages_translated = wp_get_available_translations();
582
583
		// en_US is always installed (default locale of WP)
584
		$data = array(
585
			'en_US' => array(
586
				'id'          => 'en_US',
587
				'locale'      => 'en_US',
588
				'lang'        => 'English',
589
				'lang_native' => 'English',
590
				'enabled'     => 'Default',
591
			),
592
		);
593
594
		foreach ( $this->languages_available as $locale ) {
595
			$checked = checked( isset( $this->languages[ $locale ] ), true, false );
596
597
			$enabled = sprintf( '<input type="checkbox" name="pods_i18n_enabled_languages[%s]" value="%s"%s />', esc_attr( $locale ), esc_attr( $locale ), $checked );
598
599
			$data[ $locale ] = array(
600
				'id'          => $locale,
601
				'locale'      => $locale,
602
				'lang'        => $this->languages_translated[ $locale ]['english_name'],
603
				'lang_native' => $this->languages_translated[ $locale ]['native_name'],
604
				'enabled'     => $enabled,
605
			);
606
		}
607
608
		$ui = array(
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ui. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
609
			'component'        => $component,
610
			// 'data' => $data,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
611
			// 'total' => count( $data ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
612
			// 'total_found' => count( $data ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
613
			'items'            => __( 'Languages', 'pods' ),
614
			'item'             => __( 'Language', 'pods' ),
615
			'fields'           => array(
616
				'manage' => array(
617
					'enabled'     => array(
618
						'label' => __( 'Active', 'pods' ),
619
						'type'  => 'raw',
620
					),
621
					'locale'      => array(
622
						'label'   => __( 'Locale', 'pods' ),
623
						'classes' => array( 'column-secondary' ),
624
					),
625
					'lang'        => array( 'label' => __( 'Language', 'pods' ) ),
626
					'lang_native' => array( 'label' => __( 'Native name', 'pods' ) ),
627
					/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
628
					'fields' => array(
629
						'label' => __( 'Fields', 'pods' ),
630
						'type' => 'text',
631
						'options' => array(
632
							'text_allow_html' => 1,
633
							'text_allowed_html_tags' => 'br code'
634
						)
635
					),*/
636
				),
637
			),
638
			'actions_disabled' => array( 'edit', 'add', 'delete', 'duplicate', 'view', 'export' ),
639
			'actions_custom'   => array(
640
				// 'add' => array( $this, 'admin_add' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
641
				// 'edit' => array( $this, 'admin_edit' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
642
				// 'delete' => array( $this, 'admin_delete' )
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
643
			),
644
			'search'           => false,
645
			'searchable'       => false,
646
			'sortable'         => false,
647
			'pagination'       => false,
648
		);
649
650
		/**
651
		 * Filter the language data
652
		 *
653
		 * @since 0.1
654
		 *
655
		 * @param array
656
		 */
657
		$data = apply_filters( 'pods_component_i18n_admin_data', $data );
658
659
		/**
660
		 * Filter the UI fields
661
		 *
662
		 * @since 0.1
663
		 *
664
		 * @param array
665
		 */
666
		$ui['fields'] = apply_filters( 'pods_component_i18n_admin_ui_fields', $ui['fields'], $data );
667
668
		$ui['data']        = $data;
669
		$ui['total']       = count( $data );
670
		$ui['total_found'] = count( $data );
671
672
		/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
673
		if ( !pods_is_admin( array( 'pods_i18n_activate_lanuages' ) ) )
674
			$ui[ 'actions_disabled' ][] = 'edit';*/
675
676
		echo '<div id="pods_admin_i18n" class="pods-submittable-fields">';
677
678
		// Do save action here because otherwise the loading of post_types get done first and labels aren't translated
679
		if ( pods_is_admin( $this->capability ) && isset( $_POST['_nonce_i18n'] ) && wp_verify_nonce( $_POST['_nonce_i18n'], $this->nonce ) ) {
680
			pods_message( __( 'Updated active languages.', 'pods' ) );
681
		}
682
683
		pods_ui( $ui );
684
685
		// @todo Do this in pods_ui so we don't rely on javascript
686
		echo '<div id="pods_i18n_settings_save">';
687
		wp_nonce_field( $this->nonce, '_nonce_i18n', false );
688
		submit_button();
689
		echo '</div>';
690
691
		echo '</div>';
692
	}
693
694
	/**
695
	 * The i18n option tab.
696
	 *
697
	 * @todo   Remove if not used in final version
698
	 *
699
	 * @since  0.1
700
	 *
701
	 * @param  array $tabs
702
	 * @param  array $pod
703
	 * @param  array $args
704
	 *
705
	 * @return array
706
	 */
707
	public function pod_tab( $tabs, $pod, $args ) {
708
709
		$tabs['pods-i18n'] = __( 'Translation Options', 'pods' );
710
711
		return $tabs;
712
	}
713
714
	/**
715
	 * The i18n options
716
	 *
717
	 * @todo   Remove if not used in final version
718
	 *
719
	 * @since  0.1
720
	 *
721
	 * @param  array $options
722
	 * @param  array $pod
723
	 *
724
	 * @return array
725
	 */
726
	public function pod_options( $options, $pod ) {
727
728
		// if ( $pod['type'] === '' )
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
729
		/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
730
		$options[ 'pods-i18n' ] = array(
731
			'enabled_languages' => array(
732
				'label' => __( 'Enable/Disable languages for this Pod', 'pods' ),
733
				'help' => __( 'This overwrites the defaults set in the component admin.', 'pods' ),
734
				'group' => array(),
735
			),
736
		);
737
738
		foreach ( $this->languages as $locale => $lang_data ) {
739
			$options['pods-i18n']['enabled_languages']['group']['enable_i18n'][ $locale ] = array(
740
				'label'      => $locale . ' (' . $this->create_lang_label( $lang_data ) . ')',
741
				'default'    => 1,
742
				'type'       => 'boolean',
743
			);
744
		}*/
745
746
		return $options;
747
748
	}
749
750
	/**
751
	 * Add the i18n metabox.
752
	 *
753
	 * @since 0.1
754
	 */
755
	public function admin_meta_box() {
756
757
		add_meta_box(
758
			'pods_i18n',
759
			// ID
760
			__( 'Translation options', 'pods' ),
761
			// Label
762
			array( $this, 'meta_box' ),
763
			// Callback
764
			'_pods_pod',
765
			// Screen
766
			'side',
767
			// Context (side)
768
			'default'
769
			// Priority
770
		);
771
	}
772
773
	/**
774
	 * The i18n metabox.
775
	 *
776
	 * @todo   Store enabled languages serialized instead of separate inputs
777
	 *
778
	 * @since  0.1
779
	 */
780
	public function meta_box() {
781
782
		$pod = $this->cur_pod;
783
784
		if ( ! empty( $this->languages ) ) {
785
			?>
786
			<p><?php _e( 'Enable/Disable languages for this Pod', 'pods' ); ?></p>
787
			<p>
788
				<small class="description"><?php _e( 'This overwrites the defaults set in the component admin.', 'pods' ); ?></small>
789
			</p>
790
			<div class="pods-field-enable-disable-language">
791
				<?php
792
				foreach ( $this->languages as $locale => $lang_data ) {
793
794
					if ( ! isset( $pod['options']['enable_i18n'][ $locale ] ) ) {
795
						// Enabled by default
796
						$pod['options']['enable_i18n'][ $locale ] = 1;
797
					}
798
					?>
799
					<div class="pods-field-option pods-enable-disable-language" data-locale="<?php echo esc_attr( $locale ); ?>">
800
						<?php
801
						echo PodsForm::field(
802
							'enable_i18n[' . $locale . ']', $pod['options']['enable_i18n'][ $locale ], 'boolean', array(
803
								'boolean_yes_label' => '<code>' . $locale . '</code> ' . $this->create_lang_label( $lang_data ),
804
								'boolean_no_label'  => '',
805
							)
806
						);
807
						?>
808
					</div>
809
					<?php
810
				}
811
				?>
812
			</div>
813
			<hr>
814
			<p>
815
				<button id="toggle_i18n" class="button-secondary"><?php _e( 'Toggle translation visibility', 'pods' ); ?></button>
816
			</p>
817
			<?php
818
		}//end if
819
	}
820
821
	/**
822
	 * Adds translation inputs to fields
823
	 *
824
	 * @since  0.1
825
	 * @see    PodsForm.php >> 'pods_form_ui_field_' . $type (filter)
826
	 *
827
	 * @param  string $output  The default output of the field
828
	 * @param  string $name    The field name
829
	 * @param  string $value   The field value
830
	 * @param  array  $options The field options
831
	 * @param  array  $pod     The Pod
832
	 * @param  int    $id      The field ID
833
	 *
834
	 * @return string
835
	 */
836
	public function add_i18n_inputs( $output, $name, $value, $options, $pod, $id ) {
837
838
		if ( ! empty( $pod ) || empty( $name ) || ! $this->is_translatable_field( $name ) ) {
839
			return $output;
840
		}
841
842
		$pod = $this->cur_pod;
843
		// print_r( $pod );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
844
		if ( empty( $pod ) ) {
845
			// Setting the $pod var to a non-empty value is mandatory to prevent a loop
846
			$pod = true;
847
		}
848
849
		$output .= '<br clear="both" />';
850
		$output .= '<div class="pods-i18n-field">';
851
		foreach ( $this->languages as $locale => $lang_data ) {
852
853
			if ( ! $this->obj_is_language_enabled( $locale, (array) $pod ) ) {
854
				continue;
855
			}
856
			// Our own shiny label with language information
857
			$lang_code = '<code style="font-size: 1em;">' . $locale . '</code>';
858
			/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
859
			$lang_label = $this->create_lang_label( $lang_data );
860
			if ( ! empty( $lang_label ) ) {
861
				$lang_label = $lang_code . ' ('. $lang_label .')';
862
			} else {*/
863
			$lang_label = $lang_code;
864
			// }
865
			$lang_label = '<small>' . $lang_label . '</small>';
866
867
			$style = '';
868
869
			// Add language data to name for normal strings and array formatted strings
870
			if ( strpos( $name, ']' ) !== false ) {
0 ignored issues
show
Found "!== false". Use Yoda Condition checks, you must
Loading history...
871
				// Hide the i18n options for fields by default if they are empty
872
				$field_value = pods_v( $name, $pod );
873
874
				if ( strpos( $name, 'field_data' ) !== false && empty( $field_value ) ) {
0 ignored issues
show
Found "!== false". Use Yoda Condition checks, you must
Loading history...
875
					$style = ' style="display: none;"';
876
				}
877
878
				$field_name  = rtrim( $name, ']' );
879
				$field_name .= '_' . $locale . ']';
880
			} else {
881
				$field_name = $name . '_' . $locale;
882
			}
883
884
			// Add the translation fields
885
			$output .= '<div class="pods-i18n-input pods-i18n-input-' . $locale . '" data-locale="' . $locale . '" ' . $style . '>';
886
			$output .= PodsForm::label( $field_name, $lang_label );
887
			$output .= PodsForm::field( $field_name, pods_v( $field_name, $pod ), 'text', null, $pod );
888
			$output .= '</div>';
889
		}//end foreach
890
		$output .= '</div>';
891
892
		return $output;
893
	}
894
895
	/**
896
	 * Check if a language is get to enabled for an object
897
	 *
898
	 * @since  0.1
899
	 *
900
	 * @param  string $locale The locale to validate
901
	 * @param  array  $data   Object data
902
	 *
903
	 * @return bool
904
	 */
905
	public function obj_is_language_enabled( $locale, $data ) {
906
907
		// If the locale isn't enabled in the global scope from the component it's always disabled
908
		if ( ! array_key_exists( $locale, $this->languages ) ) {
909
			return false;
910
		}
911
		$data    = (array) $data;
912
		$options = ( isset( $data['options'] ) ) ? $data['options'] : $data;
913
		// If it doesn't exist in the object data then use the default (enabled)
914
		if ( isset( $options['enable_i18n'][ $locale ] ) && false === (bool) $options['enable_i18n'][ $locale ] ) {
0 ignored issues
show
This if statement, and the following return statement can be replaced with return !(isset($options[...nable_i18n'][$locale]);.
Loading history...
915
			return false;
916
		}
917
918
		return true;
919
	}
920
921
	/**
922
	 * Create a label with the english and native name combined
923
	 *
924
	 * @since  0.1
925
	 *
926
	 * @param  array $lang_data
927
	 *
928
	 * @return string
929
	 */
930
	public function create_lang_label( $lang_data ) {
931
932
		$english_name = '';
933
		$native_name  = '';
934
935
		if ( isset( $lang_data['english_name'] ) ) {
936
			$english_name = $lang_data['english_name'];
937
		}
938
		if ( isset( $lang_data['native_name'] ) ) {
939
			$native_name = $lang_data['native_name'];
940
		}
941
942
		if ( ! empty( $native_name ) && ! empty( $english_name ) ) {
943
			if ( $native_name == $english_name ) {
944
				return $english_name;
945
			} else {
946
				return $english_name . ' / ' . $native_name;
947
			}
948
		} else {
949
			if ( ! empty( $english_name ) ) {
950
				return $english_name;
951
			}
952
			if ( ! empty( $native_name ) ) {
953
				return $native_name;
954
			}
955
		}
956
957
		return '';
958
	}
959
960
	/**
961
	 * @return mixed|void
962
	 */
963
	public function get_translatable_fields() {
964
965
		return apply_filters( 'pods_translatable_fields', $this->translatable_fields );
966
	}
967
968
}
969