ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilObjLanguage.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
30{
35 public string $separator;
36 public string $comment_separator;
37 public string $lang_default;
38 public string $lang_user;
39 public string $lang_path;
40 public string $key;
41 public string $status;
42 public string $cust_lang_path;
43
50 public function __construct(int $a_id = 0, bool $a_call_by_reference = false)
51 {
52 global $DIC;
53 $lng = $DIC->language();
54
55 $this->type = "lng";
56 parent::__construct($a_id, $a_call_by_reference);
57
58 $this->type = "lng";
59 $this->key = $this->title;
60 $this->status = $this->desc;
61 $this->lang_default = $lng->lang_default;
62 $this->lang_user = $lng->lang_user;
63 $this->lang_path = $lng->lang_path;
64 $this->cust_lang_path = $lng->getCustomLangPath();
65 $this->separator = $lng->separator;
66 $this->comment_separator = $lng->comment_separator;
67 }
68
69
73 public static function getInstalledLanguages(): array
74 {
75 $objects = array();
76 $languages = ilObject::_getObjectsByType("lng");
77 foreach ($languages as $lang) {
78 $langObj = new ilObjLanguage((int) $lang["obj_id"], false);
79 if ($langObj->isInstalled()) {
80 $objects[] = $langObj;
81 } else {
82 unset($langObj);
83 }
84 }
85 return $objects;
86 }
87
88
94 public function getKey(): string
95 {
96 return $this->key;
97 }
98
104 public function getStatus(): string
105 {
106 return $this->status;
107 }
108
112 public function isSystemLanguage(): bool
113 {
114 if ($this->key == $this->lang_default) {
115 return true;
116 } else {
117 return false;
118 }
119 }
120
124 public function isUserLanguage(): bool
125 {
126 if ($this->key == $this->lang_user) {
127 return true;
128 } else {
129 return false;
130 }
131 }
132
138 public function isInstalled(): bool
139 {
140 if (str_starts_with($this->getStatus(), "installed")) {
141 return true;
142 } else {
143 return false;
144 }
145 }
146
153 public function isLocal(): bool
154 {
155 if (substr($this->getStatus(), 10) === "local") {
156 return true;
157 } else {
158 return false;
159 }
160 }
161
168 public function install(string $scope = ""): string
169 {
170 if (!empty($scope)) {
171 if ($scope === "global") {
172 $scope = "";
173 } else {
174 $scopeExtension = "." . $scope;
175 }
176 }
177
178 if (!$this->isInstalled() || (!$this->isLocal() && !empty($scope))) {
179 if ($this->check($scope)) {
180 // lang-file is ok. Flush data in db and...
181 if (empty($scope)) {
182 $this->flush("keep_local");
183 }
184
185 // ...re-insert data from lang-file
186 $this->insert($scope);
187
188 // update information in db-table about available/installed languages
189 $newDesc = '';
190 if (empty($scope)) {
191 $newDesc = "installed";
192 } elseif ($scope === "local") {
193 $newDesc = "installed_local";
194 }
195 $this->setDescription($newDesc);
196 $this->update();
197 return $this->getKey();
198 }
199 }
200 return "";
201 }
202
203
209 public function uninstall(): string
210 {
211 if ((str_starts_with($this->status, "installed")) && ($this->key != $this->lang_default) && ($this->key != $this->lang_user)) {
212 $this->flush();
213 $this->setTitle($this->key);
214 $this->setDescription("not_installed");
215 $this->update();
216 $this->resetUserLanguage($this->key);
217
218 return $this->key;
219 }
220 return "";
221 }
222
223
227 public function refresh(): bool
228 {
229 if ($this->isInstalled() && $this->check()) {
230 $this->flush("keep_local");
231 $this->insert();
232 $this->setTitle($this->getKey());
233 $this->setDescription($this->getStatus());
234 $this->update();
235
236 if ($this->isLocal() && $this->check("local")) {
237 $this->insert("local");
238 $this->setTitle($this->getKey());
239 $this->setDescription($this->getStatus());
240 $this->update();
241 }
242
243 return true;
244 }
245
246 return false;
247 }
248
252 public static function refreshAll(): void
253 {
254 $languages = ilObject::_getObjectsByType("lng");
255 $refreshed = array();
256
257 foreach ($languages as $lang) {
258 $langObj = new ilObjLanguage($lang["obj_id"], false);
259 if ($langObj->refresh()) {
260 $refreshed[] = $langObj->getKey();
261 }
262 unset($langObj);
263 }
264
265 self::refreshPlugins($refreshed);
266 }
267
268
273 public static function refreshPlugins(?array $a_lang_keys = null): void
274 {
275 global $DIC;
276
277 $component_repository = $DIC["component.repository"];
278 foreach ($component_repository->getPlugins() as $plugin) {
279 if (!$plugin->isActive()) {
280 continue;
281 }
283 $handler->updateLanguages($a_lang_keys);
284 }
285 }
286
287
292 public static function _deleteLangData(string $a_lang_key, bool $a_keep_local_change = false): void
293 {
294 global $DIC;
295 $ilDB = $DIC->database();
296
297 if (!$a_keep_local_change) {
298 $ilDB->manipulate("DELETE FROM lng_data WHERE lang_key = " .
299 $ilDB->quote($a_lang_key, "text"));
300 } else {
301 $ilDB->manipulate("DELETE FROM lng_data WHERE lang_key = " .
302 $ilDB->quote($a_lang_key, "text") .
303 " AND local_change IS NULL");
304 }
305 }
306
311 public function flush(string $a_mode = "all"): void
312 {
313 global $DIC;
314 $ilDB = $DIC->database();
315
316 self::_deleteLangData($this->key, ($a_mode === "keep_local"));
317
318 if ($a_mode === "all") {
319 $ilDB->manipulate("DELETE FROM lng_modules WHERE lang_key = " .
320 $ilDB->quote($this->key, "text"));
321 }
322 }
323
324
331 public function getLocalChanges(string $a_min_date = "", string $a_max_date = ""): array
332 {
333 global $DIC;
334 $ilDB = $DIC->database();
335
336 if ($a_min_date === "") {
337 $a_min_date = "1980-01-01 00:00:00";
338 }
339 if ($a_max_date === "") {
340 $a_max_date = "2200-01-01 00:00:00";
341 }
342
343 $q = sprintf(
344 "SELECT * FROM lng_data WHERE lang_key = %s " .
345 "AND local_change >= %s AND local_change <= %s",
346 $ilDB->quote($this->key, "text"),
347 $ilDB->quote($a_min_date, "timestamp"),
348 $ilDB->quote($a_max_date, "timestamp")
349 );
350 $result = $ilDB->query($q);
351
352 $changes = array();
353 while ($row = $result->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
354 $changes[$row["module"]][$row["identifier"]] = $row["value"];
355 }
356 return $changes;
357 }
358
359
365 public static function _getLastLocalChange(string $a_key): string
366 {
367 global $DIC;
368 $ilDB = $DIC->database();
369
370 $q = sprintf(
371 "SELECT MAX(local_change) last_change FROM lng_data " .
372 "WHERE lang_key = %s AND local_change IS NOT NULL",
373 $ilDB->quote($a_key, "text")
374 );
375 $result = $ilDB->query($q);
376
377 if ($row = $result->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
378 return (string) $row["last_change"];
379 } else {
380 return "";
381 }
382 }
383
384
391 public static function _getLocalChangesByModule(string $a_key, string $a_module): array
392 {
393 global $DIC;
394 $ilDB = $DIC->database();
395
396 $changes = array();
397 $result = $ilDB->queryF(
398 "SELECT * FROM lng_data WHERE lang_key = %s AND module = %s AND local_change IS NOT NULL",
399 array("text", "text"),
400 array($a_key, $a_module)
401 );
402
403 while ($row = $ilDB->fetchAssoc($result)) {
404 $changes[$row["identifier"]] = $row["value"];
405 }
406 return $changes;
407 }
408
409
415 public function insert(string $scope = ""): void
416 {
417 global $DIC;
418 $ilDB = $DIC->database();
419 $scopeExtension = "";
420 if (!empty($scope)) {
421 if ($scope === "global") {
422 $scope = "";
423 } else {
424 $scopeExtension = "." . $scope;
425 }
426 }
427
429 if ($scope === "local") {
431 }
432
433 $lang_file = $path . "/ilias_" . $this->key . ".lang" . $scopeExtension;
434
435 if (is_file($lang_file)) {
436 // remove header first
437 if ($content = self::cut_header(file($lang_file))) {
438 $local_changes = null;
439 if (empty($scope)) {
440 // get all local changes for a global file
441 $local_changes = $this->getLocalChanges();
442 } elseif ($scope === "local") {
443 // get the modification date of the local file
444 // get the newer local changes for a local file
445 $min_date = gmdate("Y-m-d H:i:s", filemtime($lang_file));
446 $local_changes = $this->getLocalChanges($min_date);
447 }
448 $dbAccess = new ilObjLanguageDBAccess($ilDB, $this->key, $content, $local_changes, $scope);
449 $lang_array = $dbAccess->insertLangEntries($lang_file);
450 $dbAccess->replaceLangModules($lang_array);
451 }
452 }
453 }
454
458 final public static function replaceLangModule(string $a_key, string $a_module, array $a_array): void
459 {
460 global $DIC;
461 $ilDB = $DIC->database();
462
463 // avoid flushing the whole cache (see mantis #28818)
464 ilCachedLanguage::getInstance($a_key)->deleteInCache();
465
466 $ilDB->manipulate(sprintf(
467 "DELETE FROM lng_modules WHERE lang_key = %s AND module = %s",
468 $ilDB->quote($a_key, "text"),
469 $ilDB->quote($a_module, "text")
470 ));
471
472 /*$ilDB->manipulate(sprintf("INSERT INTO lng_modules (lang_key, module, lang_array) VALUES ".
473 "(%s,%s,%s)", $ilDB->quote($a_key, "text"),
474 $ilDB->quote($a_module, "text"),
475 $ilDB->quote(serialize($a_array), "clob")));*/
476 $ilDB->insert("lng_modules", array(
477 "lang_key" => array("text", $a_key),
478 "module" => array("text", $a_module),
479 "lang_array" => array("clob", serialize($a_array))
480 ));
481
482 // check if the module is correctly saved
483 // see mantis #20046 and #19140
484 $result = $ilDB->queryF(
485 "SELECT lang_array FROM lng_modules WHERE lang_key = %s AND module = %s",
486 array("text","text"),
487 array($a_key, $a_module)
488 );
489 $row = $ilDB->fetchAssoc($result);
490
491 $unserialied = unserialize($row["lang_array"], ["allowed_classes" => false]);
492 if (!is_array($unserialied)) {
493 $DIC->ui()->mainTemplate()->setOnScreenMessage(
494 'failure',
495 "Data for module '" . $a_module . "' of language '" . $a_key . "' is not correctly saved. " .
496 "Please check the collation of your database tables lng_data and lng_modules. It must be utf8_unicode_ci.",
497 true
498 );
499 $DIC->ctrl()->redirectByClass(ilobjlanguagefoldergui::class, 'view');
500 }
501 }
502
506 final public static function replaceLangEntry(
507 string $a_module,
508 string $a_identifier,
509 string $a_lang_key,
510 string $a_value,
511 ?string $a_local_change = null,
512 ?string $a_remarks = null
513 ): bool {
514 global $DIC;
515 $ilDB = $DIC->database();
516
517 // avoid a cache flush here (see mantis #28818)
518 // ilGlobalCache::flushAll();
519
520 if (is_string($a_remarks) && $a_remarks !== '') {
521 $a_remarks = substr($a_remarks, 0, 250);
522 }
523
524 if ($a_remarks === '') {
525 $a_remarks = null;
526 }
527
528 if ($a_value === "") {
529 $a_value = null;
530 } else {
531 $a_value = substr($a_value, 0, 4000);
532 }
533
534 $ilDB->replace(
535 "lng_data",
536 array(
537 "module" => array("text",$a_module),
538 "identifier" => array("text",$a_identifier),
539 "lang_key" => array("text",$a_lang_key)
540 ),
541 array(
542 "value" => array("text",$a_value),
543 "local_change" => array("timestamp",$a_local_change),
544 "remarks" => array("text", $a_remarks)
545 )
546 );
547 return true;
548 }
549
553 final public static function updateLangEntry(
554 string $a_module,
555 string $a_identifier,
556 string $a_lang_key,
557 string $a_value,
558 ?string $a_local_change = null,
559 ?string $a_remarks = null
560 ): void {
561 global $DIC;
562 $ilDB = $DIC->database();
563
564 if (is_string($a_remarks) && $a_remarks !== '') {
565 $a_remarks = substr($a_remarks, 0, 250);
566 }
567
568 if ($a_remarks === '') {
569 $a_remarks = null;
570 }
571
572 if ($a_value === "") {
573 $a_value = null;
574 } else {
575 $a_value = substr($a_value, 0, 4000);
576 }
577
578 $ilDB->manipulate(sprintf(
579 "UPDATE lng_data " .
580 "SET value = %s, local_change = %s, remarks = %s " .
581 "WHERE module = %s AND identifier = %s AND lang_key = %s ",
582 $ilDB->quote($a_value, "text"),
583 $ilDB->quote($a_local_change, "timestamp"),
584 $ilDB->quote($a_remarks, "text"),
585 $ilDB->quote($a_module, "text"),
586 $ilDB->quote($a_identifier, "text"),
587 $ilDB->quote($a_lang_key, "text")
588 ));
589 }
590
591
595 final public static function deleteLangEntry(string $a_module, string $a_identifier, string $a_lang_key): bool
596 {
597 global $DIC;
598 $ilDB = $DIC->database();
599
600 $ilDB->manipulate(sprintf(
601 "DELETE FROM lng_data " .
602 "WHERE module = %s AND identifier = %s AND lang_key = %s ",
603 $ilDB->quote($a_module, "text"),
604 $ilDB->quote($a_identifier, "text"),
605 $ilDB->quote($a_lang_key, "text")
606 ));
607
608 return true;
609 }
610
611
618 public function resetUserLanguage(string $lang_key): void
619 {
620 global $DIC;
621 $ilDB = $DIC->database();
622
623 $query = "UPDATE usr_pref SET " .
624 "value = " . $ilDB->quote($this->lang_default, "text") . " " .
625 "WHERE keyword = " . $ilDB->quote('language', "text") . " " .
626 "AND value = " . $ilDB->quote($lang_key, "text");
627 $ilDB->manipulate($query);
628 }
629
639 public static function cut_header(array $content)
640 {
641 foreach ($content as $key => $val) {
642 if (trim($val) === "<!-- language file start -->") {
643 return array_slice($content, $key + 1);
644 }
645 }
646
647 return false;
648 }
649
656 public function optimizeData(): bool
657 {
658 // Mantis #22313: removed table optimization
659 return true;
660 }
661
671 public function check(string $scope = ""): bool
672 {
673 global $DIC;
674 $scopeExtension = "";
675 if (!empty($scope)) {
676 if ($scope === "global") {
677 $scope = "";
678 } else {
679 $scopeExtension = "." . $scope;
680 }
681 }
682
683 $path = $this->lang_path;
684 if ($scope === "local") {
685 $path = $this->cust_lang_path;
686 }
687
688 $tmpPath = getcwd();
689
690 // dir check
691 if (!is_dir($path)) {
692 $DIC->ui()->mainTemplate()->setOnScreenMessage(
693 'failure',
694 "Directory not found: " . $path,
695 true
696 );
697 $DIC->ctrl()->redirectByClass(ilobjlanguagefoldergui::class, 'view');
698 }
699
700 chdir($path);
701
702 // compute lang-file name format
703 $lang_file = "ilias_" . $this->key . ".lang" . $scopeExtension;
704
705 // file check
706 if (!is_file($lang_file)) {
707 $DIC->ui()->mainTemplate()->setOnScreenMessage(
708 'failure',
709 "File not found: " . $lang_file,
710 true
711 );
712 $DIC->ctrl()->redirectByClass(ilobjlanguagefoldergui::class, 'view');
713 }
714
715 // header check
716 $content = self::cut_header(file($lang_file));
717 if ($content === false) {
718 $DIC->ui()->mainTemplate()->setOnScreenMessage(
719 'failure',
720 "Wrong Header in " . $lang_file,
721 true
722 );
723 $DIC->ctrl()->redirectByClass(ilobjlanguagefoldergui::class, 'view');
724 }
725
726 // check (counting) elements of each lang-entry
727 $line = 0;
728 $n = 0;
729 foreach ($content as $key => $val) {
730 $separated = explode($this->separator, trim($val));
731 $num = count($separated);
732 ++$n;
733 if ($num !== 3) {
734 $line = $n + 36;
735 $DIC->ui()->mainTemplate()->setOnScreenMessage(
736 'failure',
737 "Wrong parameter count in " . $lang_file . " in line $line (Value: $val)! Please check your language file!",
738 true
739 );
740 $DIC->ctrl()->redirectByClass(ilobjlanguagefoldergui::class, 'view');
741 }
742 if (!ilStr::isUtf8($separated[2])) {
743 $DIC->ui()->mainTemplate()->setOnScreenMessage(
744 'failure',
745 "Non UTF8 character found in " . $lang_file . " in line $line (Value: $val)! Please check your language file!",
746 true
747 );
748 $DIC->ctrl()->redirectByClass(ilobjlanguagefoldergui::class, 'view');
749 }
750 }
751
752 chdir($tmpPath);
753
754 // no error occured
755 return true;
756 }
757
761 public static function countUsers(string $a_lang): int
762 {
763 global $DIC;
764 $ilDB = $DIC->database();
765 $lng = $DIC->language();
766
767 $set = $ilDB->query("SELECT COUNT(*) cnt FROM usr_data ud JOIN usr_pref up" .
768 " ON ud.usr_id = up.usr_id " .
769 " WHERE up.value = " . $ilDB->quote($a_lang, "text") .
770 " AND up.keyword = " . $ilDB->quote("language", "text"));
771 $rec = $ilDB->fetchAssoc($set);
772
773 // add users with no usr_pref set to default language
774 if ($a_lang == $lng->lang_default) {
775 $set2 = $ilDB->query("SELECT COUNT(*) cnt FROM usr_data ud LEFT JOIN usr_pref up" .
776 " ON (ud.usr_id = up.usr_id AND up.keyword = " . $ilDB->quote("language", "text") . ")" .
777 " WHERE up.value IS NULL ");
778 $rec2 = $ilDB->fetchAssoc($set2);
779 }
780
781 return (int) $rec["cnt"] + (int) ($rec2["cnt"] ?? 0);
782 }
783} // END class.LanguageObject
Class ilObjLanguage.
flush(string $a_mode="all")
remove language data from database $a_mode "all" or "keep_local"
static _deleteLangData(string $a_lang_key, bool $a_keep_local_change=false)
Delete languge data $a_lang_key lang key.
static deleteLangEntry(string $a_module, string $a_identifier, string $a_lang_key)
Delete lang entry.
resetUserLanguage(string $lang_key)
search ILIAS for users which have selected '$lang_key' as their prefered language and reset them to d...
static updateLangEntry(string $a_module, string $a_identifier, string $a_lang_key, string $a_value, ?string $a_local_change=null, ?string $a_remarks=null)
Replace lang entry.
string $separator
separator of module, comment separator, identifier & values in language files
static _getLastLocalChange(string $a_key)
get the date of the last local change $a_key language key Return change_date "yyyy-mm-dd hh:mm:ss"
static refreshAll()
Refresh all installed languages.
isInstalled()
Check language object status, and return true if language is installed.
uninstall()
uninstall current language
getKey()
get language key
getStatus()
get language status
isLocal()
Check language object status, and return true if a local language file is installed.
static getInstalledLanguages()
Get the language objects of the installed languages.
install(string $scope="")
install current language
check(string $scope="")
Validate the logical structure of a lang file.
isSystemLanguage()
check if language is system language
isUserLanguage()
check if language is system language
static refreshPlugins(?array $a_lang_keys=null)
Refresh languages of activated plugins $a_lang_keys keys of languages to be refreshed (not yet suppor...
static replaceLangEntry(string $a_module, string $a_identifier, string $a_lang_key, string $a_value, ?string $a_local_change=null, ?string $a_remarks=null)
Replace lang entry.
static replaceLangModule(string $a_key, string $a_module, array $a_array)
Replace language module array.
insert(string $scope="")
insert language data from file into database
static _getLocalChangesByModule(string $a_key, string $a_module)
Get the local changes of a language module $a_key Language key $a_module Module key Return array iden...
refresh()
refresh current language
static cut_header(array $content)
remove lang-file haeder information from '$content' This function seeks for a special keyword where t...
static countUsers(string $a_lang)
Count number of users that use a language.
optimizeData()
optimizes the db-table langdata
__construct(int $a_id=0, bool $a_call_by_reference=false)
Constructor.
getLocalChanges(string $a_min_date="", string $a_max_date="")
get locally changed language entries $a_min_date minimum change date "yyyy-mm-dd hh:mm:ss" $a_max_dat...
Class ilObject Basic functions for all objects.
string $desc
string $title
ilLanguage $lng
setTitle(string $title)
static _getObjectsByType(string $obj_type="", ?int $owner=null)
setDescription(string $description)
static isUtf8(string $a_str)
Check whether string is utf-8.
$scope
Definition: ltiregstart.php:51
$path
Definition: ltiservices.php:30
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$handler
Definition: oai.php:29
global $lng
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:23
$lang
Definition: xapiexit.php:25