Textpattern | PHP Cross Reference | Content Management Systems |
Description: Languages panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 /** 25 * Languages panel. 26 * 27 * @package Admin\Lang 28 * @since 4.6.0 29 */ 30 31 if (!defined('txpinterface')) { 32 die('txpinterface is undefined.'); 33 } 34 35 if ($event == 'lang') { 36 require_privs('lang'); 37 38 $available_steps = array( 39 'get_language' => true, 40 'get_textpack' => true, 41 'remove_language' => true, 42 'save_language' => true, 43 'save_language_ui' => true, 44 'list_languages' => false, 45 ); 46 47 if ($step && bouncer($step, $available_steps)) { 48 $step(); 49 } else { 50 list_languages(); 51 } 52 } 53 54 /** 55 * Generates a grid of every language that Textpattern supports. 56 * 57 * @param string|array $message The activity message 58 */ 59 60 function list_languages($message = '') 61 { 62 global $txp_user, $prefs; 63 64 $allTypes = TEXTPATTERN_LANG_ACTIVE | TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_AVAILABLE; 65 $available_lang = Txp::get('\Textpattern\L10n\Lang')->available($allTypes, $allTypes); 66 $installed_lang = Txp::get('\Textpattern\L10n\Lang')->available(TEXTPATTERN_LANG_INSTALLED); 67 $active_lang = Txp::get('\Textpattern\L10n\Lang')->available(TEXTPATTERN_LANG_ACTIVE); 68 $represented_lang = array_merge($active_lang, $installed_lang); 69 70 $site_lang = get_pref('language', TEXTPATTERN_DEFAULT_LANG, true); 71 $ui_lang = get_pref('language_ui', $site_lang, true); 72 $cpanel = ''; 73 74 if (has_privs('lang.edit')) { 75 $langList = Txp::get('\Textpattern\L10n\Lang')->languageSelect('language', $site_lang); 76 $cpanel .= form( 77 tag(gTxt('active_language'), 'label', array('for' => 'language')). 78 $langList. 79 eInput('lang'). 80 sInput('save_language') 81 ); 82 } 83 84 $langList = Txp::get('\Textpattern\L10n\Lang')->languageSelect('language_ui', $ui_lang); 85 $lang_form = tag( 86 $cpanel. 87 form( 88 tag(gTxt('active_language_ui'), 'label', array('for' => 'language_ui')). 89 $langList. 90 eInput('lang'). 91 sInput('save_language_ui') 92 ), 'div', array('class' => 'txp-control-panel') 93 ); 94 95 $grid = ''; 96 $done = array(); 97 $in_use_by = safe_rows('val, user_name', 'txp_prefs', "name = 'language_ui' AND val in ('".join("','", doSlash(array_keys($represented_lang)))."') AND user_name != '".doSlash($txp_user)."'"); 98 99 $langUse = array(); 100 101 foreach ($in_use_by as $row) { 102 $langUse[$row['val']][] = $row['user_name']; 103 } 104 105 foreach ($langUse as $key => $row) { 106 $langUse[$key] = tag(eLink('admin', 'author_list', 'search_method', 'login', '('.count($row).')', 'crit', join(',', doSlash($row))), 'span', array('class' => 'txp-lang-user-count')); 107 } 108 109 // Create the widget components. 110 foreach ($represented_lang + $available_lang as $langname => $langdata) { 111 if (in_array($langname, $done)) { 112 continue; 113 } 114 115 $file_updated = (isset($langdata['db_lastmod']) && $langdata['file_lastmod'] > $langdata['db_lastmod']); 116 117 if (array_key_exists($langname, $represented_lang)) { 118 if ($file_updated) { 119 $cellclass = 'warning'; 120 $icon = 'ui-icon-alert'; 121 $status = gTxt('installed').' <span role="separator">/</span> '.gTxt('update_available'); 122 $disabled = (has_privs('lang.edit') ? '' : 'disabled'); 123 } else { 124 $cellclass = 'success'; 125 $icon = 'ui-icon-check'; 126 $status = gTxt('installed'); 127 $disabled = 'disabled'; 128 } 129 130 if (isset($available_lang[$langname])) { 131 $btnText = '<span class="ui-icon ui-icon-refresh"></span>'.sp.escape_title(gTxt('update')); 132 } else { 133 $btnText = ''; 134 $cellclass = 'warning'; 135 } 136 137 $removeText = '<span class="ui-icon ui-icon-minus"></span>'.sp.escape_title(gTxt('remove')); 138 139 $btnRemove = ( 140 array_key_exists($langname, $active_lang) 141 ? '' 142 : (has_privs('lang.edit') 143 ? tag($removeText, 'button', array( 144 'type' => 'submit', 145 'name' => 'remove_language', 146 )) 147 : '') 148 ); 149 } else { 150 $cellclass = $icon = ''; 151 $btnText = '<span class="ui-icon ui-icon-plus"></span>'.sp.escape_title(gTxt('install')); 152 $disabled = $btnRemove = ''; 153 } 154 155 $installLink = ($disabled 156 ? span($btnText, array('class' => 'txp-button disabled')) 157 : tag($btnText, 'button', array( 158 'type' => 'submit', 159 'name' => 'get_language', 160 ))); 161 162 $grid .= tag( 163 form( 164 graf( 165 ($icon ? '<span class="ui-icon '.$icon.'" role="status">'.$status.'</span>' : '').n. 166 tag(gTxt($langdata['name']), 'strong', array('dir' => 'auto')).br. 167 tag($langname, 'code', array('dir' => 'ltr')). 168 ($btnRemove && array_key_exists($langname, $langUse) ? n.$langUse[$langname] : '') 169 ). 170 graf( 171 (has_privs('lang.edit') 172 ? $installLink 173 : '') 174 .n. $btnRemove 175 ). 176 hInput('lang_code', $langname). 177 eInput('lang'). 178 sInput(null) 179 , '', '', 'post'), 180 'li', 181 array('class' => 'txp-grid-cell txp-grid-cell-2span'.($cellclass ? ' '.$cellclass : '')) 182 ).n; 183 184 $done[] = $langname; 185 } 186 187 // Output table and content. 188 pagetop(gTxt('tab_languages'), $message); 189 190 echo n.'<div class="txp-layout">'. 191 n.tag( 192 hed(gTxt('tab_languages'), 1, array('class' => 'txp-heading')), 193 'div', array('class' => 'txp-layout-1col') 194 ). 195 n.tag_start('div', array( 196 'class' => 'txp-layout-1col', 197 'id' => 'language_container', 198 )); 199 200 if (!empty($prefs['module_pophelp'])) { 201 echo graf(gTxt('language_preamble'), array('class' => 'txp-layout-textbox')); 202 } 203 204 if (isset($msg) && $msg) { 205 echo graf('<span class="ui-icon ui-icon-alert"></span> '.$msg, array('class' => 'alert-block error')); 206 } 207 208 echo $lang_form. 209 '<ul class="txp-grid txp-grid-lang">'. 210 $grid. 211 '</ul>'. 212 213 ((has_privs('lang.edit')) 214 ? hed(gTxt('install_from_textpack'), 3). 215 n.tag( 216 form( 217 '<label for="textpack-install">'.gTxt('install_textpack').'</label>'.popHelp('get_textpack'). 218 n.'<textarea class="code" id="textpack-install" name="textpack" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_SMALL.'" dir="ltr" required="required"></textarea>'. 219 fInput('submit', 'install_new', gTxt('upload')). 220 eInput('lang'). 221 sInput('get_textpack'), '', '', 'post', '', '', 'text_uploader' 222 ), 'div', array('class' => 'txp-control-panel')) 223 : ''); 224 225 echo n.tag_end('div'). // End of .txp-layout-1col. 226 n.'</div>'; // End of .txp-layout.; 227 } 228 229 /** 230 * Saves the active language. 231 */ 232 233 function save_language() 234 { 235 global $locale; 236 237 require_privs('lang.edit'); 238 239 extract(psa(array( 240 'language', 241 ))); 242 243 $txpLocale = Txp::get('\Textpattern\L10n\Locale'); 244 $langName = fetchLangName($language); 245 246 if (safe_field("lang", 'txp_lang', "lang = '".doSlash($language)."' LIMIT 1")) { 247 $txpLocale->setLocale(LC_TIME, LANG); 248 $old_formats = txp_dateformats(); 249 $candidates = array_unique(array($language, $txpLocale->getLocaleLanguage($language))); 250 $locale = $txpLocale->getLanguageLocale($language); 251 $new_locale = $txpLocale->setLocale(LC_ALL, array_filter($candidates))->getLocale(); 252 $new_language = $txpLocale->getLocaleLanguage($new_locale); 253 set_pref('locale', $new_locale); 254 $new_formats = txp_dateformats(); 255 256 foreach (array('dateformat', 'archive_dateformat', 'comments_dateformat') as $dateformat) { 257 $key = array_search(get_pref($dateformat), $old_formats); 258 259 if ($key !== false && $new_formats[$key] != $old_formats[$key]) { 260 set_pref($dateformat, $new_formats[$key]); 261 } 262 } 263 264 if ($new_locale == $locale || $new_language == $language) { 265 $msg = gTxt('preferences_saved'); 266 } else { 267 $msg = array(gTxt('locale_not_available_for_language', array('{name}' => $langName)), E_WARNING); 268 } 269 270 set_pref('language', $language); 271 list_languages($msg); 272 273 return; 274 } 275 276 list_languages(array(gTxt('language_not_installed', array('{name}' => $langName)), E_ERROR)); 277 } 278 279 /** 280 * Saves the active admin-side language. 281 */ 282 283 function save_language_ui() 284 { 285 global $locale; 286 287 extract(psa(array( 288 'language_ui', 289 ))); 290 291 if (get_pref('language_ui') != $language_ui) { 292 $langName = fetchLangName($language_ui); 293 294 if (safe_field("lang", 'txp_lang', "lang = '".doSlash($language_ui)."' LIMIT 1")) { 295 $locale = Txp::get('\Textpattern\L10n\Locale')->getLanguageLocale($language_ui); 296 297 if ($locale) { 298 set_pref('language_ui', $language_ui, 'admin', PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); 299 txp_die('', 307, '?event=lang'); 300 } else { 301 $msg = array(gTxt('locale_not_available_for_language', array('{name}' => $langName)), E_WARNING); 302 } 303 } else { 304 $msg = array(gTxt('language_not_installed', array('{name}' => $langName)), E_ERROR); 305 } 306 } else { 307 $msg = gTxt('preferences_saved'); 308 } 309 310 list_languages($msg); 311 } 312 313 /** 314 * Installs a language from a file. 315 * 316 * The HTTP POST parameter 'lang_code' is the installed language, 317 * e.g. 'en-gb', 'fi'. 318 */ 319 320 function get_language() 321 { 322 $lang_code = ps('lang_code'); 323 $langName = fetchLangName($lang_code); 324 $txpLang = Txp::get('\Textpattern\L10n\Lang'); 325 $installed = $txpLang->installed(); 326 $installString = in_array($lang_code, $installed) ? 'language_updated' : 'language_installed'; 327 328 if ($txpLang->installFile($lang_code)) { 329 callback_event('lang_installed', 'file', false, $lang_code); 330 331 $txpLang->available(TEXTPATTERN_LANG_AVAILABLE, TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_AVAILABLE); 332 Txp::get('\Textpattern\Plugin\Plugin')->installTextpacks(); 333 334 return list_languages(gTxt($installString, array('{name}' => $langName))); 335 } 336 337 return list_languages(array(gTxt('language_not_installed', array('{name}' => $langName)), E_ERROR)); 338 } 339 340 /** 341 * Writes a new language string to the database. 342 * 343 * The language is taken from a 'lang_code' HTTP POST or GET parameter. 344 * 345 * The '$value' argument takes a string as an array. This array consists of keys 346 * 'name', 'event', 'data', 'uLastmod'. 347 * 348 * @param array $value The string 349 * @param int $key Not used 350 */ 351 352 function install_lang_key(&$value, $key) 353 { 354 extract(gpsa(array( 355 'lang_code', 356 'updating', 357 ))); 358 359 $exists = safe_field( 360 "name", 361 'txp_lang', 362 "name = '".doSlash($value['name'])."' AND lang = '".doSlash($lang_code)."'" 363 ); 364 365 $q = 366 "name = '".doSlash($value['name'])."', 367 event = '".doSlash($value['event'])."', 368 data = '".doSlash($value['data'])."', 369 lastmod = '".doSlash(strftime('%Y%m%d%H%M%S', $value['uLastmod']))."'"; 370 371 if ($exists !== false) { 372 $value['ok'] = safe_update( 373 'txp_lang', 374 $q, 375 "owner = '".doSlash(TEXTPATTERN_LANG_OWNER_SYSTEM)."' AND lang = '".doSlash($lang_code)."' AND name = '".doSlash($value['name'])."'" 376 ); 377 } else { 378 $value['ok'] = safe_insert( 379 'txp_lang', 380 "$q, lang = '".doSlash($lang_code)."'" 381 ); 382 } 383 } 384 385 /** 386 * Installs a Textpack. 387 * 388 * The Textpack to load is fed by a 'textpack' HTTP POST parameter. 389 */ 390 391 function get_textpack() 392 { 393 require_privs('lang.edit'); 394 395 $textpack = ps('textpack'); 396 $n = Txp::get('\Textpattern\L10n\Lang')->installTextpack($textpack, true); 397 list_languages(gTxt('textpack_strings_installed', array('{count}' => $n))); 398 } 399 400 /** 401 * Remove all language strings for the given lang code. 402 * 403 * Removed language code is specified with 'lang_code' HTTP POST 404 * parameter. 405 */ 406 407 function remove_language() 408 { 409 global $event; 410 411 require_privs('lang.edit'); 412 413 $lang_code = ps('lang_code'); 414 $langName = fetchLangName($lang_code); 415 416 $ret = safe_delete('txp_lang', "lang = '".doSlash($lang_code)."'"); 417 418 if ($ret) { 419 callback_event('lang_deleted', '', 0, $lang_code); 420 $msg = gTxt('language_deleted', array('{name}' => $langName)); 421 $represented_lang = Txp::get('\Textpattern\L10n\Lang')->available( 422 TEXTPATTERN_LANG_ACTIVE | TEXTPATTERN_LANG_INSTALLED, 423 TEXTPATTERN_LANG_ACTIVE | TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_AVAILABLE 424 ); 425 426 $site_lang = get_pref('language', TEXTPATTERN_DEFAULT_LANG, true); 427 $ui_lang = get_pref('language_ui', $site_lang, true); 428 $ui_lang = (array_key_exists($ui_lang, $represented_lang)) ? $ui_lang : $site_lang; 429 set_pref('language_ui', $ui_lang, 'admin', PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); 430 load_lang($ui_lang, $event); 431 } else { 432 $msg = gTxt('cannot_delete', array('{thing}' => $langName)); 433 } 434 435 list_languages($msg); 436 } 437 438 /** 439 * Get the lang name from the given language file. 440 * 441 * @param string $lang_code Language designator 442 * @return string 443 */ 444 445 function fetchLangName($lang_code) 446 { 447 $txpLang = Txp::get('\Textpattern\L10n\Lang'); 448 $langFile = $txpLang->findFilename($lang_code); 449 $langInfo = $txpLang->fetchMeta($langFile); 450 $langName = (isset($langInfo['name'])) ? $langInfo['name'] : $lang_code; 451 452 return $langName; 453 } 454 455 /** 456 * Lists all language files in the 'lang' directory. 457 * 458 * @return array Available language filenames 459 * @deprecated in 4.7.0 460 */ 461 462 function get_lang_files() 463 { 464 return Txp::get('\Textpattern\L10n\Lang')->files(); 465 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title