PluginProbe
Polylang / 1.2.3
Polylang v1.2.3
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.2.3, at admin/admin-model.php

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