Skip to content

Commit 3594e41

Browse files
committed
Merge branch 'develop' into fix/214-231-pull-requests-combined
* develop: Remove the reliance on `$lang.php` and use our static list of RTL languages. See #216. Introduce a static list of RTL languages from GlotPress and Wikipedia. This query doesn't contribute to the pre-caching of the subsequent `get_post()` calls, because they've already been fetched by `bbl_get_default_lang_post()` above. Fixes #251. If a term doesn't have a transid, allow it to fall through so one is created rather than throwing an exception. Remove the unused term resyncing functionality which hasn't been used since #106. Validate the content lang and interface lang before setting them. See #227. Remove error suppression on the term editing screen. See #223. Remove error suppression when saving language options or viewing the language options admin screen. See #223. Remove unnecessary theme functionality from the base `Babble_Plugin` class. See #223. Avoid throwing exceptions in `Babble_Taxonomies::get_transid()` and `Babble_Taxonomies::set_transid()`. See #218. Make the `$name` parameter in `Babble_Plugin::setup()` a required parameter, avoid throwing an exception. See #218. Avoid throwing an exception in `is_public_lang()`. Now accepts a language object or a language code string. See #218. Conflicts: class-taxonomy.php
2 parents 0038873 + 548c545 commit 3594e41

8 files changed

+137
-92
lines changed

api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ function bbl_get_current_lang_code() {
6666
* Given a lang object or lang code, this checks whether the
6767
* language is public or not.
6868
*
69-
* @param string $lang_code A language code
69+
* @param string|object $lang_code A language code or a language object
7070
* @return boolean True if public
7171
* @access public
7272
**/

class-languages.php

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,43 @@ class Babble_Languages extends Babble_Plugin {
6666
**/
6767
protected $errors;
6868

69+
/**
70+
* Array of language codes where the language writes right-to-left.
71+
*
72+
* Based primarily on GlotPress' list of locales and Wikipedia's article on RTL:
73+
*
74+
* @link https://glotpress.trac.wordpress.org/browser/trunk/locales/locales.php
75+
* @link https://en.wikipedia.org/wiki/Right-to-left
76+
*
77+
* @var array
78+
*/
79+
protected static $rtl_languages = array(
80+
'ar', // Arabic / العربية
81+
'arc', // Aramaic
82+
'arq', // Algerian Arabic / الدارجة الجزايرية
83+
'azb', // South Azerbaijani / گؤنئی آذربایجان
84+
'az_TR', // Azerbaijani (Turkey) / Azərbaycan Türkcəsi
85+
'bcc', // Balochi Southern / بلوچی مکرانی
86+
'bqi', // Bakthiari / بختياري
87+
'ckb', // Sorani Kurdish / کوردی
88+
'dv', // Dhivehi
89+
'fa', // Persian / فارسی
90+
'fa_IR', // Persian / فارسی
91+
'fa_AF', // Persian (Afghanistan) / (افغانستان) فارسی
92+
'glk', // Gilaki / گیلکی
93+
'ha', // Hausa / هَوُسَ
94+
'haz', // Hazaragi / هزاره گی
95+
'he', // Hebrew / עִבְרִית
96+
'he_IL', // Hebrew / עִבְרִית
97+
'mzn', // Mazanderani / مازِرونی
98+
'pnb', // Western Punjabi / پنجابی
99+
'ps', // Pashto / پښتو
100+
'sd', // Sindhi / سنڌي
101+
'ug', // Uyghur / ئۇيغۇرچە
102+
'ur', // Urdu / اردو
103+
'yi', // Yiddish / ייִדיש'
104+
);
105+
69106
/**
70107
* Setup any add_action or add_filter calls. Initiate properties.
71108
*
@@ -153,21 +190,37 @@ public function options() {
153190
$langs = $this->merge_lang_sets( $this->available_langs, $this->lang_prefs );
154191
// Merge in any POSTed field values
155192
foreach ( $langs as $code => & $lang ) {
156-
$lang->url_prefix = ( @ isset( $_POST[ 'url_prefix_' . $code ] ) ) ? $_POST[ "url_prefix_$code" ] : @ $lang->url_prefix;
157-
if ( ! $lang->url_prefix )
158-
$lang->url_prefix = $lang->url_prefix;
159-
$lang->text_direction = $lang->text_direction;
193+
if ( ! empty( $_POST[ "url_prefix_$code" ] ) ) {
194+
$lang->url_prefix = $_POST[ "url_prefix_$code" ];
195+
} else if ( empty( $lang->url_prefix ) ) {
196+
$parts = explode( '_', $lang->code );
197+
$lang->url_prefix = $parts[0];
198+
}
199+
if ( ! in_array( $lang->text_direction, array( 'ltr', 'rtl' ) ) ) {
200+
// @TODO is this needed?
201+
$lang->text_direction = 'ltr';
202+
}
160203
// This line must come after the text direction value is set
161204
$lang->input_lang_class = ( 'rtl' == $lang->text_direction ) ? 'lang-rtl' : 'lang-ltr' ;
162-
$lang->display_name = ( @ isset( $_POST[ "display_name_$code" ] ) ) ? $_POST[ "display_name_$code" ] : @ $lang->display_name;
163-
if ( ! $lang->display_name )
205+
206+
if ( ! empty( $_POST[ "display_name_$code" ] ) ) {
207+
$lang->display_name = $_POST[ "display_name_$code" ];
208+
} else if ( empty( $lang->display_name ) ) {
164209
$lang->display_name = $lang->name;
210+
}
211+
165212
// Note any url_prefix errors
166-
$lang->url_prefix_error = ( @ $this->errors[ "url_prefix_$code" ] ) ? 'babble-error' : '0' ;
213+
if ( ! empty( $this->errors[ "url_prefix_$code" ] ) ) {
214+
$lang->url_prefix_error = 'babble-error';
215+
} else {
216+
$lang->url_prefix_error = '0';
217+
}
218+
167219
// Flag the active languages
168220
$lang->active = false;
169-
if ( in_array( $code, $this->active_langs ) )
221+
if ( in_array( $code, $this->active_langs ) ) {
170222
$lang->active = true;
223+
}
171224

172225
}
173226
$vars = array();
@@ -204,12 +257,13 @@ public function get_active_langs() {
204257
* Given a lang object or lang code, this checks whether the
205258
* language is public or not.
206259
*
207-
* @param string $lang_code A language code
260+
* @param string|object $lang_code A language code or a language object
208261
* @return boolean True if public
209262
**/
210263
public function is_public_lang( $lang_code ) {
211-
if ( ! is_string( $lang_code ) )
212-
throw new exception( 'Please provide a lang_code for the is_public_lang method.' );
264+
if ( is_object( $lang_code ) and ! empty( $lang_code->lang ) ) {
265+
$lang_code = $lang_code->lang;
266+
}
213267
return in_array( $lang_code, $this->public_langs );
214268
}
215269

@@ -330,8 +384,19 @@ protected function maybe_process_languages() {
330384
$url_prefixes = array();
331385
foreach ( $this->available_langs as $code => $lang ) {
332386
$lang_pref = new stdClass;
333-
$lang_pref->display_name = @ $_POST[ 'display_name_' . $code ];
334-
$lang_pref->url_prefix = @ $_POST[ 'url_prefix_' . $code ];
387+
388+
if ( ! empty( $_POST[ 'display_name_' . $code ] ) ) {
389+
$lang_pref->display_name = $_POST[ 'display_name_' . $code ];
390+
} else {
391+
$lang_pref->display_name = $lang->name;
392+
}
393+
394+
if ( ! empty( $_POST[ 'url_prefix_' . $code ] ) ) {
395+
$lang_pref->url_prefix = $_POST[ 'url_prefix_' . $code ];
396+
} else {
397+
$lang_pref->url_prefix = $lang->url_prefix;
398+
}
399+
335400
// Check we don't have more than one language using the same url prefix
336401
if ( array_key_exists( $lang_pref->url_prefix, $url_prefixes ) ) {
337402
$lang_1 = $this->format_code_lang( $code );
@@ -351,8 +416,11 @@ protected function maybe_process_languages() {
351416
if ( ! $this->errors ) {
352417
$langs = $this->merge_lang_sets( $this->available_langs, $this->lang_prefs );
353418
$active_langs = array();
354-
foreach ( (array) @ $_POST[ 'active_langs' ] as $code )
355-
$active_langs[ $langs[ $code ]->url_prefix ] = $code;
419+
if ( ! empty( $_POST[ 'active_langs' ] ) && is_array( $_POST[ 'active_langs' ] ) ) {
420+
foreach ( $_POST[ 'active_langs' ] as $code ) {
421+
$active_langs[ $langs[ $code ]->url_prefix ] = $code;
422+
}
423+
}
356424
if ( count( $active_langs ) < 2 ) {
357425
$this->set_admin_error( __( 'You must set at least two languages as active.', 'babble' ) );
358426
} else {
@@ -365,8 +433,11 @@ protected function maybe_process_languages() {
365433
$this->set_admin_error( __( 'You must set at least your default language as public.', 'babble' ) );
366434
} else {
367435
$public_langs = (array) $_POST[ 'public_langs' ];
368-
if ( ! in_array( @ $_POST[ 'default_lang' ], $public_langs ) )
436+
if ( empty( $_POST[ 'default_lang' ] ) ) {
437+
$this->set_admin_error( __( 'You must choose a default language.', 'babble' ) );
438+
} else if ( ! in_array( $_POST[ 'default_lang' ], $public_langs ) ) {
369439
$this->set_admin_error( __( 'You must set your default language as public.', 'babble' ) );
440+
}
370441
}
371442
}
372443
// Finish up, redirecting if we're all OK
@@ -375,7 +446,7 @@ protected function maybe_process_languages() {
375446
$this->update_option( 'public_langs', $public_langs );
376447

377448
// First the default language
378-
$default_lang = @ $_POST[ 'default_lang' ];
449+
$default_lang = $_POST[ 'default_lang' ];
379450
$this->update_option( 'default_lang', $default_lang );
380451
// Now the prefs
381452
$this->update_option( 'lang_prefs', $lang_prefs );
@@ -409,7 +480,7 @@ protected function parse_available_languages() {
409480
'name' => $this->format_code_lang( $prefix ),
410481
'code' => $lang_code,
411482
'url_prefix' => $prefix,
412-
'text_direction' => $this->is_rtl( $lang_code ),
483+
'text_direction' => ( self::is_rtl( $lang_code ) ? 'rtl' : 'ltr' ),
413484
);
414485
// Cast to an object, in case we want to start using actual classes
415486
// at some point in the future.
@@ -428,21 +499,13 @@ protected function parse_available_languages() {
428499
}
429500

430501
/**
431-
* Parse (DON'T require or include) the [lang_code].php locale file in the languages
432-
* directory to work if the specified language is right to left. (We can't include or
433-
* require because it may contain function names which clash with other locale files.)
502+
* Is the given language a right-to-left language?
434503
*
435-
* @param string $lang The language code to retrieve RTL info for
504+
* @param string $lang_code The language code to retrieve RTL info for
436505
* @return bool True if the language is RTL
437506
**/
438-
protected function is_rtl( $lang ) {
439-
$locale_file = WP_LANG_DIR . "/$lang.php";
440-
if ( ( 0 === validate_file( $lang ) ) && is_readable( $locale_file ) ) {
441-
$locale_file_code = file_get_contents( $locale_file );
442-
// Regex to find something looking like: $text_direction = 'rtl';
443-
return ( (bool) preg_match( '/\$text_direction\s?=\s?[\'|"]rtl[\'|"]\s?;/i', $locale_file_code ) ) ? 'rtl' : 'ltr';
444-
}
445-
return 'ltr';
507+
public static function is_rtl( $lang_code ) {
508+
return in_array( $lang_code, self::$rtl_languages, true );
446509
}
447510

448511
/**

class-locale.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,19 +235,31 @@ public function set_locale( $locale ) {
235235
$this->set_content_lang( $lang );
236236
}
237237

238+
$active_langs = bbl_get_active_langs();
239+
$active_lang_codes = wp_list_pluck( $active_langs, 'code' );
240+
$active_lang_prefixes = wp_list_pluck( $active_langs, 'url_prefix' );
241+
238242
if ( is_admin() ) {
239-
// @FIXME: At this point a mischievous XSS "attack" could set a user's admin area language for them
240243
if ( isset( $_POST[ 'interface_lang' ] ) ) {
241-
$this->set_interface_lang( $_POST[ 'interface_lang' ] );
244+
$lang = $_POST[ 'interface_lang' ];
245+
if ( ! in_array( $lang, $active_lang_codes, true ) ) {
246+
$lang = bbl_get_default_lang_code();
247+
}
248+
$this->set_interface_lang( $lang );
242249
}
243-
// @FIXME: At this point a mischievous XSS "attack" could set a user's content language for them
244250
if ( isset( $_GET[ 'lang' ] ) ) {
245-
$this->set_content_lang( $_GET[ 'lang' ] );
251+
$lang = $_GET[ 'lang' ];
252+
if ( ! in_array( $lang, $active_lang_codes, true ) ) {
253+
$lang = bbl_get_default_lang_code();
254+
}
255+
$this->set_content_lang( $lang );
246256
}
247257
} else { // Front end
248-
// @FIXME: Should probably check the available languages here
249-
if ( preg_match( $this->lang_regex, $this->get_request_string(), $matches ) )
250-
$this->set_content_lang_from_prefix( $matches[ 0 ] );
258+
if ( preg_match( $this->lang_regex, $this->get_request_string(), $matches ) ) {
259+
if ( in_array( $matches[ 0 ], $active_lang_prefixes, true ) ) {
260+
$this->set_content_lang_from_prefix( $matches[ 0 ] );
261+
}
262+
}
251263
}
252264

253265
if ( ! isset( $this->content_lang ) || ! $this->content_lang )

class-plugin.php

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -95,35 +95,21 @@ class Babble_Plugin {
9595
**/
9696
protected $type;
9797

98-
/**
99-
* Note the name of the function to call when the theme is activated.
100-
*
101-
* @var string
102-
**/
103-
protected $theme_activation_function;
104-
10598
/**
10699
* Initiate!
107100
*
108101
* @return void
109102
* @author Simon Wheatley
110103
**/
111-
public function setup( $name = '', $type = null ) {
112-
if ( ! $name )
113-
throw new exception( "Please pass the name parameter into the setup method." );
104+
public function setup( $name, $type = null ) {
114105
$this->name = $name;
115106

116107
// Attempt to handle a Windows
117108
$ds = ( defined( 'DIRECTORY_SEPARATOR' ) ) ? DIRECTORY_SEPARATOR : '\\';
118109
$file = str_replace( $ds, '/', __FILE__ );
119110
$plugins_dir = str_replace( $ds, '/', dirname( __FILE__ ) );
120-
// Setup the dir and url for this plugin/theme
121-
if ( 'theme' == $type ) {
122-
// This is a theme
123-
$this->type = 'theme';
124-
$this->dir = get_stylesheet_directory();
125-
$this->url = get_stylesheet_directory_uri();
126-
} elseif ( stripos( $file, $plugins_dir ) !== false || 'plugin' == $type ) {
111+
// Setup the dir and url for this plugin
112+
if ( stripos( $file, $plugins_dir ) !== false || 'plugin' == $type ) {
127113
// This is a plugin
128114
$this->type = 'plugin';
129115

@@ -181,29 +167,9 @@ function load_locale() {
181167
function register_activation ( $pluginfile = __FILE__, $function = '' ) {
182168
if ( $this->type == 'plugin' ) {
183169
add_action ('activate_'.basename (dirname ($pluginfile)).'/'.basename ($pluginfile), array ($this, $function == '' ? 'activate' : $function));
184-
} elseif ( $this->type == 'theme' ) {
185-
$this->theme_activation_function = ( $function ) ? $function : 'activate';
186-
add_action ('load-themes.php', array ( $this, 'theme_activation' ) );
187170
}
188171
}
189172

190-
/**
191-
* Hack to catch theme activation. We hook the load-themes.php action, look for the
192-
* "activated" GET param and make a big fat assumption if we find it.
193-
*
194-
* @return void
195-
* @author Simon Wheatley
196-
**/
197-
public function theme_activation() {
198-
$activated = (bool) @ $_GET[ 'activated' ];
199-
if ( ! $activated )
200-
return;
201-
if ( ! $this->theme_activation_function )
202-
return;
203-
// Looks like the theme might just have been activated, call the registered function
204-
$this->{$this->theme_activation_function}();
205-
}
206-
207173
/**
208174
* Special deactivation function that takes into account the plugin directory
209175
*

class-post-public.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,12 +550,9 @@ public function the_posts( array $posts, WP_Query & $wp_query ) {
550550
if ( ! $subs_index )
551551
return $posts;
552552

553-
$subs_posts = get_posts( array( 'include' => array_values( $subs_index ), 'post_status' => 'publish' ) );
554-
// @FIXME: Check the above get_posts call results are cached somewhere… I think they are
555553
// @FIXME: Alternative approach: hook on save_post to save the current value to the translation, BUT content could get out of date – in post_content_filtered
556554
foreach ( $posts as & $post ) {
557555
// @TODO why does this only override the title/excerpt/content? Why not override the post object entirely?
558-
// @FIXME: I'm assuming this get_post call is cached, which it seems to be
559556
if( isset( $subs_index[ $post->ID ] ) ) {
560557
$default_post = get_post( $subs_index[ $post->ID ] );
561558
if ( empty( $post->post_title ) )

class-switcher-content.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ protected function populate_links() {
9696
$this->jobs = bbl_get_incomplete_post_jobs( get_option( 'page_for_posts' ) );
9797
} else if ( ( !is_admin() and ( is_tax() || is_category() ) ) || $editing_term ) {
9898
if ( isset( $_REQUEST[ 'tag_ID' ] ) )
99-
$term = get_term( (int) @ $_REQUEST[ 'tag_ID' ], $this->screen->taxonomy );
99+
$term = get_term( absint( $_REQUEST[ 'tag_ID' ] ), $this->screen->taxonomy );
100100
else
101101
$term = get_queried_object();
102102
$this->translations = bbl_get_term_translations( $term->term_id, $term->taxonomy );

class-taxonomy.php

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ public function __construct() {
4949
add_action( 'init', array( $this, 'init_early' ), 0 );
5050
add_action( 'parse_request', array( $this, 'parse_request' ) );
5151
add_action( 'registered_taxonomy', array( $this, 'registered_taxonomy' ), 10, 3 );
52-
add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
5352
add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 5 );
5453
add_filter( 'get_terms', array( $this, 'get_terms' ) );
5554
add_filter( 'term_link', array( $this, 'term_link' ), 10, 3 );
@@ -263,18 +262,6 @@ public function created_new_shadow_post( $new_post_id, $origin_post_id ) {
263262
$this->no_recursion = false;
264263
}
265264

266-
/**
267-
* Hooks the WP save_post action to resync data
268-
* when requested.
269-
*
270-
* @param int $post_id The ID of the WP post
271-
* @param object $post The WP Post object
272-
* @return void
273-
**/
274-
public function save_post( $post_id, $post ) {
275-
$this->maybe_resync_terms( $post_id, $post );
276-
}
277-
278265
/**
279266
* Hooks the WordPress term_link filter to provide functions to provide
280267
* appropriate links for the shadow taxonomies.

tests/test-languages.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
class Test_Languages extends Babble_UnitTestCase {
4+
5+
public function setUp() {
6+
$this->install_languages();
7+
8+
parent::setUp();
9+
}
10+
11+
public function test_post_translations() {
12+
13+
$this->assertFalse( Babble_Languages::is_rtl( 'en_US' ) );
14+
$this->assertFalse( Babble_Languages::is_rtl( 'fr_FR' ) );
15+
$this->assertFalse( Babble_Languages::is_rtl( 'invalid' ) );
16+
$this->assertTrue( Babble_Languages::is_rtl( 'ar' ) );
17+
18+
}
19+
20+
}

0 commit comments

Comments
 (0)