diff --git a/components/ILIAS/Conditions/classes/class.ilConditionHandlerGUI.php b/components/ILIAS/Conditions/classes/class.ilConditionHandlerGUI.php index f78c8f5eb5fa..698e289ea3bc 100755 --- a/components/ILIAS/Conditions/classes/class.ilConditionHandlerGUI.php +++ b/components/ILIAS/Conditions/classes/class.ilConditionHandlerGUI.php @@ -27,6 +27,7 @@ use ILIAS\Filesystem\Stream\Streams; use ILIAS\UI\Component\Input\Container\Form\Standard as StandardForm; use ILIAS\UI\Component\Input\Field\Section as Section; +use ILIAS\Refinery\ConstraintViolationException; /** * class ilConditionHandlerGUI @@ -799,11 +800,33 @@ protected function confirmDeleteConditionTrigger(): void $this->refinery->byTrying( [ $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->int()), + // Actions for entire table sends a fixed token in an array, instead of all row ids + $this->refinery->custom()->transformation(function ($var) { + if ( + is_array($var) && + count($var) === 1 && + (string) $var[0] === 'ALL_OBJECTS' + ) { + return $var; + } + throw new UnexpectedValueException('Unexpected string in row_id array.'); + }), $this->refinery->always([]) ] ) ); + if ($condition_trigger_ids === ['ALL_OBJECTS']) { + $condition_trigger_ids = []; + foreach (ilConditionHandler::_getPersistedConditionsOfTarget( + $this->getTargetRefId(), + $this->getTargetId(), + $this->getTargetType() + ) as $condition) { + $condition_trigger_ids[] = $condition['id']; + } + } + $items = []; foreach ($condition_trigger_ids as $condition_trigger_id) { $condition = ilConditionHandler::_getCondition($condition_trigger_id); diff --git a/components/ILIAS/DataCollection/classes/Table/class.ilDclTable.php b/components/ILIAS/DataCollection/classes/Table/class.ilDclTable.php index fff19ffa5b83..96ef87d16265 100755 --- a/components/ILIAS/DataCollection/classes/Table/class.ilDclTable.php +++ b/components/ILIAS/DataCollection/classes/Table/class.ilDclTable.php @@ -18,7 +18,9 @@ declare(strict_types=1); -use ILIAS\Modules\DataCollection\Fields\Formula\FormulaParser\Token\Tokenizer; + +use ILIAS\Modules\DataCollection\Fields\Formula\FormulaParser\Math\Functions; +use ILIAS\Modules\DataCollection\Fields\Formula\FormulaParser\Math\Operators; class ilDclTable { @@ -530,7 +532,11 @@ public function getFirstTableViewId(int $ref_id, int $user_id = 0, bool $with_de */ public function getFieldsForFormula(): array { - $syntax_chars = array_merge(Tokenizer::$operators, Tokenizer::$functions, ['(', ')', ',']); + $syntax_chars = array_merge( + array_map(static fn(Operators $function): string => $function->value, Operators::cases()), + array_map(static fn(Functions $function): string => $function->value, Functions::cases()), + ['(', ')', ','] + ); foreach ($this->getFields() as $field) { if (in_array($field->getDatatypeId(), ilDclFormulaFieldModel::SUPPORTED_FIELDS)) { foreach ($syntax_chars as $element) { diff --git a/components/ILIAS/File/classes/Capabilities/Capability.php b/components/ILIAS/File/classes/Capabilities/Capability.php index add3631ee6af..d07fee24b96b 100644 --- a/components/ILIAS/File/classes/Capabilities/Capability.php +++ b/components/ILIAS/File/classes/Capabilities/Capability.php @@ -28,11 +28,16 @@ class Capability { private bool $unlocked = false; private ?URI $uri = null; + /** + * @var Permissions[] + */ + private array $permissions = []; public function __construct( private Capabilities $capability, - private Permissions $permission + Permissions ... $permissions ) { + $this->permissions = $permissions; } public function withUnlocked(bool $unlocked): Capability @@ -62,9 +67,9 @@ public function getCapability(): Capabilities return $this->capability; } - public function getPermission(): Permissions + public function getPermissions(): array { - return $this->permission; + return $this->permissions; } } diff --git a/components/ILIAS/File/classes/Capabilities/CapabilityBuilder.php b/components/ILIAS/File/classes/Capabilities/CapabilityBuilder.php index eb77cc36d0ce..e22ad6f9b243 100644 --- a/components/ILIAS/File/classes/Capabilities/CapabilityBuilder.php +++ b/components/ILIAS/File/classes/Capabilities/CapabilityBuilder.php @@ -52,6 +52,7 @@ public function __construct( private readonly \ilCtrlInterface $ctrl, private readonly ActionRepository $action_repository, private readonly Services $http, + private readonly TypeResolver $type_resolver, private readonly URIBuilder $static_url ) { $this->checks = [ @@ -79,19 +80,18 @@ public function get(int $ref_id, bool $do_checks = true): CapabilityCollection * which will return the first unlocked Capability */ $capabilities = [ - new Capability(Capabilities::FORCED_INFO_PAGE, Permissions::VISIBLE), + new Capability(Capabilities::FORCED_INFO_PAGE, ...Permissions::ANY()), new Capability(Capabilities::VIEW_EXTERNAL, Permissions::VIEW_CONTENT), new Capability(Capabilities::EDIT_EXTERNAL, Permissions::EDIT_CONTENT), new Capability(Capabilities::DOWNLOAD, Permissions::READ), new Capability(Capabilities::MANAGE_VERSIONS, Permissions::WRITE), new Capability(Capabilities::EDIT_SETTINGS, Permissions::WRITE), - new Capability(Capabilities::INFO_PAGE, Permissions::VISIBLE), + new Capability(Capabilities::INFO_PAGE, ...Permissions::ANY()), new Capability(Capabilities::NONE, Permissions::NONE), new Capability(Capabilities::UNZIP, Permissions::WRITE), ]; - - if (\ilObject2::_lookupType($ref_id, true) !== 'file') { + if ($this->type_resolver->resolveType($ref_id) !== 'file') { return new CapabilityCollection($capabilities); } diff --git a/components/ILIAS/File/classes/Capabilities/Check/Download.php b/components/ILIAS/File/classes/Capabilities/Check/Download.php index 8453a0fcda16..41f3fd7a7501 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/Download.php +++ b/components/ILIAS/File/classes/Capabilities/Check/Download.php @@ -49,7 +49,7 @@ public function maybeUnlock( int $ref_id, ): Capability { $this->info = $info; - return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, $capability->getPermission())); + return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())); } public function maybeBuildURI(Capability $capability, CheckHelpers $helpers, int $ref_id): Capability diff --git a/components/ILIAS/File/classes/Capabilities/Check/Edit.php b/components/ILIAS/File/classes/Capabilities/Check/Edit.php index 234cd398839d..bd2abcea9e73 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/Edit.php +++ b/components/ILIAS/File/classes/Capabilities/Check/Edit.php @@ -41,7 +41,7 @@ public function maybeUnlock( \ilObjFileInfo $info, int $ref_id, ): Capability { - return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, $capability->getPermission())); + return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())); } public function maybeBuildURI(Capability $capability, CheckHelpers $helpers, int $ref_id): Capability diff --git a/components/ILIAS/File/classes/Capabilities/Check/EditContent.php b/components/ILIAS/File/classes/Capabilities/Check/EditContent.php index bb65a7b8a0ef..e925f7c6a2a1 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/EditContent.php +++ b/components/ILIAS/File/classes/Capabilities/Check/EditContent.php @@ -40,7 +40,7 @@ public function maybeUnlock( \ilObjFileInfo $info, int $ref_id, ): Capability { - if (!$this->hasPermission($helpers, $ref_id, $capability->getPermission())) { + if (!$this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())) { return $capability->withUnlocked(false); } diff --git a/components/ILIAS/File/classes/Capabilities/Check/Info.php b/components/ILIAS/File/classes/Capabilities/Check/Info.php index 53b8d52cae2c..b150a162e327 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/Info.php +++ b/components/ILIAS/File/classes/Capabilities/Check/Info.php @@ -41,7 +41,7 @@ public function maybeUnlock( \ilObjFileInfo $info, int $ref_id, ): Capability { - return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, $capability->getPermission())); + return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())); } public function maybeBuildURI(Capability $capability, CheckHelpers $helpers, int $ref_id): Capability diff --git a/components/ILIAS/File/classes/Capabilities/Check/Manage.php b/components/ILIAS/File/classes/Capabilities/Check/Manage.php index e4ad2c815770..3a92028ced73 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/Manage.php +++ b/components/ILIAS/File/classes/Capabilities/Check/Manage.php @@ -41,7 +41,7 @@ public function maybeUnlock( \ilObjFileInfo $info, int $ref_id, ): Capability { - return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, $capability->getPermission())); + return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())); } public function maybeBuildURI(Capability $capability, CheckHelpers $helpers, int $ref_id): Capability diff --git a/components/ILIAS/File/classes/Capabilities/Check/Unzip.php b/components/ILIAS/File/classes/Capabilities/Check/Unzip.php index bc8cefb803d3..2dff28b195f3 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/Unzip.php +++ b/components/ILIAS/File/classes/Capabilities/Check/Unzip.php @@ -43,7 +43,7 @@ public function maybeUnlock( return $capability; } - return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, $capability->getPermission())); + return $capability->withUnlocked($this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())); } public function maybeBuildURI(Capability $capability, CheckHelpers $helpers, int $ref_id): Capability diff --git a/components/ILIAS/File/classes/Capabilities/Check/ViewContent.php b/components/ILIAS/File/classes/Capabilities/Check/ViewContent.php index aabb32544d10..667da43696c1 100644 --- a/components/ILIAS/File/classes/Capabilities/Check/ViewContent.php +++ b/components/ILIAS/File/classes/Capabilities/Check/ViewContent.php @@ -40,7 +40,7 @@ public function maybeUnlock( \ilObjFileInfo $info, int $ref_id, ): Capability { - if (!$this->hasPermission($helpers, $ref_id, $capability->getPermission())) { + if (!$this->hasPermission($helpers, $ref_id, ...$capability->getPermissions())) { return $capability->withUnlocked(false); } diff --git a/components/ILIAS/File/classes/Capabilities/CoreTypeResolver.php b/components/ILIAS/File/classes/Capabilities/CoreTypeResolver.php new file mode 100644 index 000000000000..5de13f9f3de7 --- /dev/null +++ b/components/ILIAS/File/classes/Capabilities/CoreTypeResolver.php @@ -0,0 +1,33 @@ + + */ +class CoreTypeResolver implements TypeResolver +{ + public function resolveType(int $ref_id): string + { + return \ilObject2::_lookupType($ref_id, true); + } + +} diff --git a/components/ILIAS/File/classes/Capabilities/Permissions.php b/components/ILIAS/File/classes/Capabilities/Permissions.php index 94fde1590482..e5f3f6147fbf 100644 --- a/components/ILIAS/File/classes/Capabilities/Permissions.php +++ b/components/ILIAS/File/classes/Capabilities/Permissions.php @@ -36,4 +36,20 @@ enum Permissions: string case COPY = 'copy'; case EDIT_CONTENT = 'edit_file'; + public static function ANY(): array + { + return [ + self::VISIBLE, + self::READ, + self::VIEW_CONTENT, + self::READ_LP, + self::EDIT_LP, + self::EDIT_PERMISSIONS, + self::WRITE, + self::DELETE, + self::COPY, + self::EDIT_CONTENT + ]; + } + } diff --git a/components/ILIAS/File/classes/Capabilities/TypeResolver.php b/components/ILIAS/File/classes/Capabilities/TypeResolver.php new file mode 100644 index 000000000000..ae6a5508a793 --- /dev/null +++ b/components/ILIAS/File/classes/Capabilities/TypeResolver.php @@ -0,0 +1,30 @@ + + */ +interface TypeResolver +{ + public function resolveType(int $ref_id): string; + +} diff --git a/components/ILIAS/File/classes/class.ilFileStaticURLHandler.php b/components/ILIAS/File/classes/class.ilFileStaticURLHandler.php index b747b5ba7e08..70d3da68193c 100755 --- a/components/ILIAS/File/classes/class.ilFileStaticURLHandler.php +++ b/components/ILIAS/File/classes/class.ilFileStaticURLHandler.php @@ -25,6 +25,7 @@ use ILIAS\File\Capabilities\CapabilityBuilder; use ILIAS\components\WOPI\Discovery\ActionDBRepository; use ILIAS\File\Capabilities\Capabilities; +use ILIAS\File\Capabilities\CoreTypeResolver; /** * @author Fabian Schmid @@ -47,6 +48,7 @@ public function __construct() $DIC->ctrl(), new ActionDBRepository($DIC->database()), $DIC->http(), + new CoreTypeResolver(), $DIC['static_url.uri_builder'] ); } diff --git a/components/ILIAS/File/classes/class.ilObjFileGUI.php b/components/ILIAS/File/classes/class.ilObjFileGUI.php index 4c8fa550b414..918bac897959 100755 --- a/components/ILIAS/File/classes/class.ilObjFileGUI.php +++ b/components/ILIAS/File/classes/class.ilObjFileGUI.php @@ -38,6 +38,7 @@ use ILIAS\File\Capabilities\Capabilities; use ILIAS\File\Capabilities\CapabilityBuilder; use ILIAS\File\Capabilities\CapabilityCollection; +use ILIAS\File\Capabilities\CoreTypeResolver; /** * GUI class for file objects. @@ -132,6 +133,7 @@ public function __construct(int $a_id = 0, int $a_id_type = self::REPOSITORY_NOD $this->ctrl, $this->action_repo, $DIC->http(), + new CoreTypeResolver(), $DIC['static_url.uri_builder'] ); $this->capabilities = $capability_builder->get($a_id); diff --git a/components/ILIAS/File/classes/class.ilObjFileListGUI.php b/components/ILIAS/File/classes/class.ilObjFileListGUI.php index 44f670a2a619..21bc4837d84d 100755 --- a/components/ILIAS/File/classes/class.ilObjFileListGUI.php +++ b/components/ILIAS/File/classes/class.ilObjFileListGUI.php @@ -22,6 +22,7 @@ use ILIAS\components\WOPI\Discovery\ActionDBRepository; use ILIAS\File\Capabilities\Capabilities; use ILIAS\File\Capabilities\CapabilityBuilder; +use ILIAS\File\Capabilities\CoreTypeResolver; /** * Class ilObjFileListGUI @@ -54,6 +55,7 @@ public function __construct(int $context = self::CONTEXT_REPOSITORY) $this->ctrl, new ActionDBRepository($DIC->database()), $DIC->http(), + new CoreTypeResolver(), $DIC['static_url.uri_builder'] ); diff --git a/components/ILIAS/File/docs/README.md b/components/ILIAS/File/docs/README.md new file mode 100644 index 000000000000..89ad3cafa1cb --- /dev/null +++ b/components/ILIAS/File/docs/README.md @@ -0,0 +1,39 @@ +File Object +=========== + +# General Information + +The file object is the files that were created in the magazine via ‘Add new +Object’. The file object uses many of the central file services and also offers +WOPI actions, for example. + +# Capabilities + +The file object uses an internal mechanism to handle all the ‘functions’ that a +user with a file object can perform. The capabilities currently check +authorisations, whether WOPI actions are available for a file and also certain +settings per object. The capabilities per user and file object are calculated +once per request and are then used to provide links to actions, create tabs and +also handle static URLs, for example. In particular, the capabilities are also +used to decide which action to open by default when clicking on a file object in +the magazine. +Here is a list (based on unit tests) of cases in which a default action is to be +expected: + + + +| User's Permissions | WOPI View Action av. | WOPI Edit Action av. | Click-Setting | Expected Capability | +| --------------------------------------------- | -------------------- | -------------------- | ------------- | ------------------- | +| read | Yes | Yes | Open | DOWNLOAD | +| write, read | Yes | Yes | Open | DOWNLOAD | +| edit_file | Yes | Yes | Open | EDIT_EXTERNAL | +| read, visible | No | No | Info-Page | FORCED_INFO_PAGE | +| read, write, visible, edit_file, view_content | Yes | Yes | Info-Page | FORCED_INFO_PAGE | +| write, read | Yes | Yes | Info-Page | FORCED_INFO_PAGE | +| visible | Yes | Yes | Open | INFO_PAGE | +| write | Yes | Yes | Open | MANAGE_VERSIONS | +| none | Yes | Yes | Open | NONE | +| edit_file, view_content | Yes | Yes | Open | VIEW_EXTERNAL | +| read, write, visible, edit_file, view_content | Yes | Yes | Open | VIEW_EXTERNAL | + + diff --git a/components/ILIAS/File/tests/Capabilities/CapabilityTest.php b/components/ILIAS/File/tests/Capabilities/CapabilityTest.php new file mode 100644 index 000000000000..3772e882df6e --- /dev/null +++ b/components/ILIAS/File/tests/Capabilities/CapabilityTest.php @@ -0,0 +1,332 @@ +file_info_repository = $this->createMock(\ilObjFileInfoRepository::class); + $this->access = $this->createMock(\ilAccessHandler::class); + $this->ctrl = $this->createMock(\ilCtrlInterface::class); + $this->action_repository = $this->createMock(ActionRepository::class); + $this->http = $this->createMock(Services::class); + $this->static_url = $this->createMock(URIBuilder::class); + $this->type_resolver = $this->createMock(TypeResolver::class); + + $this->type_resolver->method('resolveType') + ->withAnyParameters() + ->willReturn('file'); + + $this->capability_builder = new CapabilityBuilder( + $this->file_info_repository, + $this->access, + $this->ctrl, + $this->action_repository, + $this->http, + $this->type_resolver, + $this->static_url + ); + } + + protected function tearDown(): void + { + } + + public static function tearDownAfterClass(): void + { + if (!self::$update_readme) { + return; + } + self::updateREADME(); + } + + public static function environmentProvider(): array + { + return [ + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => true, + 'user_permissions' => [ + Permissions::READ, + Permissions::WRITE, + Permissions::VISIBLE, + Permissions::EDIT_CONTENT, + Permissions::VIEW_CONTENT + ], + 'expected_best' => Capabilities::FORCED_INFO_PAGE + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::READ, + Permissions::WRITE, + Permissions::VISIBLE, + Permissions::EDIT_CONTENT, + Permissions::VIEW_CONTENT + ], + 'expected_best' => Capabilities::VIEW_EXTERNAL + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::EDIT_CONTENT, + Permissions::VIEW_CONTENT + ], + 'expected_best' => Capabilities::VIEW_EXTERNAL + ], + [ + 'wopi_view' => false, + 'wopi_edit' => false, + 'infopage_first' => true, + 'user_permissions' => [ + Permissions::READ, + Permissions::VISIBLE + ], + 'expected_best' => Capabilities::FORCED_INFO_PAGE + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::EDIT_CONTENT, + ], + 'expected_best' => Capabilities::EDIT_EXTERNAL + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::READ, + ], + 'expected_best' => Capabilities::DOWNLOAD + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::WRITE, + Permissions::READ, + ], + 'expected_best' => Capabilities::DOWNLOAD + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::WRITE, + ], + 'expected_best' => Capabilities::MANAGE_VERSIONS + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::VISIBLE, + ], + 'expected_best' => Capabilities::INFO_PAGE + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => true, + 'user_permissions' => [ + Permissions::WRITE, + Permissions::READ, + ], + 'expected_best' => Capabilities::FORCED_INFO_PAGE + ], + [ + 'wopi_view' => true, + 'wopi_edit' => true, + 'infopage_first' => false, + 'user_permissions' => [ + Permissions::NONE, + ], + 'expected_best' => Capabilities::NONE + ], + ]; + } + + #[DataProvider('environmentProvider')] + public function testCapabilityPriority( + bool $wopi_view, + bool $wopi_edit, + bool $infopage_first, + array $permissions, + Capabilities $expected_best + ): void { + $ref_id = 42; + + $this->access->method('checkAccess') + ->willReturnCallback( + function (string $permission) use ($permissions): bool { + $checked_permissions = explode(',', $permission); + $common_permissions = array_intersect( + array_map(static fn(Permissions $p): string => $p->value, $permissions), + $checked_permissions + ); + return $common_permissions !== []; + } + ); + + $file_info = $this->createMock(\ilObjFileInfo::class); + $file_info->method('shouldDownloadDirectly') + ->willReturn(!$infopage_first); + + $this->file_info_repository->method('getByRefId') + ->with($ref_id) + ->willReturn($file_info); + + $this->action_repository->method('hasEditActionForSuffix') + ->willReturn($wopi_edit); + + $this->action_repository->method('hasViewActionForSuffix') + ->willReturn($wopi_view); + + $capabilities = $this->capability_builder->get($ref_id); + $best = $capabilities->getBest(); + + $this->assertEquals($expected_best, $best->getCapability()); + + self::$readme_infos[] = [ + implode(', ', array_map(fn(Permissions $p): string => $p->value, $permissions)), // permissions + ($wopi_view ? 'Yes' : 'No'), + ($wopi_edit ? 'Yes' : 'No'), + ($infopage_first ? 'Info-Page' : 'Open'), + $best->getCapability()->name + ]; + } + + private static function updateREADME(): void + { + // UPDATE README + $readme_file = __DIR__ . '/../../docs/README.md'; + $readme_content = file_get_contents($readme_file); + + $table = [ + [ + 'User\'s Permissions', + 'WOPI View Action av.', + 'WOPI Edit Action av.', + 'Click-Setting', + 'Expected Capability' + ] + ]; + $readme_infos = self::$readme_infos; + // sort $readme_infos by last column + usort($readme_infos, static function ($a, $b): int { + $a_string = implode('', array_reverse($a)); + $b_string = implode('', array_reverse($b)); + + return strcmp((string) $a_string, (string) $b_string); + }); + + $table = array_merge($table, $readme_infos); + + // Define the markers for the block + $start_marker = ""; + $end_marker = ""; + + // Prepare the new block content + $new_block = $start_marker . "\n\n" . self::arrayToMarkdownTable($table) . "\n\n" . $end_marker; + + // Replace the content between the markers + $pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '/s'; + $readme_content = preg_replace($pattern, $new_block, $readme_content); + + file_put_contents($readme_file, $readme_content); + } + + private static function arrayToMarkdownTable(array $data): string + { + // Check if the input array is valid + if (empty($data) || !is_array($data[0])) { + throw new InvalidArgumentException("Input must be a non-empty array of arrays."); + } + + // Calculate the maximum width of each column + $col_widths = array_map( + static fn($col_index): int => max( + array_map(static fn($row): int => isset($row[$col_index]) ? mb_strlen((string) $row[$col_index]) : 0, $data) + ), + array_keys($data[0]) + ); + + // Function to pad a row's columns to match the maximum width + $pad_row = static fn($row): array => array_map(static function ($value, $index) use ($col_widths): string { + $value ??= ''; // Handle missing values + return str_pad($value, $col_widths[$index], " ", STR_PAD_RIGHT); + }, $row, array_keys($col_widths)); + + // Format the header and rows + $header = $pad_row($data[0]); + $rows = array_map($pad_row, array_slice($data, 1)); + + // Build the Markdown table + $header_row = "| " + . implode(" | ", $header) + . " |"; + $sep_row = "| " + . implode(" | ", array_map(static fn($width): string => str_repeat("-", $width), $col_widths)) + . " |"; + $data_rows = array_map(static fn($row): string => "| " . implode(" | ", $row) . " |", $rows); + + // Combine all parts + return implode("\n", array_merge([$header_row, $sep_row], $data_rows)); + } + +} diff --git a/components/ILIAS/GlobalScreen/resources/callback_handler.php b/components/ILIAS/GlobalScreen/resources/callback_handler.php index b9c1133729a2..d77e792d0c99 100755 --- a/components/ILIAS/GlobalScreen/resources/callback_handler.php +++ b/components/ILIAS/GlobalScreen/resources/callback_handler.php @@ -1,10 +1,28 @@ run(); } diff --git a/components/ILIAS/GlobalScreen/resources/gs_content.php b/components/ILIAS/GlobalScreen/resources/gs_content.php index e111a98cb887..40252738d757 100644 --- a/components/ILIAS/GlobalScreen/resources/gs_content.php +++ b/components/ILIAS/GlobalScreen/resources/gs_content.php @@ -19,6 +19,7 @@ namespace ILIAS\GlobalScreen\Client; require_once(__DIR__ . '/../vendor/composer/vendor/autoload.php'); +require_once(__DIR__ . '/../artifacts/bootstrap_default.php'); use ILIAS\GlobalScreen\Scope\MainMenu\Collector\Renderer\Hasher; use ILIAS\GlobalScreen\Scope\MainMenu\Factory\Item\Lost; @@ -29,8 +30,6 @@ class ContentRenderer public function run() { - \ilContext::init(\ilContext::CONTEXT_WAC); - \ilInitialisation::initILIAS(); global $DIC; $GS = $DIC->globalScreen(); @@ -58,5 +57,7 @@ public function run() } if (php_sapi_name() !== 'cli') { + \ilContext::init(\ilContext::CONTEXT_WAC); + entry_point('ILIAS Legacy Initialisation Adapter'); (new ContentRenderer())->run(); } diff --git a/components/ILIAS/GlobalScreen/resources/notify.php b/components/ILIAS/GlobalScreen/resources/notify.php index 82c7357fc18e..8efe3165b39d 100755 --- a/components/ILIAS/GlobalScreen/resources/notify.php +++ b/components/ILIAS/GlobalScreen/resources/notify.php @@ -1,6 +1,5 @@ run(); diff --git a/components/ILIAS/GlobalScreen/src/Client/CallbackHandler.php b/components/ILIAS/GlobalScreen/src/Client/CallbackHandler.php index 1171e371f772..e1661c8a8ad5 100755 --- a/components/ILIAS/GlobalScreen/src/Client/CallbackHandler.php +++ b/components/ILIAS/GlobalScreen/src/Client/CallbackHandler.php @@ -1,6 +1,5 @@ ctrl = $DIC->ctrl(); $this->wrapper = $DIC->http()->wrapper(); diff --git a/components/ILIAS/WOPI/resources/js/dist/wopi.min.js b/components/ILIAS/WOPI/resources/js/dist/wopi.min.js index e04487c30b5e..13bf3a350fa1 100755 --- a/components/ILIAS/WOPI/resources/js/dist/wopi.min.js +++ b/components/ILIAS/WOPI/resources/js/dist/wopi.min.js @@ -72,6 +72,12 @@ }); }; + il.WOPI.fillParent = function () { + { + this.windowResize(); + } + }; + il.WOPI.windowResize = function () { const iframeHeight = document.getElementById('mainspacekeeper').clientHeight - this.offset; const iframeWidth = this.editorFrame.parentElement.offsetWidth - 0; @@ -114,7 +120,8 @@ this.editorFrame = editorFrame; // eslint-disable-next-line max-len this.editorFrameWindow = editorFrame.contentWindow || (editorFrame.contentDocument.document || editorFrame.contentDocument); - this.windowResize(); + this.inline = inline; + this.fillParent(); // BUILD FORM const form = document.createElement('form'); @@ -172,13 +179,13 @@ // Add event listener to resize the editor iframe document.defaultView.addEventListener('resize', () => { - il.WOPI.windowResize(editorFrame); + il.WOPI.fillParent(); }); // resize after some time to make sure the editor is loaded and mainmenu has been collapsed setTimeout( () => { - il.WOPI.windowResize(editorFrame); + il.WOPI.fillParent(); }, 200, ); diff --git a/components/ILIAS/WOPI/resources/js/src/index.js b/components/ILIAS/WOPI/resources/js/src/index.js index 75ba86a88034..0425e676b78f 100755 --- a/components/ILIAS/WOPI/resources/js/src/index.js +++ b/components/ILIAS/WOPI/resources/js/src/index.js @@ -73,6 +73,32 @@ il.WOPI.save = function () { }); }; +il.WOPI.fillParent = function () { + const variant = 'default'; + if (variant === 'default') { + this.windowResize(); + } else { + const parent = document.getElementById('mainspacekeeper'); + + // get the width and height of the parent container + const rect = parent.getBoundingClientRect(); + // get the padding of the parent container + const computed = window.getComputedStyle(parent, null); + // calculate the available width inside the parent container + + const parentWidth = rect.width + - parseInt(computed.getPropertyValue('padding-left'), 10) + - parseInt(computed.getPropertyValue('padding-right'), 10); + // calculate the available height inside the parent container + const parentHeight = rect.height + - parseInt(computed.getPropertyValue('padding-top'), 10) + - parseInt(computed.getPropertyValue('padding-bottom'), 10); + + this.editorFrame.setAttribute('width', parentWidth); + this.editorFrame.setAttribute('height', parentHeight); + } +}; + il.WOPI.windowResize = function () { const iframeHeight = document.getElementById('mainspacekeeper').clientHeight - this.offset; const iframeWidth = this.editorFrame.parentElement.offsetWidth - 0; @@ -115,7 +141,8 @@ il.WOPI.init = function () { this.editorFrame = editorFrame; // eslint-disable-next-line max-len this.editorFrameWindow = editorFrame.contentWindow || (editorFrame.contentDocument.document || editorFrame.contentDocument); - this.windowResize(); + this.inline = inline; + this.fillParent(); // BUILD FORM const form = document.createElement('form'); @@ -173,13 +200,13 @@ il.WOPI.init = function () { // Add event listener to resize the editor iframe document.defaultView.addEventListener('resize', () => { - il.WOPI.windowResize(editorFrame); + il.WOPI.fillParent(); }); // resize after some time to make sure the editor is loaded and mainmenu has been collapsed setTimeout( () => { - il.WOPI.windowResize(editorFrame); + il.WOPI.fillParent(); }, 200, );