PluginProbe
Polylang / 1.6
Polylang v1.6
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / admin / admin-model.php

admin-model.php in Polylang 1.6, at admin/admin-model.php

399 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * extends the PLL_Model class with methods needed only in Polylang settings pages
5 *
6 * @since 1.2
7 */
8 class PLL_Admin_Model extends PLL_Model {
9
10 /*
11 * adds a new language
12 * creates a default category for this language
13 *
14 * list of arguments that $args must contain:
15 * name -> language name (used only for display)
16 * slug -> language code (ideally 2-letters ISO 639-1 language code
17 * locale -> WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
18 * rtl -> 1 if rtl language, 0 otherwise
19 * term_group -> language order when displayed
20 *
21 * optional arguments that $args can contain:
22 * no_default_cat -> if set, no default category will be created for this language
23 *
24 * @since 1.2
25 *
26 * @param array $args
27 * @return bool true if success / false if failed
28 */
29 public function add_language($args) {
30 if (!$this->validate_lang($args))
31 return false;
32
33 // first the language taxonomy
34 $description = serialize(array('locale' => $args['locale'], 'rtl' => $args['rtl']));
35 $r = wp_insert_term($args['name'], 'language', array('slug' => $args['slug'], 'description' => $description));
36 if (is_wp_error($r)) {
37 // avoid an ugly fatal error if something went wrong (reported once in the forum)
38 add_settings_error('general', 'pll_add_language', __('Impossible to add the language.', 'polylang'));
39 return false;
40 }
41 wp_update_term((int) $r['term_id'], 'language', array('term_group' => $args['term_group'])); // can't set the term group directly in wp_insert_term
42
43 // the term_language taxonomy
44 // don't want shared terms so use a different slug
45 wp_insert_term($args['name'], 'term_language', array('slug' => 'pll_' . $args['slug']));
46
47 $this->clean_languages_cache(); // udpate the languages list now !
48
49 if (!isset($this->options['default_lang'])) {
50 // if this is the first language created, set it as default language
51 $this->options['default_lang'] = $args['slug'];
52 update_option('polylang', $this->options);
53
54 // and assign default language to default category
55 $this->set_term_language((int) get_option('default_category'), (int) $r['term_id']);
56 }
57 elseif (empty($args['no_default_cat']))
58 $this->create_default_category($args['slug']);
59
60 flush_rewrite_rules(); // refresh rewrite rules
61
62 add_settings_error('general', 'pll_languages_created', __('Language added.', 'polylang'), 'updated');
63 return true;
64 }
65
66 /*
67 * create a default category for a language
68 *
69 * @since 1.2
70 *
71 * @param object|string|int $lang language
72 */
73 public function create_default_category($lang) {
74 $lang = $this->get_language($lang);
75
76 // create a new category
77 $cat_name = __('Uncategorized');
78 $cat_slug = sanitize_title($cat_name . '-' . $lang->slug);
79 $cat = wp_insert_term($cat_name, 'category', array('slug' => $cat_slug));
80
81 // check that the category was not previously created (in case the language was deleted and recreated)
82 $cat = isset($cat->error_data['term_exists']) ? $cat->error_data['term_exists'] : $cat['term_id'];
83
84 // set language
85 $this->set_term_language((int) $cat, $lang);
86
87 // this is a translation of the default category
88 $default = (int) get_option('default_category');
89 $translations = $this->get_translations('term', $default);
90 if (empty($translations)) {
91 if ($lg = $this->get_term_language($default))
92 $translations[$lg->slug] = $default;
93 else
94 $translations = array();
95 }
96
97 $this->save_translations('term', (int) $cat, $translations);
98 }
99
100 /*
101 * delete a language
102 *
103 * @since 1.2
104 *
105 * @param int $lang_id language term_id
106 */
107 public function delete_language($lang_id) {
108 $lang = $this->get_language((int) $lang_id);
109 $default_cats = $this->get_translations('term', get_option('default_category'));
110
111 // delete the translations
112 $this->update_translations($lang->slug);
113
114 // delete language option in widgets
115 foreach ($GLOBALS['wp_registered_widgets'] as $widget) {
116 if (!empty($widget['callback'][0]) && !empty($widget['params'][0]['number'])) {
117 $obj = $widget['callback'][0];
118 $number = $widget['params'][0]['number'];
119 if (is_object($obj) && method_exists($obj, 'get_settings') && method_exists($obj, 'save_settings')) {
120 $settings = $obj->get_settings();
121 if (isset($settings[$number]['pll_lang']) && $settings[$number]['pll_lang'] == $lang->slug) {
122 unset($settings[$number]['pll_lang']);
123 $obj->save_settings($settings);
124 }
125 }
126 }
127 }
128
129
130 // delete menus locations
131 if (!empty($this->options['nav_menus'])) {
132 foreach ($this->options['nav_menus'] as $theme => $locations) {
133 foreach ($locations as $location => $languages) {
134 unset($this->options['nav_menus'][$theme][$location][$lang->slug]);
135 }
136 }
137 }
138
139 // delete users options
140 foreach (get_users(array('fields' => 'ID')) as $user_id) {
141 delete_user_meta($user_id, 'user_lang', $lang->locale);
142 delete_user_meta($user_id, 'pll_filter_content', $lang->slug);
143 delete_user_meta($user_id, 'description_'.$lang->slug);
144 }
145
146 // delete the string translations
147 $post = get_page_by_title('polylang_mo_' . $lang->term_id, OBJECT, 'polylang_mo');
148 if (!empty($post))
149 wp_delete_post($post->ID);
150
151 // delete domain
152 unset($this->options['domains'][$lang->slug]);
153
154 // delete the language itself
155 wp_delete_term($lang->term_id, 'language');
156 wp_delete_term($lang->tl_term_id, 'term_language');
157
158 // update languages list
159 $this->clean_languages_cache();
160
161 // oops ! we deleted the default language...
162 if ($this->options['default_lang'] == $lang->slug) {
163 if ($slugs = $this->get_languages_list(array('fields' => 'slug'))) {
164 $this->options['default_lang'] = $slug = reset($slugs); // arbitrary choice...
165
166 // the default category should be in the default language
167 if (isset($default_cats[$slug]))
168 update_option('default_category', $default_cats[$slug]);
169 }
170 else
171 unset($this->options['default_lang']);
172 }
173
174 update_option('polylang', $this->options);
175 flush_rewrite_rules(); // refresh rewrite rules
176 add_settings_error('general', 'pll_languages_deleted', __('Language deleted.', 'polylang'), 'updated');
177 }
178
179 /*
180 * update language properties
181 *
182 * list of arguments that $args must contain:
183 * lang_id -> term_id of the language to modify
184 * name -> language name (used only for display)
185 * slug -> language code (ideally 2-letters ISO 639-1 language code
186 * locale -> WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
187 * rtl -> 1 if rtl language, 0 otherwise
188 * term_group -> language order when displayed
189 *
190 * @since 1.2
191 *
192 * @param array $args
193 * @return bool true if success / false if failed
194 */
195 public function update_language($args) {
196 $lang = $this->get_language((int) $args['lang_id']);
197 if (!$this->validate_lang($args, $lang))
198 return false;
199
200 // Update links to this language in posts and terms in case the slug has been modified
201 $slug = $args['slug'];
202 $old_slug = $lang->slug;
203
204 // FIXME should do this in an action 'edit_term' to prevent translations to break when sharing a term with nav_menu?
205 if ($old_slug != $slug) {
206 // update the language slug in translations
207 $this->update_translations($old_slug, $slug);
208
209 // update language option in widgets
210 foreach ($GLOBALS['wp_registered_widgets'] as $widget) {
211 if (!empty($widget['callback'][0]) && !empty($widget['params'][0]['number'])) {
212 $obj = $widget['callback'][0];
213 $number = $widget['params'][0]['number'];
214 if (is_object($obj) && method_exists($obj, 'get_settings') && method_exists($obj, 'save_settings')) {
215 $settings = $obj->get_settings();
216 if (isset($settings[$number]['pll_lang']) && $settings[$number]['pll_lang'] == $old_slug) {
217 $settings[$number]['pll_lang'] = $slug;
218 $obj->save_settings($settings);
219 }
220 }
221 }
222 }
223
224 // update menus locations
225 if (!empty($this->options['nav_menus'])) {
226 foreach ($this->options['nav_menus'] as $theme => $locations) {
227 foreach ($locations as $location => $languages) {
228 if (!empty($this->options['nav_menus'][$theme][$location][$old_slug])) {
229 $this->options['nav_menus'][$theme][$location][$slug] = $this->options['nav_menus'][$theme][$location][$old_slug];
230 unset($this->options['nav_menus'][$theme][$location][$old_slug]);
231 }
232
233 }
234 }
235 }
236
237 // update domains
238 if (!empty($this->options['domains'][$old_slug])) {
239 $this->options['domains'][$slug] = $this->options['domains'][$old_slug];
240 unset($this->options['domains'][$slug]);
241 }
242
243 // update the default language option if necessary
244 if ($this->options['default_lang'] == $old_slug)
245 $this->options['default_lang'] = $slug;
246 }
247
248 update_option('polylang', $this->options);
249
250 // and finally update the language itself
251 $description = serialize(array('locale' => $args['locale'], 'rtl' => $args['rtl']));
252 wp_update_term((int) $lang->term_id, 'language', array('slug' => $slug, 'name' => $args['name'], 'description' => $description, 'term_group' => $args['term_group']));
253 wp_update_term((int) $lang->tl_term_id, 'term_language', array('slug' => 'pll_' . $slug, 'name' => $args['name']));
254
255 $this->clean_languages_cache();
256 flush_rewrite_rules(); // refresh rewrite rules
257 add_settings_error('general', 'pll_languages_updated', __('Language updated.', 'polylang'), 'updated');
258 return true;
259 }
260
261 /*
262 * validates data entered when creating or updating a language
263 *
264 * @since 0.4
265 *
266 * @param array $args
267 * @param object $lang optional the language currently updated, the language is created if not set
268 * @return bool true if success / false if failed
269 * @see PLL_Admin_Model::add_language
270 */
271 protected function validate_lang($args, $lang = null) {
272 // validate locale
273 if ( !preg_match('#^[A-Za-z_]+$#', $args['locale']))
274 add_settings_error('general', 'pll_invalid_locale', __('Enter a valid WordPress locale', 'polylang'));
275
276 // validate slug characters
277 if (!preg_match('#^[a-z_-]+$#', $args['slug']))
278 add_settings_error('general', 'pll_invalid_slug', __('The language code contains invalid characters', 'polylang'));
279
280 // validate slug is unique
281 if ($this->get_language($args['slug']) && ($lang === null || (isset($lang) && $lang->slug != $args['slug'])))
282 add_settings_error('general', 'pll_non_unique_slug', __('The language code must be unique', 'polylang'));
283
284 // validate name
285 if ($args['name'] == '')
286 add_settings_error('general', 'pll_invalid_name', __('The language must have a name', 'polylang'));
287
288 return get_settings_errors() ? false : true;
289 }
290
291 /*
292 * used to set the language of posts or terms in mass
293 *
294 * @since 1.2
295 *
296 * @param string $type either 'post' or 'term'
297 * @param array $ids array of post ids or term ids
298 * @param object|string $lang object or slug
299 */
300 public function set_language_in_mass($type, $ids, $lang) {
301 global $wpdb;
302
303 $lang = $this->get_language($lang);
304 $tt_id = 'term' == $type ? $lang->tl_term_taxonomy_id : $lang->term_taxonomy_id;
305
306 foreach ($ids as $id)
307 $values[] = $wpdb->prepare("(%d, %d)", $id, $tt_id);
308
309 if (!empty($values)) {
310 $values = array_unique($values);
311 $wpdb->query("INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id) VALUES " . implode(',', $values));
312 $lang->update_count(); // updating term count is mandatory (thanks to AndyDeGroo)
313 }
314 }
315
316 /*
317 * returns unstranslated posts and terms ids (used in settings)
318 *
319 * @since 0.9
320 *
321 * @return array array made of an array of post ids and an array of term ids
322 */
323 public function get_objects_with_no_lang() {
324 $posts = get_posts(array(
325 'numberposts' => -1,
326 'nopaging' => true,
327 'post_type' => $this->get_translated_post_types(),
328 'post_status' => 'any',
329 'fields' => 'ids',
330 'tax_query' => array(array(
331 'taxonomy' => 'language',
332 'terms' => $this->get_languages_list(array('fields' => 'term_id')),
333 'operator' => 'NOT IN'
334 ))
335 ));
336
337 $terms = get_terms($this->get_translated_taxonomies(), array('get'=>'all', 'fields'=>'ids'));
338 $groups = $this->get_languages_list(array('fields' => 'tl_term_id'));
339 $tr_terms = get_objects_in_term($groups, 'term_language');
340 $terms = array_unique(array_diff($terms, $tr_terms)); // array_unique to avoid duplicates if a term is in more than one taxonomy
341
342 return apply_filters('pll_get_objects_with_no_lang', empty($posts) && empty($terms) ? false : array('posts' => $posts, 'terms' => $terms));
343 }
344
345 /*
346 * used to delete translations or update the translations when a language slug has been modified in settings
347 *
348 * @since 0.5
349 *
350 * @param string $old_slug the old language slug
351 * @param string $new_slug optional, the new language slug, if not set it means the correspondant has been deleted
352 */
353 public function update_translations($old_slug, $new_slug = '') {
354 global $wpdb;
355
356 $terms = get_terms(array('post_translations', 'term_translations'));
357
358 foreach ($terms as $term) {
359 $tr = unserialize($term->description);
360 if (!empty($tr[$old_slug])) {
361 if ($new_slug)
362 $tr[$new_slug] = $tr[$old_slug]; // suppress this for delete
363 else {
364 $dr['id'][] = (int) $tr[$old_slug];
365 $dr['tt'][] = (int) $term->term_taxonomy_id;
366 }
367 unset($tr[$old_slug]);
368
369 if (empty($tr) || 1 == count($tr)) {
370 $dt['t'][] = (int) $term->term_id;
371 $dt['tt'][] = (int) $term->term_taxonomy_id;
372 }
373 else {
374 $ut['case'][] = $wpdb->prepare('WHEN %d THEN %s', $term->term_id, serialize($tr));
375 $ut['in'][] = (int) $term->term_id;
376 }
377 }
378 }
379
380 // delete relationships
381 if (!empty($dr))
382 $wpdb->query("DELETE FROM $wpdb->term_relationships
383 WHERE object_id IN ( " . implode(',', $dr['id']) . " )
384 AND term_taxonomy_id IN ( " . implode(',', $dr['tt']) . " )");
385
386 // delete terms
387 if (!empty($dt)) {
388 $wpdb->query("DELETE FROM $wpdb->terms WHERE term_id IN ( " . implode(',', $dt['t']) . " ) ");
389 $wpdb->query("DELETE FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ( " . implode(',', $dt['tt']) . " ) ");
390 }
391
392 // update terms
393 if (!empty($ut))
394 $wpdb->query("UPDATE $wpdb->term_taxonomy
395 SET description = ( CASE term_id " . implode(' ', $ut['case']) . " END )
396 WHERE term_id IN ( " . implode(',', $ut['in']) . " )");
397 }
398 }
399