ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
SaveValuesMergesOntoOverlayTest.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
24use PHPUnit\Framework\Attributes\DataProvider;
25use PHPUnit\Framework\Attributes\PreserveGlobalState;
26use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
27use PHPUnit\Framework\MockObject\Stub;
28
37#[RunTestsInSeparateProcesses]
38#[PreserveGlobalState(false)]
40{
41 private ?string $fixture_directory = null;
43 private bool $created_client_data_dir_root = false;
44
45 protected function setUp(): void
46 {
48
49 if (!defined('ILIAS_ABSOLUTE_PATH')) {
50 define('ILIAS_ABSOLUTE_PATH', realpath(__DIR__ . '/../../../../'));
51 }
52 $this->created_client_data_dir_root = MigratedPoFixture::ensureClientDataDirDefinedOrSkip($this);
53
55 (new ReflectionClass(ilCachedLanguage::class))->getProperty('instances')->setValue(null, []);
56 }
57
58 protected function tearDown(): void
59 {
60 if ($this->fixture_directory !== null) {
61 MigratedPoFixture::removeShippedDirectory('components/ILIAS/Language/tests/' . $this->fixture_directory);
63 }
64 // never remove a foreign CLIENT_DATA_DIR
65 if ($this->created_client_data_dir_root && defined('CLIENT_DATA_DIR') && is_dir(CLIENT_DATA_DIR)) {
67 }
68
70 }
71
76 private function seedShippedAndOverlay(
77 string $module,
78 array $shipped_entries,
79 ?array $overlay_entries
81 $this->fixture_directory ??= 'tmp-savevalues-fixtures-' . bin2hex(random_bytes(4));
82 $relative_path = 'components/ILIAS/Language/tests/' . $this->fixture_directory . '/';
83
85 $relative_path,
86 $module,
87 'de',
88 MigratedPoFixture::catalog($module, $shipped_entries)
89 );
90
91 if ($overlay_entries !== null) {
93 rtrim(CLIENT_DATA_DIR, '/') . '/lang/' . $module . '/de/' . $module . '_de',
94 MigratedPoFixture::catalog($module, $overlay_entries)
95 );
96 }
97
98 return MigratedPoFixture::directory($module, $relative_path);
99 }
100
101 private function registerDirectoryManager(LanguageFileDirectory ...$contributed): void
102 {
103 $this->setGlobalVariable(
104 LanguageFileDirectoryManager::class,
106 );
107 }
108
109 private function callCurrentModuleContent(string $lang_key, string $module): ?array
110 {
111 return (new ReflectionClass(ilObjLanguageExt::class))
112 ->getMethod('currentModuleContent')
113 ->invoke(null, $lang_key, $module);
114 }
115
117 private function stubLng(): ilLanguage
118 {
119 $lng = (new ReflectionClass(ilLanguage::class))->newInstanceWithoutConstructor();
120 $this->setGlobalVariable('lng', $lng);
121
122 return $lng;
123 }
124
132 private function mockDatabaseForWrites(?array $lng_modules_row, array $lng_data_rows = []): ilDBInterface&Stub
133 {
134 $lng_modules_stmt = $this->createStub(ilDBStatement::class);
135 $remarks_stmt = $this->createStub(ilDBStatement::class);
136 $lng_data_stmt = $this->createStub(ilDBStatement::class);
137 $query_f_stmt = $this->createStub(ilDBStatement::class);
138
139 $db = $this->createStub(ilDBInterface::class);
140 $db->method('quote')->willReturnCallback(static fn($value, string $type = ''): string => "'" . (string) $value . "'");
141 $db->method('in')->willReturn("module IN ('placeholder')");
142 $db->method('query')->willReturnCallback(
143 static function (string $sql) use ($lng_modules_stmt, $remarks_stmt, $lng_data_stmt): ilDBStatement {
144 if (str_contains($sql, 'lng_modules')) {
145 return $lng_modules_stmt;
146 }
147 if (str_contains($sql, 'remarks')) {
148 return $remarks_stmt;
149 }
150 return $lng_data_stmt;
151 }
152 );
153 $db->method('queryF')->willReturn($query_f_stmt);
154
155 $rows = $lng_data_rows;
156 $db->method('fetchAssoc')->willReturnCallback(
157 function (ilDBStatement $statement) use (
158 $lng_modules_stmt,
159 $remarks_stmt,
160 $lng_data_stmt,
161 $query_f_stmt,
162 $lng_modules_row,
163 &$rows
164 ) {
165 if ($statement === $query_f_stmt) {
166 return ['lang_array' => serialize([])];
167 }
168 if ($statement === $lng_modules_stmt) {
169 return $lng_modules_row === null ? null : ['lang_array' => serialize($lng_modules_row)];
170 }
171 if ($statement === $remarks_stmt) {
172 return null;
173 }
174 if ($statement === $lng_data_stmt) {
175 $row = array_shift($rows);
176 return $row ?? null;
177 }
178 return null;
179 }
180 );
181 $db->method('manipulate')->willReturn(0);
182 $db->method('insert')->willReturn(1);
183
184 $this->setGlobalVariable('ilDB', $db);
185
186 return $db;
187 }
188
189 private function seedEmptyGlobalLanguageFile(string $lang_key): void
190 {
191 $fake_global_file = (new ReflectionClass(ilLanguageFile::class))->newInstanceWithoutConstructor();
192 $fake_global_file->setAllValues([]);
193 $fake_global_file->setAllComments([]);
194
195 $cache = (new ReflectionClass(ilLanguageFile::class))->getProperty('global_file_objects');
196 $existing = $cache->isInitialized() ? $cache->getValue() : [];
197 $cache->setValue(null, $existing + [$lang_key => $fake_global_file]);
198 }
199
201 private function callSaveValues(
202 string $lang_key,
203 array $values,
204 array $remarks,
205 bool $merge_onto_current_content
206 ): array {
207 return (new ReflectionClass(ilObjLanguageExt::class))
208 ->getMethod('saveValues')
209 ->invoke(null, $lang_key, $values, $remarks, $merge_onto_current_content)['unwritten'];
210 }
211
214 {
215 $directory = $this->seedShippedAndOverlay(
216 'ctest',
217 ['greeting' => 'Shipped'],
218 ['greeting' => 'Aus dem Overlay']
219 );
220 $this->registerDirectoryManager($directory);
221
222 $db = $this->createMock(ilDBInterface::class);
223 $db->expects($this->never())->method('query');
224 $this->setGlobalVariable('ilDB', $db);
225
226 $this->assertSame(
227 ['greeting' => 'Aus dem Overlay'],
228 $this->callCurrentModuleContent('de', 'ctest')
229 );
230 }
231
237 #[DataProvider('writingOperationsOnUnreadableContent')]
238 public function testAnUnreadableModuleIsLeftOutCompletelyAndAReadableOneIsWrittenNormally(string $operation, string $broken): void
239 {
240 $directory = $this->seedShippedAndOverlay('ctest4', ['greeting' => 'Shipped'], ['greeting' => 'Lokal']);
241 $this->seedShippedAndOverlay('ctest5', ['greeting' => 'Shipped', 'farewell' => 'Tschüss'], ['greeting' => 'Lokal']);
242 $overlay_po = rtrim(CLIENT_DATA_DIR, '/') . '/lang/ctest4/de/ctest4_de.po';
243 if ($broken === 'overlay') {
244 file_put_contents($overlay_po, "msgid \"kaputt\n");
245 } else {
246 file_put_contents(
247 constant('ILIAS_ABSOLUTE_PATH') . '/components/ILIAS/Language/tests/' . $this->fixture_directory . '/ctest4_de.po',
248 "msgid \"kaputt\n"
249 );
250 }
251 $overlay_before = (string) file_get_contents($overlay_po);
252 $this->registerDirectoryManager($directory, MigratedPoFixture::directory('ctest5', 'components/ILIAS/Language/tests/' . $this->fixture_directory . '/'));
253 $lng = $this->stubLng();
254 $this->seedEmptyGlobalLanguageFile('de');
255 $db = $this->mockDatabaseForWrites(['greeting' => 'Aus lng_modules']);
256 $inserted_modules = [];
257 $db->method('insert')->willReturnCallback(static function (string $table, array $values) use (&$inserted_modules): int {
258 $inserted_modules[] = $table . ':' . ($values['module'][1] ?? '');
259 return 1;
260 });
261 $lng_data_writes = [];
262 $db->method('replace')->willReturnCallback(static function (string $table, array $keys) use (&$lng_data_writes): int {
263 $lng_data_writes[] = $table . ':' . ($keys['module'][1] ?? '');
264 return 1;
265 });
266 $deleted = [];
267 $db->method('manipulate')->willReturnCallback(static function (string $sql) use (&$deleted): int {
268 if (str_starts_with($sql, 'DELETE FROM lng_data')) {
269 $deleted[] = $sql;
270 }
271 return 0;
272 });
273 $warnings = [];
274 $logger = $this->createStub(ilLogger::class);
275 $logger->method('warning')->willReturnCallback(static function (string $message) use (&$warnings): void {
276 $warnings[] = $message;
277 });
278 $logger_factory = $this->createStub(ilLoggerFactory::class);
279 $logger_factory->method('getComponentLogger')->willReturn($logger);
280 $this->setGlobalVariable('ilLoggerFactory', $logger_factory);
281
282 $values = ['ctest4' . $lng->separator . 'greeting' => 'Neu', 'ctest5' . $lng->separator . 'greeting' => 'Neu'];
283 $unwritten = $operation === 'save'
284 ? ilObjLanguageExt::_saveValues('de', $values)
285 : ilObjLanguageExt::_deleteValues('de', $values);
286
287 $this->assertSame(['ctest4'], $unwritten);
288 $this->assertMatchesRegularExpression('/module "ctest4" for "de" cannot be read/', implode("\n", $warnings));
289 $this->assertSame(['lng_modules:ctest5'], $inserted_modules, 'only the readable module is written');
290 $this->assertSame($operation === 'save' ? ['lng_data:ctest5'] : [], $lng_data_writes);
291 $this->assertCount($operation === 'delete' ? 1 : 0, $deleted);
292 $this->assertSame($overlay_before, (string) file_get_contents($overlay_po));
293 }
294
298 public static function writingOperationsOnUnreadableContent(): array
299 {
300 return [
301 'save, shipped po' => ['save', 'shipped'],
302 'save, overlay' => ['save', 'overlay'],
303 'delete, shipped po' => ['delete', 'shipped'],
304 'delete, overlay' => ['delete', 'overlay'],
305 ];
306 }
307
310 {
311 $directory = $this->seedShippedAndOverlay('ctest6', ['greeting' => 'Shipped'], ['greeting' => 'Lokal']);
312 file_put_contents(rtrim(CLIENT_DATA_DIR, '/') . '/lang/ctest6/de/ctest6_de.po', "msgid \"kaputt\n");
313 $this->registerDirectoryManager($directory);
314 $lng = $this->stubLng();
315 $this->seedEmptyGlobalLanguageFile('de');
316 $db = $this->mockDatabaseForWrites(null);
317 $logger_factory = $this->createStub(ilLoggerFactory::class);
318 $logger_factory->method('getComponentLogger')->willReturn($this->createStub(ilLogger::class));
319 $this->setGlobalVariable('ilLoggerFactory', $logger_factory);
320 $captured = null;
321 $db->method('insert')->willReturnCallback(static function (string $table, array $values) use (&$captured): int {
322 if ($table === 'lng_modules') {
323 $captured = unserialize($values['lang_array'][1] ?? '', ['allowed_classes' => false]);
324 }
325 return 1;
326 });
327
328 $this->callSaveValues('de', ['ctest6' . $lng->separator . 'greeting' => 'Neu'], [], false);
329
330 $this->assertSame(['greeting' => 'Neu'], $captured);
331 }
332
335 {
336 $directory = $this->seedShippedAndOverlay('ctest7', ['greeting' => 'Shipped', 'farewell' => 'Tschüss'], ['farewell' => 'Pfiat di']);
337 $this->registerDirectoryManager($directory);
338 $lng = $this->stubLng();
339 $this->seedEmptyGlobalLanguageFile('de');
340 $db = $this->mockDatabaseForWrites(null);
341 $logger_factory = $this->createStub(ilLoggerFactory::class);
342 $logger_factory->method('getComponentLogger')->willReturn($this->createStub(ilLogger::class));
343 $this->setGlobalVariable('ilLoggerFactory', $logger_factory);
344 $captured = null;
345 $db->method('insert')->willReturnCallback(static function (string $table, array $values) use (&$captured): int {
346 if ($table === 'lng_modules') {
347 $captured = unserialize($values['lang_array'][1] ?? '', ['allowed_classes' => false]);
348 }
349 return 1;
350 });
351
352 $unwritten = ilObjLanguageExt::_saveValues('de', ['ctest7' . $lng->separator . 'greeting' => 'Neu']);
353
354 $this->assertSame([], $unwritten);
355 $this->assertSame(['greeting' => 'Neu', 'farewell' => 'Pfiat di'], $captured);
356 $overlay = MigratedPoFixture::readPo(rtrim(CLIENT_DATA_DIR, '/') . '/lang/ctest7/de/ctest7_de.po');
357 $this->assertSame('Pfiat di', $overlay->find(null, 'farewell')?->getTranslation());
358 $this->assertSame('Neu', $overlay->find(null, 'greeting')?->getTranslation());
359 }
360
366 {
367 if (function_exists('posix_geteuid') && posix_geteuid() === 0) {
368 $this->markTestSkipped('A non-writable directory cannot be forced for root (no root-proof variant here).');
369 }
370 // a real local change ("greeting") must survive, else sync() has an empty delta and writes nothing
371 $directory = $this->seedShippedAndOverlay(
372 'ctest3',
373 ['greeting' => 'Shipped', 'farewell' => 'Tschüss'],
374 ['greeting' => 'Lokal']
375 );
376 $this->registerDirectoryManager($directory);
377 $lng = $this->stubLng();
378 $logger = $this->createMock(ilLogger::class);
379 $logger->expects($this->atLeastOnce())->method('warning');
380 $logger_factory = $this->createStub(ilLoggerFactory::class);
381 $logger_factory->method('getComponentLogger')->willReturn($logger);
382 $this->setGlobalVariable('ilLoggerFactory', $logger_factory);
383 $this->mockDatabaseForWrites(['greeting' => 'Lokal', 'farewell' => 'Tschüss']);
384
385 // the files stay readable, but their directory is non-writable, so the write itself fails
386 $overlay_directory = rtrim(CLIENT_DATA_DIR, '/') . '/lang/ctest3/de';
387 chmod($overlay_directory, 0555);
388
389 set_error_handler(static fn(): bool => true, E_WARNING);
390 try {
391 $unwritten = ilObjLanguageExt::_deleteValues('de', ['ctest3' . $lng->separator . 'farewell' => 'irrelevant']);
392 } finally {
393 restore_error_handler();
394 chmod($overlay_directory, 0775);
395 }
396
397 $this->assertSame(['ctest3'], $unwritten);
398 }
399
402 {
403 $GLOBALS['DIC'] = new \ILIAS\DI\Container();
404
405 $result = ilObjLanguageExt::findInvalidMarkupOfChangedValues('zz_no_db_needed', [
406 'common#:#greeting' => 'Hallo',
407 'common#:#formatting' => '<b>Hallo</b>',
408 ]);
409
410 $this->assertSame([], $result);
411 }
412
415 {
416 $lang_key = 'zz_changed_markup_test';
417 $bad = '<script>alert(1)</script>';
418 $this->seedEmptyGlobalLanguageFile($lang_key);
419 $this->stubLng();
420
421 $rows = [
422 ['module' => 'common', 'identifier' => 'unmodified', 'value' => $bad],
423 ['module' => 'common', 'identifier' => 'changed', 'value' => 'Hallo'],
424 ];
425 $statement = $this->createStub(ilDBStatement::class);
426 $statement->method('fetchRow')->willReturnCallback(static function () use (&$rows) {
427 return array_shift($rows);
428 });
429 $db = $this->createStub(ilDBInterface::class);
430 $db->method('quote')->willReturnCallback(static fn($value, string $type = ''): string => "'" . (string) $value . "'");
431 $db->method('in')->willReturn("module IN ('placeholder')");
432 $db->method('like')->willReturn('1=1');
433 $db->method('query')->willReturn($statement);
434 $this->setGlobalVariable('ilDB', $db);
435
437 'common#:#unmodified' => $bad,
438 'common#:#changed' => $bad,
439 ]);
440
441 $this->assertArrayNotHasKey('common#:#unmodified', $result, 'unchanged from the current lng_data value - not rejected');
442 $this->assertArrayHasKey('common#:#changed', $result, 'genuinely changed to a disallowed value - rejected');
443 }
444
447 {
448 $lang_key = 'zz_merge_false_test';
449 $this->seedEmptyGlobalLanguageFile($lang_key);
450 $lng = $this->stubLng();
451 $db = $this->mockDatabaseForWrites(['greeting' => 'Alt', 'farewell' => 'Tschüss']);
452
453 $captured = null;
454 $db->method('insert')->willReturnCallback(
455 static function (string $table, array $values) use (&$captured): int {
456 if ($table === 'lng_modules') {
457 $captured = unserialize($values['lang_array'][1] ?? '', ['allowed_classes' => false]);
458 }
459 return 1;
460 }
461 );
462
463 $this->callSaveValues(
464 $lang_key,
465 ['wtest' . $lng->separator . 'greeting' => 'Neu'],
466 [],
467 false
468 );
469
470 $this->assertSame(['greeting' => 'Neu'], $captured);
471 }
472
475 {
476 $lang_key = 'zz_merge_true_test';
477 $this->seedEmptyGlobalLanguageFile($lang_key);
478 $lng = $this->stubLng();
479 $db = $this->mockDatabaseForWrites(['greeting' => 'Alt', 'farewell' => 'Tschüss']);
480
481 $captured = null;
482 $db->method('insert')->willReturnCallback(
483 static function (string $table, array $values) use (&$captured): int {
484 if ($table === 'lng_modules') {
485 $captured = unserialize($values['lang_array'][1] ?? '', ['allowed_classes' => false]);
486 }
487 return 1;
488 }
489 );
490
491 $this->callSaveValues(
492 $lang_key,
493 ['wtest' . $lng->separator . 'greeting' => 'Neu'],
494 [],
495 true
496 );
497
498 $this->assertSame(['greeting' => 'Neu', 'farewell' => 'Tschüss'], $captured);
499 }
500}
static removeShippedDirectory(string $relative_path)
static ensureClientDataDirDefinedOrSkip(\PHPUnit\Framework\TestCase $test)
Guards every fixture that writes under CLIENT_DATA_DIR: a full-suite run may have defined it as the r...
static writePair(string $base_path, TranslationCatalog $catalog)
Writes "<base>.po" and, like MigratedLanguageFileSync::sync(), its compiled revision named by current...
static directory(string $prefix, string $path, bool $local=false)
static catalog(string $module, array $entries)
static removeDirectory(string $directory)
static readPo(string $file)
static writeShippedPo(string $relative_path, string $module, string $lang_key, TranslationCatalog $catalog)
Writes $catalog as the shipped .po: a module only counts as migrated while that exists.
static resetRuntime(?string $artifact_directory=null)
Forgets the static state of the runtime read path and points build and runtime at $artifact_directory...
ilObjLanguageExt::currentModuleContent() (what a partial save/delete is applied to) and the $merge_on...
testMergeOntoCurrentContentTrueKeepsAKeyThatIsNotInTheNewEntries()
The default (true) contrasted with the test above: the untouched "farewell" survives.
mockDatabaseForWrites(?array $lng_modules_row, array $lng_data_rows=[])
$ilDB mock with a controllable lng_modules row and lng_data rows; the three SELECTs (lng_modules row,...
testFindInvalidMarkupOfChangedValuesNeverReadsCurrentOrShippedValuesWhenNothingIsSuspicious()
The expensive reads must not run when every value passes the plain markup check (an empty $GLOBALS['D...
testAnUnreadableModuleIsLeftOutCompletelyAndAReadableOneIsWrittenNormally(string $operation, string $broken)
A module whose shipped .po or overlay cannot be read: nothing of its change is written - no lng_data,...
callCurrentModuleContent(string $lang_key, string $module)
testOverlayValueIsUsedEvenWhenItDiffersFromTheLngModulesRowAndTheDatabaseIsNeverConsulted()
->never() on query(): once a readable overlay is found the database must not be queried at all.
testSavingOneKeyKeepsTheOverlaysLocalChangeOfAnotherKey()
A readable overlay: the local change of another key stays when one key is saved.
testAnImportWithoutMergingNeverChecksTheCurrentContent()
"delete"-mode import: the entries are the complete new content, nothing is read first - written as be...
testMergeOntoCurrentContentFalseDropsAKeyThatIsNotInTheNewEntries()
With $merge_onto_current_content=false ("delete"-mode import) $to_save is the complete new content: a...
bool $created_client_data_dir_root
True if this test defined CLIENT_DATA_DIR itself; tearDown() removes only such a root.
seedShippedAndOverlay(string $module, array $shipped_entries, ?array $overlay_entries)
callSaveValues(string $lang_key, array $values, array $remarks, bool $merge_onto_current_content)
registerDirectoryManager(LanguageFileDirectory ... $contributed)
testDeleteValuesReportsTheModuleWhoseOverlayCouldNotBeWritten()
An overlay write failure never undoes the database write: the module is reported back in the returned...
testFindInvalidMarkupOfChangedValuesAppliesTheCurrentAndShippedComparison()
A value failing the plain check is rejected only if it also differs from both the current and the shi...
Class ilLanguageBaseTest.
language handling
Class ilObjLanguageExt.
static _deleteValues(string $a_lang_key, array $a_values=array())
Delete a set of translation in the database.
static findInvalidMarkupOfChangedValues(string $a_lang_key, array $values)
The values a save into $a_lang_key would change and that contain markup TranslationMarkupPolicy does ...
static _saveValues(string $a_lang_key, array $a_values=array(), array $a_remarks=array())
Save a set of translation in the database.
const CLIENT_DATA_DIR
Definition: constants.php:46
if(!isset($arguments[0])) if(count( $arguments) > 3) if(! $remove_from_lang &&isset($arguments[2]) &&!is_dir($arguments[2])) $module
return['3gp', '7z', 'ai', 'aif', 'aifc', 'aiff', 'au', 'arw', 'avi', 'backup', 'bak', 'bas', 'bpmn', 'bpmn2', 'bmp', 'bib', 'bibtex', 'bz', 'bz2', 'c', 'c++', 'cc', 'cct', 'cdf', 'cer', 'class', 'cls', 'conf', 'cpp', 'crt', 'crs', 'crw', 'cr2', 'css', 'cst', 'csv', 'cur', 'db', 'dcr', 'des', 'dng', 'doc', 'docx', 'dot', 'dotx', 'dtd', 'dvi', 'el', 'eps', 'epub', 'f', 'f77', 'f90', 'flv', 'for', 'g3', 'gif', 'gl', 'gan', 'ggb', 'gsd', 'gsm', 'gtar', 'gz', 'gzip', 'h', 'hpp', 'htm', 'html', 'htmls', 'ibooks', 'ico', 'ics', 'ini', 'ipynb', 'java', 'jbf', 'jpeg', 'jpg', 'js', 'jsf', 'jso', 'json', 'latex', 'lang', 'less', 'log', 'lsp', 'ltx', 'm1v', 'm2a', 'm2v', 'm3u', 'm4a', 'm4v', 'markdown', 'm', 'mat', 'md', 'mdl', 'mdown', 'mid', 'min', 'midi', 'mobi', 'mod', 'mov', 'movie', 'mp2', 'mp3', 'mp4', 'mpa', 'mpeg', 'mpg', 'mph', 'mpga', 'mpp', 'mpt', 'mpv', 'mpx', 'mv', 'mw', 'mv4', 'nb', 'nbp', 'nef', 'nif', 'niff', 'obj', 'obm', 'odt', 'ods', 'odp', 'odg', 'odf', 'oga', 'ogg', 'ogv', 'old', 'p', 'pas', 'pbm', 'pcl', 'pct', 'pcx', 'pdf', 'pgm', 'pic', 'pict', 'png', 'por', 'pov', 'project', 'properties', 'ppa', 'ppm', 'pps', 'ppsx', 'ppt', 'pptx', 'ppz', 'ps', 'psd', 'pwz', 'qt', 'qtc', 'qti', 'qtif', 'r', 'ra', 'ram', 'rar', 'rast', 'rda', 'rev', 'rexx', 'ris', 'rf', 'rgb', 'rm', 'rmd', 'rmi', 'rmm', 'rmp', 'rt', 'rtf', 'rtx', 'rv', 's', 's3m', 'sav', 'sbs', 'sec', 'sdml', 'sgm', 'sgml', 'smi', 'smil', 'srt', 'sps', 'spv', 'stl', 'svg', 'swa', 'swf', 'swz', 'tar', 'tex', 'texi', 'texinfo', 'text', 'tgz', 'tif', 'tiff', 'ttf', 'txt', 'tmp', 'uvproj', 'vdf', 'vimeo', 'viv', 'vivo', 'vrml', 'vsdx', 'vtt', 'wav', 'webm', 'wmv', 'wmx', 'wmz', 'woff', 'wwd', 'xhtml', 'xif', 'xls', 'xlsx', 'xmind', 'xml', 'xsl', 'xsd', 'zip']
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setGlobalVariable(string $name, mixed $value)
A directory of language files with a path and a prefix for them.
Interface ilDBInterface.
Interface ilDBStatement.
global $lng
Definition: privfeed.php:26
$GLOBALS["DIC"]
Definition: wac.php:66