Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Pages/Ajax/ReservationPopupPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ public function PageLoad()

$this->Set('ReservationId', $this->GetReservationId());

$this->Display('Ajax/respopup.tpl');
$this->Display('Ajax/reservation_popup.tpl');
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Pages/Ajax/ResourceDetailsPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function PageLoad()
{
$this->presenter->PageLoad();

$this->smarty->display('Ajax/resourcedetails.tpl');
$this->smarty->display('Ajax/resource_popup.tpl');
}

public function BindResource(BookableResource $resource)
Expand Down
2 changes: 1 addition & 1 deletion Pages/Ajax/UserDetailsPopupPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function __construct()
public function PageLoad()
{
$this->presenter->PageLoad(ServiceLocator::GetServer()->GetUserSession());
$this->Display('Ajax/user_details.tpl');
$this->Display('Ajax/user_popup.tpl');
}

public function SetCanViewUser($canView)
Expand Down
152 changes: 152 additions & 0 deletions tpl/Ajax/reservation_popup.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
{if $authorized}
{*CHECK IF USER HAS PERMISSIONS TO THE RESOURCES OF THE RESERVATIONS, HIDE DETAILS IF HE DOESN'T HAVE PERMISSIONS TO ALL OF THEM*}
{assign var=isResourcePermitted value=false}
{foreach from=$resources item=checkResourcePermission}
{if in_array($checkResourcePermission->Id(), $CanViewResourceReservations)}
{assign var=isResourcePermitted value=true}
{break}
{/if}
{/foreach}
{*HOWEVER THE USER CAN SEE THE RESERVATION IF HE IS A OWNER, PARTICIPANT OR INVITEE*}
{if $isResourcePermitted == false}
{if $UserId == $OwnerId || $IAmParticipating || $IAmInvited}
{assign var=isResourcePermitted value=true}
{/if}
{/if}

{* Don't show anything if user doesn't have permissions - this prevents tooltip from appearing *}
{if !$isResourcePermitted}
{* Return empty - no tooltip will be shown *}
{else}
<div class="res_popup_details mb-0">

{capture "name"}
<div class="user fw-bold">
{if $hideUserInfo || $hideDetails}
{translate key=Private}
{else}
{$fullName}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This popup HTML is injected into a Bootstrap tooltip with data-bs-html=true (see reservationPopup.js). $fullName is output without HTML escaping, so any </& content would be interpreted as markup inside the tooltip. Please escape $fullName before output.

Suggested change
{$fullName}
{$fullName|escape}

Copilot uses AI. Check for mistakes.
{/if}
</div>
{/capture}
{$formatter->Add('name', $smarty.capture.name)}

{capture "email"}
<div class="email">
{if !$hideUserInfo && !$hideDetails}
{$email}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this template is rendered as HTML inside a Bootstrap tooltip (data-bs-html=true), $email is treated as markup. It's currently output without escaping; please HTML-escape it (or output a deliberately constructed safe link) to prevent injection.

Suggested change
{$email}
{$email|escape:'html'}

Copilot uses AI. Check for mistakes.
{/if}
</div>
{/capture}
{$formatter->Add('email', $smarty.capture.email)}

{capture "phone"}
<div class="phone">
{if !$hideUserInfo && !$hideDetails}
{$phone}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$phone is output unescaped inside HTML tooltip content (data-bs-html=true). Please HTML-escape it (or generate a safe tel: link) to avoid potential markup injection/broken rendering.

Suggested change
{$phone}
{$phone|escape:'html'}

Copilot uses AI. Check for mistakes.
{/if}
</div>
{/capture}
{$formatter->Add('phone', $smarty.capture.phone)}

{capture "dates"}
{assign var="key" value="res_popup"}
{if $startDate->DateEquals($endDate)}
{assign var="key" value="res_popup_time"}
{/if}
<div class="dates">{formatdate date=$startDate key=res_popup} - {formatdate date=$endDate key=$key}</div>
{/capture}
{$formatter->Add('dates', $smarty.capture.dates)}

{capture "title"}
{if !$hideDetails}
<div class="title">{if $title neq ''}{$title|escape:'html'}{else}{translate key=NoTitleLabel}{/if}</div>
{/if}
{/capture}
{$formatter->Add('title', $smarty.capture.title)}

{capture "resources"}
<div class="resources">
{translate key="Resources"} ({$resources|@count}):
{foreach from=$resources item=resource name=resource_loop}
{$resource->Name()}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resource names are inserted into HTML tooltip content via {$resource->Name()} without escaping. Since tooltip rendering uses data-bs-html=true, resource names containing special characters could be interpreted as markup. Please apply HTML escaping to Name() output here.

Suggested change
{$resource->Name()}
{$resource->Name()|escape:'html'}

Copilot uses AI. Check for mistakes.

{if !$smarty.foreach.resource_loop.last}, {/if}
{/foreach}
</div>
{/capture}
{$formatter->Add('resources', $smarty.capture.resources)}

{capture "participants"}
{if !$hideUserInfo && !$hideDetails}
<div class="users">
{translate key="Participants"} ({$participants|@count}):
{foreach from=$participants item=user name=participant_loop}
{if !$user->IsOwner()}
{fullname first=$user->FirstName|unescape:'html' last=$user->LastName|unescape:'html'}
{/if}
{if !$smarty.foreach.participant_loop.last}, {/if}
{/foreach}
</div>
{/if}
{/capture}
{$formatter->Add('participants', $smarty.capture.participants)}

{capture "accessories"}
{if !$hideDetails}
<div class="accessories">
{translate key="Accessories"} ({$accessories|@count}):
{foreach from=$accessories item=accessory name=accessory_loop}
{$accessory->Name} ({$accessory->QuantityReserved})
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessory names are output unescaped inside HTML tooltip content (data-bs-html=true). Please HTML-escape $accessory->Name to avoid markup injection/broken tooltip rendering.

Suggested change
{$accessory->Name} ({$accessory->QuantityReserved})
{$accessory->Name|escape:'html'} ({$accessory->QuantityReserved})

Copilot uses AI. Check for mistakes.
{if !$smarty.foreach.accessory_loop.last}, {/if}
{/foreach}
</div>
{/if}
{/capture}
{$formatter->Add('accessories', $smarty.capture.accessories)}

{capture "description"}
{if !$hideDetails}
<div class="summary">
{if $summary neq ''}{$summary|truncate:300:"..."|escape:'html'|nl2br}{else}{translate key=NoDescriptionLabel}{/if}
</div>
{/if}
{/capture}
{$formatter->Add('description', $smarty.capture.description)}

{capture "attributes"}
{if !$hideDetails}
{if $attributes|default:array()|count > 0}
<br />
{foreach from=$attributes item=attribute}
{assign var=attr value="att`$attribute->Id()`"}
{capture name="attr"}
<div>{control type="AttributeControl" attribute=$attribute readonly=true tooltip=true}</div>
{/capture}
{$smarty.capture.attr}
{$formatter->Add($attr, $smarty.capture.attr)}
{/foreach}
{/if}
{/if}
{/capture}
{$formatter->Add('attributes', $smarty.capture.attributes)}

{capture "pending"}
{if $requiresApproval}
<div class="pendingApproval text-warning">{translate key=PendingApproval}</div>
{/if}
{/capture}
{$formatter->Add('pending', $smarty.capture.pending)}

{capture "duration"}
<div class="duration">{$duration}</div>
{/capture}
{$formatter->Add('duration', $smarty.capture.duration)}
<!-- {$ReservationId} -->

{$formatter->Display()}
</div>
{/if}
{else}
{* User not authenticated - no tooltip *}
{/if}
28 changes: 14 additions & 14 deletions tpl/Ajax/resourcedetails.tpl → tpl/Ajax/resource_popup.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@
{if $Attributes|default:array()|count > 0}
{foreach from=$Attributes item=attribute}
<div>
{control type="AttributeControl" attribute=$attribute readonly=true}
{control type="AttributeControl" attribute=$attribute readonly=true tooltip=true}
</div>
{/foreach}
{/if}
{if $ResourceTypeAttributes && $ResourceTypeAttributes|default:array()|count > 0}
{foreach from=$ResourceTypeAttributes item=attribute}
<div>
{control type="AttributeControl" attribute=$attribute readonly=true}
{control type="AttributeControl" attribute=$attribute readonly=true tooltip=true}
</div>
{/foreach}
{/if}
Expand Down Expand Up @@ -154,21 +154,21 @@
<div class="description {$class}">
<span class="fw-bold mt-2">{translate key=Description}</span>
<div class="descriptionContent px-2">
{if $description neq ''}
{$description|html_entity_decode|url2link|nl2br}
{else}
{translate key=NoDescriptionLabel}
{/if}
{if $description neq ''}
{$description|html_entity_decode|url2link|nl2br}
{else}
{translate key=NoDescriptionLabel}
{/if}
</div>
<span class="fw-bold mt-2">{translate key=Notes}</span>
<span class="fw-bold mt-2">{translate key=Notes}</span>
<div class="noteContent px-2">
{if $notes neq ''}
{$notes|html_entity_decode|url2link|nl2br}
{else}
{translate key=NoNotesLabel}
{/if}
{if $notes neq ''}
{$notes|html_entity_decode|url2link|nl2br}
{else}
{translate key=NoNotesLabel}
{/if}
</div>
</div>
</div>
</div>
</div>
</div>
149 changes: 0 additions & 149 deletions tpl/Ajax/respopup.tpl

This file was deleted.

Loading
Loading