Textpattern | PHP Cross Reference | Content Management Systems |
Description: Languages panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * http://textpattern.com 6 * 7 * Copyright (C) 2016 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 <http://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 include_once txpath.'/lib/txplib_update.php'; 36 37 if ($event == 'lang') { 38 require_privs('lang'); 39 40 $available_steps = array( 41 'get_language' => true, 42 'get_textpack' => true, 43 'remove_language' => true, 44 'save_language' => true, 45 'list_languages' => false, 46 ); 47 48 if ($step && bouncer($step, $available_steps)) { 49 $step(); 50 } else { 51 list_languages(); 52 } 53 } 54 55 /** 56 * Generate a <select> element of installed languages. 57 * 58 * @param string $name The HTML name and ID to assign to the select control 59 * @param string $val The currently active language identifier (en-gb, fr-fr, ...) 60 * @return string HTML 61 */ 62 63 function languages($name, $val) 64 { 65 $installed_langs = safe_column("lang", 'txp_lang', "1 = 1 GROUP BY lang"); 66 67 $vals = array(); 68 69 foreach ($installed_langs as $lang) { 70 $vals[$lang] = safe_field("data", 'txp_lang', "name = '".doSlash($lang)."' AND lang = '".doSlash($lang)."'"); 71 72 if (trim($vals[$lang]) == '') { 73 $vals[$lang] = $lang; 74 } 75 } 76 77 asort($vals); 78 reset($vals); 79 80 return selectInput($name, $vals, $val, false, true, $name); 81 } 82 83 /** 84 * Generates a <table> of every language that Textpattern supports. 85 * 86 * If requested with HTTP POST parameter 'force' set anything other than 'file', 87 * outputs any errors in RPC server connection. 88 * 89 * @param string|array $message The activity message 90 */ 91 92 function list_languages($message = '') 93 { 94 require_once txpath.'/lib/IXRClass.php'; 95 96 $active_lang = safe_field("val", 'txp_prefs', "name = 'language'"); 97 98 $lang_form = tag( 99 form( 100 tag(gTxt('active_language'), 'label', array('for' => 'language')). 101 languages('language', $active_lang). 102 eInput('lang'). 103 sInput('save_language') 104 ), 'div', array( 105 'class' => 'txp-control-panel', 106 ) 107 ); 108 109 $client = new IXR_Client(RPC_SERVER); 110 // $client->debug = true; 111 112 $available_lang = array(); 113 $rpc_connect = false; 114 $show_files = false; 115 116 // Get items from RPC. 117 @set_time_limit(90); // TODO: 90 seconds: seriously? 118 if ($client->query('tups.listLanguages', get_pref('blog_uid'))) { 119 $rpc_connect = true; 120 $response = $client->getResponse(); 121 122 foreach ($response as $language) { 123 $available_lang[$language['language']]['rpc_lastmod'] = gmmktime($language['lastmodified']->hour, $language['lastmodified']->minute, $language['lastmodified']->second, $language['lastmodified']->month, $language['lastmodified']->day, $language['lastmodified']->year); 124 } 125 } elseif (gps('force') != 'file') { 126 $msg = gTxt('rpc_connect_error')."<!--".$client->getErrorCode().' '.$client->getErrorMessage()."-->"; 127 } 128 129 // Get items from Filesystem. 130 $files = get_lang_files(); 131 132 if (is_array($files) && !empty($files)) { 133 foreach ($files as $file) { 134 if ($fp = @fopen(txpath.DS.'lang'.DS.$file, 'r')) { 135 $name = preg_replace('/\.(txt|textpack)$/i', '', $file); 136 $firstline = fgets($fp, 4069); 137 fclose($fp); 138 139 if (strpos($firstline, '#@version') !== false) { 140 @list($fversion, $ftime) = explode(';', trim(substr($firstline, strpos($firstline, ' ', 1)))); 141 } else { 142 $fversion = $ftime = null; 143 } 144 145 $available_lang[$name]['file_note'] = (isset($fversion)) ? $fversion : 0; 146 $available_lang[$name]['file_lastmod'] = (isset($ftime)) ? $ftime : 0; 147 } 148 } 149 } 150 151 // Get installed items from the database. 152 // We need a value here for the language itself, not for each one of the rows. 153 $rows = safe_rows("lang, UNIX_TIMESTAMP(MAX(lastmod)) AS lastmod", 'txp_lang', "1 = 1 GROUP BY lang ORDER BY lastmod DESC"); 154 $installed_lang = array(); 155 156 foreach ($rows as $language) { 157 $available_lang[$language['lang']]['db_lastmod'] = $language['lastmod']; 158 159 if ($language['lang'] != $active_lang) { 160 $installed_lang[] = $language['lang']; 161 } 162 } 163 164 $list = ''; 165 166 // Create the language table components. 167 foreach ($available_lang as $langname => $langdat) { 168 $file_updated = (isset($langdat['db_lastmod']) && @$langdat['file_lastmod'] > $langdat['db_lastmod']); 169 $rpc_updated = (@$langdat['rpc_lastmod'] > @$langdat['db_lastmod']); 170 171 $rpc_install = tda( 172 ($rpc_updated) 173 ? strong( 174 eLink( 175 'lang', 176 'get_language', 177 'lang_code', 178 $langname, 179 (isset($langdat['db_lastmod']) 180 ? gTxt('update') 181 : gTxt('install') 182 ), 183 'updating', 184 isset($langdat['db_lastmod']), 185 '' 186 ) 187 ). 188 n.span(safe_strftime('%d %b %Y %X', @$langdat['rpc_lastmod']), array('class' => 'date modified')) 189 : ( 190 (isset($langdat['rpc_lastmod']) 191 ? gTxt('up_to_date') 192 : '-' 193 ). 194 (isset($langdat['db_lastmod']) 195 ? n.span(safe_strftime('%d %b %Y %X', $langdat['db_lastmod']), array('class' => 'date modified')) 196 : '' 197 ) 198 ), (isset($langdat['db_lastmod']) && $rpc_updated) 199 ? ' class="highlight lang-value"' 200 : ' class="lang-value"' 201 ); 202 203 $lang_file = tda( 204 (isset($langdat['file_lastmod'])) 205 ? strong( 206 eLink( 207 'lang', 208 'get_language', 209 'lang_code', 210 $langname, 211 ( 212 ($file_updated) 213 ? gTxt('update') 214 : gTxt('install') 215 ), 216 'force', 217 'file', 218 '' 219 ) 220 ). 221 n.span(safe_strftime(get_pref('archive_dateformat'), $langdat['file_lastmod']), array( 222 'class' => 'date '.($file_updated ? 'created' : 'modified'), 223 )) 224 : '-', ' class="lang-value languages_detail'.((isset($langdat['db_lastmod']) && $rpc_updated) ? ' highlight' : '').'"' 225 ); 226 227 $list .= tr( 228 // Lang-Name and Date. 229 hCell( 230 gTxt($langname), '', (isset($langdat['db_lastmod']) && $rpc_updated) 231 ? ' class="highlight lang-label" scope="row"' 232 : ' class="lang-label" scope="row"' 233 ). 234 n.$rpc_install. 235 n.$lang_file. 236 tda((in_array($langname, $installed_lang) ? dLink('lang', 'remove_language', 'lang_code', $langname, 1) : '-'), ' class="languages_detail'.((isset($langdat['db_lastmod']) && $rpc_updated) ? ' highlight' : '').'"') 237 ).n; 238 } 239 240 // Output table and content. 241 pagetop(gTxt('tab_languages'), $message); 242 243 echo n.'<div class="txp-layout">'. 244 n.tag( 245 hed(gTxt('tab_languages'), 1, array('class' => 'txp-heading')), 246 'div', array('class' => 'txp-layout-1col') 247 ). 248 n.tag_start('div', array( 249 'class' => 'txp-layout-1col', 250 'id' => 'language_container', 251 )); 252 253 if (isset($msg) && $msg) { 254 echo graf('<span class="ui-icon ui-icon-alert"></span> '.$msg, array('class' => 'alert-block error')); 255 } 256 257 echo $lang_form, 258 n.tag( 259 toggle_box('languages_detail'), 'div', array('class' => 'txp-list-options')). 260 n.tag_start('div', array('class' => 'txp-listtables')). 261 n.tag_start('table', array('class' => 'txp-list')). 262 n.tag_start('thead'). 263 tr( 264 hCell( 265 gTxt('language'), '', ' scope="col"' 266 ). 267 hCell( 268 gTxt('from_server').popHelp('install_lang_from_server'), '', ' scope="col"' 269 ). 270 hCell( 271 gTxt('from_file').popHelp('install_lang_from_file'), '', ' class="languages_detail" scope="col"' 272 ). 273 hCell( 274 gTxt('remove_lang').popHelp('remove_lang'), '', ' class="languages_detail" scope="col"' 275 ) 276 ). 277 n.tag_end('thead'). 278 n.tag_start('tbody'). 279 $list. 280 n.tag_end('tbody'). 281 n.tag_end('table'). 282 n.tag_end('div'). // End of .txp-listtables. 283 284 hed(gTxt('install_from_textpack'), 2). 285 n.tag( 286 form( 287 '<label for="textpack-install">'.gTxt('install_textpack').'</label>'.popHelp('get_textpack'). 288 n.'<textarea class="code" id="textpack-install" name="textpack" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_SMALL.'" dir="ltr"></textarea>'. 289 fInput('submit', 'install_new', gTxt('upload')). 290 eInput('lang'). 291 sInput('get_textpack'), '', '', 'post', '', '', 'text_uploader' 292 ), 'div', array('class' => 'txp-control-panel')); 293 294 echo n.tag_end('div'). // End of .txp-layout-1col. 295 n.'</div>'; // End of .txp-layout.; 296 } 297 298 /** 299 * Saves the active language. 300 */ 301 302 function save_language() 303 { 304 global $textarray, $locale; 305 306 extract(psa(array( 307 'language', 308 ))); 309 310 if (safe_field("lang", 'txp_lang', "lang = '".doSlash($language)."' LIMIT 1")) { 311 $locale = Txp::get('\Textpattern\L10n\Locale')->getLanguageLocale($language); 312 $new_locale = $prefs['locale'] = Txp::get('\Textpattern\L10n\Locale')->setLocale(LC_ALL, array($language, 'C'))->getLocale(); 313 set_pref('locale', $new_locale); 314 if ($new_locale == $locale) { 315 $msg = gTxt('preferences_saved'); 316 } else { 317 $msg = array(gTxt('locale_not_available_for_language', array('{name}' => $language)), E_WARNING); 318 } 319 set_pref('language', $language); 320 $textarray = load_lang($language); 321 list_languages($msg); 322 323 return; 324 } 325 326 list_languages(array(gTxt('language_not_installed', array('{name}' => $language)), E_ERROR)); 327 } 328 329 /** 330 * Installs a language from the RPC server or from a file. 331 * 332 * This function fetches language strings for the given language code from 333 * either the RPC server or a file. 334 * 335 * Action is taken based on three HTTP POST parameters: 'lang_code', 'force' and 336 * 'updating'. The 'lang_code' is the installed langauge, e.g. 'en-gb', 'fi-fi'. 337 * The 'force' when set to 'file' can be used force an installation from a local 338 * file. The 'updating' specifies whether only to install (0) or to update (1). 339 */ 340 341 function get_language() 342 { 343 global $prefs, $textarray; 344 require_once txpath.'/lib/IXRClass.php'; 345 $lang_code = gps('lang_code'); 346 347 $client = new IXR_Client(RPC_SERVER); 348 // $client->debug = true; 349 350 @set_time_limit(90); // TODO: 90 seconds: seriously? 351 if (gps('force') == 'file' || !$client->query('tups.getLanguage', $prefs['blog_uid'], $lang_code)) { 352 if ((gps('force') == 'file' || gps('updating') !== '1') && install_language_from_file($lang_code)) { 353 if (defined('LANG')) { 354 $textarray = load_lang(LANG); 355 } 356 357 callback_event('lang_installed', 'file', false, $lang_code); 358 359 return list_languages(gTxt($lang_code).sp.gTxt('updated')); 360 } else { 361 pagetop(gTxt('installing_language')); 362 echo graf('<span class="ui-icon ui-icon-alert"></span> '.gTxt('rpc_connect_error')."<!--".$client->getErrorCode().' '.$client->getErrorMessage()."-->", array('class' => 'alert-block error')); 363 } 364 } else { 365 $response = $client->getResponse(); 366 $lang_struct = unserialize($response); 367 368 if ($lang_struct === false) { 369 $errors = $size = 1; 370 } else { 371 array_walk($lang_struct, 'install_lang_key'); 372 $size = count($lang_struct); 373 $errors = 0; 374 375 for ($i = 0; $i < $size; $i++) { 376 $errors += (!$lang_struct[$i]['ok']); 377 } 378 379 if (defined('LANG')) { 380 $textarray = load_lang(LANG); 381 } 382 } 383 384 $msg = gTxt($lang_code).sp.gTxt('updated'); 385 386 callback_event('lang_installed', 'remote', false, $lang_code); 387 388 if ($errors > 0) { 389 $msg = array($msg.sprintf(" (%s errors, %s ok)", $errors, ($size-$errors)), E_ERROR); 390 } 391 392 list_languages($msg); 393 } 394 } 395 396 /** 397 * Writes a new language string to the database. 398 * 399 * The language is taken from a 'lang_code' HTTP POST or GET parameter. 400 * 401 * The '$value' argument takes a string as an array. This array consists of keys 402 * 'name', 'event', 'data', 'uLastmod'. 403 * 404 * @param array $value The string 405 * @param int $key Not used 406 */ 407 408 function install_lang_key(&$value, $key) 409 { 410 extract(gpsa(array( 411 'lang_code', 412 'updating', 413 ))); 414 415 $exists = safe_field( 416 "name", 417 'txp_lang', 418 "name = '".doSlash($value['name'])."' AND lang = '".doSlash($lang_code)."'" 419 ); 420 421 $q = 422 "name = '".doSlash($value['name'])."', 423 event = '".doSlash($value['event'])."', 424 data = '".doSlash($value['data'])."', 425 lastmod = '".doSlash(strftime('%Y%m%d%H%M%S', $value['uLastmod']))."'"; 426 427 if ($exists !== false) { 428 $value['ok'] = safe_update( 429 'txp_lang', 430 $q, 431 "owner = '".doSlash(TEXTPATTERN_LANG_OWNER_SYSTEM)."' AND lang = '".doSlash($lang_code)."' AND name = '".doSlash($value['name'])."'" 432 ); 433 } else { 434 $value['ok'] = safe_insert( 435 'txp_lang', 436 "$q, lang = '".doSlash($lang_code)."'" 437 ); 438 } 439 } 440 441 /** 442 * Installs a Textpack. 443 * 444 * The Textpack is feeded by a 'textpack' HTTP POST parameter. 445 * 446 * @see install_textpack() 447 */ 448 449 function get_textpack() 450 { 451 $textpack = ps('textpack'); 452 $n = install_textpack($textpack, true); 453 list_languages(gTxt('textpack_strings_installed', array('{count}' => $n))); 454 } 455 456 /** 457 * Remove all language strings for the given lang code. 458 * 459 * Removed language code is specified with 'lang_code' HTTP POST 460 * parameter. 461 */ 462 463 function remove_language() 464 { 465 $lang_code = ps('lang_code'); 466 $ret = safe_delete('txp_lang', "lang = '".doSlash($lang_code)."'"); 467 468 if ($ret) { 469 callback_event('lang_deleted', '', 0, $lang_code); 470 $msg = gTxt($lang_code).sp.gTxt('deleted'); 471 } else { 472 $msg = gTxt('cannot_delete', array('{thing}' => $lang_code)); 473 } 474 475 list_languages($msg); 476 } 477 478 /** 479 * Lists all language files in the 'lang' directory. 480 * 481 * @return array Available language filenames 482 */ 483 484 function get_lang_files() 485 { 486 $lang_dir = txpath.DS.'lang'.DS; 487 488 if (!is_dir($lang_dir)) { 489 trigger_error('Lang directory is not a directory: '.$lang_dir, E_USER_WARNING); 490 491 return array(); 492 } 493 494 if (chdir($lang_dir)) { 495 if ($files = glob('*.{txt,textpack}', GLOB_BRACE)) { 496 return $files; 497 } 498 } 499 500 return array(); 501 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title