PluginProbe
Polylang / 2.5.2
Polylang v2.5.2
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
← All changes | admin/admin-filters-columns.php +70 -168 3.02.5.2 View file →
@@ -1,8 +1,5 @@
1 1 <?php
2 -/**
3 - * @package Polylang
4 - */
5 2
6 3 /**
7 4 * Adds the language column in posts and terms list tables
8 5 * Manages quick edit and bulk edit as well
@@ -9,26 +6,11 @@
9 6 *
10 7 * @since 1.2
11 8 */
12 9 class PLL_Admin_Filters_Columns {
13 - /**
14 - * @var PLL_Model
15 - */
16 - public $model;
10 + public $links, $model, $filter_lang;
17 11
18 12 /**
19 - * @var PLL_Admin_Links
20 - */
21 - public $links;
22 -
23 - /**
24 - * Language selected in the admin language filter.
25 - *
26 - * @var PLL_Language
27 - */
28 - public $filter_lang;
29 -
30 - /**
31 13 * Constructor: setups filters and actions
32 14 *
33 15 * @since 1.2
34 16 *
@@ -38,42 +20,39 @@
38 20 $this->links = &$polylang->links;
39 21 $this->model = &$polylang->model;
40 22 $this->filter_lang = &$polylang->filter_lang;
41 23
42 - // Hide the column of the filtered language.
43 - add_filter( 'hidden_columns', array( $this, 'hidden_columns' ) ); // Since WP 4.4.
44 -
45 - // Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels.
24 + // Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels
46 25 foreach ( $this->model->get_translated_post_types() as $type ) {
47 26 // Use the latest filter late as some plugins purely overwrite what's done by others :(
48 - // Specific case for media.
27 + // Specific case for media
49 28 add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
50 29 add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
51 30 }
52 31
53 - // Quick edit and bulk edit.
54 - add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
55 - add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
32 + // Quick edit and bulk edit
33 + add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
34 + add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
56 35
57 - // Adds the language column in the 'Categories' and 'Post Tags' tables.
36 + // Adds the language column in the 'Categories' and 'Post Tags' tables
58 37 foreach ( $this->model->get_translated_taxonomies() as $tax ) {
59 38 add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
60 39 add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
61 40 }
62 41
63 - // Ajax responses to update list table rows.
42 + // Ajax responses to update list table rows
64 43 add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
65 44 add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
66 45 }
67 46
68 47 /**
69 - * Adds languages and translations columns in posts, pages, media, categories and tags tables.
48 + * Adds languages and translations columns in posts, pages, media, categories and tags tables
70 49 *
71 50 * @since 0.8.2
72 51 *
73 - * @param string[] $columns List of table columns.
74 - * @param string $before The column before which we want to add our languages.
75 - * @return string[] Modified list of columns.
52 + * @param array $columns List of table columns
53 + * @param string $before The column before which we want to add our languages
54 + * @return array modified list of columns
76 55 */
77 56 protected function add_column( $columns, $before ) {
78 57 if ( $n = array_search( $before, array_keys( $columns ) ) ) {
79 58 $end = array_slice( $columns, $n );
@@ -80,9 +59,12 @@
80 59 $columns = array_slice( $columns, 0, $n );
81 60 }
82 61
83 62 foreach ( $this->model->get_languages_list() as $language ) {
84 - $columns[ 'language_' . $language->slug ] = $this->get_flag_html( $language ) . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>';
63 + // Don't add the column for the filtered language
64 + if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
65 + $columns[ 'language_' . $language->slug ] = $language->flag ? $language->flag . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>' : esc_html( $language->slug );
66 + }
85 67 }
86 68
87 69 return isset( $end ) ? array_merge( $columns, $end ) : $columns;
88 70 }
@@ -94,12 +76,12 @@
94 76 *
95 77 * @return string first language column name
96 78 */
97 79 protected function get_first_language_column() {
98 - $columns = array();
99 -
100 80 foreach ( $this->model->get_languages_list() as $language ) {
101 - $columns[] = 'language_' . $language->slug;
81 + if ( empty( $this->filter_lang ) || $language->slug != $this->filter_lang->slug ) {
82 + $columns[] = 'language_' . $language->slug;
83 + }
102 84 }
103 85
104 86 return empty( $columns ) ? '' : reset( $columns );
105 87 }
@@ -104,29 +86,14 @@
104 86 return empty( $columns ) ? '' : reset( $columns );
105 87 }
106 88
107 89 /**
108 - * Hides the column for the filtered language.
90 + * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
109 91 *
110 - * @since 2.7
111 - *
112 - * @param string[] $hidden Array of hidden columns.
113 - * @return string[]
114 - */
115 - public function hidden_columns( $hidden ) {
116 - if ( ! empty( $this->filter_lang ) ) {
117 - $hidden[] = 'language_' . $this->filter_lang->slug;
118 - }
119 - return $hidden;
120 - }
121 -
122 - /**
123 - * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables.
124 - *
125 92 * @since 0.1
126 93 *
127 - * @param string[] $columns List of posts table columns.
128 - * @return string[] Modified list of columns.
94 + * @param array $columns list of posts table columns
95 + * @return array modified list of columns
129 96 */
130 97 public function add_post_column( $columns ) {
131 98 return $this->add_column( $columns, 'comments' );
132 99 }
@@ -138,13 +105,12 @@
138 105 * @since 0.1
139 106 *
140 107 * @param string $column Column name
141 108 * @param int $post_id
142 - * @return void
143 109 */
144 110 public function post_column( $column, $post_id ) {
145 - $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
146 - $lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->post->get_language( $post_id ); // phpcs:ignore WordPress.Security.NonceVerification
111 + $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save' === $_REQUEST['action'];
112 + $lang = $inline ? $this->model->get_language( $_POST['inline_lang_choice'] ) : $this->model->post->get_language( $post_id );
147 113
148 114 if ( false === strpos( $column, 'language_' ) || ! $lang ) {
149 115 return;
150 116 }
@@ -150,26 +116,22 @@
150 116 }
151 117
152 118 $language = $this->model->get_language( substr( $column, 9 ) );
153 119
154 - if ( empty( $language ) ) {
155 - return;
156 - }
157 -
158 120 // Hidden field containing the post language for quick edit
159 121 if ( $column == $this->get_first_language_column() ) {
160 122 printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
161 123 }
162 124
125 + $post_type_object = get_post_type_object( get_post_type( $post_id ) );
126 +
163 127 // Link to edit post ( or a translation )
164 128 if ( $id = $this->model->post->get( $post_id, $language ) ) {
165 129 // get_edit_post_link returns nothing if the user cannot edit the post
166 130 // Thanks to Solinx. See http://wordpress.org/support/topic/feature-request-incl-code-check-for-capabilities-in-admin-screens
167 131 if ( $link = get_edit_post_link( $id ) ) {
168 - $flag = '';
169 132 if ( $id === $post_id ) {
170 - $flag = $this->get_flag_html( $language );
171 - $class = 'pll_column_flag';
133 + $class = 'pll_icon_tick';
172 134 /* translators: accessibility text, %s is a native language name */
173 135 $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
174 136 } else {
175 137 $class = esc_attr( 'pll_icon_edit translation_' . $id );
@@ -175,33 +137,26 @@
175 137 $class = esc_attr( 'pll_icon_edit translation_' . $id );
176 138 /* translators: accessibility text, %s is a native language name */
177 139 $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
178 140 }
179 -
180 - $post = get_post( $id );
181 -
182 - if ( ! empty( $post ) ) {
183 - printf(
184 - '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
185 - esc_attr( $class ),
186 - esc_attr( $post->post_title ),
187 - esc_url( $link ),
188 - esc_html( $s ),
189 - $flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
190 - );
191 - }
141 + printf(
142 + '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span></a>',
143 + esc_attr( $class ),
144 + esc_attr( get_post( $id )->post_title ),
145 + esc_url( $link ),
146 + esc_html( $s )
147 + );
192 148 } elseif ( $id === $post_id ) {
193 149 printf(
194 - '<span class="pll_column_flag" style=""><span class="screen-reader-text">%1$s</span>%2$s</span>',
150 + '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
195 151 /* translators: accessibility text, %s is a native language name */
196 - esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) ),
197 - $this->get_flag_html( $language ) // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
152 + esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
198 153 );
199 154 }
200 155 }
201 156 // Link to add a new translation
202 157 else {
203 - echo $this->links->new_post_translation_link( $post_id, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
158 + echo $this->links->new_post_translation_link( $post_id, $language );
204 159 }
205 160 }
206 161
207 162 /**
@@ -209,16 +164,17 @@
209 164 *
210 165 * @since 0.9
211 166 *
212 167 * @param string $column column name
168 + * @param string $type either 'edit-tags' for terms list table or post type for posts list table
213 169 * @return string unmodified $column
214 170 */
215 - public function quick_edit_custom_box( $column ) {
171 + public function quick_edit_custom_box( $column, $type ) {
216 172 if ( $column == $this->get_first_language_column() ) {
217 173
218 174 $elements = $this->model->get_languages_list();
219 175 if ( current_filter() == 'bulk_edit_custom_box' ) {
220 - array_unshift( $elements, (object) array( 'slug' => -1, 'name' => __( '&mdash; No Change &mdash;', 'polylang' ) ) );
176 + array_unshift( $elements, (object) array( 'slug' => -1, 'name' => __( '&mdash; No Change &mdash;' ) ) );
221 177 }
222 178
223 179 $dropdown = new PLL_Walker_Dropdown();
224 180 // The hidden field 'old_lang' allows to pass the old language to ajax request
@@ -231,9 +187,9 @@
231 187 </label>
232 188 </div>
233 189 </fieldset>',
234 190 esc_html__( 'Language', 'polylang' ),
235 - $dropdown->walk( $elements, -1, array( 'name' => 'inline_lang_choice', 'id' => '' ) ) // phpcs:ignore WordPress.Security.EscapeOutput
191 + $dropdown->walk( $elements, array( 'name' => 'inline_lang_choice', 'id' => '' ) )
236 192 );
237 193 }
238 194 return $column;
239 195 }
@@ -238,14 +194,14 @@
238 194 return $column;
239 195 }
240 196
241 197 /**
242 - * Adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table.
198 + * Adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table
243 199 *
244 200 * @since 0.1
245 201 *
246 - * @param string[] $columns List of terms table columns.
247 - * @return string[] modified List of columns.
202 + * @param array $columns list of terms table columns
203 + * @return array modified list of columns
248 204 */
249 205 public function add_term_column( $columns ) {
250 206 return $this->add_column( $columns, 'posts' );
251 207 }
@@ -257,33 +213,19 @@
257 213 *
258 214 * @param string $out
259 215 * @param string $column Column name
260 216 * @param int $term_id
261 - * @return string
262 217 */
263 218 public function term_column( $out, $column, $term_id ) {
264 - $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save-tax' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
265 - if ( false === strpos( $column, 'language_' ) || ! ( $lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->term->get_language( $term_id ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
219 + $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save-tax' === $_REQUEST['action'];
220 + if ( false === strpos( $column, 'language_' ) || ! ( $lang = $inline ? $this->model->get_language( $_POST['inline_lang_choice'] ) : $this->model->term->get_language( $term_id ) ) ) {
266 221 return $out;
267 222 }
268 223
269 - if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
270 - $post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
271 - }
224 + $post_type = isset( $GLOBALS['post_type'] ) ? $GLOBALS['post_type'] : $_REQUEST['post_type']; // 2nd case for quick edit
225 + $taxonomy = isset( $GLOBALS['taxonomy'] ) ? $GLOBALS['taxonomy'] : $_REQUEST['taxonomy'];
272 226
273 - if ( isset( $GLOBALS['post_type'] ) ) {
274 - $post_type = $GLOBALS['post_type'];
275 - }
276 -
277 - if ( isset( $_REQUEST['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
278 - $taxonomy = sanitize_key( $_REQUEST['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
279 - }
280 -
281 - if ( isset( $GLOBALS['taxonomy'] ) ) {
282 - $taxonomy = $GLOBALS['taxonomy'];
283 - }
284 -
285 - if ( ! isset( $taxonomy, $post_type ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
227 + if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
286 228 return $out;
287 229 }
288 230
289 231 $term_id = (int) $term_id;
@@ -288,12 +230,8 @@
288 230
289 231 $term_id = (int) $term_id;
290 232 $language = $this->model->get_language( substr( $column, 9 ) );
291 233
292 - if ( empty( $language ) ) {
293 - return $out;
294 - }
295 -
296 234 if ( $column == $this->get_first_language_column() ) {
297 235 $out = sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
298 236
299 237 // Identify the default categories to disable the language dropdown in js
@@ -304,12 +242,10 @@
304 242
305 243 // Link to edit term ( or a translation )
306 244 if ( ( $id = $this->model->term->get( $term_id, $language ) ) && $term = get_term( $id, $taxonomy ) ) {
307 245 if ( $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
308 - $flag = '';
309 246 if ( $id === $term_id ) {
310 - $flag = $this->get_flag_html( $language );
311 - $class = 'pll_column_flag';
247 + $class = 'pll_icon_tick';
312 248 /* translators: accessibility text, %s is a native language name */
313 249 $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
314 250 } else {
315 251 $class = esc_attr( 'pll_icon_edit translation_' . $id );
@@ -316,21 +252,19 @@
316 252 /* translators: accessibility text, %s is a native language name */
317 253 $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
318 254 }
319 255 $out .= sprintf(
320 - '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
256 + '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span></a>',
321 257 $class,
322 258 esc_attr( $term->name ),
323 259 esc_url( $link ),
324 - esc_html( $s ),
325 - $flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
260 + esc_html( $s )
326 261 );
327 262 } elseif ( $id === $term_id ) {
328 - $out .= sprintf(
329 - '<span class="pll_column_flag"><span class="screen-reader-text">%1$s</span>%2$s</span>',
263 + $out .= printf(
264 + '<span class="pll_icon_tick"><span class="screen-reader-text">%s</span></span>',
330 265 /* translators: accessibility text, %s is a native language name */
331 - esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) ),
332 - $this->get_flag_html( $language ) // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
266 + esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) )
333 267 );
334 268 }
335 269 }
336 270
@@ -345,35 +279,25 @@
345 279 /**
346 280 * Update rows of translated posts when the language is modified in quick edit
347 281 *
348 282 * @since 1.7
349 - *
350 - * @return void
351 283 */
352 284 public function ajax_update_post_rows() {
353 - check_ajax_referer( 'inlineeditnonce', '_pll_nonce' );
285 + global $wp_list_table;
354 286
355 - if ( ! isset( $_POST['post_type'], $_POST['post_id'], $_POST['screen'] ) ) {
356 - wp_die( 0 );
287 + if ( ! post_type_exists( $post_type = $_POST['post_type'] ) || ! $this->model->is_translated_post_type( $post_type ) ) {
288 + die( 0 );
357 289 }
358 290
359 - $post_type = sanitize_key( $_POST['post_type'] );
291 + check_ajax_referer( 'inlineeditnonce', '_pll_nonce' );
360 292
361 - if ( ! post_type_exists( $post_type ) || ! $this->model->is_translated_post_type( $post_type ) ) {
362 - wp_die( 0 );
363 - }
364 -
365 - /** @var WP_Posts_List_Table $wp_list_table */
366 - $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
367 -
368 293 $x = new WP_Ajax_Response();
294 + $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
369 295
370 - // Collect old translations
371 - $translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
296 + $translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // collect old translations
297 + $translations = array_merge( $translations, array( $_POST['post_id'] ) ); // add current post
372 298 $translations = array_map( 'intval', $translations );
373 299
374 - $translations = array_merge( $translations, array( (int) $_POST['post_id'] ) ); // Add current post
375 -
376 300 foreach ( $translations as $post_id ) {
377 301 $level = is_post_type_hierarchical( $post_type ) ? count( get_ancestors( $post_id, $post_type ) ) : 0;
378 302 if ( $post = get_post( $post_id ) ) {
379 303 ob_start();
@@ -389,36 +313,26 @@
389 313 /**
390 314 * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
391 315 *
392 316 * @since 1.7
393 - *
394 - * @return void
395 317 */
396 318 public function ajax_update_term_rows() {
397 - check_ajax_referer( 'pll_language', '_pll_nonce' );
319 + global $wp_list_table;
398 320
399 - if ( ! isset( $_POST['taxonomy'], $_POST['term_id'], $_POST['screen'] ) ) {
400 - wp_die( 0 );
321 + if ( ! taxonomy_exists( $taxonomy = $_POST['taxonomy'] ) || ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
322 + die( 0 );
401 323 }
402 324
403 - $taxonomy = sanitize_key( $_POST['taxonomy'] );
325 + check_ajax_referer( 'pll_language', '_pll_nonce' );
404 326
405 - if ( ! taxonomy_exists( $taxonomy ) || ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
406 - wp_die( 0 );
407 - }
408 -
409 - /** @var WP_Terms_List_Table $wp_list_table */
410 - $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
411 -
412 327 $x = new WP_Ajax_Response();
328 + $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
413 329
414 - // Collect old translations
415 - $translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
330 + $translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // collect old translations
331 + $translations = array_merge( $translations, $this->model->term->get_translations( (int) $_POST['term_id'] ) ); // add current translations
332 + $translations = array_unique( $translations ); // remove duplicates
416 333 $translations = array_map( 'intval', $translations );
417 334
418 - $translations = array_merge( $translations, $this->model->term->get_translations( (int) $_POST['term_id'] ) ); // Add current translations
419 - $translations = array_unique( $translations ); // Remove duplicates
420 -
421 335 foreach ( $translations as $term_id ) {
422 336 $level = is_taxonomy_hierarchical( $taxonomy ) ? count( get_ancestors( $term_id, $taxonomy ) ) : 0;
423 337 if ( $tag = get_term( $term_id, $taxonomy ) ) {
424 338 ob_start();
@@ -428,18 +342,6 @@
428 342 }
429 343 }
430 344
431 345 $x->send();
432 - }
433 -
434 - /**
435 - * Returns the language flag or teh language slug if there is no flag.
436 - *
437 - * @since 2.8
438 - *
439 - * @param PLL_Language $language PLL_Language object.
440 - * @return string
441 - */
442 - protected function get_flag_html( $language ) {
443 - return $language->flag ? $language->flag : sprintf( '<abbr>%s</abbr>', esc_html( $language->slug ) );
444 346 }
445 347 }