Skip to content

ahgExhibitionPlugin - Technical Documentation

Version: 1.0.0 Category: GLAM Dependencies: atom-framework Sectors: Archive, Museum, Gallery, Library, DAM


Overview

Comprehensive exhibition management system for GLAM/DAM institutions. Manages the complete exhibition lifecycle from concept to archive, including object selection, storyline creation, event scheduling, and task tracking.


Architecture

+---------------------------------------------------------------------+
|                      ahgExhibitionPlugin                             |
+---------------------------------------------------------------------+
|                                                                       |
|  +---------------------------------------------------------------+   |
|  |                   Plugin Configuration                        |   |
|  |  ahgExhibitionPluginConfiguration.class.php                   |   |
|  |  - Registers routes                                           |   |
|  |  - Enables exhibition module                                  |   |
|  +---------------------------------------------------------------+   |
|                              |                                        |
|                              v                                        |
|  +---------------------------------------------------------------+   |
|  |                   ExhibitionService                           |   |
|  |  lib/Services/ExhibitionService.php                           |   |
|  |  - Exhibition CRUD operations                                 |   |
|  |  - Status transitions                                         |   |
|  |  - Object management                                          |   |
|  |  - Section management                                         |   |
|  |  - Storyline management                                       |   |
|  |  - Event management                                           |   |
|  |  - Checklist management                                       |   |
|  |  - Statistics and reporting                                   |   |
|  +---------------------------------------------------------------+   |
|                              |                                        |
|                              v                                        |
|  +---------------------------------------------------------------+   |
|  |                   ExhibitionWorkflow                          |   |
|  |  lib/Workflow/ExhibitionWorkflow.php                          |   |
|  |  - State machine definition                                   |   |
|  |  - Valid transitions                                          |   |
|  |  - Transition requirements                                    |   |
|  |  - Progress tracking                                          |   |
|  +---------------------------------------------------------------+   |
|                              |                                        |
|                              v                                        |
|  +---------------------------------------------------------------+   |
|  |                   Actions Controller                          |   |
|  |  modules/exhibition/actions/actions.class.php                 |   |
|  |  - HTTP request handling                                      |   |
|  |  - Form processing                                            |   |
|  |  - AJAX endpoints                                             |   |
|  +---------------------------------------------------------------+   |
|                              |                                        |
|                              v                                        |
|  +---------------------------------------------------------------+   |
|  |                   Database Tables                             |   |
|  |  exhibition, exhibition_object, exhibition_section,           |   |
|  |  exhibition_storyline, exhibition_event, exhibition_checklist |   |
|  +---------------------------------------------------------------+   |
|                                                                       |
+---------------------------------------------------------------------+

Database Schema

ERD Diagram

+----------------------+         +----------------------+
|   exhibition_venue   |         |   exhibition         |
+----------------------+         +----------------------+
| PK id                |<--+     | PK id                |
|    name              |   |     |    title             |
|    code              |   |     |    subtitle          |
|    venue_type        |   |     |    slug (unique)     |
|    address_line1     |   |     |    description       |
|    city              |   +---->| FK venue_id          |
|    country           |         |    exhibition_type   |
|    contact_*         |         |    status            |
|    has_climate_ctrl  |         |    opening_date      |
|    has_security      |         |    closing_date      |
|    is_active         |         |    curator_id        |
+----------------------+         |    budget_amount     |
        |                        |    created_by        |
        v                        +----------------------+
+----------------------+                  |
| exhibition_gallery   |                  |
+----------------------+                  |
| PK id                |                  |
| FK venue_id          |                  |
|    name              |                  |
|    gallery_type      |                  |
|    floor_level       |                  |
|    square_meters     |                  |
|    has_climate_ctrl  |                  |
|    max_lux_level     |                  |
|    max_visitors      |                  |
+----------------------+                  |
                                          |
        +---------------------+-----------+-----------+
        |                     |                       |
        v                     v                       v
+----------------------+ +----------------------+ +----------------------+
| exhibition_section   | | exhibition_object    | | exhibition_storyline |
+----------------------+ +----------------------+ +----------------------+
| PK id                | | PK id                | | PK id                |
| FK exhibition_id     | | FK exhibition_id     | | FK exhibition_id     |
|    title             | | FK section_id        | |    title             |
|    subtitle          | | FK information_obj_id| |    slug              |
|    description       | |    sequence_order    | |    narrative_type    |
|    section_type      | |    display_position  | |    introduction      |
|    sequence_order    | |    status            | |    target_audience   |
|    gallery_name      | |    requires_loan     | |    is_primary        |
|    theme             | |    loan_id           | |    duration_minutes  |
|    target_temp_*     | |    insurance_value   | |    created_by        |
|    max_lux_level     | |    label_text        | +----------------------+
+----------------------+ |    installed_by      |           |
        |                |    installed_at      |           v
        |                +----------------------+ +----------------------+
        |                          |             | exhibition_storyline |
        |                          |             |        _stop         |
        |                          |             +----------------------+
        |                          |             | PK id                |
        +--------------------------+------------>| FK storyline_id      |
                                                 | FK exhibition_obj_id |
                                                 |    sequence_order    |
                                                 |    stop_number       |
                                                 |    narrative_text    |
                                                 |    audio_transcript  |
                                                 +----------------------+

+----------------------+         +----------------------+
| exhibition_event     |         | exhibition_checklist |
+----------------------+         +----------------------+
| PK id                |         | PK id                |
| FK exhibition_id     |         | FK exhibition_id     |
|    title             |         | FK template_id       |
|    event_type        |         |    name              |
|    event_date        |         |    checklist_type    |
|    start_time        |         |    due_date          |
|    end_time          |         |    status            |
|    max_attendees     |         |    assigned_to       |
|    requires_regist.  |         +----------------------+
|    is_free           |                  |
|    ticket_price      |                  v
|    presenter_name    |         +----------------------+
|    status            |         | exhibition_checklist |
+----------------------+         |        _item         |
                                 +----------------------+
+----------------------+         | PK id                |
| exhibition_status    |         | FK checklist_id      |
|      _history        |         |    name              |
+----------------------+         |    description       |
| PK id                |         |    is_required       |
| FK exhibition_id     |         |    is_completed      |
|    from_status       |         |    completed_at      |
|    to_status         |         |    completed_by      |
|    changed_by        |         +----------------------+
|    change_reason     |
|    created_at        |         +----------------------+
+----------------------+         | exhibition_checklist |
                                 |      _template       |
+----------------------+         +----------------------+
| exhibition_media     |         | PK id                |
+----------------------+         |    name              |
| PK id                |         |    checklist_type    |
| FK exhibition_id     |         |    items (JSON)      |
| FK section_id        |         |    is_default        |
|    media_type        |         +----------------------+
|    usage_type        |
|    file_path         |
|    title             |
|    is_primary        |
+----------------------+

Core Tables

exhibition

Main exhibition records.

CREATE TABLE exhibition (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(500) NOT NULL,
    subtitle VARCHAR(500),
    slug VARCHAR(255) UNIQUE,
    description TEXT,
    theme TEXT,
    exhibition_type ENUM('permanent','temporary','traveling','online','pop_up') DEFAULT 'temporary',
    status ENUM('concept','planning','preparation','installation','open','closing','closed','archived','canceled') DEFAULT 'concept',

    -- Dates
    planning_start_date DATE,
    preparation_start_date DATE,
    installation_start_date DATE,
    opening_date DATE,
    closing_date DATE,
    actual_closing_date DATE,

    -- Venue
    venue_id BIGINT UNSIGNED,
    venue_name VARCHAR(255),
    venue_address TEXT,
    is_external_venue TINYINT(1) DEFAULT 0,

    -- Admission
    admission_fee DECIMAL(10,2),
    admission_currency VARCHAR(3) DEFAULT 'ZAR',
    is_free_admission TINYINT(1) DEFAULT 0,
    expected_visitors INT,
    actual_visitors INT,

    -- Budget & Insurance
    budget_amount DECIMAL(12,2),
    budget_currency VARCHAR(3) DEFAULT 'ZAR',
    actual_cost DECIMAL(12,2),
    total_insurance_value DECIMAL(15,2),
    insurance_policy_number VARCHAR(100),

    -- Team
    curator_id INT,
    curator_name VARCHAR(255),
    designer_name VARCHAR(255),
    organized_by VARCHAR(255),

    -- Metadata
    project_code VARCHAR(50),
    notes TEXT,
    internal_notes TEXT,
    created_by INT,
    updated_by INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_exhibition_status (status),
    INDEX idx_exhibition_type (exhibition_type),
    INDEX idx_exhibition_dates (opening_date, closing_date),
    INDEX idx_exhibition_venue (venue_id)
);

exhibition_object

Links information objects to exhibitions.

CREATE TABLE exhibition_object (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    exhibition_id BIGINT UNSIGNED NOT NULL,
    section_id BIGINT UNSIGNED,
    information_object_id INT NOT NULL,
    sequence_order INT DEFAULT 0,
    display_position VARCHAR(100),

    -- Status
    status ENUM('proposed','confirmed','on_loan_request','installed','removed','returned') DEFAULT 'proposed',

    -- Loan
    requires_loan TINYINT(1) DEFAULT 0,
    loan_id BIGINT UNSIGNED,
    lender_institution VARCHAR(255),

    -- Display requirements
    display_case_required TINYINT(1) DEFAULT 0,
    mount_required TINYINT(1) DEFAULT 0,
    mount_description TEXT,
    special_lighting TINYINT(1) DEFAULT 0,
    lighting_notes TEXT,
    security_level ENUM('standard','enhanced','maximum') DEFAULT 'standard',

    -- Environmental
    climate_controlled TINYINT(1) DEFAULT 0,
    max_lux_level INT,
    uv_filtering_required TINYINT(1) DEFAULT 0,
    rotation_required TINYINT(1) DEFAULT 0,
    max_display_days INT,
    display_start_date DATE,
    display_end_date DATE,

    -- Condition reports
    pre_installation_condition_report_id BIGINT UNSIGNED,
    post_exhibition_condition_report_id BIGINT UNSIGNED,

    -- Insurance
    insurance_value DECIMAL(15,2),

    -- Labels
    label_text TEXT,
    label_credits TEXT,
    extended_label TEXT,
    audio_stop_number VARCHAR(20),

    -- Installation
    installation_notes TEXT,
    handling_notes TEXT,
    installed_by INT,
    installed_at TIMESTAMP,
    removed_by INT,
    removed_at TIMESTAMP,

    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    FOREIGN KEY (exhibition_id) REFERENCES exhibition(id) ON DELETE CASCADE,
    FOREIGN KEY (section_id) REFERENCES exhibition_section(id) ON DELETE SET NULL,
    INDEX idx_exobj_exhibition (exhibition_id),
    INDEX idx_exobj_section (section_id),
    INDEX idx_exobj_object (information_object_id),
    INDEX idx_exobj_status (status)
);

Exhibition Types

Type Description
permanent Long-term or indefinite display
temporary Fixed duration exhibition
traveling Moves between venues
online Virtual/digital exhibition
pop_up Short-term temporary display

Status Workflow

States

Status Label Color Order Description
concept Concept #9e9e9e 1 Initial concept development
planning Planning #2196f3 2 Detailed planning phase
preparation Preparation #ff9800 3 Physical/content preparation
installation Installation #9c27b0 4 Physical installation
open Open #4caf50 5 Exhibition open to public
closing Closing #ff5722 6 Preparing for closure
closed Closed #795548 7 Closed, deinstallation
archived Archived #607d8b 8 Final state
canceled Canceled #f44336 9 Exhibition was canceled

Valid Transitions

concept       --> planning, canceled
planning      --> concept, preparation, canceled
preparation   --> planning, installation, canceled
installation  --> preparation, open, canceled
open          --> closing
closing       --> open (reopen), closed
closed        --> archived
archived      --> (final state)
canceled      --> concept (revive)

State Machine Diagram

                              +----------+
                              | CANCELED |<------------------+
                              +----------+                   |
                                   ^                         |
                                   |                         |
+----------+    +----------+    +-------------+    +--------------+
| CONCEPT  |--->| PLANNING |--->| PREPARATION |--->| INSTALLATION |
+----------+    +----------+    +-------------+    +--------------+
     ^              |                  |                   |
     |              v                  v                   v
     |         (return)           (return)             (return)
     |                                                     |
     |                                                     v
     |         +----------+    +----------+    +--------+
     +---------|CANCELED  |    | CLOSING  |<---| OPEN   |
               +----------+    +----------+    +--------+
                                    |              ^
                                    v              |
                              +----------+    (reopen)
                              | CLOSED   |         |
                              +----------+---------+
                                    |
                                    v
                              +----------+
                              | ARCHIVED |
                              +----------+

Service Methods

ExhibitionService

namespace ahgExhibitionPlugin\Services;

class ExhibitionService
{
    // Constants
    public const TYPES = [...];           // Exhibition types
    public const STATUSES = [...];        // Status definitions
    public const OBJECT_STATUSES = [...]; // Object status labels

    // Exhibition CRUD
    public function create(array $data, int $userId): int
    public function get(int $exhibitionId, bool $includeDetails = false): ?array
    public function getBySlug(string $slug): ?array
    public function update(int $exhibitionId, array $data, int $userId): bool
    public function transitionStatus(int $exhibitionId, string $newStatus, int $userId, ?string $reason = null): bool
    public function search(array $filters = [], int $limit = 50, int $offset = 0): array

    // Sections
    public function addSection(int $exhibitionId, array $data): int
    public function getSections(int $exhibitionId): array
    public function updateSection(int $sectionId, array $data): bool
    public function deleteSection(int $sectionId): bool
    public function reorderSections(int $exhibitionId, array $sectionOrder): bool

    // Objects
    public function addObject(int $exhibitionId, int $objectId, array $data = []): int
    public function getObjects(int $exhibitionId, ?int $sectionId = null): array
    public function updateObject(int $exhibitionObjectId, array $data): bool
    public function updateObjectStatus(int $exhibitionObjectId, string $status, int $userId, ?string $notes = null): bool
    public function removeObject(int $exhibitionObjectId): bool
    public function reorderObjects(array $order): void
    public function checkObjectAvailability(int $objectId, int $exhibitionId): array

    // Storylines
    public function createStoryline(int $exhibitionId, array $data, int $userId): int
    public function getStorylines(int $exhibitionId): array
    public function updateStoryline(int $storylineId, array $data): void
    public function deleteStoryline(int $storylineId): void
    public function getStorylineWithStops(int $storylineId): ?array
    public function addStorylineStop(int $storylineId, ?int $exhibitionObjectId, array $data): int
    public function updateStorylineStop(int $stopId, array $data): void
    public function deleteStorylineStop(int $stopId): void

    // Events
    public function createEvent(int $exhibitionId, array $data, int $userId): int
    public function getEvents(int $exhibitionId, bool $upcomingOnly = false): array
    public function updateEvent(int $eventId, array $data): void
    public function deleteEvent(int $eventId): void

    // Checklists
    public function createChecklistFromTemplate(int $exhibitionId, int $templateId, ?int $assignedTo = null): int
    public function getChecklists(int $exhibitionId): array
    public function completeChecklistItem(int $itemId, int $userId, ?string $notes = null): bool
    public function addChecklistItem(int $checklistId, array $data): int

    // Statistics & Reports
    public function getExhibitionStatistics(int $exhibitionId): array
    public function getStatistics(): array
    public function generateObjectList(int $exhibitionId): array

    // Helpers
    public function getTypes(): array
    public function getStatuses(): array
    public function getValidTransitions(string $status): array
    public function getChecklistTemplates(): array
}

Routes

Route URL Pattern Action
exhibition_index /exhibitions Browse exhibitions
exhibition_dashboard /exhibition/dashboard Dashboard view
exhibition_add /exhibition/add Create exhibition
exhibition_show /exhibition/:id View exhibition
exhibition_edit /exhibition/:id/edit Edit exhibition
exhibition_objects /exhibition/:id/objects Manage objects
exhibition_sections /exhibition/:id/sections Manage sections
exhibition_storylines /exhibition/:id/storylines Manage storylines
exhibition_storyline /exhibition/:id/storyline/:storyline_id Edit storyline
exhibition_events /exhibition/:id/events Manage events
exhibition_checklists /exhibition/:id/checklists Manage checklists
exhibition_object_list /exhibition/:id/object-list Object list report
exhibition_venues /exhibition/venues Manage venues
exhibition_view /exhibition/:slug View by slug

CLI Commands

# List exhibitions
php symfony museum:exhibition --list

# Show currently open exhibitions
php symfony museum:exhibition --current

# Show upcoming exhibitions
php symfony museum:exhibition --upcoming

# Show overdue exhibitions (past closing date but not closed)
php symfony museum:exhibition --overdue

# Show exhibition details
php symfony museum:exhibition --show=1
php symfony museum:exhibition --show=summer-landscapes-2026

# Generate object list (table, JSON, or CSV)
php symfony museum:exhibition --object-list=1
php symfony museum:exhibition --object-list=1 --format=csv
php symfony museum:exhibition --object-list=1 --format=json

# Show statistics
php symfony museum:exhibition --statistics

# Change exhibition status
php symfony museum:exhibition --exhibition-id=1 --status=planning

# Install database schema
php symfony museum:exhibition --install-schema

# Filter by year or type
php symfony museum:exhibition --list --year=2026
php symfony museum:exhibition --list --type=temporary

Configuration

The plugin uses database tables for configuration. No settings are stored in config.php.

Checklist Templates

Checklist templates are stored in exhibition_checklist_template:

INSERT INTO exhibition_checklist_template (name, checklist_type, items, is_default) VALUES
('Planning Checklist', 'planning', '[
    {"name": "Define exhibition theme", "required": true},
    {"name": "Identify target audience", "required": true},
    {"name": "Set preliminary budget", "required": true},
    {"name": "Initial object selection", "required": false}
]', 1);

Object Availability Checking

The service checks for scheduling conflicts when adding objects:

// Returns conflicts with other exhibitions and loans
$conflicts = $service->checkObjectAvailability($objectId, $exhibitionId);
// Returns: ['exhibitions' => [...], 'loans' => [...]]

This prevents double-booking objects across exhibitions and loan agreements.


Integration Points

Information Objects

Links to AtoM's information_object table via exhibition_object.information_object_id.

Digital Objects

Retrieves thumbnails from digital_object table for display.

Users

References user table for: - curator_id - created_by, updated_by - assigned_to (checklists) - installed_by, removed_by (objects) - completed_by (checklist items)

Loans (if ahgLoanPlugin enabled)

Can link to loan records via exhibition_object.loan_id for loaned objects.


Templates

Template Purpose
indexSuccess.php Browse exhibitions list
dashboardSuccess.php Exhibition dashboard
showSuccess.php Exhibition detail view
addSuccess.php Create exhibition form
editSuccess.php Edit exhibition form
objectsSuccess.php Manage exhibition objects
sectionsSuccess.php Manage sections
storylinesSuccess.php Manage storylines
storylineSuccess.php Edit single storyline with stops
eventsSuccess.php Manage events
checklistsSuccess.php Manage checklists
objectListSuccess.php Object list report
_objectListCsv.php CSV export partial

Event Hooks

The ExhibitionWorkflow defines transition triggers:

'open_exhibition' => [
    'triggers' => ['send_opening_notification'],
],
'cancel_exhibition' => [
    'requires_comment' => true,
    'triggers' => ['send_cancellation_notification'],
],

AJAX Endpoints

Action Method Purpose
executeTransition POST Change exhibition status
executeReorderObjects POST Drag-drop reorder objects
executeSearchObjects GET Search objects for adding
executeCompleteItem POST Complete checklist item

Statistics

Exhibition Statistics

$stats = $service->getExhibitionStatistics($exhibitionId);
// Returns:
// - object_count
// - objects_by_status
// - section_count
// - storyline_count
// - event_count
// - total_insurance_value
// - checklist_progress

Global Statistics

$stats = $service->getStatistics();
// Returns:
// - total_exhibitions
// - by_status
// - by_type
// - current_exhibitions
// - upcoming_exhibitions
// - total_objects_on_display
// - total_insurance_value

Security

Required Permissions

  • View exhibitions: Authenticated user
  • Create/Edit exhibitions: Editor role or higher
  • Delete exhibitions: Administrator only
  • Change status: Based on workflow permissions

Access Control

The plugin uses AtoM's standard ACL system. No custom permissions are defined.


Installation

# Enable the plugin
php bin/atom extension:enable ahgExhibitionPlugin

# Install database schema
php symfony museum:exhibition --install-schema

# Clear cache
php symfony cc

Troubleshooting

Issue Solution
Exhibition not showing Check status filter, verify exhibition exists
Objects not appearing Verify information_object_id is valid
Status transition fails Check valid transitions for current status
Checklist not creating Verify template exists and is valid JSON
Slug conflict Slugs must be unique - change title or slug

Part of the AtoM AHG Framework