-
Notifications
You must be signed in to change notification settings - Fork 329
Feature/attribute scope #902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces support for custom attribute scopes for resources, allowing attributes to be filtered by specific resources or resource types, similar to how it's handled for reservations. The PR also includes type annotation improvements throughout the codebase.
Key changes:
- New
GetResourceAttributesmethod in AttributeService to filter attributes based on resource ID and resource type ID - Enhanced template logic to display attributes conditionally based on secondary entity assignments
- Type-safe comparison operators using strict equality (
===) for CustomAttributeCategory comparisons
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tpl/Admin/Resources/manage_resources.tpl | Implements conditional attribute rendering based on resource type and direct resource assignment |
| tpl/Admin/Attributes/manage_attributes.tpl | Updates form fields with better labeling, tooltip help text, and visibility rules configuration |
| lib/Application/Attributes/AttributeService.php | Adds GetResourceAttributes method with filtering logic and stub methods for interface compatibility |
| lib/Application/Schedule/ScheduleResourceFilter.php | Updates type comparisons to use type-safe casting |
| lib/Application/Reservation/Validation/CustomAttributeValidationRule.php | Converts loose equality to strict equality for category comparisons |
| Domain/CustomAttribute.php | Extends WithSecondaryEntities to support RESOURCE category in addition to RESERVATION |
| Domain/Access/AttributeFilter.php | Adds type casting for attribute type comparisons |
| Pages/Admin/ManageResourcesPage.php | Updates attribute type comparison with type casting |
| Pages/Admin/ManageAttributesPage.php | Adds SetCategoryVisibilityRules method to define field visibility per category |
| Presenters/Admin/ManageAttributesPresenter.php | Calls SetCategoryVisibilityRules on page load |
| WebServices/Controllers/AttributeSaveController.php | Updates validation with strict type comparisons and adds DATETIME type support |
| Web/scripts/admin/attributes.js | Implements visibility rules and secondary category management (contains duplicate code) |
| tests/fakes/FakeAttributeService.php | Adds stub implementations for new interface methods |
| tests/Presenters/Admin/ManageAttributesPresenterTest.php | Adds comprehensive test cases for resource and resource type secondary entities |
| lang/*.php | Adds help text translations for AdminOnly, Private, and LimitAttributeScope in 7 languages |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c82fe7c to
18bf701
Compare
18bf701 to
f02596d
Compare
Adds the scoping feature to resources by resource and resource type Allows to collect custom attributes just for specific limited scopes fix: scopefield on edit still visible
Better description of attribute properties and options in admin management page
…pes and CustomAttributeCategory fixes phpstan issues for wrong type comparisson
f02596d to
827091e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| {* If attribute has secondary entities, check them based on category *} | ||
| {if $attribute->HasSecondaryEntities()} | ||
| {if $attribute->SecondaryCategory() == CustomAttributeCategory::RESOURCE_TYPE} |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent comparison operator used in Smarty template. This uses loose equality (==) to compare SecondaryCategory() with CustomAttributeCategory constants. While Smarty templates don't support strict comparisons like PHP, for better type safety and consistency with the PHP code changes in this PR, consider documenting that these comparisons expect integer values or adding explicit type handling if the method can return different types.
| <div class="mb-4"> | ||
| <span class="fs-6 fw-bold">{translate key='CustomAttributes'}</span> | ||
| <a id="customAttributesControl" class="link-primary" href="#" | ||
| data-bs-toggle="collapse" | ||
| data-bs-target="#customAttributes{$id}"> | ||
| <i id="customAttributesIcon{$id}" | ||
| class="bi bi-chevron-down"></i> | ||
| </a> | ||
| <div id="customAttributes{$id}" class="collapse show"> | ||
| <div class="row"> | ||
| {* All attributes - check resource type filtering first, then direct assignment *} | ||
| {foreach from=$AttributeList item=attribute name=attrLoop} | ||
| {assign var=showAttribute value=false} | ||
|
|
||
| {* If attribute has secondary entities, check them based on category *} | ||
| {if $attribute->HasSecondaryEntities()} | ||
| {if $attribute->SecondaryCategory() == CustomAttributeCategory::RESOURCE_TYPE} | ||
| {* Check if resource's type matches any secondary entity (resource type) *} | ||
| {foreach from=$attribute->SecondaryEntityIds() item=secondaryId} | ||
| {if $secondaryId == $resource->GetResourceTypeId()} | ||
| {assign var=showAttribute value=true} | ||
| {break} | ||
| {/if} | ||
| {/foreach} | ||
| {elseif $attribute->SecondaryCategory() == CustomAttributeCategory::RESOURCE} | ||
| {* Check if this specific resource matches any secondary entity (resource) *} | ||
| {foreach from=$attribute->SecondaryEntityIds() item=secondaryId} | ||
| {if $secondaryId == $id} | ||
| {assign var=showAttribute value=true} | ||
| {break} | ||
| {/if} | ||
| {/foreach} | ||
| {/if} | ||
| {/foreach} | ||
| {if $hasResults} | ||
| {* Content at the end of the iteration *} | ||
| </div> | ||
| </div> | ||
| {* Otherwise, show if attribute applies directly to this specific resource *} | ||
| {elseif $attribute->AppliesToEntity($id)} | ||
| {assign var=showAttribute value=true} | ||
| {* Or show if attribute has no specific entity assignments (applies to all) *} | ||
| {elseif $attribute->EntityIds()|count == 0} | ||
| {assign var=showAttribute value=true} | ||
| {/if} | ||
|
|
||
| {if $showAttribute} | ||
| {include file='Admin/InlineAttributeEdit.tpl' id=$id attribute=$attribute value=$resource->GetAttributeValue($attribute->Id())} | ||
| {/if} | ||
| {/foreach} | ||
| </div> | ||
| {/if} | ||
| {/if} | ||
| </div> | ||
| </div> |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "Custom Attributes" section is now always displayed, even when there are no attributes to show. The previous implementation used a hasResults flag to conditionally display the entire section only when attributes existed. This change means users will see an empty "Custom Attributes" collapsible section when no attributes apply to a resource, which could be confusing. Consider adding a check to only display this section when at least one attribute will be shown.
| $(".cancel").click(function () { | ||
| $(this).closest('.dialog').dialog("close"); | ||
| }); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate event handler for .cancel elements. The same click handler is registered twice (lines 105-107 and 129-131), which could cause the dialog close action to be triggered multiple times. Remove one of these duplicate handlers.
| $(".cancel").click(function () { | |
| $(this).closest('.dialog').dialog("close"); | |
| }); |
| } | ||
|
|
||
| if ($value->Type() == CustomAttributeTypes::SINGLE_LINE_TEXTBOX || $value->Type() == CustomAttributeTypes::MULTI_LINE_TEXTBOX) { | ||
| if ((int)$value->Type() == CustomAttributeTypes::SINGLE_LINE_TEXTBOX || (int)$value->Type() == CustomAttributeTypes::MULTI_LINE_TEXTBOX) { |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent comparison operator. This line uses loose equality (==) instead of strict equality (===). For consistency with the rest of the changes in this PR where type casting is used with strict comparison, this should be changed to use === for type safety.
| if ((int)$value->Type() == CustomAttributeTypes::SINGLE_LINE_TEXTBOX || (int)$value->Type() == CustomAttributeTypes::MULTI_LINE_TEXTBOX) { | |
| if ((int)$value->Type() === CustomAttributeTypes::SINGLE_LINE_TEXTBOX || (int)$value->Type() === CustomAttributeTypes::MULTI_LINE_TEXTBOX) { |
| public function GetResourceAttributes(UserSession $userSession, $resourceId = 0, $resourceTypeId = 0) | ||
| { | ||
| $attributes = []; | ||
| $customAttributes = $this->GetByCategory(CustomAttributeCategory::RESOURCE); | ||
|
|
||
| foreach ($customAttributes as $attribute) { | ||
| $secondaryCategory = $attribute->SecondaryCategory(); | ||
| $shouldInclude = false; | ||
|
|
||
| if (empty($secondaryCategory)) { | ||
| // No secondary filtering, but don't include here - these are handled statically | ||
| continue; | ||
| } else { | ||
| if ((int)$secondaryCategory === CustomAttributeCategory::RESOURCE_TYPE && $resourceTypeId > 0) { | ||
| // Check if this attribute applies to the specific resource type | ||
| $shouldInclude = in_array($resourceTypeId, $attribute->SecondaryEntityIds()); | ||
| } elseif ((int)$secondaryCategory === CustomAttributeCategory::RESOURCE && $resourceId > 0) { | ||
| // Check if this attribute applies to the specific resource | ||
| $shouldInclude = in_array($resourceId, $attribute->SecondaryEntityIds()); | ||
|
|
||
| // Also check if user has permission to see this resource | ||
| if ($shouldInclude) { | ||
| $allowedResources = $this->GetAllowedResources($userSession); | ||
| $shouldInclude = array_key_exists($resourceId, $allowedResources); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if ($shouldInclude) { | ||
| // Check admin-only attributes | ||
| $viewableForAdmin = (!$attribute->AdminOnly() || ($attribute->AdminOnly() && $userSession->IsAdmin)); | ||
|
|
||
| // Check private attributes | ||
| $viewableForPrivate = (!$attribute->IsPrivate() || ($attribute->IsPrivate() && $userSession->IsAdmin)); | ||
|
|
||
| if ($viewableForAdmin && $viewableForPrivate) { | ||
| $attributes[] = new LBAttribute($attribute, null); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return $attributes; | ||
| } |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new GetResourceAttributes method in AttributeService is added to the IAttributeService interface but doesn't appear to be called anywhere in the codebase. While it might be intended for future use or external API consumption, consider adding tests that exercise this method to ensure it works correctly. Currently, only the FakeAttributeService stub implementation exists in the test files.
This pull request introduces support for custom attribute scopes for ressources. Meaning based on specific resource or resource type custom attributes are shown similar to how its handled for reservation.
I previously committed this code but some final testing and changes were required
While going through the custom attributes I also added some types where suggested