Heratio Help Center article. Category: Plugin Reference.
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 Heratio 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 Heratio AHG Framework