ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilSetupLanguage.php
Go to the documentation of this file.
1 <?php
2 
20 declare(strict_types=1);
21 
44 {
45  public array $text;
46  public string $lang_default = "en";
47  public string $lang_path;
48  public string $lang_key;
49  public string $separator = "#:#";
50  public string $comment_separator = "###";
51  protected ilDBInterface $db;
52 
53  public function __construct(string $a_lang_key)
54  {
55  $this->lang_key = $a_lang_key ?: $this->lang_default;
56  $il_absolute_path = realpath(__DIR__ . "/../../../../../");
57  $this->lang_path = $il_absolute_path . "/lang";
58  $this->cust_lang_path = $il_absolute_path . "/lang/customizing";
59  }
60 
68  public function txt(string $a_topic, string $a_default_lang_fallback_mod = ''): string
69  {
70  global $log;
71 
72  if (empty($a_topic)) {
73  return "";
74  }
75 
76  $translation = $this->text[$a_topic] ?? '';
77 
78  //get position of the comment_separator
79  $pos = strpos($translation, $this->comment_separator);
80 
81  if ($pos !== false) {
82  // remove comment
83  $translation = substr($translation, 0, $pos);
84  }
85 
86  if ($translation === "") {
87  $log->writeLanguageLog($a_topic, $this->lang_key);
88  return "-" . $a_topic . "-";
89  }
90 
91  return $translation;
92  }
93 
101  public function installLanguages(array $a_lang_keys, array $a_local_keys)
102  {
103  global $ilDB;
104 
105  if (empty($a_lang_keys)) {
106  $a_lang_keys = array();
107  }
108 
109  if (empty($a_local_keys)) {
110  $a_local_keys = array();
111  }
112 
113  $err_lang = array();
114 
115  $db_langs = $this->getAvailableLanguages();
116 
117  foreach ($a_lang_keys as $lang_key) {
118  if ($this->checkLanguage($lang_key)) {
119  $this->flushLanguage($lang_key, "keep_local");
120  $this->insertLanguage($lang_key);
121 
122  if (in_array($lang_key, $a_local_keys, true) && is_dir($this->cust_lang_path)) {
123  if ($this->checkLanguage($lang_key, "local")) {
124  $this->insertLanguage($lang_key, "local");
125  } else {
126  $err_lang[] = $lang_key;
127  }
128  }
129 
130  // register language first time install
131  if (!array_key_exists($lang_key, $db_langs)) {
132  if (in_array($lang_key, $a_local_keys, true)) {
133  $itype = "installed_local";
134  } else {
135  $itype = "installed";
136  }
137  $lid = $ilDB->nextId("object_data");
138  $query = "INSERT INTO object_data " .
139  "(obj_id,type,title,description,owner,create_date,last_update) " .
140  "VALUES " .
141  "(" .
142  $ilDB->quote($lid, "integer") . "," .
143  $ilDB->quote("lng", "text") . "," .
144  $ilDB->quote($lang_key, "text") . "," .
145  $ilDB->quote($itype, "text") . "," .
146  $ilDB->quote("-1", "integer") . "," .
147  $ilDB->now() . "," .
148  $ilDB->now() .
149  ")";
150  $ilDB->manipulate($query);
151  }
152  } else {
153  $err_lang[] = $lang_key;
154  }
155  }
156 
157  foreach ($db_langs as $key => $val) {
158  if (!in_array($key, $err_lang, true)) {
159  if (in_array($key, $a_lang_keys, true)) {
160  if (in_array($key, $a_local_keys, true)) {
161  $ld = "installed_local";
162  } else {
163  $ld = "installed";
164  }
165  $query = "UPDATE object_data SET " .
166  "description = " . $ilDB->quote($ld, "text") . ", " .
167  "last_update = " . $ilDB->quote(gmdate("Y-m-d H:i:s"), "timestamp") . " " .
168  "WHERE obj_id = " . $ilDB->quote($val["obj_id"], "integer") . " " .
169  "AND type = " . $ilDB->quote("lng", "text");
170  $ilDB->manipulate($query);
171  } else {
172  $this->flushLanguage($key, "all");
173 
174  if (strpos($val["status"], "installed") === 0) {
175  $query = "UPDATE object_data SET " .
176  "description = " . $ilDB->quote("not_installed", "text") . ", " .
177  "last_update = " . $ilDB->quote(gmdate("Y-m-d H:i:s"), "timestamp") . " " .
178  "WHERE obj_id = " . $ilDB->quote($val["obj_id"], "integer") . " " .
179  "AND type = " . $ilDB->quote("lng", "text");
180  $ilDB->manipulate($query);
181  }
182  }
183  }
184  }
185 
186  return ($err_lang) ?: true;
187  }
188 
192  public function getInstalledLanguages(): array
193  {
194  global $ilDB;
195 
196  $arr = [];
197  if ($ilDB instanceof ilDBInterface) {
198  $query = "SELECT * FROM object_data " .
199  "WHERE type = " . $ilDB->quote("lng", "text") . " " .
200  "AND " . $ilDB->like("description", "text", "installed%");
201  $r = $ilDB->query($query);
202 
203  while ($row = $ilDB->fetchObject($r)) {
204  $arr[] = $row->title;
205  }
206  }
207  return $arr;
208  }
209 
213  public function getInstalledLocalLanguages(): array
214  {
215  global $ilDB;
216 
217  $arr = [];
218  if ($ilDB instanceof ilDBInterface) {
219  $query = "SELECT * FROM object_data " .
220  "WHERE type = " . $ilDB->quote("lng", "text") . " " .
221  "AND description = " . $ilDB->quote("installed_local", "text");
222  $r = $ilDB->query($query);
223 
224  while ($row = $ilDB->fetchObject($r)) {
225  $arr[] = $row->title;
226  }
227  }
228  return $arr;
229  }
230 
234  protected function getAvailableLanguages(): array
235  {
236  global $ilDB;
237 
238  $arr = array();
239 
240  $query = "SELECT * FROM object_data " .
241  "WHERE type = " . $ilDB->quote("lng", "text");
242  $r = $ilDB->query($query);
243 
244  while ($row = $ilDB->fetchObject($r)) {
245  $arr[$row->title]["obj_id"] = $row->obj_id;
246  $arr[$row->title]["status"] = $row->description;
247  }
248 
249  return $arr;
250  }
251 
263  protected function checkLanguage(string $a_lang_key, string $scope = ""): bool
264  {
265  $scopeExtension = "";
266  if (!empty($scope)) {
267  if ($scope === "global") {
268  $scope = "";
269  } else {
270  $scopeExtension = "." . $scope;
271  }
272  }
273 
275  if ($scope === "local") {
277  }
278 
279  $tmpPath = getcwd();
280  chdir($path);
281 
282  // compute lang-file name format
283  $lang_file = "ilias_" . $a_lang_key . ".lang" . $scopeExtension;
284 
285  // file check
286  if (!is_file($lang_file)) {
287  chdir($tmpPath);
288  return false;
289  }
290 
291  // header check
292  if (!$content = $this->cut_header(file($lang_file))) {
293  chdir($tmpPath);
294  return false;
295  }
296 
297  // check (counting) elements of each lang-entry
298  foreach ($content as $key => $val) {
299  $separated = explode($this->separator, trim($val));
300  $num = count($separated);
301 
302  if ($num !== 3) {
303  chdir($tmpPath);
304  return false;
305  }
306  }
307 
308  chdir($tmpPath);
309 
310  // no error occured
311  return true;
312  }
313 
324  protected function cut_header(array $content)
325  {
326  foreach ($content as $key => $val) {
327  if (trim($val) === "<!-- language file start -->") {
328  return array_slice($content, $key + 1);
329  }
330  }
331  return false;
332  }
333 
339  protected function flushLanguage(string $a_lang_key, string $a_mode = "all"): void
340  {
341  global $ilDB;
342 
343  self::_deleteLangData($a_lang_key, ($a_mode === "keep_local"));
344 
345  if ($a_mode === "all") {
346  $ilDB->manipulate("DELETE FROM lng_modules WHERE lang_key = " .
347  $ilDB->quote($a_lang_key, "text"));
348  }
349  }
350 
356  public static function _deleteLangData(string $a_lang_key, bool $a_keep_local_change): void
357  {
358  global $ilDB;
359 
360  if (!$a_keep_local_change) {
361  $ilDB->manipulate("DELETE FROM lng_data WHERE lang_key = " .
362  $ilDB->quote($a_lang_key, "text"));
363  } else {
364  $ilDB->manipulate("DELETE FROM lng_data WHERE lang_key = " .
365  $ilDB->quote($a_lang_key, "text") .
366  " AND local_change IS NULL");
367  }
368  }
369 
377  public function getLocalChanges(string $a_lang_key, string $a_min_date = "", string $a_max_date = ""): array
378  {
379  global $ilDB;
380 
381  if ($a_min_date === "") {
382  $a_min_date = "1980-01-01 00:00:00";
383  }
384  if ($a_max_date === "") {
385  $a_max_date = "2200-01-01 00:00:00";
386  }
387 
388  $q = sprintf(
389  "SELECT * FROM lng_data WHERE lang_key = %s " .
390  "AND local_change >= %s AND local_change <= %s",
391  $ilDB->quote($a_lang_key, "text"),
392  $ilDB->quote($a_min_date, "timestamp"),
393  $ilDB->quote($a_max_date, "timestamp")
394  );
395  $result = $ilDB->query($q);
396 
397  $changes = array();
398  while ($row = $result->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
399  $changes[$row["module"]][$row["identifier"]] = $row["value"];
400  }
401  return $changes;
402  }
403 
404 
405  //TODO: remove redundant checks here!
412  protected function insertLanguage(string $lang_key, string $scope = ""): void
413  {
414  global $ilDB;
415 
416  $lang_array = array();
417 
418  $scopeExtension = "";
419  if (!empty($scope)) {
420  if ($scope === "global") {
421  $scope = "";
422  } else {
423  $scopeExtension = "." . $scope;
424  }
425  }
426 
428  if ($scope === "local") {
430  }
431 
432  $tmpPath = getcwd();
433  chdir($path);
434 
435  $lang_file = "ilias_" . $lang_key . ".lang" . $scopeExtension;
436  $change_date = null;
437 
438  if (is_file($lang_file)) {
439  // initialize the array for updating lng_modules below
440  $lang_array = [];
441  $lang_array["common"] = [];
442 
443  // remove header first
444  if ($content = $this->cut_header(file($lang_file))) {
445  // get the local changes from the database
446  if (empty($scope)) {
447  $local_changes = $this->getLocalChanges($lang_key);
448  } elseif ($scope === "local") {
449  // set the change date to import time for a local file
450  // get the modification date of the local file
451  // get the newer local changes for a local file
452  $change_date = gmdate("Y-m-d H:i:s", time());
453  $min_date = gmdate("Y-m-d H:i:s", filemtime($lang_file));
454  $local_changes = $this->getLocalChanges($lang_key, $min_date);
455  }
456 
457  $query_check = false;
458  $query = "INSERT INTO lng_data (module,identifier,lang_key,value,local_change,remarks) VALUES ";
459  foreach ($content as $key => $val) {
460  // split the line of the language file
461  // [0]: module
462  // [1]: identifier
463  // [2]: value
464  // [3]: comment (optional)
465  $separated = explode($this->separator, trim($val));
466 
467  //get position of the comment_separator
468  $pos = strpos($separated[2], $this->comment_separator);
469 
470  if ($pos !== false) {
471  //cut comment of
472  $separated[2] = substr($separated[2], 0, $pos);
473  }
474 
475  // check if the value has a local change
476  if (isset($local_changes[$separated[0]])) {
477  $local_value = $local_changes[$separated[0]][$separated[1]] ?? "";
478  } else {
479  $local_value = "";
480  }
481 
482  if (empty($scope)) {
483  if ($local_value !== "" && $local_value !== $separated[2]) {
484  // keep the locally changed value
485  $lang_array[$separated[0]][$separated[1]] = $local_value;
486  continue;
487  }
488  } elseif ($scope === "local") {
489  if ($local_value !== "") {
490  // keep a locally changed value that is newer than the local file
491  $lang_array[$separated[0]][$separated[1]] = $local_value;
492  continue;
493  }
494  }
495 
496  $query .= sprintf(
497  "(%s,%s,%s,%s,%s,%s),",
498  $ilDB->quote($separated[0], "text"),
499  $ilDB->quote($separated[1], "text"),
500  $ilDB->quote($lang_key, "text"),
501  $ilDB->quote($separated[2], "text"),
502  $ilDB->quote($change_date, "timestamp"),
503  $ilDB->quote($separated[3] ?? null, "text")
504  );
505  $query_check = true;
506  $lang_array[$separated[0]][$separated[1]] = $separated[2];
507  }
508  $query = rtrim($query, ",") . " ON DUPLICATE KEY UPDATE value=VALUES(value),remarks=VALUES(remarks);";
509  if ($query_check) {
510  $ilDB->manipulate($query);
511  }
512  }
513 
514  $query = "INSERT INTO lng_modules (module, lang_key, lang_array) VALUES ";
515  $modules_to_delete = [];
516  foreach ($lang_array as $module => $lang_arr) {
517  if ($scope === "local") {
518  $q = "SELECT * FROM lng_modules WHERE " .
519  " lang_key = " . $ilDB->quote($lang_key, "text") .
520  " AND module = " . $ilDB->quote($module, "text");
521  $set = $ilDB->query($q);
522  $row = $ilDB->fetchAssoc($set);
523  $arr2 = unserialize($row["lang_array"], ["allowed_classes" => false]);
524  if (is_array($arr2)) {
525  $lang_arr = array_merge($arr2, $lang_arr);
526  }
527  }
528  $query .= sprintf(
529  "(%s,%s,%s),",
530  $ilDB->quote($module, "text"),
531  $ilDB->quote($lang_key, "text"),
532  $ilDB->quote(serialize($lang_arr), "clob"),
533  );
534  $modules_to_delete[] = $module;
535  }
536 
537  $inModulesToDelete = $ilDB->in('module', $modules_to_delete, false, 'text');
538  $ilDB->manipulate(sprintf(
539  "DELETE FROM lng_modules WHERE lang_key = %s AND $inModulesToDelete",
540  $ilDB->quote($lang_key, "text")
541  ));
542 
543  $query = rtrim($query, ",") . ";";
544  $ilDB->manipulate($query);
545  }
546 
547  chdir($tmpPath);
548  }
549 
554  public function getLocalLanguages(): array
555  {
556  $local_langs = array();
557  if (is_dir($this->cust_lang_path)) {
558  $d = dir($this->cust_lang_path);
559  $tmpPath = getcwd();
560  chdir($this->cust_lang_path);
561 
562  // get available .lang.local files
563  while ($entry = $d->read()) {
564  if (is_file($entry) && (preg_match("~(^ilias_.{2}\.lang.local$)~", $entry))) {
565  $lang_key = substr($entry, 6, 2);
566  $local_langs[] = $lang_key;
567  }
568  }
569 
570  chdir($tmpPath);
571  }
572 
573  return $local_langs;
574  }
575 
579  public function getInstallableLanguages(): array
580  {
581  $d = dir($this->lang_path);
582  $tmpPath = getcwd();
583  chdir($this->lang_path);
584 
585  $installableLanguages = [];
586  // get available lang-files
587  while ($entry = $d->read()) {
588  if (is_file($entry) && (preg_match("~(^ilias_.{2}\.lang$)~", $entry))) {
589  $lang_key = substr($entry, 6, 2);
590  $installableLanguages[] = $lang_key;
591  }
592  }
593 
594  chdir($tmpPath);
595 
596  return $installableLanguages;
597  }
598 
604  public function setDbHandler(ilDBInterface $a_db_handler): bool
605  {
606  $this->db = &$a_db_handler;
607  return true;
608  }
609 
610  public function loadLanguageModule(string $a_module): void
611  {
612  }
613 }
$scope
Definition: ltiregstart.php:51
getInstallableLanguages()
Return installable languages.
flushLanguage(string $a_lang_key, string $a_mode="all")
remove language data from database $a_lang_key language key $a_mode "all" or "keep_local" ...
getLocalChanges(string $a_lang_key, string $a_min_date="", string $a_max_date="")
get locally changed language entries $a_lang_key language key $a_min_date minimum change date "yyyy-m...
__construct(string $a_lang_key)
txt(string $a_topic, string $a_default_lang_fallback_mod='')
gets the text for a given topic
language handling for setup
$path
Definition: ltiservices.php:30
cut_header(array $content)
Remove *.lang header information from &#39;$content&#39;.
checkLanguage(string $a_lang_key, string $scope="")
validate the logical structure of a lang-file
installLanguages(array $a_lang_keys, array $a_local_keys)
install languages
getInstalledLanguages()
get already installed languages (in db)
getInstalledLocalLanguages()
get already installed local languages (in db)
loadLanguageModule(string $a_module)
$q
Definition: shib_logout.php:18
getAvailableLanguages()
get already registered languages (in db)
getLocalLanguages()
Searches for the existence of *.lang.local files.
string $cust_lang_path
insertLanguage(string $lang_key, string $scope="")
insert language data from file in database
setDbHandler(ilDBInterface $a_db_handler)
set db handler object object db handler Return true on success
static _deleteLangData(string $a_lang_key, bool $a_keep_local_change)
Delete languge data.
$r