ahgRightsPlugin - Technical Documentation
Version: 1.1.2
Category: Rights Management
Dependencies: atom-framework, ahgCorePlugin
Overview
Comprehensive rights management system for archival materials implementing PREMIS rights vocabulary, RightsStatements.org standards, Creative Commons licensing, Traditional Knowledge Labels (Local Contexts), embargo management, and orphan works due diligence tracking.
Architecture
+---------------------------------------------------------------------+
| ahgRightsPlugin |
+---------------------------------------------------------------------+
| |
| +---------------------------------------------------------------+ |
| | RightsService | |
| | - Rights record CRUD | |
| | - Embargo management | |
| | - TK Label assignment | |
| | - Orphan work tracking | |
| | - Access checking | |
| +---------------------------------------------------------------+ |
| | |
| v |
| +---------------------------------------------------------------+ |
| | Database Layer | |
| | +------------------+ +------------------+ +---------------+ | |
| | | rights_record | | rights_embargo | | rights_tk_* | | |
| | | rights_grant | | rights_orphan_* | | rights_cc_* | | |
| | | rights_statement | | rights_territory | | rights_deriv* | | |
| | +------------------+ +------------------+ +---------------+ | |
| +---------------------------------------------------------------+ |
| | |
| v |
| +---------------------------------------------------------------+ |
| | UI Components | |
| | +------------------+ +------------------+ +---------------+ | |
| | | rightsAdmin | | rights module | | Display | | |
| | | (admin pages) | | (record views) | | Integration | | |
| | +------------------+ +------------------+ +---------------+ | |
| +---------------------------------------------------------------+ |
| |
+---------------------------------------------------------------------+
Database Schema
ERD Diagram
+----------------------------------+ +----------------------------------+
| rights_record | | rights_statement |
+----------------------------------+ +----------------------------------+
| PK id INT AUTO_INCREMENT | | PK id BIGINT AUTO_INCREMENT |
| object_id INT (FK) | | uri VARCHAR(255) UNIQUE |
| basis ENUM | | code VARCHAR(50) UNIQUE |
| FK rights_statement_id INT |<------+ category ENUM |
| FK cc_license_id INT | | icon_filename VARCHAR(100) |
| copyright_status ENUM | | is_active TINYINT |
| copyright_holder VARCHAR(255) | | sort_order INT |
| copyright_holder_actor_id INT | +----------------------------------+
| copyright_jurisdiction VARCHAR| |
| copyright_determination_date | v
| license_identifier VARCHAR | +----------------------------------+
| license_terms TEXT | | rights_statement_i18n |
| statute_citation VARCHAR | +----------------------------------+
| statute_jurisdiction VARCHAR | | PK id BIGINT AUTO_INCREMENT |
| donor_name VARCHAR | | FK rights_statement_id BIGINT |
| policy_identifier VARCHAR | | culture VARCHAR(10) |
| start_date DATE | | name VARCHAR(255) |
| end_date DATE | | definition TEXT |
| created_by INT | | scope_note TEXT |
| created_at DATETIME | +----------------------------------+
+----------------------------------+
| |
| |
v v
+------------------+ +----------------------------------+
| rights_grant | | rights_cc_license |
+------------------+ +----------------------------------+
| PK id INT | | PK id INT AUTO_INCREMENT |
| FK rights_record | | code VARCHAR(20) UNIQUE |
| _id INT | | version VARCHAR(10) |
| act ENUM | | uri VARCHAR(255) |
| restriction | | allows_commercial TINYINT |
| ENUM | | allows_derivatives TINYINT |
| start_date | | requires_share_alike TINYINT |
| end_date | | requires_attribution TINYINT |
| condition_type| | badge_url VARCHAR(255) |
| condition_val | | is_active TINYINT |
+------------------+ +----------------------------------+
+----------------------------------+ +----------------------------------+
| rights_embargo | | rights_tk_label |
+----------------------------------+ +----------------------------------+
| PK id INT AUTO_INCREMENT | | PK id INT AUTO_INCREMENT |
| object_id INT (FK) | | code VARCHAR(20) UNIQUE |
| embargo_type ENUM | | category ENUM (tk/bc/attr) |
| reason ENUM | | uri VARCHAR(255) |
| start_date DATE | | color VARCHAR(7) |
| end_date DATE | | icon_path VARCHAR(255) |
| auto_release TINYINT | | sort_order INT |
| review_date DATE | | is_active TINYINT |
| review_interval_months INT | +----------------------------------+
| status ENUM | |
| lifted_at DATETIME | v
| lifted_by INT | +----------------------------------+
| notify_before_days INT | | rights_object_tk_label |
| notification_sent TINYINT | +----------------------------------+
| notify_emails TEXT (JSON) | | PK id INT AUTO_INCREMENT |
| created_by INT | | object_id INT (FK) |
| created_at DATETIME | | FK tk_label_id INT |
+----------------------------------+ | community_name VARCHAR |
| | community_contact TEXT |
v | custom_text TEXT |
+----------------------------------+ | verified TINYINT |
| rights_embargo_log | | verified_by VARCHAR |
+----------------------------------+ | verified_date DATE |
| PK id INT AUTO_INCREMENT | +----------------------------------+
| FK embargo_id INT |
| action ENUM |
| old_status VARCHAR |
| new_status VARCHAR | +----------------------------------+
| old_end_date DATE | | rights_orphan_work |
| new_end_date DATE | +----------------------------------+
| notes TEXT | | PK id INT AUTO_INCREMENT |
| performed_by INT | | object_id INT (FK) |
| performed_at DATETIME | | status ENUM |
+----------------------------------+ | work_type ENUM |
| search_started_date DATE |
+----------------------------------+ | search_completed_date DATE |
| rights_territory | | search_jurisdiction VARCHAR |
+----------------------------------+ | rights_holder_found TINYINT |
| PK id INT AUTO_INCREMENT | | rights_holder_name VARCHAR |
| FK rights_record_id INT | | contact_attempted TINYINT |
| territory_type ENUM | | intended_use TEXT |
| country_code VARCHAR(10) | | proposed_fee DECIMAL(10,2) |
| is_gdpr_territory TINYINT | +----------------------------------+
| gdpr_legal_basis VARCHAR | |
+----------------------------------+ v
+----------------------------------+
+----------------------------------+ | rights_orphan_search_step |
| rights_derivative_rule | +----------------------------------+
+----------------------------------+ | PK id INT AUTO_INCREMENT |
| PK id INT AUTO_INCREMENT | | FK orphan_work_id INT |
| object_id INT | | source_type ENUM |
| collection_id INT | | source_name VARCHAR |
| is_global TINYINT | | source_url VARCHAR |
| rule_type ENUM | | search_date DATE |
| priority INT | | search_terms TEXT |
| applies_to_roles JSON | | results_found TINYINT |
| applies_to_clearance JSON | | results_description TEXT |
| watermark_text VARCHAR | | evidence_file_path VARCHAR |
| watermark_image_path VARCHAR | | screenshot_path VARCHAR |
| watermark_position ENUM | | performed_by INT |
| watermark_opacity INT | +----------------------------------+
| max_width INT |
| max_height INT |
+----------------------------------+
ENUM Values
Rights Basis
ENUM ( 'copyright' , 'license' , 'statute' , 'donor' , 'policy' , 'other' )
Copyright Status
ENUM ( 'copyrighted' , 'public_domain' , 'unknown' )
PREMIS Acts
ENUM ( 'render' , 'disseminate' , 'replicate' , 'migrate' , 'modify' ,
'delete' , 'print' , 'use' , 'publish' , 'excerpt' , 'annotate' ,
'move' , 'sell' )
Restriction Types
ENUM ( 'allow' , 'disallow' , 'conditional' )
Embargo Types
ENUM ( 'full' , 'metadata_only' , 'digital_only' , 'partial' )
Embargo Reasons
ENUM ( 'donor_restriction' , 'copyright' , 'privacy' , 'legal' ,
'commercial' , 'research' , 'cultural' , 'security' , 'other' )
Embargo Status
ENUM ( 'active' , 'pending' , 'lifted' , 'expired' , 'extended' )
Work Types (Orphan Works)
ENUM ( 'literary' , 'dramatic' , 'musical' , 'artistic' , 'film' ,
'sound_recording' , 'broadcast' , 'typographical' , 'database' ,
'photograph' , 'other' )
Search Source Types
ENUM ( 'database' , 'registry' , 'publisher' , 'author_society' ,
'archive' , 'library' , 'internet' , 'newspaper' , 'other' )
TK Label Categories
ENUM ( 'tk' , 'bc' , 'attribution' )
Rights Statement Categories
ENUM ( 'in-copyright' , 'no-copyright' , 'other' )
Service Methods
RightsService
namespace Plugins\ahgRightsPlugin\Services;
class RightsService
{
// Singleton
public static function getInstance(?string $culture = null): self
// =================================================================
// RIGHTS RECORDS
// =================================================================
public function getRightsForObject(int $objectId): Collection
public function getRightsRecord(int $id): ?object
public function createRightsRecord(array $data): int
public function updateRightsRecord(int $id, array $data): bool
public function deleteRightsRecord(int $id): bool
// =================================================================
// RIGHTS GRANTS (PREMIS Acts)
// =================================================================
public function getGrantsForRecord(int $recordId): Collection
public function createGrant(int $recordId, array $data): int
// =================================================================
// EMBARGO MANAGEMENT
// =================================================================
public function getEmbargo(int $objectId): ?object
public function getActiveEmbargoes(): Collection
public function getExpiringEmbargoes(int $days = 30): Collection
public function getEmbargoesForReview(): Collection
public function createEmbargo(array $data): int
public function liftEmbargo(int $id, ?string $reason = null, ?int $userId = null): bool
public function extendEmbargo(int $id, string $newEndDate, ?string $reason = null, ?int $userId = null): bool
public function processExpiredEmbargoes(): int
// =================================================================
// ORPHAN WORKS
// =================================================================
public function getOrphanWork(int $objectId): ?object
public function getOrphanWorkSearchSteps(int $orphanWorkId): Collection
public function createOrphanWork(array $data): int
public function addOrphanWorkSearchStep(int $orphanWorkId, array $data): int
public function completeOrphanWorkSearch(int $id, bool $rightsHolderFound = false): bool
// =================================================================
// TK LABELS
// =================================================================
public function getTkLabelsForObject(int $objectId): Collection
public function assignTkLabel(int $objectId, int $labelId, array $data = []): int
public function removeTkLabel(int $objectId, int $labelId): bool
// =================================================================
// TERRITORY RESTRICTIONS
// =================================================================
public function getTerritoriesForRecord(int $recordId): Collection
public function addTerritory(int $recordId, string $countryCode, string $type = 'include', bool $isGdpr = false): int
// =================================================================
// ACCESS CHECKS
// =================================================================
public function checkAccess(int $objectId, ?int $userId = null, ?string $purpose = null): array
// =================================================================
// REFERENCE DATA
// =================================================================
public function getRightsStatements(): Collection
public function getCcLicenses(): Collection
public function getTkLabels(): Collection
public function getFormOptions(): array
// =================================================================
// STATISTICS
// =================================================================
public function getStatistics(): array
}
Usage Examples
Creating a Rights Record
use Plugins\ahgRightsPlugin\Services\RightsService;
$service = RightsService::getInstance();
$rightsId = $service->createRightsRecord([
'object_id' => 12345,
'basis' => 'copyright',
'copyright_status' => 'copyrighted',
'copyright_holder' => 'John Smith Estate',
'copyright_jurisdiction' => 'ZA',
'copyright_determination_date' => '2026-01-15',
'rights_statement_id' => 1, // InC
'start_date' => '1990-01-01',
'end_date' => '2060-12-31',
'rights_note' => 'Copyright held by family estate.',
'created_by' => $userId,
'grants' => [
['act' => 'render', 'restriction' => 'allow'],
['act' => 'disseminate', 'restriction' => 'conditional', 'condition_value' => 'Attribution required'],
['act' => 'modify', 'restriction' => 'disallow'],
],
]);
Managing Embargoes
// Create embargo
$embargoId = $service->createEmbargo([
'object_id' => 12345,
'embargo_type' => 'full',
'reason' => 'donor_restriction',
'start_date' => '2026-01-30',
'end_date' => '2030-12-31',
'auto_release' => true,
'review_date' => '2028-01-30',
'notify_before_days' => 30,
'notify_emails' => ['archivist@example.org'],
'reason_note' => 'Donor requested restriction for 5 years.',
'created_by' => $userId,
]);
// Extend embargo
$service->extendEmbargo($embargoId, '2035-12-31', 'Donor requested extension', $userId);
// Lift embargo
$service->liftEmbargo($embargoId, 'Restriction period completed', $userId);
// Process all expired embargoes (for cron)
$count = $service->processExpiredEmbargoes();
Assigning TK Labels
// Get available TK labels
$labels = $service->getTkLabels();
// Assign label to object
$service->assignTkLabel(12345, $labelId, [
'community_name' => 'Example Community',
'community_contact' => 'elder@community.org',
'custom_text' => 'Traditional knowledge protocols apply.',
'verified' => false,
'created_by' => $userId,
]);
// Get labels for object
$objectLabels = $service->getTkLabelsForObject(12345);
// Remove label
$service->removeTkLabel(12345, $labelId);
Orphan Works Due Diligence
// Start orphan work search
$orphanId = $service->createOrphanWork([
'object_id' => 12345,
'work_type' => 'photograph',
'search_jurisdiction' => 'ZA',
'intended_use' => 'Digitization and online access',
'created_by' => $userId,
]);
// Add search steps
$service->addOrphanWorkSearchStep($orphanId, [
'source_type' => 'registry',
'source_name' => 'SAMRO',
'source_url' => 'https://www.samro.org.za',
'search_date' => '2026-01-30',
'search_terms' => 'photographer name, date range',
'results_found' => false,
'results_description' => 'No matching records found.',
'performed_by' => $userId,
]);
// Complete search
$service->completeOrphanWorkSearch($orphanId, false);
Access Checking
$accessCheck = $service->checkAccess(12345, $userId);
// Returns:
[
'accessible' => false,
'restrictions' => [
['type' => 'embargo', 'reason' => 'donor_restriction', 'until' => '2030-12-31'],
],
'embargo' => {...},
'rights_statement' => ['code' => 'InC', 'name' => 'In Copyright', 'uri' => '...'],
'cc_license' => null,
'tk_labels' => [...],
'required_actions' => [
['type' => 'tk_consultation', 'label' => 'TK-C', 'community' => 'Example Community'],
],
]
Seed Data
Rights Statements (12 standard)
Code
Category
Name
InC
in-copyright
In Copyright
InC-OW-EU
in-copyright
In Copyright - EU Orphan Work
InC-EDU
in-copyright
In Copyright - Educational Use Permitted
InC-NC
in-copyright
In Copyright - Non-Commercial Use Permitted
InC-RUU
in-copyright
In Copyright - Rights-holder Unlocatable
NoC-CR
no-copyright
No Copyright - Contractual Restrictions
NoC-NC
no-copyright
No Copyright - Non-Commercial Use Only
NoC-OKLR
no-copyright
No Copyright - Other Known Legal Restrictions
NoC-US
no-copyright
No Copyright - United States
CNE
other
Copyright Not Evaluated
UND
other
Copyright Undetermined
NKC
other
No Known Copyright
Creative Commons Licenses (8 standard)
Code
Commercial
Derivatives
Share Alike
CC0-1.0
Yes
Yes
No
CC-BY-4.0
Yes
Yes
No
CC-BY-SA-4.0
Yes
Yes
Yes
CC-BY-NC-4.0
No
Yes
No
CC-BY-NC-SA-4.0
No
Yes
Yes
CC-BY-ND-4.0
Yes
No
No
CC-BY-NC-ND-4.0
No
No
No
PDM-1.0
Yes
Yes
No
TK Labels (19 standard)
Code
Category
Name
TK-A
attribution
TK Attribution
TK-NC
tk
TK Non-Commercial
TK-C
tk
TK Community Voice
TK-CV
tk
TK Culturally Sensitive
TK-SS
tk
TK Secret/Sacred
TK-MC
tk
TK Multiple Communities
TK-MR
tk
TK Men Restricted
TK-WR
tk
TK Women Restricted
TK-SR
tk
TK Seasonal
TK-F
tk
TK Family
TK-O
tk
TK Outreach
TK-V
tk
TK Verified
TK-NV
tk
TK Non-Verified
BC-R
bc
BC Research Use
BC-CB
bc
BC Consent Before
BC-P
bc
BC Provenance
BC-MC
bc
BC Multiple Communities
BC-CL
bc
BC Clan
BC-O
bc
BC Outreach
Routes
Rights Admin Module
Route
Action
Description
/rightsAdmin
index
Dashboard
/rightsAdmin/embargoes
embargoes
List embargoes
/rightsAdmin/embargoEdit/:id
embargoEdit
Edit embargo
/rightsAdmin/embargoLift/:id
embargoLift
Lift embargo
/rightsAdmin/embargoExtend/:id
embargoExtend
Extend embargo
/rightsAdmin/processExpired
processExpired
Auto-release expired
/rightsAdmin/orphanWorks
orphanWorks
List orphan works
/rightsAdmin/orphanWorkEdit/:id
orphanWorkEdit
Edit orphan work
/rightsAdmin/addSearchStep
addSearchStep
Add search step
/rightsAdmin/tkLabels
tkLabels
TK label management
/rightsAdmin/assignTkLabel
assignTkLabel
Assign TK label
/rightsAdmin/removeTkLabel
removeTkLabel
Remove TK label
/rightsAdmin/statements
statements
View statements/licenses
/rightsAdmin/report/:type
report
Generate reports
Rights Module (Record-Level)
Route
Action
Description
/rights/:slug
index
View rights for record
/rights/:slug/add
add
Add rights record
/rights/:slug/edit/:id
edit
Edit rights record
/rights/:slug/delete/:id
delete
Delete rights record
/rights/:slug/embargo
editEmbargo
Edit embargo
/rights/:slug/releaseEmbargo/:id
releaseEmbargo
Release embargo
/rights/:slug/tkLabels
tkLabels
Manage TK labels
/rights/:slug/orphanWork
orphanWork
Orphan work management
API Endpoints
Route
Action
Description
/api/rights/check/:id
apiCheck
Check access rights
/api/rights/embargo/:id
apiEmbargo
Get embargo status
Display Integration
extension.json Configuration
{
"display_actions" : [
{
"id" : "rights_action" ,
"label" : "Rights" ,
"icon" : "bi-shield-lock" ,
"template" : "ahgRightsPlugin/templates/display/_rights.php" ,
"contexts" : [ "informationobject" , "actor" ],
"permission" : "read" ,
"weight" : 30
}
],
"display_panels" : [
{
"id" : "rights_panel" ,
"label" : "Rights & Restrictions" ,
"template" : "ahgRightsPlugin/templates/display/_rights_section.php" ,
"contexts" : [ "informationobject" ],
"position" : "sidebar" ,
"weight" : 50
}
]
}
Template Integration
Include in detail templates:
<?php include_partial ( 'rights/rightsPanel' , [ 'resource' => $resource ]); ?>
Cron Jobs
Process Expired Embargoes
Add to crontab for automatic embargo release:
# Daily at 2:00 AM
0 2 * * * cd /usr/share/nginx/archive && php symfony rights:process-expired
Embargo Notifications
# Daily at 8:00 AM - send expiring embargo notifications
0 8 * * * cd /usr/share/nginx/archive && php symfony rights:notify-expiring
Configuration
Setting
Default
Description
rights_default_jurisdiction
ZA
Default copyright jurisdiction
embargo_notify_days
30
Days before end to notify
orphan_search_minimum_steps
5
Minimum search steps required
tk_require_verification
false
Require TK label verification
Compliance Mapping
Standard
Requirement
Implementation
PREMIS
Rights basis vocabulary
basis ENUM field
PREMIS
Granted rights (acts)
rights_grant table
RightsStatements.org
12 standardized statements
rights_statement table
Creative Commons
License metadata
rights_cc_license table
Local Contexts
TK/BC Labels
rights_tk_label tables
GDPR
Territory restrictions
rights_territory table
POPIA (SA)
Access controls
embargo + access check
PAIA (SA)
Public access
access check integration
Tables Summary
Table
Purpose
i18n
rights
Base AtoM rights (legacy)
rights_i18n
rights_record
Extended rights records
rights_record_i18n
rights_grant
PREMIS acts/restrictions
rights_grant_i18n
rights_statement
RightsStatements.org vocab
rights_statement_i18n
rights_cc_license
Creative Commons licenses
rights_cc_license_i18n
rights_tk_label
TK/BC label definitions
rights_tk_label_i18n
rights_object_tk_label
Label assignments
-
rights_embargo
Embargo records
rights_embargo_i18n
rights_embargo_log
Embargo audit trail
-
rights_orphan_work
Orphan work records
rights_orphan_work_i18n
rights_orphan_search_step
Search documentation
-
rights_territory
Geographic restrictions
-
rights_derivative_rule
Watermark/derivative rules
-
rights_derivative_log
Derivative generation log
-
creative_commons_license
CC license definitions
creative_commons_license_i18n
Security
Permissions
Action
Required Role
View rights
Any authenticated
Add/edit rights
Editor or Administrator
Delete rights
Administrator
Manage embargoes
Administrator
Assign TK labels
Editor or Administrator
View admin dashboard
Administrator
Access Control
Rights checks are performed via checkAccess() which evaluates:
Active embargoes
Rights grants (disallow restrictions)
TK label restrictions
Territory restrictions
User role/clearance level
Installation
# 1. Ensure plugin symlink exists
ln -s /usr/share/nginx/archive/atom-ahg-plugins/ahgRightsPlugin \
/usr/share/nginx/archive/plugins/ahgRightsPlugin
# 2. Install database schema
mysql -u root archive < /usr/share/nginx/archive/plugins/ahgRightsPlugin/database/install.sql
# 3. Clear cache
php symfony cc
# 4. Enable plugin via Admin UI or CLI
php bin/atom extension:enable ahgRightsPlugin
Part of the AtoM AHG Framework
February 4, 2026
February 4, 2026