Skip to content

Commit

Permalink
New Styling plugin:
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
JohnXLivingston committed Dec 12, 2024
1 parent 9ed39a2 commit 8bc7c8f
Show file tree
Hide file tree
Showing 20 changed files with 225 additions and 3 deletions.
22 changes: 22 additions & 0 deletions Domain/ReservationItemView.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public function GetTextColor();
*/
public function GetBorderColor();

/**
* @return string[]
*/
public function GetAdditonalCSSClasses();

/**
* @return string
*/
Expand Down Expand Up @@ -403,6 +408,11 @@ class ReservationItemView implements IReservedItemView

private $ownerGroupIds = [];

/**
* @var StylingFactory
*/
private $StylingFactory;

/**
* @param $referenceNumber string
* @param $startDate Date
Expand Down Expand Up @@ -522,6 +532,8 @@ public function __construct(

$this->Attributes = CustomAttributes::Parse($attribute_list);
$this->UserPreferences = UserPreferences::Parse($preferences);

$this->StylingFactory= PluginManager::Instance()->LoadStyling();
}

/**
Expand Down Expand Up @@ -990,6 +1002,11 @@ public function GetBorderColor()
return '';
}

public function GetAdditonalCSSClasses()
{
return $this->StylingFactory->GetReservationAdditonalCSSClasses($this) ?? [];
}

public function GetTitle()
{
return $this->Title;
Expand Down Expand Up @@ -1285,6 +1302,11 @@ public function GetBorderColor()
return '';
}

public function GetAdditonalCSSClasses()
{
return [];
}

public function GetTitle()
{
return $this->Title;
Expand Down
9 changes: 9 additions & 0 deletions Pages/Admin/ManageConfigurationPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ public function SetPreReservationPluginValues($values);
*/
public function SetPostReservationPluginValues($values);

/**
* @param string[] $values
*/
public function SetStylingPluginValues($values);

/**
* @return int
*/
Expand Down Expand Up @@ -253,6 +258,10 @@ public function SetPostReservationPluginValues($values)
{
$this->Set('PostReservationPluginValues', $values);
}
public function SetStylingPluginValues($values)
{
$this->Set('StylingPluginValues', $values);
}

public function GetHomePageId()
{
Expand Down
5 changes: 5 additions & 0 deletions Pages/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
36 changes: 36 additions & 0 deletions Pages/StylingPluginPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

// debugging tools / libs
if (file_exists(ROOT_DIR . 'vendor/autoload.php')) {
require ROOT_DIR . 'vendor/autoload.php';
}

require_once(ROOT_DIR . 'lib/Common/namespace.php');

interface IStylingPluginPage
{
public function PageLoad();
}

class StylingPluginPage implements IStylingPluginPage
{
public function PageLoad()
{
$userSession = ServiceLocator::GetServer()->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();
}
}
1 change: 1 addition & 0 deletions Presenters/Admin/ManageConfigurationPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
8 changes: 8 additions & 0 deletions Web/css/styling-plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

define('ROOT_DIR', '../../');

require_once(ROOT_DIR . 'Pages/StylingPluginPage.php');

$page = new StylingPluginPage();
$page->PageLoad();
8 changes: 5 additions & 3 deletions Web/scripts/schedule.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? `<span class="reservation-new">${opts.newLabel}</span>` : "";
const isUpdated = reservation.IsUpdated ? `<span class="reservation-updated">${opts.updatedLabel}</span>` : "";
const isDraggable = reservation.IsReservation && ((reservation.IsOwner && !reservation.IsPast) || reservation.IsAdmin);
Expand All @@ -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 = $(`<div
class="${className} ${mine} ${past} ${participant} ${isPending} event"
class="${className} ${mine} ${past} ${participant} ${isPending} ${additonalCSSClasses} event"
style="${style} ${color}"
data-resid="${reservation.ReferenceNumber}"
data-resourceid="${reservation.ResourceId}"
Expand Down Expand Up @@ -465,6 +466,7 @@ function Schedule(opts, resourceGroups) {
const isNew = res.IsNew ? `<span class="reservation-new">${opts.newLabel}</span>` : "";
const isUpdated = res.IsUpdated ? `<span class="reservation-updated">${opts.updatedLabel}</span>` : "";
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};` : "";
Expand All @@ -483,7 +485,7 @@ function Schedule(opts, resourceGroups) {
let startTime = startsBefore ? opts.midnightLabel : res.StartTime;
let endTime = endsAfter ? opts.midnightLabel : res.EndTime;
const div = $(`<div
class="${className} ${mine} ${past} ${participant} ${isPending} condensed-event"
class="${className} ${mine} ${past} ${participant} ${isPending} ${additonalCSSClasses} condensed-event"
style="${color}"
data-resid="${res.ReferenceNumber}">
<span>${startTime}-${endTime}</span>
Expand Down Expand Up @@ -634,7 +636,7 @@ function Schedule(opts, resourceGroups) {
}
const style = `left:${left}px; top:${top}px; width:${width}px; height:${divHeight}px;`;
const div = $(`<div
class="${className} ${mine} ${past} ${participant} ${isPending} event"
class="${className} ${mine} ${past} ${participant} ${isPending} ${additonalCSSClasses} event"
style="${style} ${color}"
data-resid="${res.ReferenceNumber}"
data-resourceid="${res.ResourceId}"
Expand Down
1 change: 1 addition & 0 deletions config/config.devel.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
$conf['settings']['plugins']['PostRegistration'] = '';
$conf['settings']['plugins']['PreReservation'] = '';
$conf['settings']['plugins']['PostReservation'] = '';
$conf['settings']['plugins']['Styling'] = '';
/**
* Installation settings
*/
Expand Down
1 change: 1 addition & 0 deletions config/config.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
$conf['settings']['plugins']['PostRegistration'] = '';
$conf['settings']['plugins']['PreReservation'] = '';
$conf['settings']['plugins']['PostReservation'] = '';
$conf['settings']['plugins']['Styling'] = '';
/**
* Installation settings
*/
Expand Down
10 changes: 10 additions & 0 deletions lib/Application/Schedule/ReservationListItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ public function GetBorderColor()
return $this->item->GetBorderColor();
}

public function GetAdditonalCSSClasses()
{
return $this->item->GetAdditonalCSSClasses() ?? [];
}

/**
* @return string
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -516,4 +522,8 @@ class ReservationListItemDto
* @var string|null
*/
public $BufferedEndTime;
/**
* @var string[]
*/
public $AdditonalCSSClasses;
}
5 changes: 5 additions & 0 deletions lib/Application/Schedule/ReservationSlot.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ public function BorderColor()
return $this->_reservation->GetBorderColor();
}

public function GetAdditonalCSSClasses()
{
return $this->_reservation->GetAdditonalCSSClasses() ?? [];
}

/**
* @return ReservationItemView
*/
Expand Down
45 changes: 45 additions & 0 deletions lib/Application/Styling/StylingFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

interface IStylingFactory
{
/**
* Returns a file path (on the server) to an additional CSS file to use.
* @param UserSession $userSession
* @return null|string
*/
public function AdditionalCSS(UserSession $userSession);

/**
* You can add some CSS classes to reservations items.
* Those classes can for example depends on some attributes.
* @param IReservedItemView $item
* @return string[]
*/
public function GetReservationAdditonalCSSClasses(IReservedItemView $item);
}

class StylingFactory implements IStylingFactory
{
public function __construct() {}

/**
* Returns a file path (on the server) to an additional CSS file to use.
* @param UserSession $userSession
* @return null|string
*/
public function AdditionalCSS(UserSession $userSession)
{
return null;
}

/**
* You can add some CSS classes to reservations items.
* Those classes can for example depends on some attributes.
* @param IReservedItemView $item
* @return string[]
*/
public function GetReservationAdditonalCSSClasses(IReservedItemView $item)
{
return [];
}
}
3 changes: 3 additions & 0 deletions lib/Application/Styling/namespace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

require_once(ROOT_DIR . 'lib/Application/Styling/StylingFactory.php');
21 changes: 21 additions & 0 deletions lib/Common/PluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,27 @@ public function LoadPostRegistration()
return $postRegistration;
}

/**
* Loads the configured Styling plugin, if one exists
* If no plugin exists, the default PreReservationFactory class is returned
*
* @return IPreReservationFactory
*/
public function LoadStyling()
{
require_once(ROOT_DIR . 'lib/Application/Styling/namespace.php');

$factory = new StylingFactory();

$plugin = $this->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'
Expand Down
1 change: 1 addition & 0 deletions lib/Config/ConfigKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
7 changes: 7 additions & 0 deletions plugins/Styling/StylingExample/StylingExample.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
table.reservations .reserved.mine {
background-color: red;
}

.custom-example-class {
border: 2px dotted orange;
}
30 changes: 30 additions & 0 deletions plugins/Styling/StylingExample/StylingExample.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

class StylingExample implements IStylingFactory
{
/**
* @var StylingFactory
*/
private $factoryToDecorate;

public function __construct(StylingFactory $factoryToDecorate)
{
$this->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;
}
}
8 changes: 8 additions & 0 deletions tests/Presenters/Admin/ManageConfigurationPresenterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
4 changes: 4 additions & 0 deletions tpl/Admin/Configuration/manage_configuration.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
<select id="{$name}" name="{$name}" class="form-select">
{html_options values=$PostReservationPluginValues output=$PostReservationPluginValues selected=$setting->Value}
</select>
{elseif $setting->Key == ConfigKeys::PLUGIN_STYLING}
<select id="{$name}" name="{$name}" class="form-select">
{html_options values=$StylingPluginValues output=$StylingPluginValues selected=$setting->Value}
</select>
{elseif $setting->Type == ConfigSettingType::String}
<input id="{$name}" type="text" size="50" name="{$name}" value="{$setting->Value|escape}"
class="form-control" />
Expand Down
Loading

0 comments on commit 8bc7c8f

Please sign in to comment.