ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilObjLanguage.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
29 class ilObjLanguage extends ilObject
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 (strpos($this->getStatus(), "installed") === 0) {
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 ((strpos($this->status, "installed") === 0) && ($this->key != $this->lang_default) && ($this->key != $this->lang_user)) {
212  $this->flush('all');
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  }
282  $handler = new ilPluginLanguage($plugin);
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 = date("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)) {
494  $ilErr = $DIC["ilErr"];
495  $ilErr->raiseError(
496  "Data for module '" . $a_module . "' of language '" . $a_key . "' is not correctly saved. " .
497  "Please check the collation of your database tables lng_data and lng_modules. It must be utf8_unicode_ci.",
498  $ilErr->MESSAGE
499  );
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  include_once "./Services/Utilities/classes/class.ilStr.php";
674  $scopeExtension = "";
675  if (!empty($scope)) {
676  if ($scope === "global") {
677  $scope = "";
678  } else {
679  $scopeExtension = "." . $scope;
680  }
681  }
682 
684  if ($scope === "local") {
686  }
687 
688  $tmpPath = getcwd();
689 
690  // dir check
691  if (!is_dir($path)) {
692  $this->ilias->raiseError("Directory not found: " . $path, $this->ilias->error_obj->MESSAGE);
693  }
694 
695  chdir($path);
696 
697  // compute lang-file name format
698  $lang_file = "ilias_" . $this->key . ".lang" . $scopeExtension;
699 
700  // file check
701  if (!is_file($lang_file)) {
702  $this->ilias->raiseError("File not found: " . $lang_file, $this->ilias->error_obj->MESSAGE);
703  }
704 
705  // header check
706  $content = self::cut_header(file($lang_file));
707  if ($content === false) {
708  $this->ilias->raiseError("Wrong Header in " . $lang_file, $this->ilias->error_obj->MESSAGE);
709  }
710 
711  // check (counting) elements of each lang-entry
712  $line = 0;
713  $n = 0;
714  foreach ($content as $key => $val) {
715  $separated = explode($this->separator, trim($val));
716  $num = count($separated);
717  ++$n;
718  if ($num !== 3) {
719  $line = $n + 36;
720  $this->ilias->raiseError("Wrong parameter count in " . $lang_file . " in line $line (Value: $val)! Please check your language file!", $this->ilias->error_obj->MESSAGE);
721  }
722  if (!ilStr::isUtf8($separated[2])) {
723  $this->ilias->raiseError("Non UTF8 character found in " . $lang_file . " in line $line (Value: $val)! Please check your language file!", $this->ilias->error_obj->MESSAGE);
724  }
725  }
726 
727  chdir($tmpPath);
728 
729  // no error occured
730  return true;
731  }
732 
736  public static function countUsers(string $a_lang): int
737  {
738  global $DIC;
739  $ilDB = $DIC->database();
740  $lng = $DIC->language();
741 
742  $set = $ilDB->query("SELECT COUNT(*) cnt FROM usr_data ud JOIN usr_pref up" .
743  " ON ud.usr_id = up.usr_id " .
744  " WHERE up.value = " . $ilDB->quote($a_lang, "text") .
745  " AND up.keyword = " . $ilDB->quote("language", "text"));
746  $rec = $ilDB->fetchAssoc($set);
747 
748  // add users with no usr_pref set to default language
749  if ($a_lang == $lng->lang_default) {
750  $set2 = $ilDB->query("SELECT COUNT(*) cnt FROM usr_data ud LEFT JOIN usr_pref up" .
751  " ON (ud.usr_id = up.usr_id AND up.keyword = " . $ilDB->quote("language", "text") . ")" .
752  " WHERE up.value IS NULL ");
753  $rec2 = $ilDB->fetchAssoc($set2);
754  }
755 
756  return (int) $rec["cnt"] + (int) ($rec2["cnt"] ?? 0);
757  }
758 } // END class.LanguageObject
insert(string $scope="")
insert language data from file into database
optimizeData()
optimizes the db-table langdata
string $title
static _deleteLangData(string $a_lang_key, bool $a_keep_local_change=false)
Delete languge data $a_lang_key lang key.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
isUserLanguage()
check if language is system language
$scope
Definition: ltiregstart.php:53
static refreshPlugins(array $a_lang_keys=null)
Refresh languages of activated plugins $a_lang_keys keys of languages to be refreshed (not yet suppor...
string $separator
separator of module, comment separator, identifier & values in language files
string $desc
Class ilObjLanguage.
static getInstalledLanguages()
Get the language objects of the installed languages.
static countUsers(string $a_lang)
Count number of users that use a language.
setTitle(string $title)
isInstalled()
Check language object status, and return true if language is installed.
static _getObjectsByType(string $obj_type="", int $owner=null)
$ilErr
Definition: raiseError.php:17
$path
Definition: ltiservices.php:32
global $DIC
Definition: feed.php:28
setDescription(string $desc)
getStatus()
get language status
uninstall()
uninstall current language
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.
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...
ilLanguage $lng
isSystemLanguage()
check if language is system language
resetUserLanguage(string $lang_key)
search ILIAS for users which have selected &#39;$lang_key&#39; as their prefered language and reset them to d...
install(string $scope="")
install current language
static isUtf8(string $a_str)
Check whether string is utf-8.
refresh()
refresh current language
header include for all ilias files.
$query
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.
$lang
Definition: xapiexit.php:26
check(string $scope="")
Validate the logical structure of a lang file.
__construct(Container $dic, ilPlugin $plugin)
flush(string $a_mode="all")
remove language data from database $a_mode "all" or "keep_local"
static refreshAll()
Refresh all installed languages.
isLocal()
Check language object status, and return true if a local language file is installed.
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...
static cut_header(array $content)
remove lang-file haeder information from &#39;$content&#39; This function seeks for a special keyword where t...
__construct(int $a_id=0, bool $a_call_by_reference=false)
Constructor.
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 deleteLangEntry(string $a_module, string $a_identifier, string $a_lang_key)
Delete lang entry.
getKey()
get language key