Skip to the content.

← Back to Awards Plugin

5.2.13 Recommendation Policy Reference

Last Updated: December 4, 2025
Status: Complete
Plugin: Awards
Source: plugins/Awards/src/Policy/RecommendationPolicy.php

Overview

The RecommendationPolicy class provides comprehensive authorization control for Recommendation entities within the Awards plugin. It implements sophisticated state machine management, workflow authorization, and dynamic approval level validation through integration with the KMP RBAC system.

Class Definition

namespace Awards\Policy;

class RecommendationPolicy extends BasePolicy

State Machine Authorization Architecture

The RecommendationPolicy implements complex workflow authorization:

Dynamic Approval Authority

The policy implements dynamic approval authority through level-specific permissions:

Feature Description
Dynamic Methods canApproveLevel* methods generated based on award levels
Level Discovery Award levels discovered from LevelsTable
Authority Validation Approval authority validated against specific award levels
Magic Method Implementation __call() handles dynamic method resolution

Authorization Methods

Standard Methods (Inherited from BasePolicy)

Method Purpose
canView() Recommendation viewing with organizational access
canEdit() Recommendation editing authorization
canDelete() Recommendation removal authorization
canIndex() Recommendation listing with scoping

Custom Authorization Methods

canAdd()

Open authorization for recommendation submission.

public function canAdd(KmpIdentityInterface $user, BaseEntity|Table $entity, ...$optionalArgs): bool

Returns: Always true - recommendation submission is open to all authenticated users.

Purpose: Enables community participation in the award recommendation process.


canViewSubmittedByMember()

Authorizes access to recommendations submitted by a specific member.

public function canViewSubmittedByMember(KmpIdentityInterface $user, BaseEntity $entity, ...$args): bool

Authorization Logic:

  1. Direct access if user is the recommendation requester (requester_id matches)
  2. Fallback to permission-based authorization for administrative access

canViewSubmittedForMember()

Authorizes access to recommendations where a member is the subject.

public function canViewSubmittedForMember(KmpIdentityInterface $user, BaseEntity $entity, ...$args): bool

Purpose: Supports administrative oversight and approval workflows for recommendations about specific members.


canViewEventRecommendations()

Authorizes access to event-specific recommendations.

public function canViewEventRecommendations(KmpIdentityInterface $user, BaseEntity $entity, ...$args): bool

Purpose: Supports ceremony coordination and event-based recommendation processing.


canViewGatheringRecommendations()

Authorizes access to gathering-specific recommendations.

public function canViewGatheringRecommendations(KmpIdentityInterface $user, BaseEntity $entity, ...$args): bool

Purpose: Supports gathering management and event coordination.


canExport()

Authorizes recommendation data export operations.

public function canExport(KmpIdentityInterface $user, BaseEntity $entity, ...$args): bool

Purpose: Supports administrative reporting and data analysis.


canUseBoard()

Authorizes access to the kanban-style recommendation board.

public function canUseBoard(KmpIdentityInterface $user, BaseEntity $entity, ...$args): bool

Purpose: Enables workflow visualization and state management through the board interface.


canViewHidden()

Authorizes access to hidden or archived recommendations.

public function canViewHidden(KmpIdentityInterface $user, BaseEntity $entity, ...$optionalArgs): bool

Purpose: Supports administrative oversight and data management.


canViewPrivateNotes()

Authorizes access to private administrative notes on recommendations.

public function canViewPrivateNotes(KmpIdentityInterface $user, BaseEntity $entity, ...$optionalArgs): bool

Purpose: Supports administrative communication and workflow coordination.


canAddNote()

Authorizes the addition of notes to recommendations.

public function canAddNote(KmpIdentityInterface $user, BaseEntity $entity, ...$optionalArgs): bool

Purpose: Supports workflow documentation and administrative communication.


canUpdateStates()

Authorizes bulk state transition operations.

public function canUpdateStates(KmpIdentityInterface $user, BaseEntity $entity, ...$optionalArgs): bool

Purpose: Supports efficient batch processing and workflow management.

Dynamic Method System

__call() Magic Method

Handles dynamic approval authority methods based on award level names.

public function __call($name, $arguments)

Method Pattern: canApproveLevel{LevelName} (e.g., canApproveLevelAoA, canApproveLevelGoA)

Example:

// Dynamic level approval checking
$recommendation = $this->Recommendations->get($id, ['contain' => ['Awards.Levels']]);
$levelName = $recommendation->award->level->name; // e.g., "AoA"

if ($this->Authorization->can($recommendation, 'canApproveLevel' . $levelName)) {
    $this->processApproval($recommendation);
}

getDynamicMethods()

Returns names of dynamically generated methods based on award levels.

public static function getDynamicMethods(): array

Returns: Array of method names like ['canApproveLevelAoA', 'canApproveLevelGoA', ...]

Purpose:

Authorization Flow

sequenceDiagram
    participant Controller
    participant Authorization
    participant RecommendationPolicy
    participant BasePolicy
    participant PermissionsLoader
    
    Controller->>Authorization: authorize($recommendation)
    Authorization->>RecommendationPolicy: canEdit($user, $recommendation)
    RecommendationPolicy->>BasePolicy: _hasPolicy()
    BasePolicy->>BasePolicy: before() - Super User Check
    BasePolicy->>PermissionsLoader: Resolve Permissions
    PermissionsLoader-->>BasePolicy: Permission Result
    BasePolicy-->>RecommendationPolicy: Authorization Decision
    RecommendationPolicy-->>Authorization: bool
    Authorization-->>Controller: Authorized/Denied

Dynamic Approval Flow

sequenceDiagram
    participant Controller
    participant Authorization
    participant RecommendationPolicy
    participant __call
    participant BasePolicy
    
    Controller->>Authorization: can($rec, 'canApproveLevelAoA')
    Authorization->>RecommendationPolicy: canApproveLevelAoA($user, $rec)
    RecommendationPolicy->>__call: __call('canApproveLevelAoA', [$user, $rec])
    __call->>BasePolicy: _hasPolicy($user, 'canApproveLevelAoA', $rec)
    BasePolicy-->>__call: Authorization Result
    __call-->>Authorization: bool
    Authorization-->>Controller: Authorized/Denied

Usage Examples

Controller Integration

// RecommendationsController with workflow authorization
public function view($id) {
    $recommendation = $this->Recommendations->get($id);
    $this->Authorization->authorize($recommendation);
    $this->set(compact('recommendation'));
}

public function updateStates() {
    $recommendations = $this->request->getData('recommendations');
    foreach ($recommendations as $recData) {
        $recommendation = $this->Recommendations->get($recData['id']);
        $this->Authorization->authorize($recommendation, 'updateStates');
        // State update processing...
    }
}

Member Self-Service Access

// Member accessing their own recommendations
$recommendations = $this->Recommendations->find()
    ->where(['requester_id' => $currentUser->getIdentifier()]);

foreach ($recommendations as $recommendation) {
    if ($this->Authorization->can($recommendation, 'viewSubmittedByMember')) {
        $accessibleRecommendations[] = $recommendation;
    }
}

Dynamic Approval Authorization

// Process approval based on award level
public function processApproval($recommendationId, $newState) {
    $recommendation = $this->Recommendations->get($recommendationId, [
        'contain' => ['Awards.Levels']
    ]);
    
    $levelName = $recommendation->award->level->name;
    $approvalMethod = 'canApproveLevel' . $levelName;
    
    if (!$this->Authorization->can($recommendation, $approvalMethod)) {
        throw new ForbiddenException('Not authorized to approve this level');
    }
    
    // Process approval...
}

Administrative Board Access

// Administrative recommendation board
public function adminBoardAccess() {
    $sampleRecommendation = $this->Recommendations->newEmptyEntity();
    
    if (!$this->Authorization->can($sampleRecommendation, 'useBoard')) {
        throw new ForbiddenException('Board access not authorized');
    }
    
    return $this->render('board');
}

Policy Introspection

// Discover available approval methods
$availableMethods = RecommendationPolicy::getDynamicMethods();
// Returns: ['canApproveLevelAoA', 'canApproveLevelGoA', 'canApproveLevelPeerage', ...]

// Configure permissions for all approval levels
foreach ($availableMethods as $method) {
    $this->createPermissionIfNotExists($method);
}

Integration Points

Recommendations Controller Integration

Awards System Integration

RBAC System Integration

Member Management Integration

Security Considerations

Access Control Security

Data Protection

Dynamic Security