From 8bc7c8f95dec9a03e6caeb507d066a8feb3effbc Mon Sep 17 00:00:00 2001 From: John Livingston Date: Wed, 11 Dec 2024 12:43:51 +0100 Subject: [PATCH] New Styling plugin: This plugin can be used to customize the appearance. This commit includes some basic features, more could be added later. Current features: * plugin can add a CSS file (this file can depend on the $userSession) * plugin can add CSS classes on the reservation items, that depends on the reservation item (you can for example read some custom attributes to change the appearance) --- Domain/ReservationItemView.php | 22 +++++++++ Pages/Admin/ManageConfigurationPage.php | 9 ++++ Pages/Page.php | 5 +++ Pages/StylingPluginPage.php | 36 +++++++++++++++ .../Admin/ManageConfigurationPresenter.php | 1 + Web/css/styling-plugin.php | 8 ++++ Web/scripts/schedule.js | 8 ++-- config/config.devel.php | 1 + config/config.dist.php | 1 + .../Schedule/ReservationListItem.php | 10 +++++ lib/Application/Schedule/ReservationSlot.php | 5 +++ lib/Application/Styling/StylingFactory.php | 45 +++++++++++++++++++ lib/Application/Styling/namespace.php | 3 ++ lib/Common/PluginManager.php | 21 +++++++++ lib/Config/ConfigKeys.php | 1 + .../Styling/StylingExample/StylingExample.css | 7 +++ .../Styling/StylingExample/StylingExample.php | 30 +++++++++++++ .../ManageConfigurationPresenterTest.php | 8 ++++ .../Configuration/manage_configuration.tpl | 4 ++ tpl/globalheader.tpl | 3 ++ 20 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 Pages/StylingPluginPage.php create mode 100644 Web/css/styling-plugin.php create mode 100644 lib/Application/Styling/StylingFactory.php create mode 100644 lib/Application/Styling/namespace.php create mode 100644 plugins/Styling/StylingExample/StylingExample.css create mode 100644 plugins/Styling/StylingExample/StylingExample.php diff --git a/Domain/ReservationItemView.php b/Domain/ReservationItemView.php index d511ff923..80e91ee9a 100644 --- a/Domain/ReservationItemView.php +++ b/Domain/ReservationItemView.php @@ -68,6 +68,11 @@ public function GetTextColor(); */ public function GetBorderColor(); + /** + * @return string[] + */ + public function GetAdditonalCSSClasses(); + /** * @return string */ @@ -403,6 +408,11 @@ class ReservationItemView implements IReservedItemView private $ownerGroupIds = []; + /** + * @var StylingFactory + */ + private $StylingFactory; + /** * @param $referenceNumber string * @param $startDate Date @@ -522,6 +532,8 @@ public function __construct( $this->Attributes = CustomAttributes::Parse($attribute_list); $this->UserPreferences = UserPreferences::Parse($preferences); + + $this->StylingFactory= PluginManager::Instance()->LoadStyling(); } /** @@ -990,6 +1002,11 @@ public function GetBorderColor() return ''; } + public function GetAdditonalCSSClasses() + { + return $this->StylingFactory->GetReservationAdditonalCSSClasses($this) ?? []; + } + public function GetTitle() { return $this->Title; @@ -1285,6 +1302,11 @@ public function GetBorderColor() return ''; } + public function GetAdditonalCSSClasses() + { + return []; + } + public function GetTitle() { return $this->Title; diff --git a/Pages/Admin/ManageConfigurationPage.php b/Pages/Admin/ManageConfigurationPage.php index 5ffea4282..789ec245f 100644 --- a/Pages/Admin/ManageConfigurationPage.php +++ b/Pages/Admin/ManageConfigurationPage.php @@ -89,6 +89,11 @@ public function SetPreReservationPluginValues($values); */ public function SetPostReservationPluginValues($values); + /** + * @param string[] $values + */ + public function SetStylingPluginValues($values); + /** * @return int */ @@ -253,6 +258,10 @@ public function SetPostReservationPluginValues($values) { $this->Set('PostReservationPluginValues', $values); } + public function SetStylingPluginValues($values) + { + $this->Set('StylingPluginValues', $values); + } public function GetHomePageId() { diff --git a/Pages/Page.php b/Pages/Page.php index 11eb6b0e8..a3437ec34 100644 --- a/Pages/Page.php +++ b/Pages/Page.php @@ -106,6 +106,11 @@ protected function __construct($titleKey = '', $pageDepth = 0) $this->smarty->assign('CssUrl', 'custom-style.css'); } + $stylingFactory = PluginManager::Instance()->LoadStyling(); + if (!empty($stylingFactory->AdditionalCSS($userSession))) { + $this->smarty->assign('CssStylingFile', 'styling-plugin.php'); + } + $this->smarty->assign('FaviconUrl', 'favicon.ico'); if (file_exists($this->path . 'custom-favicon.png')) { $this->smarty->assign('FaviconUrl', 'custom-favicon.png'); diff --git a/Pages/StylingPluginPage.php b/Pages/StylingPluginPage.php new file mode 100644 index 000000000..363135d03 --- /dev/null +++ b/Pages/StylingPluginPage.php @@ -0,0 +1,36 @@ +GetUserSession(); + + header('Content-type: text/css'); + $factory = PluginManager::Instance()->LoadStyling(); + $path = $factory->AdditionalCSS($userSession); + if (empty($path)) { + http_response_code(200); + die(); + } + if (!file_exists($path)) { + http_response_code(404); + die(); + } + http_response_code(200); + readfile($path); + die(); + } +} diff --git a/Presenters/Admin/ManageConfigurationPresenter.php b/Presenters/Admin/ManageConfigurationPresenter.php index e501e7412..6d81f408f 100644 --- a/Presenters/Admin/ManageConfigurationPresenter.php +++ b/Presenters/Admin/ManageConfigurationPresenter.php @@ -157,6 +157,7 @@ private function PopulatePlugins() $this->page->SetPostRegistrationPluginValues($plugins['PostRegistration']); $this->page->SetPreReservationPluginValues($plugins['PreReservation']); $this->page->SetPostReservationPluginValues($plugins['PostReservation']); + $this->page->SetStylingPluginValues($plugins['Styling']); } public function Update() diff --git a/Web/css/styling-plugin.php b/Web/css/styling-plugin.php new file mode 100644 index 000000000..78c460e0b --- /dev/null +++ b/Web/css/styling-plugin.php @@ -0,0 +1,8 @@ +PageLoad(); diff --git a/Web/scripts/schedule.js b/Web/scripts/schedule.js index 0e44bd2e3..bce84ce83 100644 --- a/Web/scripts/schedule.js +++ b/Web/scripts/schedule.js @@ -276,6 +276,7 @@ function Schedule(opts, resourceGroups) { const past = reservation.IsPast ? "past" : ""; const participant = reservation.IsParticipant ? "participating" : ""; const isPending = reservation.IsPending ? "pending" : ""; + const additonalCSSClasses = reservation.AdditonalCSSClasses ? reservation.AdditonalCSSClasses.join(" ") : ""; const isNew = reservation.IsNew ? `${opts.newLabel}` : ""; const isUpdated = reservation.IsUpdated ? `${opts.updatedLabel}` : ""; const isDraggable = reservation.IsReservation && ((reservation.IsOwner && !reservation.IsPast) || reservation.IsAdmin); @@ -284,7 +285,7 @@ function Schedule(opts, resourceGroups) { let color = reservation.BackgroundColor !== "" ? `background-color:${reservation.BackgroundColor};color:${reservation.TextColor};` : ""; const style = `left:${left}px; top:${top}px; width:${width}px; height:${divHeight}px;`; const div = $(`
${opts.newLabel}` : ""; const isUpdated = res.IsUpdated ? `${opts.updatedLabel}` : ""; const isPending = res.IsPending ? "pending" : ""; + const additonalCSSClasses = res.AdditonalCSSClasses ? res.AdditonalCSSClasses.join(" ") : ""; const isDraggable = res.IsReservation && ((res.IsOwner && !res.IsPast) || res.IsAdmin); const draggableAttribute = isDraggable ? 'draggable="true"' : ""; let color = res.BackgroundColor !== "" ? `background-color:${res.BackgroundColor};color:${res.TextColor};` : ""; @@ -483,7 +485,7 @@ function Schedule(opts, resourceGroups) { let startTime = startsBefore ? opts.midnightLabel : res.StartTime; let endTime = endsAfter ? opts.midnightLabel : res.EndTime; const div = $(`
${startTime}-${endTime} @@ -634,7 +636,7 @@ function Schedule(opts, resourceGroups) { } const style = `left:${left}px; top:${top}px; width:${width}px; height:${divHeight}px;`; const div = $(`
item->GetBorderColor(); } + public function GetAdditonalCSSClasses() + { + return $this->item->GetAdditonalCSSClasses() ?? []; + } + /** * @return string */ @@ -209,6 +214,7 @@ public function AsDto($currentUser) $dto->BorderColor = $this->GetBorderColor(); $dto->BackgroundColor = $this->GetColor(); $dto->TextColor = $this->GetTextColor(); + $dto->AdditonalCSSClasses = $this->GetAdditonalCSSClasses(); $dto->IsReservation = $this->IsReservation(); $dto->IsBuffered = false; $dto->IsBuffer = false; @@ -516,4 +522,8 @@ class ReservationListItemDto * @var string|null */ public $BufferedEndTime; + /** + * @var string[] + */ + public $AdditonalCSSClasses; } diff --git a/lib/Application/Schedule/ReservationSlot.php b/lib/Application/Schedule/ReservationSlot.php index cf6d6995f..8ea821ac1 100644 --- a/lib/Application/Schedule/ReservationSlot.php +++ b/lib/Application/Schedule/ReservationSlot.php @@ -234,6 +234,11 @@ public function BorderColor() return $this->_reservation->GetBorderColor(); } + public function GetAdditonalCSSClasses() + { + return $this->_reservation->GetAdditonalCSSClasses() ?? []; + } + /** * @return ReservationItemView */ diff --git a/lib/Application/Styling/StylingFactory.php b/lib/Application/Styling/StylingFactory.php new file mode 100644 index 000000000..337c4ffd5 --- /dev/null +++ b/lib/Application/Styling/StylingFactory.php @@ -0,0 +1,45 @@ +LoadPlugin(ConfigKeys::PLUGIN_STYLING, 'Styling', $factory); + + if (!is_null($plugin)) { + return $plugin; + } + + return $factory; + } + /** * @param string $configKey key to use * @param string $pluginSubDirectory subdirectory name under 'plugins' diff --git a/lib/Config/ConfigKeys.php b/lib/Config/ConfigKeys.php index 8f3c7800d..4683f45bb 100644 --- a/lib/Config/ConfigKeys.php +++ b/lib/Config/ConfigKeys.php @@ -49,6 +49,7 @@ class ConfigKeys public const PLUGIN_POSTREGISTRATION = 'PostRegistration'; public const PLUGIN_PRERESERVATION = 'PreReservation'; public const PLUGIN_POSTRESERVATION = 'PostReservation'; + public const PLUGIN_STYLING = 'Styling'; public const RESERVATION_START_TIME_CONSTRAINT = 'start.time.constraint'; public const RESERVATION_UPDATES_REQUIRE_APPROVAL = 'updates.require.approval'; diff --git a/plugins/Styling/StylingExample/StylingExample.css b/plugins/Styling/StylingExample/StylingExample.css new file mode 100644 index 000000000..fb5bc4137 --- /dev/null +++ b/plugins/Styling/StylingExample/StylingExample.css @@ -0,0 +1,7 @@ +table.reservations .reserved.mine { + background-color: red; +} + +.custom-example-class { + border: 2px dotted orange; +} diff --git a/plugins/Styling/StylingExample/StylingExample.php b/plugins/Styling/StylingExample/StylingExample.php new file mode 100644 index 000000000..915d7a0bd --- /dev/null +++ b/plugins/Styling/StylingExample/StylingExample.php @@ -0,0 +1,30 @@ +factoryToDecorate = $factoryToDecorate; + } + + public function AdditionalCSS(UserSession $userSession) + { + return realpath(__DIR__ . DIRECTORY_SEPARATOR . 'StylingExample.css'); + } + + public function GetReservationAdditonalCSSClasses(IReservedItemView $item) + { + $additionalCSSClasses = $this->factoryToDecorate->GetReservationAdditonalCSSClasses($item) ?? []; + + if (str_starts_with($item->GetTitle(), 'Example')) { + $additionalCSSClasses[] = 'custom-example-class'; + } + + return $additionalCSSClasses; + } +} diff --git a/tests/Presenters/Admin/ManageConfigurationPresenterTest.php b/tests/Presenters/Admin/ManageConfigurationPresenterTest.php index a6a56a255..487c8616a 100644 --- a/tests/Presenters/Admin/ManageConfigurationPresenterTest.php +++ b/tests/Presenters/Admin/ManageConfigurationPresenterTest.php @@ -314,6 +314,14 @@ public function SetPostReservationPluginValues($values) // TODO: Implement SetPostReservationPluginValues() method. } + /** + * @param string[] $values + */ + public function SetStylingPluginValues($values) + { + // TODO: Implement SetStylingPluginValues() method. + } + /** * @return int */ diff --git a/tpl/Admin/Configuration/manage_configuration.tpl b/tpl/Admin/Configuration/manage_configuration.tpl index 7961569ed..439f76771 100644 --- a/tpl/Admin/Configuration/manage_configuration.tpl +++ b/tpl/Admin/Configuration/manage_configuration.tpl @@ -79,6 +79,10 @@ + {elseif $setting->Key == ConfigKeys::PLUGIN_STYLING} + {elseif $setting->Type == ConfigSettingType::String} diff --git a/tpl/globalheader.tpl b/tpl/globalheader.tpl index eba190a43..a4c306265 100644 --- a/tpl/globalheader.tpl +++ b/tpl/globalheader.tpl @@ -84,6 +84,9 @@ {if isset($CssUrl) && $CssUrl neq ''} {cssfile src=$CssUrl} {/if} + {if isset($CssStylingFile) && $CssStylingFile neq ''} + {cssfile src='styling-plugin.php'} + {/if} {if isset($CssExtensionFile) && $CssExtensionFile neq ''} {cssfile src=$CssExtensionFile} {/if}