diff --git a/components/ILIAS/Style/System/classes/Overview/SystemStylesTable.php b/components/ILIAS/Style/System/classes/Overview/SystemStylesTable.php new file mode 100644 index 000000000000..b00b713f550c --- /dev/null +++ b/components/ILIAS/Style/System/classes/Overview/SystemStylesTable.php @@ -0,0 +1,498 @@ +lng->loadLanguageModule('ui'); + $users_title = $this->lng->txt('users'); + + $columns = [ + self::COL_STYLE_TITLE => $this->column_factory->text($this->lng->txt('style_name'))->withIsSortable(true), + self::COL_SKIN_TITLE => $this->column_factory->text($this->lng->txt('skin_name'))->withIsSortable(true), + self::COL_SUBSTYLE_OF => $this->column_factory->text($this->lng->txt('sty_substyle_of'))->withIsSortable(false), + self::COL_SCOPE => $this->column_factory->text($this->lng->txt('scope'))->withIsSortable(false), + self::COL_DEFAULT => $this->column_factory->boolean( + $this->lng->txt('default'), + $this->ui_factory->symbol()->icon()->custom('assets/images/standard/icon_ok.svg', $this->lng->txt('yes'), Icon::SMALL), + $this->ui_factory->symbol()->icon()->custom('assets/images/standard/icon_not_ok.svg', $this->lng->txt('no'), Icon::SMALL), + )->withIsSortable(false), + self::COL_STATUS => $this->column_factory->text($this->lng->txt('status'))->withIsSortable(false), + self::COL_USERS => $this->column_factory->text($users_title) + ->withIsSortable(true) + ->withOrderingLabels( + "$users_title, {$this->lng->txt('order_option_generic_ascending')}", + "$users_title, {$this->lng->txt('order_option_generic_descending')}", + ), + self::COL_VERSION => $this->column_factory->text($this->lng->txt('version'))->withIsSortable(false), + ]; + + $url_builder = new URLBuilder(new DataFactory()->uri($this->request->getUri()->__toString())); + + /** @var URLBuilder $url_builder */ + [$url_builder, $action_parameter_token, $row_id_token] = $url_builder->acquireParameters( + [self::TABLE_IDENTIFIER], + 'table_action', + 'row_ids', + ); + + $actions = [ + self::ACTION_SET_DEFAULT => $this->action_factory->single( + $this->lng->txt('default_skin_style'), + $url_builder->withParameter($action_parameter_token, self::ACTION_SET_DEFAULT), + $row_id_token, + ), + self::ACTION_ACTIVATE => $this->action_factory->standard( + $this->lng->txt('sty_activate'), + $url_builder->withParameter($action_parameter_token, self::ACTION_ACTIVATE), + $row_id_token, + ), + self::ACTION_DEACTIVATE => $this->action_factory->standard( + $this->lng->txt('sty_deactivate'), + $url_builder->withParameter($action_parameter_token, self::ACTION_DEACTIVATE), + $row_id_token, + ), + self::ACTION_EXPORT => $this->action_factory->single( + $this->lng->txt('export'), + $url_builder->withParameter($action_parameter_token, self::ACTION_EXPORT), + $row_id_token, + ), + self::ACTION_ASSIGN_STYLE => $this->action_factory->single( + $this->lng->txt('sty_change_user_assignment'), + $url_builder->withParameter($action_parameter_token, self::ACTION_ASSIGN_STYLE), + $row_id_token, + )->withAsync(), + self::ACTION_ASSIGN_CATEGORIES => $this->action_factory->single( + $this->lng->txt('sty_cat_assignments'), + $url_builder->withParameter($action_parameter_token, self::ACTION_ASSIGN_CATEGORIES), + $row_id_token, + ), + ]; + + return $this->ui_factory->table() + ->data( + new SystemStylesTableDataRetrieval($this->lng, $this->ui_factory, $this->renderer), + $this->lng->txt('manage_system_styles'), + $columns, + ) + ->withId(self::TABLE_IDENTIFIER) + ->withActions($is_management_enabled ? $actions : []) + ->withRequest($this->request); + } + + public function performAction(ArrayBasedRequestWrapper $query, bool $actions_enabled): void + { + if (!$query->has(self::QUERY_ACTION_KEY) || !$query->has(self::QUERY_ROW_IDS_KEY)) { + return; + } + + if (!$actions_enabled) { + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('permission_denied'), ilSystemStyleMessage::TYPE_ERROR), + ); + return; + } + + /** @var string $action */ + $action = $query->retrieve(self::QUERY_ACTION_KEY, $this->refinery->to()->string()); + /** @var string[] $row_ids */ + $row_ids = $query->retrieve( + self::QUERY_ROW_IDS_KEY, + $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->string()), + ); + + switch ($action) { + case self::ACTION_SET_DEFAULT: + $this->setDefaultAction($row_ids); + break; + case self::ACTION_ACTIVATE: + $this->activate($row_ids); + break; + case self::ACTION_DEACTIVATE: + $this->deactivate($row_ids); + break; + case self::ACTION_ASSIGN_STYLE: + $this->assignStyle($row_ids); + // no break + case self::ACTION_ASSIGN_CATEGORIES: + $this->assignCategories($row_ids); + break; + case self::ACTION_EXPORT: + $this->export($row_ids); + break; + default: + } + } + + /** + * @param string[] $row_ids + */ + private function setDefaultAction(array $row_ids): void + { + $style = $this->singleParentStyle($row_ids); + if ($style === null) { + $this->backToOverview(); + return; + } + + $skin_id = (string) $style['skin_id']; + $style_id = (string) $style['style_id']; + if (!ilSystemStyleSettings::_lookupActivatedStyle($skin_id, $style_id)) { + $this->message_stack->addMessage( + new ilSystemStyleMessage( + $style['style_name'] . ': ' . $this->lng->txt('inactive'), + ilSystemStyleMessage::TYPE_ERROR, + ), + ); + $this->backToOverview(); + return; + } + + if (ilSystemStyleSettings::getCurrentDefaultSkin() !== $skin_id + || ilSystemStyleSettings::getCurrentDefaultStyle() !== $style_id + ) { + ilSystemStyleSettings::setCurrentDefaultStyle($skin_id, $style_id); + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('msg_obj_modified'), ilSystemStyleMessage::TYPE_SUCCESS), + ); + } + + $this->backToOverview(); + } + + /** + * @param string[] $row_ids + */ + private function activate(array $row_ids): void + { + $changed = false; + foreach ($this->parentStyles($row_ids) as $style) { + $skin_id = (string) $style['skin_id']; + $style_id = (string) $style['style_id']; + if (ilSystemStyleSettings::_lookupActivatedStyle($skin_id, $style_id)) { + continue; + } + + ilSystemStyleSettings::_activateStyle($skin_id, $style_id); + $changed = true; + } + + if ($changed) { + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('msg_obj_modified'), ilSystemStyleMessage::TYPE_SUCCESS), + ); + } + + $this->backToOverview(); + } + + /** + * @param string[] $row_ids + */ + private function deactivate(array $row_ids): void + { + $default_skin = ilSystemStyleSettings::getCurrentDefaultSkin(); + $default_style = ilSystemStyleSettings::getCurrentDefaultStyle(); + $to_deactivate = []; + + foreach ($this->parentStyles($row_ids) as $id => $style) { + $skin_id = (string) $style['skin_id']; + $style_id = (string) $style['style_id']; + if ($skin_id === $default_skin && $style_id === $default_style) { + $this->message_stack->addMessage( + new ilSystemStyleMessage( + $this->lng->txt('cant_deactivate_default_style'), + ilSystemStyleMessage::TYPE_ERROR, + ), + ); + continue; + } + + if (ilObjUser::_getNumberOfUsersForStyle((string) $style['template_id'], $style_id) > 0) { + $this->message_stack->addMessage( + new ilSystemStyleMessage( + $style['style_name'] . ': ' . $this->lng->txt('cant_deactivate_if_users_assigned'), + ilSystemStyleMessage::TYPE_ERROR, + ), + ); + continue; + } + + if (!ilSystemStyleSettings::_lookupActivatedStyle($skin_id, $style_id)) { + continue; + } + + $to_deactivate[$id] = $style; + } + + if ($to_deactivate === []) { + $this->backToOverview(); + return; + } + + $remaining_active = 0; + foreach (ilStyleDefinition::getAllSkinStyles() ?? [] as $id => $style) { + if (($style['substyle_of'] ?? '') !== '' || isset($to_deactivate[$id])) { + continue; + } + if (ilSystemStyleSettings::_lookupActivatedStyle((string) $style['skin_id'], (string) $style['style_id'])) { + $remaining_active++; + } + } + + if ($remaining_active < 1) { + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('at_least_one_style'), ilSystemStyleMessage::TYPE_ERROR), + ); + $this->backToOverview(); + return; + } + + foreach ($to_deactivate as $style) { + ilSystemStyleSettings::_deactivateStyle((string) $style['skin_id'], (string) $style['style_id']); + } + + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('msg_obj_modified'), ilSystemStyleMessage::TYPE_SUCCESS), + ); + $this->backToOverview(); + } + + /** + * @param string[] $row_ids + */ + private function assignStyle(array $row_ids): never + { + $style = $this->assignmentStyle($row_ids); + if ($style === null) { + $this->showAsync( + $this->ui_factory->messageBox()->failure($this->lng->txt('permission_denied')), + ); + } + + $this->ctrl->setParameterByClass(ilSystemStyleOverviewGUI::class, 'old_skin_id', (string) $style['skin_id']); + $this->ctrl->setParameterByClass(ilSystemStyleOverviewGUI::class, 'old_style_id', (string) $style['style_id']); + $this->showAsync( + $this->overview->getAssignmentCreationModal((string) $style['style_name']) + ?? $this->ui_factory->messageBox()->failure($this->lng->txt('permission_denied')), + ); + } + + /** + * @param string[] $row_ids + */ + private function export(array $row_ids): void + { + $style = $this->singleParentStyle($row_ids); + if ($style === null) { + $this->backToOverview(); + return; + } + + $skin_id = (string) $style['skin_id']; + if ($skin_id === 'default' || $skin_id === 'other' || ($style['substyle_of'] ?? '') !== '') { + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('permission_denied'), ilSystemStyleMessage::TYPE_ERROR), + ); + $this->backToOverview(); + return; + } + + try { + $this->skin_factory->skinStyleContainerFromId($skin_id, $this->message_stack)->export(); + } catch (Exception $e) { + $this->message_stack->addMessage( + new ilSystemStyleMessage( + "{$this->lng->txt('zip_export_failed')} {$e->getMessage()}", + ilSystemStyleMessage::TYPE_ERROR, + ), + ); + $this->backToOverview(); + } + } + + /** + * @param string[] $row_ids + */ + private function assignCategories(array $row_ids): void + { + $style = $this->singleStyle($row_ids); + if ($style === null || ($style['substyle_of'] ?? '') === '') { + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('permission_denied'), ilSystemStyleMessage::TYPE_ERROR), + ); + $this->backToOverview(); + return; + } + + $this->ctrl->setParameterByClass(ilSystemStyleConfigGUI::class, 'skin_id', (string) $style['skin_id']); + $this->ctrl->setParameterByClass(ilSystemStyleConfigGUI::class, 'style_id', (string) $style['style_id']); + $this->ctrl->redirectByClass(ilSystemStyleConfigGUI::class, 'assignStyle'); + } + + /** + * @param string[] $row_ids + * @return array> + */ + private function parentStyles(array $row_ids): array + { + return array_filter( + $this->selectedStyles($row_ids), + static fn(array $style): bool => ($style['substyle_of'] ?? '') === '', + ); + } + + /** + * @param string[] $row_ids + * @return array|null + */ + private function singleParentStyle(array $row_ids): ?array + { + $styles = $this->parentStyles($row_ids); + if (count($styles) !== 1) { + $this->message_stack->addMessage( + new ilSystemStyleMessage($this->lng->txt('permission_denied'), ilSystemStyleMessage::TYPE_ERROR), + ); + return null; + } + + return reset($styles); + } + + /** + * @param string[] $row_ids + * @return array|null + */ + private function assignmentStyle(array $row_ids): ?array + { + if (count($row_ids) === 1 && $row_ids[0] === 'other') { + return [ + 'skin_id' => 'other', + 'style_id' => 'other', + 'style_name' => 'other', + 'substyle_of' => '', + ]; + } + + $style = $this->singleStyle($row_ids); + if ($style === null || ($style['substyle_of'] ?? '') !== '') { + return null; + } + + return $style; + } + + /** + * @param string[] $row_ids + * @return array|null + */ + private function singleStyle(array $row_ids): ?array + { + $styles = $this->selectedStyles($row_ids); + if (count($styles) !== 1) { + return null; + } + + return reset($styles); + } + + /** + * @param string[] $row_ids + * @return array> + */ + private function selectedStyles(array $row_ids): array + { + $all_styles = ilStyleDefinition::getAllSkinStyles() ?? []; + if ($row_ids === [] || $row_ids[0] === self::ALL_OBJECTS) { + return $all_styles; + } + + return array_intersect_key($all_styles, array_flip($row_ids)); + } + + private function backToOverview(): void + { + $this->message_stack->sendMessages(); + $this->ctrl->redirectByClass(ilSystemStyleOverviewGUI::class, 'edit'); + } + + private function showAsync(Component $component): never + { + $this->http->saveResponse( + $this->http->response()->withBody( + Streams::ofString($this->renderer->renderAsync($component)), + ), + ); + $this->http->sendResponse(); + $this->http->close(); + } +} diff --git a/components/ILIAS/Style/System/classes/Overview/SystemStylesTableDataRetrieval.php b/components/ILIAS/Style/System/classes/Overview/SystemStylesTableDataRetrieval.php new file mode 100644 index 000000000000..87d1a08cd094 --- /dev/null +++ b/components/ILIAS/Style/System/classes/Overview/SystemStylesTableDataRetrieval.php @@ -0,0 +1,399 @@ +|null */ + private ?array $row_data = null; + + public function __construct( + private readonly Language $lng, + private readonly UIFactory $ui_factory, + private readonly Renderer $renderer, + ) { + } + + public function getRows( + DataRowBuilder $row_builder, + array $visible_column_ids, + Range $range, + Order $order, + mixed $additional_viewcontrol_data, + mixed $filter_data, + mixed $additional_parameters + ): Generator { + $rows = array_slice($this->sortRecords($this->getRowData(), $order), $range->getStart(), $range->getLength(), true); + foreach ($rows as $id => $row) { + yield $this->applyRowActions($row_builder->buildDataRow($id, $row), $id, $row); + } + } + + public function getTotalRowCount( + mixed $additional_viewcontrol_data, + mixed $filter_data, + mixed $additional_parameters + ): ?int { + return count($this->getRowData()); + } + + /** + * @return array + */ + private function getRowData(): array + { + if ($this->row_data === null) { + $this->row_data = $this->buildRowData(); + } + + return $this->row_data; + } + + /** + * @return array + */ + private function buildRowData(): array + { + $rows = []; + + foreach ($this->getStyles() as $id => $style) { + $is_substyle = $style['substyle_of'] !== ''; + $is_other = $id === 'other'; + + $row = [ + SystemStylesTable::COL_STYLE_TITLE => $style['style_name'], + SystemStylesTable::COL_SKIN_TITLE => $style['skin_name'], + SystemStylesTable::COL_SUBSTYLE_OF => $style['substyle_of_name'], + SystemStylesTable::COL_SCOPE => $this->buildScope($style, $is_substyle), + SystemStylesTable::COL_VERSION => $style['version'], + ]; + + if (!$is_substyle && !$is_other) { + $row[SystemStylesTable::COL_DEFAULT] = + ilSystemStyleSettings::getCurrentDefaultSkin() === $style['skin_id'] + && ilSystemStyleSettings::getCurrentDefaultStyle() === $style['style_id']; + } + + if (!$is_other) { + $status_style_id = $is_substyle ? $style['substyle_of'] : $style['style_id']; + $is_active = ilSystemStyleSettings::_lookupActivatedStyle($style['skin_id'], $status_style_id); + $row[SystemStylesTable::COL_STATUS] = $this->lng->txt($is_active ? 'active' : 'inactive'); + } + $row[SystemStylesTable::COL_USERS] = $is_substyle ? '-' : (int) $style['users']; + + $rows[$id] = $row; + } + + return $rows; + } + + /** + * @return array + */ + private function getStyles(): array + { + // output 'other' row for all users, that are not assigned to any existing style + $users_missing_styles = 0; + foreach (ilObjUser::_getAllUserAssignedStyles() as $skin_style_id) { + $style_arr = explode(':', $skin_style_id); + if (!isset($style_arr[1]) || !ilStyleDefinition::styleExists($style_arr[1])) { + $users_missing_styles += ilObjUser::_getNumberOfUsersForStyle($style_arr[0], $style_arr[1] ?? ''); + } + } + + $all_styles = ilStyleDefinition::getAllSkinStyles() ?? []; + if ($users_missing_styles > 0) { + $all_styles['other'] = + [ + 'title' => $this->lng->txt('other'), + 'id' => 'other', + 'template_id' => '', + 'skin_id' => 'other', + 'style_id' => 'other', + 'skin_name' => 'other', + 'style_name' => 'other', + 'users' => $users_missing_styles, + 'version' => '-', + 'substyle_of' => '', + 'substyle_of_name' => '', + ]; + } + + return $all_styles; + } + + /** + * @param array{ + * style_title: string, + * skin_title: string, + * substyle_of: string, + * scope: string, + * version: string, + * default?: bool, + * status?: string, + * users?: int|string + * } $row + */ + private function applyRowActions(DataRow $data_row, string $row_id, array $row): DataRow + { + $style = $this->styleById($row_id); + $is_other = $row_id === 'other'; + $is_substyle = ($style['substyle_of'] ?? '') !== ''; + $is_parent = !$is_substyle && !$is_other; + $is_default = ($row[SystemStylesTable::COL_DEFAULT] ?? false) === true; + $is_active = !$is_other && ilSystemStyleSettings::_lookupActivatedStyle( + $style['skin_id'] ?? '', + $is_substyle ? ($style['substyle_of'] ?? '') : ($style['style_id'] ?? ''), + ); + + if (!$is_parent || $is_default || !$is_active) { + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_SET_DEFAULT); + } + + if (!$is_parent) { + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_ACTIVATE); + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_DEACTIVATE); + } else { + $data_row = $data_row->withDisabledAction( + $is_active ? SystemStylesTable::ACTION_ACTIVATE : SystemStylesTable::ACTION_DEACTIVATE, + ); + + if (($row[SystemStylesTable::COL_USERS] ?? 0) > 0 || $is_default) { + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_DEACTIVATE); + } + } + + if (!$is_parent || ($style['skin_id'] ?? '') === 'default') { + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_EXPORT); + } + + if ($is_substyle || $this->isAssignmentDisabled($row)) { + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_ASSIGN_STYLE); + } + + if (!$is_substyle) { + $data_row = $data_row->withDisabledAction(SystemStylesTable::ACTION_ASSIGN_CATEGORIES); + } + + return $data_row; + } + + /** + * @return array{ + * title: string, + * id: string, + * template_id: string, + * template_name?: string, + * skin_id: string, + * style_id: string, + * skin_name: string, + * style_name: string, + * users: int, + * version: string, + * substyle_of: string, + * substyle_of_name: string + * }|array{} + */ + private function styleById(string $row_id): array + { + if ($row_id === 'other') { + return []; + } + + return (ilStyleDefinition::getAllSkinStyles() ?? [])[$row_id] ?? []; + } + + /** + * @param array{ + * style_title: string, + * skin_title: string, + * substyle_of: string, + * scope: string, + * version: string, + * default?: bool, + * status?: string, + * users?: int|string + * } $row + */ + private function isAssignmentDisabled(array $row): bool + { + $style_name = $row[SystemStylesTable::COL_STYLE_TITLE] ?? ''; + + return array_all( + ilStyleDefinition::getAllSkinStyles() ?? [], + static fn(array $skin_style): bool => ($skin_style['substyle_of'] ?? '') !== '' + || $style_name === ($skin_style['style_name'] ?? ''), + ); + } + + /** + * @param array{ + * title: string, + * id: string, + * template_id: string, + * template_name?: string, + * skin_id: string, + * style_id: string, + * skin_name: string, + * style_name: string, + * users: int, + * version: string, + * substyle_of: string, + * substyle_of_name: string + * } $style + */ + private function buildScope(array $style, bool $is_substyle): string + { + if (!$is_substyle) { + return $this->lng->txt('global'); + } + + $categories = []; + foreach (ilSystemStyleSettings::getSubStyleCategoryAssignments( + $style['skin_id'], + $style['substyle_of'], + $style['style_id'], + ) as $assignment) { + $category_title = ilObject::_lookupTitle(ilObject::_lookupObjId((int) $assignment['ref_id'])); + if ($category_title) { + $categories[] = $category_title; + } + } + + return $this->lng->txt('local') . $this->renderer->render($this->ui_factory->listing()->unordered($categories)); + } + + /** + * @param array $records + * @return array + */ + private function sortRecords(array $records, Order $order): array + { + $sort_by = $order->get(); + if ($sort_by === []) { + return $records; + } + + uasort( + $records, + function (array $left, array $right) use ($sort_by): int { + foreach ($sort_by as $field => $direction) { + $result = $this->compareValues($left[$field] ?? null, $right[$field] ?? null); + if ($result === 0) { + continue; + } + + return $direction === Order::DESC ? -$result : $result; + } + + return 0; + } + ); + + return $records; + } + + private function compareValues(mixed $left, mixed $right): int + { + if ($left === $right || is_object($left) || is_object($right)) { + return 0; + } + + if ($left === null) { + return -1; + } + + if ($right === null) { + return 1; + } + + if (is_numeric($left) && is_numeric($right)) { + return $left <=> $right; + } + + return strnatcasecmp((string) $left, (string) $right); + } +} diff --git a/components/ILIAS/Style/System/classes/Overview/class.ilSystemStyleOverviewGUI.php b/components/ILIAS/Style/System/classes/Overview/class.ilSystemStyleOverviewGUI.php index 5e9473dd8aab..5c12cc36d47e 100755 --- a/components/ILIAS/Style/System/classes/Overview/class.ilSystemStyleOverviewGUI.php +++ b/components/ILIAS/Style/System/classes/Overview/class.ilSystemStyleOverviewGUI.php @@ -18,12 +18,15 @@ declare(strict_types=1); +use ILIAS\HTTP\Services as HttpServices; +use ILIAS\UI\Component\Modal\RoundTrip; use ILIAS\UI\Factory; use ILIAS\HTTP\Wrapper\WrapperFactory; use ILIAS\Refinery\Factory as Refinery; use ILIAS\UI\Renderer; use ILIAS\FileUpload\FileUpload; use ILIAS\Language\Language; +use Psr\Http\Message\ServerRequestInterface; class ilSystemStyleOverviewGUI { @@ -43,9 +46,6 @@ class ilSystemStyleOverviewGUI protected ilHelpGUI $help; protected FileUpload $upload; protected string $ref_id; - protected bool $read_only = true; - protected bool $management_enabled = false; - protected string $style_id; public function __construct( @@ -61,11 +61,13 @@ public function __construct( FileUpload $upload, ilTabsGUI $tabs, ilHelpGUI $help, + private readonly ServerRequestInterface $request, + private readonly HttpServices $http, string $skin_id, string $style_id, string $ref_id, - bool $read_only, - bool $management_enabled + private readonly bool $read_only = true, + private readonly bool $management_enabled = false ) { $this->ctrl = $ctrl; $this->lng = $lng; @@ -84,57 +86,38 @@ public function __construct( $this->ref_id = $ref_id; $this->upload = $upload; $this->config = new ilSystemStyleConfig(); - $this->setReadOnly($read_only); - $this->setManagementEnabled($management_enabled); } public function executeCommand(): void { - $cmd = $this->ctrl->getCmd(); + $cmd = $this->ctrl->getCmd() ?? ''; - if ($cmd == '') { - $cmd = $this->isReadOnly() ? 'view' : 'edit'; + if ($cmd === '') { + $cmd = $this->read_only ? 'view' : 'edit'; } switch ($cmd) { - case 'addSystemStyle': - case 'addSubStyle': - case 'saveNewSystemStyle': - case 'saveNewSubStyle': - case 'copyStyle': - case 'importStyle': - case 'deleteStyles': - case 'deleteStyle': - case 'confirmDelete': - if (!$this->isManagementEnabled()) { - throw new ilObjectException($this->lng->txt('permission_denied')); - } - $this->$cmd(); - break; case 'cancel': case 'edit': - case 'export': + $this->edit(); + break; case 'moveUserStyles': - case 'saveStyleSettings': - if ($this->isReadOnly()) { - throw new ilObjectException($this->lng->txt('permission_denied')); - } - $this->$cmd(); + $this->moveUserStyles(); break; case 'view': - $this->$cmd(); + $this->view(); break; } + $this->message_stack->sendMessages(); } protected function view(): void { - $table = new ilSystemStylesTableGUI($this, 'edit'); - $this->tpl->setContent($table->getHTML() . $table->getModalsHtml()); + $this->showOverview(false); } - public function getAssignmentCreationModal(string $style_name = ""): ?\ILIAS\UI\Component\Modal\RoundTrip + public function getAssignmentCreationModal(string $style_name = ''): ?RoundTrip { $options = []; foreach (ilStyleDefinition::getAllSkinStyles() as $id => $skin_style) { @@ -143,8 +126,8 @@ public function getAssignmentCreationModal(string $style_name = ""): ?\ILIAS\UI\ } } - $default = "default:delos"; - if ($style_name == "Delos") { + $default = 'default:delos'; + if ($style_name == 'Delos') { $default = key($options); } @@ -166,74 +149,58 @@ public function getAssignmentCreationModal(string $style_name = ""): ?\ILIAS\UI\ return $this->ui_factory->modal()->roundtrip( $this->lng->txt('change_assignment'), [], - ["new_style" => $select], + ['new_style' => $select], $this->ctrl->getLinkTargetByClass(ilSystemStyleOverviewGUI::class, 'moveUserStyles') ); } - protected function cancel(): void - { - $this->edit(); - } - public function edit(): void { - $table = new ilSystemStylesTableGUI($this, 'edit'); - $table->addActions($this->isManagementEnabled()); - $this->tpl->setContent($table->getHTML() . $table->getModalsHtml()); + if ($this->read_only) { + throw new ilObjectException($this->lng->txt('permission_denied')); + } + + $this->showOverview($this->management_enabled); } - public function saveStyleSettings(): void + private function showOverview(bool $actions_enabled): void { - $active_styles = $this->request_wrapper->post()->retrieve('st_act', $this->refinery->identity()); - - if ($this->checkStyleSettings($this->message_stack, $active_styles)) { - $all_styles = ilStyleDefinition::getAllSkinStyles(); - foreach ($all_styles as $style) { - if (!isset($active_styles[$style['id']])) { - ilSystemStyleSettings::_deactivateStyle($style['template_id'], $style['style_id']); - } else { - ilSystemStyleSettings::_activateStyle($style['template_id'], $style['style_id']); - } - } + $table = new SystemStylesTable( + $this->ui_factory, + $this->ui_factory->table()->column(), + $this->ui_factory->table()->action(), + $this->lng, + $this->request, + $this->renderer, + $this->refinery, + $this->skin_factory, + $this->ctrl, + $this->message_stack, + $this, + $this->http, + ); + $table->performAction($this->request_wrapper->query(), $actions_enabled); - //set default skin and style - if ($this->request_wrapper->post()->has('default_skin_style')) { - $sknst = $this->request_wrapper->post()->retrieve( - 'default_skin_style', - $this->refinery->string()->splitString(':') - ); - ilSystemStyleSettings::setCurrentDefaultStyle($sknst[0], $sknst[1]); - } - $this->message_stack->addMessage(new ilSystemStyleMessage( - $this->lng->txt('msg_obj_modified'), - ilSystemStyleMessage::TYPE_SUCCESS - )); - } - $this->message_stack->sendMessages(); - $this->ctrl->redirect($this, 'edit'); + $this->tpl->setContent($this->renderer->render($table->build($actions_enabled))); } public function moveUserStyles(): void { - global $DIC; - - $request = $DIC->http()->request(); + if ($this->read_only) { + throw new ilObjectException($this->lng->txt('permission_denied')); + } - $modal = $this->getAssignmentCreationModal()->withRequest($request); - [$new_skin, $new_style] = $modal->getData()["new_style"]; + $modal = $this->getAssignmentCreationModal()->withRequest($this->request); + [$new_skin, $new_style] = $modal->getData()['new_style']; $old_skin = $this->request_wrapper->query()->retrieve('old_skin_id', $this->refinery->kindlyTo()->string()); $old_style = $this->request_wrapper->query()->retrieve('old_style_id', $this->refinery->kindlyTo()->string()); - - if ($old_style == 'other') { // get all user assigned styles $all_user_styles = ilObjUser::_getAllUserAssignedStyles(); - // move users that are not assigned to - // currently existing style + // move users that are not assigned to currently existing style foreach ($all_user_styles as $style) { if (!ilStyleDefinition::styleExists($style)) { [$old_skin, $old_style] = explode(':', $style); @@ -249,87 +216,4 @@ public function moveUserStyles(): void $this->message_stack->sendMessages(); $this->ctrl->redirect($this, 'edit'); } - - protected function checkStyleSettings(ilSystemStyleMessageStack $message_stack, ?array $active_styles): bool - { - $passed = true; - - if (!$active_styles || count($active_styles) < 1) { - $passed = false; - $message_stack->addMessage(new ilSystemStyleMessage( - $this->lng->txt('at_least_one_style'), - ilSystemStyleMessage::TYPE_ERROR - )); - } - - if ($this->request_wrapper->post()->has('default_skin_style')) { - $default_style = $this->request_wrapper->post()->retrieve( - 'default_skin_style', - $this->refinery->kindlyTo()->string() - ); - } else { - $default_style = $this->config->getDefaultStyleId(); - } - - - - if (!isset($active_styles[$default_style])) { - $passed = false; - $message_stack->addMessage(new ilSystemStyleMessage( - $this->lng->txt('cant_deactivate_default_style'), - ilSystemStyleMessage::TYPE_ERROR - )); - } - - // check if a style should be deactivated, that still has - // a user assigned to - $all_styles = ilStyleDefinition::getAllSkinStyles(); - - foreach ($all_styles as $style) { - if (!isset($active_styles[$style['id']])) { - if (ilObjUser::_getNumberOfUsersForStyle($style['template_id'], $style['style_id']) > 0) { - $passed = false; - $message_stack->addMessage(new ilSystemStyleMessage( - $style['style_name'] . ': ' . $this->lng->txt('cant_deactivate_if_users_assigned'), - ilSystemStyleMessage::TYPE_ERROR - )); - } - } - } - return $passed; - } - - - protected function export(): void - { - try { - $this->style_container->export(); - } catch (Exception $e) { - $this->message_stack->addMessage(new ilSystemStyleMessage( - $this->lng->txt('zip_export_failed') . ' ' . $e->getMessage(), - ilSystemStyleMessage::TYPE_ERROR - )); - } - } - - - public function isReadOnly(): bool - { - return $this->read_only; - } - - public function setReadOnly(bool $read_only): void - { - $this->read_only = $read_only; - } - - public function isManagementEnabled(): bool - { - return $this->management_enabled; - } - - public function setManagementEnabled(bool $management_enabled): void - { - $this->management_enabled = $management_enabled; - } } diff --git a/components/ILIAS/Style/System/classes/Overview/class.ilSystemStylesTableGUI.php b/components/ILIAS/Style/System/classes/Overview/class.ilSystemStylesTableGUI.php deleted file mode 100755 index 64e4dd3d37d5..000000000000 --- a/components/ILIAS/Style/System/classes/Overview/class.ilSystemStylesTableGUI.php +++ /dev/null @@ -1,276 +0,0 @@ -ui = $DIC->ui(); - $this->getStyles(); - - $this->setLimit(9999); - $this->setTitle($this->lng->txt('manage_system_styles')); - $this->addColumn($this->lng->txt('')); - $this->addColumn($this->lng->txt('style_name'), 'style_name'); - $this->addColumn($this->lng->txt('skin_name'), 'skin_name'); - $this->addColumn($this->lng->txt('sty_substyle_of')); - $this->addColumn($this->lng->txt('scope')); - $this->addColumn($this->lng->txt('default')); - $this->addColumn($this->lng->txt('active')); - $this->addColumn($this->lng->txt('users'), 'users'); - $this->addColumn($this->lng->txt('version')); - $this->setRowTemplate('tpl.sys_styles_row.html', 'components/ILIAS/Style/System'); - $this->setEnableHeader(true); - } - - /** - * @param $management_enabled - * @param bool|true $read_documentation - */ - public function addActions($management_enabled, bool $read_documentation = true) - { - $this->setManagementEnabled($management_enabled); - $this->setReadDocumentation($read_documentation); - - $this->setFormAction($this->ctrl->getFormAction($this->getParentObject())); - $this->addCommandButton('saveStyleSettings', $this->lng->txt('save')); - $this->setRowTemplate('tpl.sys_styles_row_with_actions.html', 'components/ILIAS/Style/System'); - - if ($read_documentation || $management_enabled) { - $this->setWithActions(true); - - $this->addColumn($this->lng->txt('actions')); - } - } - - /** - * - */ - public function getStyles() - { - // get all user assigned styles - $all_user_styles = ilObjUser::_getAllUserAssignedStyles(); - - // output 'other' row for all users, that are not assigned to - // any existing style - $users_missing_styles = 0; - foreach ($all_user_styles as $skin_style_id) { - $style_arr = explode(':', $skin_style_id); - if (!ilStyleDefinition::styleExists($style_arr[1])) { - $users_missing_styles += ilObjUser::_getNumberOfUsersForStyle($style_arr[0], $style_arr[1]); - } - } - $all_styles = ilStyleDefinition::getAllSkinStyles(); - if ($users_missing_styles > 0) { - $all_styles['other'] = - [ - 'title' => $this->lng->txt('other'), - 'id' => 'other', - 'template_id' => '', - 'skin_id' => 'other', - 'style_id' => 'other', - 'skin_name' => 'other', - 'style_name' => 'other', - 'users' => $users_missing_styles, - 'version' => '-', - 'substyle_of' => '' - ]; - } - - $this->setData($all_styles); - } - - /** - * @param array $a_set - * @noinspection PhpIfWithCommonPartsInspection - */ - protected function fillRow(array $a_set): void - { - $this->tpl->setVariable('STYLE_NAME', $a_set['style_name']); - $this->tpl->setVariable('SKIN_NAME', $a_set['skin_name']); - $is_substyle = isset($a_set['substyle_of']) && $a_set['substyle_of'] != ''; - - if (!$is_substyle) { - $this->tpl->setVariable('USERS', $a_set['users']); - } else { - $this->tpl->setVariable('USERS', '-'); - } - - if ($a_set['id'] != 'other') { - $this->tpl->setCurrentBlock('default_input'); - - if (!$is_substyle) { - $this->tpl->setVariable('DEFAULT_ID', $a_set['id']); - if (ilSystemStyleSettings::getCurrentDefaultSkin() == $a_set['skin_id'] && - ilSystemStyleSettings::getCurrentDefaultStyle() == $a_set['style_id'] - ) { - $this->tpl->setVariable('CHECKED_DEFAULT', " checked='checked' "); - } else { - $this->tpl->setVariable('CHECKED_DEFAULT'); - } - $this->tpl->parseCurrentBlock(); - } - - $this->tpl->setCurrentBlock('active_input'); - $this->tpl->setVariable('ACTIVE_ID', $a_set['id']); - - if ($is_substyle) { - $this->tpl->setVariable('DISABLED_ACTIVE', 'disabled'); - - if (ilSystemStyleSettings::_lookupActivatedStyle($a_set['skin_id'], $a_set['substyle_of'])) { - $this->tpl->setVariable('CHECKED_ACTIVE', " checked='checked' "); - } else { - $this->tpl->setVariable('CHECKED_ACTIVE'); - } - } elseif (ilSystemStyleSettings::_lookupActivatedStyle($a_set['skin_id'], $a_set['style_id'])) { - $this->tpl->setVariable('CHECKED_ACTIVE', " checked='checked' "); - } else { - $this->tpl->setVariable('CHECKED_ACTIVE'); - } - - $this->tpl->parseCurrentBlock(); - } - - if ($is_substyle) { - $this->tpl->setVariable('SUB_STYLE_OF', $a_set['substyle_of_name']); - - $assignments = ilSystemStyleSettings::getSubStyleCategoryAssignments( - $a_set['skin_id'], - $a_set['substyle_of'], - $a_set['style_id'] - ); - - $categories = []; - - foreach ($assignments as $assignment) { - $category_title = ilObject::_lookupTitle(ilObject::_lookupObjId((int) $assignment['ref_id'])); - if ($category_title) { - $categories[] = $category_title; - } - } - - $listing = $this->ui->factory()->listing()->unordered($categories); - $this->tpl->setVariable( - 'CATEGORIES', - $this->lng->txt('local') . $this->ui->renderer()->render($listing) - ); - } else { - $this->tpl->setVariable('SUB_STYLE_OF'); - $this->tpl->setVariable('CATEGORIES', $this->lng->txt('global')); - } - - $this->tpl->setVariable('VERSION', $a_set['version']); - - if ($this->isWithActions()) { - $action_items = []; - - /** @noinspection PhpIfWithCommonPartsInspection */ - - if ($this->isManagementEnabled()) { - $this->ctrl->setParameterByClass(ilSystemStyleConfigGUI::class, 'skin_id', $a_set['skin_id']); - $this->ctrl->setParameterByClass(ilSystemStyleConfigGUI::class, 'style_id', $a_set['style_id']); - - $this->ctrl->setParameterByClass(ilSystemStyleOverviewGUI::class, 'skin_id', $a_set['skin_id']); - $this->ctrl->setParameterByClass(ilSystemStyleOverviewGUI::class, 'style_id', $a_set['style_id']); - - if ($is_substyle) { - $action_items[] = $this->ui->factory()->link()->standard( - $this->lng->txt('sty_cat_assignments'), - $this->ctrl->getLinkTargetByClass('ilsystemstyleconfiggui', 'assignStyle') - ); - } else { - if ($a_set['skin_id'] != 'default' && $a_set['skin_id'] != 'other') { - $action_items[] = $this->ui->factory()->link()->standard( - $this->lng->txt('export'), - $this->ctrl->getLinkTargetByClass(ilSystemStyleOverviewGUI::class, 'export') - ); - } - - $this->ctrl->setParameterByClass(ilSystemStyleOverviewGUI::class, 'old_skin_id', $a_set['skin_id']); - $this->ctrl->setParameterByClass(ilSystemStyleOverviewGUI::class, 'old_style_id', $a_set['style_id']); - - $assignment_modal = $this->parent_obj->getAssignmentCreationModal($a_set['style_name']); - - if ($assignment_modal) { - $this->modals[] = $assignment_modal; - - $action_items[] = $this->ui->factory()->button()->shy( - $this->lng->txt('sty_change_user_assignment'), - "#" - )->withOnClick($assignment_modal->getShowSignal()); - } - } - } - - $this->tpl->setCurrentBlock('actions'); - $action_dropdown = $this->ui->factory()->dropdown()->standard($action_items)->withLabel( - $this->lng->txt('actions') - ); - $this->tpl->setVariable('ACTIONS', $this->ui->renderer()->render($action_dropdown)); - $this->tpl->parseCurrentBlock(); - } - } - - public function getModalsHtml(): string - { - return $this->ui->renderer()->render($this->modals); - } - - public function isWithActions(): bool - { - return $this->with_actions; - } - - public function setWithActions(bool $with_actions): void - { - $this->with_actions = $with_actions; - } - - public function isManagementEnabled(): bool - { - return $this->management_enabled; - } - - public function setManagementEnabled(bool $management_enabled) - { - $this->management_enabled = $management_enabled; - } - - public function isReadDocumentation(): bool - { - return $this->read_documentation; - } - - public function setReadDocumentation(bool $read_documentation) - { - $this->read_documentation = $read_documentation; - } -} diff --git a/components/ILIAS/Style/System/classes/class.ilSystemStyleMainGUI.php b/components/ILIAS/Style/System/classes/class.ilSystemStyleMainGUI.php index 1b82c4877f5d..dd16addc6a47 100755 --- a/components/ILIAS/Style/System/classes/class.ilSystemStyleMainGUI.php +++ b/components/ILIAS/Style/System/classes/class.ilSystemStyleMainGUI.php @@ -18,6 +18,7 @@ declare(strict_types=1); +use ILIAS\HTTP\Services as HttpServices; use ILIAS\UI\Implementation\Crawler\Entry\ComponentEntries as Entries; use ILIAS\UI\Factory; use ILIAS\UI\Renderer; @@ -54,6 +55,7 @@ class ilSystemStyleMainGUI protected FileUpload $upload; protected ilTree $tree; protected ilObjUser $user; + protected HttpServices $http; protected ilSystemStyleMessageStack $message_stack; public function __construct() @@ -82,6 +84,7 @@ public function __construct() $this->tree = $DIC->repositoryTree(); $this->skin_factory = new ilSkinFactory($this->lng); $this->user = $DIC->user(); + $this->http = $DIC->http(); $this->message_stack = new ilSystemStyleMessageStack($this->tpl); $this->ref_id = $this->request_wrapper->query()->retrieve('ref_id', $this->refinery->kindlyTo()->string()); @@ -204,6 +207,8 @@ protected function executeDefaultCommand(ilSkinFactory $skin_factory, string $sk $this->upload, $this->tabs, $this->help, + $this->request, + $this->http, $skin_id, $style_id, $this->ref_id, diff --git a/components/ILIAS/Style/System/templates/default/tpl.sys_styles_row.html b/components/ILIAS/Style/System/templates/default/tpl.sys_styles_row.html deleted file mode 100755 index d0fb28264946..000000000000 --- a/components/ILIAS/Style/System/templates/default/tpl.sys_styles_row.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - {STYLE_NAME} - - - {SKIN_NAME} - - - {SUB_STYLE_OF} - - - {CATEGORIES} - - - - - - - - - {USERS} - - - {VERSION} - - diff --git a/components/ILIAS/Style/System/templates/default/tpl.sys_styles_row_with_actions.html b/components/ILIAS/Style/System/templates/default/tpl.sys_styles_row_with_actions.html deleted file mode 100755 index 5a27fa277699..000000000000 --- a/components/ILIAS/Style/System/templates/default/tpl.sys_styles_row_with_actions.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - {STYLE_NAME} - - - {SKIN_NAME} - - - {SUB_STYLE_OF} - - - {CATEGORIES} - - - - - - - - - {USERS} - - - {VERSION} - - - - {ACTIONS} - - - diff --git a/components/ILIAS/Style/System/test/Overview/ilSystemStyleOverviewGUITest.php b/components/ILIAS/Style/System/test/Overview/ilSystemStyleOverviewGUITest.php index a7415c2b2d3a..46201e8e6304 100755 --- a/components/ILIAS/Style/System/test/Overview/ilSystemStyleOverviewGUITest.php +++ b/components/ILIAS/Style/System/test/Overview/ilSystemStyleOverviewGUITest.php @@ -21,14 +21,18 @@ require_once('vendor/composer/vendor/autoload.php'); include_once('./components/ILIAS/UI/tests/UITestHelper.php'); +use ILIAS\HTTP\Services as HttpServices; use ILIAS\HTTP\Wrapper\WrapperFactory; use ILIAS\Data\Factory as DataFactory; use ILIAS\Refinery\Factory as Refinery; use ILIAS\FileUpload\FileUpload; use ILIAS\Tests\Refinery\ilLanguageMock; +use Psr\Http\Message\ServerRequestInterface; class ilSystemStyleOverviewGUITest extends ilSystemStyleBaseFS { + use UITestHelper; + protected ilSystemStyleOverviewGUI $overview_gui; protected ilCtrl $ctrl; @@ -36,7 +40,6 @@ class ilSystemStyleOverviewGUITest extends ilSystemStyleBaseFS protected function setUp(): void { parent::setUp(); - $ui_helper = new UITestHelper(); $this->ctrl = $this->getMockBuilder(ilCtrl::class)->disableOriginalConstructor()->onlyMethods([ 'getFormAction', @@ -44,21 +47,19 @@ protected function setUp(): void ])->getMock(); $lng = new ilLanguageMock(); - $tpl = $ui_helper->mainTemplate(); - $ui_factory = $ui_helper->factory(); - $renderer = $ui_helper->renderer(); - $request = $this->getMockBuilder(WrapperFactory::class)->disableOriginalConstructor()->onlyMethods([ - ])->getMock(); + $tpl = $this->mainTemplate(); + $ui_factory = $this->factory(); + $renderer = $this->renderer(); + $request_wrapper = $this->getMockBuilder(WrapperFactory::class)->disableOriginalConstructor()->getMock(); - $toolbar = $this->getMockBuilder(ilToolbarGUI::class)->disableOriginalConstructor()->onlyMethods([])->getMock(); - $tabs = $this->getMockBuilder(ilTabsGUI::class)->disableOriginalConstructor()->onlyMethods([])->getMock(); + $toolbar = $this->getMockBuilder(ilToolbarGUI::class)->disableOriginalConstructor()->getMock(); + $tabs = $this->getMockBuilder(ilTabsGUI::class)->disableOriginalConstructor()->getMock(); $data_factory = new DataFactory(); $refinery = new Refinery($data_factory, $lng); $factory = new ilSkinFactory($this->lng, $this->system_style_config); - $help = $this->getMockBuilder(ilHelpGUI::class)->disableOriginalConstructor()->onlyMethods([ - ])->getMock(); - $upload = $this->getMockBuilder(FileUpload::class)->disableOriginalConstructor()->onlyMethods([])->getMock(); + $help = $this->getMockBuilder(ilHelpGUI::class)->disableOriginalConstructor()->getMock(); + $upload = $this->getMockBuilder(FileUpload::class)->disableOriginalConstructor()->getMock(); $this->overview_gui = new ilSystemStyleOverviewGUI( $this->ctrl, @@ -66,13 +67,15 @@ protected function setUp(): void $tpl, $ui_factory, $renderer, - $request, + $request_wrapper, $toolbar, $refinery, $factory, $upload, $tabs, $help, + $this->getMockBuilder(ServerRequestInterface::class)->getMock(), + $this->getMockBuilder(HttpServices::class)->disableOriginalConstructor()->getMock(), $this->container->getSkin()->getId(), $this->style->getId(), '1', diff --git a/components/ILIAS/Style/lang/style.pot b/components/ILIAS/Style/lang/style.pot index fab3ec1bf6a2..a6c11b6ca1a3 100644 --- a/components/ILIAS/Style/lang/style.pot +++ b/components/ILIAS/Style/lang/style.pot @@ -308,6 +308,9 @@ msgstr "" msgid "sty_acopy" msgstr "" +msgid "sty_activate" +msgstr "" + msgid "sty_add_assignment" msgstr "" @@ -518,6 +521,9 @@ msgstr "" msgid "sty_custom_par_info" msgstr "" +msgid "sty_deactivate" +msgstr "" + msgid "sty_default" msgstr "" diff --git a/components/ILIAS/Style/lang/style_de.po b/components/ILIAS/Style/lang/style_de.po index bc75adbb2c8f..da3604aee93f 100644 --- a/components/ILIAS/Style/lang/style_de.po +++ b/components/ILIAS/Style/lang/style_de.po @@ -311,6 +311,9 @@ msgstr "Ordner für das Speichern von Tönen vom betreffenden Skin. Dieser Ordne msgid "sty_acopy" msgstr "Kopie" +msgid "sty_activate" +msgstr "Aktivieren" + msgid "sty_add_assignment" msgstr "Zuordnung hinzufügen" @@ -521,6 +524,9 @@ msgstr "Zusatzparameter" msgid "sty_custom_par_info" msgstr "Definieren Sie Zusatzparameter im Format \"Parameter: Wert\"." +msgid "sty_deactivate" +msgstr "Deaktivieren" + msgid "sty_default" msgstr "Standard" diff --git a/components/ILIAS/Style/lang/style_en.po b/components/ILIAS/Style/lang/style_en.po index ea495d33ed37..0f50447d1b68 100644 --- a/components/ILIAS/Style/lang/style_en.po +++ b/components/ILIAS/Style/lang/style_en.po @@ -311,6 +311,9 @@ msgstr "Folder to store sounds of the skin into. This directory can be shared wi msgid "sty_acopy" msgstr "Copy" +msgid "sty_activate" +msgstr "Activate" + msgid "sty_add_assignment" msgstr "Add Assignment" @@ -521,6 +524,9 @@ msgstr "Custom Parameters" msgid "sty_custom_par_info" msgstr "Add custom CSS parameters using format ‘parameter: value’." +msgid "sty_deactivate" +msgstr "Deactivate" + msgid "sty_default" msgstr "Default"