diff --git a/docs/api/README.md b/docs/api/README.md
new file mode 100644
index 0000000..00175c7
--- /dev/null
+++ b/docs/api/README.md
@@ -0,0 +1,11 @@
+# DAM Open API Documentation
+
+This directory contains documentation for integrating with the DAM Open API.
+
+## Contents
+
+- [Overview](overview.md) - General overview of the API
+- [Authentication](authentication.md) - Authentication methods
+- [Endpoints](endpoints.md) - Available API endpoints
+- [Integration Examples](integration-examples.md) - Code examples for integration
+- [Best Practices](best-practices.md) - Recommended practices for API usage
diff --git a/docs/api/authentication.md b/docs/api/authentication.md
new file mode 100644
index 0000000..5c0229e
--- /dev/null
+++ b/docs/api/authentication.md
@@ -0,0 +1,47 @@
+# Authentication
+
+## Overview
+
+DAM Open API uses HTTP Basic Authentication for securing API access. Authentication is handled through the `BasicAuthWithExclude` provider which extends Drupal's standard Basic Auth.
+
+## Authentication Methods
+
+### Basic Authentication
+
+The API uses standard HTTP Basic Authentication. You need to include an `Authorization` header with each request.
+
+```
+Authorization: Basic {base64_encoded_credentials}
+```
+
+Where `{base64_encoded_credentials}` is the Base64 encoding of `username:password`.
+
+### Example
+
+```php
+$username = 'api_user';
+$password = 'api_password';
+$auth_header = 'Authorization: Basic ' . base64_encode("$username:$password");
+```
+
+## Custom Authentication Provider
+
+DAM Open extends Drupal's Basic Authentication with a custom provider (`BasicAuthWithExclude`) that:
+
+1. Applies authentication only to JSON:API routes
+2. Handles LDAP integration for user authentication
+3. Provides exclusion mechanisms for specific routes
+
+## Required Permissions
+
+To access the API endpoints, the authenticated user must have the following permission:
+
+- `access tml jsonapi resources`
+
+## Security Best Practices
+
+1. **Use HTTPS**: Always use HTTPS for API communication to encrypt credentials
+2. **Dedicated API User**: Create a dedicated user with minimal permissions for API access
+3. **Credential Management**: Store API credentials securely and never hardcode them
+4. **Token Rotation**: Regularly change API user passwords
+5. **IP Restrictions**: Consider restricting API access to specific IP addresses
diff --git a/docs/api/best-practices.md b/docs/api/best-practices.md
new file mode 100644
index 0000000..e7164a1
--- /dev/null
+++ b/docs/api/best-practices.md
@@ -0,0 +1,181 @@
+# Best Practices
+
+This document outlines recommended practices for using the DAM Open API effectively and securely.
+
+## Performance Optimization
+
+### Implement Caching
+
+Cache API responses to reduce the number of requests and improve performance:
+
+```php
+// Example of simple caching in PHP
+function getCachedAssets($base_url, $username, $password, $cache_duration = 3600) {
+ $cache_file = 'dam_assets_cache.json';
+ $cache_time = file_exists($cache_file) ? filemtime($cache_file) : 0;
+
+ if (time() - $cache_time > $cache_duration) {
+ // Cache expired or doesn't exist, fetch fresh data
+ $assets = fetch_dam_assets($base_url, $username, $password);
+ file_put_contents($cache_file, json_encode($assets));
+ return $assets;
+ } else {
+ // Return cached data
+ return json_decode(file_get_contents($cache_file), true);
+ }
+}
+```
+
+### Use Pagination
+
+When fetching large collections of assets, use pagination to limit the response size:
+
+```
+GET /jsonapi/media/image?page[limit]=10&page[offset]=0
+```
+
+### Request Only Needed Fields
+
+Specify which fields you need to reduce response size:
+
+```
+GET /jsonapi/media/image?fields[media--image]=name,thumbnail,field_category
+```
+
+## Security
+
+### Secure Credential Storage
+
+Never hardcode API credentials in your application code. Use environment variables or a secure configuration system:
+
+```php
+// PHP example using environment variables
+$username = getenv('DAM_API_USERNAME');
+$password = getenv('DAM_API_PASSWORD');
+```
+
+### Implement Rate Limiting
+
+Implement rate limiting in your application to avoid overwhelming the API:
+
+```php
+// Simple rate limiting example
+function rateLimitedRequest($endpoint, $headers, $last_request_time, $min_interval = 1) {
+ $current_time = microtime(true);
+ if ($current_time - $last_request_time < $min_interval) {
+ // Sleep to respect rate limit
+ usleep(($min_interval - ($current_time - $last_request_time)) * 1000000);
+ }
+
+ // Make the request
+ $ch = curl_init($endpoint);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ return [
+ 'response' => $response,
+ 'timestamp' => microtime(true)
+ ];
+}
+```
+
+### Use HTTPS
+
+Always use HTTPS for API communication to ensure data security.
+
+## Error Handling
+
+### Implement Robust Error Handling
+
+Handle API errors gracefully:
+
+```php
+function safeApiRequest($endpoint, $headers) {
+ $ch = curl_init($endpoint);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+ $response = curl_exec($ch);
+ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ if ($http_code >= 400) {
+ // Handle error
+ return [
+ 'success' => false,
+ 'error' => 'API request failed with code ' . $http_code,
+ 'response' => $response
+ ];
+ }
+
+ return [
+ 'success' => true,
+ 'data' => json_decode($response, true)
+ ];
+}
+```
+
+### Log API Interactions
+
+Log API interactions for debugging and monitoring:
+
+```php
+function logApiRequest($endpoint, $method, $status_code, $response_time) {
+ $log_entry = date('Y-m-d H:i:s') . " | $method | $endpoint | $status_code | {$response_time}ms\n";
+ file_put_contents('api_log.txt', $log_entry, FILE_APPEND);
+}
+```
+
+## Integration Patterns
+
+### Webhook Integration
+
+Consider implementing webhooks to receive updates when assets change:
+
+1. Set up an endpoint in your application to receive webhook notifications
+2. Register your webhook URL with the DAM Open system
+3. Process incoming webhook notifications to update your local cache
+
+### Batch Processing
+
+For operations involving multiple assets, use batch processing:
+
+```php
+function processBatchOfAssets($assets, $operation_callback) {
+ $results = [];
+ $batch_size = 10;
+ $total = count($assets);
+
+ for ($i = 0; $i < $total; $i += $batch_size) {
+ $batch = array_slice($assets, $i, $batch_size);
+ foreach ($batch as $asset) {
+ $results[] = $operation_callback($asset);
+ }
+ // Add a small delay between batches
+ usleep(500000); // 500ms
+ }
+
+ return $results;
+}
+```
+
+## Testing
+
+### Create a Test Environment
+
+Set up a separate test environment for API integration development:
+
+1. Use a separate API user for testing
+2. Test with a limited dataset
+3. Implement automated tests for your integration
+
+### Monitor API Usage
+
+Monitor your API usage to identify patterns and optimize your integration:
+
+1. Track request frequency
+2. Monitor response times
+3. Analyze error rates
+4. Identify most frequently accessed resources
diff --git a/docs/api/endpoints.md b/docs/api/endpoints.md
new file mode 100644
index 0000000..e203070
--- /dev/null
+++ b/docs/api/endpoints.md
@@ -0,0 +1,127 @@
+# API Endpoints
+
+## Media Assets
+
+### Get Media Assets
+
+```
+GET /jsonapi/media/image
+```
+
+Retrieves a list of media assets of type "image".
+
+**Parameters:**
+- `filter[field_name][value]`: Filter by field value
+- `page[limit]`: Number of items per page
+- `page[offset]`: Offset for pagination
+- `sort`: Field to sort by (e.g., `sort=created`)
+
+**Response:**
+```json
+{
+ "data": [
+ {
+ "type": "media--image",
+ "id": "uuid",
+ "attributes": {
+ "name": "Image Name",
+ "created": "2023-01-01T12:00:00+00:00",
+ "changed": "2023-01-01T12:00:00+00:00",
+ "thumbnail": {
+ "alt": "Alt text",
+ "title": "Title text",
+ "url": "https://example.com/sites/default/files/styles/thumbnail/private/image.jpg"
+ },
+ "assets": {
+ "medium": "https://example.com/sites/default/files/styles/medium/private/image.jpg",
+ "large": "https://example.com/sites/default/files/styles/large/private/image.jpg"
+ },
+ "field_category": ["Category Name"],
+ "field_keywords": ["Keyword1", "Keyword2"],
+ "field_gps_gpslatitude": "47.497912",
+ "field_gps_gpslongitude": "19.040235",
+ "field_iptc_by_line": "Photographer Name",
+ "field_iptc_caption": "Image Caption",
+ "field_iptc_object_name": "Object Name"
+ }
+ }
+ ]
+}
+```
+
+### Get Single Media Asset
+
+```
+GET /jsonapi/media/image/{uuid}
+```
+
+Retrieves a single media asset by its UUID.
+
+**Response:**
+Same structure as above but with a single item.
+
+## File Access
+
+### Get File
+
+```
+GET /system/files/{file_path}
+```
+
+Retrieves a file by its path.
+
+### Get Private File
+
+```
+GET /system/private/file/download/{file_path}
+```
+
+Retrieves a private file by its path. Requires authentication.
+
+### Get Image Style
+
+```
+GET /image-style/{style}/private/{file_path}
+```
+
+Retrieves an image processed with the specified image style.
+
+**Parameters:**
+- `style`: The image style to apply (e.g., `thumbnail`, `medium`, `large`)
+- `file_path`: Path to the file
+
+## Taxonomy
+
+### Get Categories
+
+```
+GET /jsonapi/taxonomy_term/category
+```
+
+Retrieves a list of categories.
+
+### Get Keywords
+
+```
+GET /jsonapi/taxonomy_term/keywords
+```
+
+Retrieves a list of keywords.
+
+## Media Collections
+
+### Get Collections
+
+```
+GET /jsonapi/media_collection/media_collection
+```
+
+Retrieves a list of media collections.
+
+### Get Collection Items
+
+```
+GET /jsonapi/media_collection_item/media_collection_item
+```
+
+Retrieves a list of media collection items.
diff --git a/docs/api/integration-examples.md b/docs/api/integration-examples.md
new file mode 100644
index 0000000..3c2ac79
--- /dev/null
+++ b/docs/api/integration-examples.md
@@ -0,0 +1,233 @@
+# Integration Examples
+
+This document provides code examples for integrating with the DAM Open API using different programming languages and frameworks.
+
+## PHP Examples
+
+### Basic API Request
+
+```php
+ [
+ 'header' => 'Authorization: Basic ' . base64_encode("$username:$password")
+ ]
+ ]);
+
+ // Return image tag with authenticated URL
+ return '';
+}
+```
+
+### Using Guzzle HTTP Client
+
+```php
+request('GET', $base_url . '/jsonapi/media/image', [
+ 'headers' => [
+ 'Accept' => 'application/vnd.api+json',
+ 'Authorization' => 'Basic ' . base64_encode("$username:$password"),
+ ],
+ ]);
+
+ return json_decode($response->getBody(), TRUE);
+}
+```
+
+## JavaScript Examples
+
+### Fetch API
+
+```javascript
+/**
+ * Fetch media assets from DAM Open
+ */
+async function fetchDamAssets(baseUrl, username, password) {
+ const endpoint = `${baseUrl}/jsonapi/media/image`;
+ const response = await fetch(endpoint, {
+ headers: {
+ 'Accept': 'application/vnd.api+json',
+ 'Authorization': 'Basic ' + btoa(`${username}:${password}`)
+ }
+ });
+
+ return await response.json();
+}
+
+/**
+ * Display an image from DAM Open
+ */
+function displayDamImage(imageUri, style = 'medium', baseUrl, username, password) {
+ const imageUrl = `${baseUrl}/image-style/${style}/private/${imageUri}`;
+
+ // Create image element with authenticated URL
+ const img = document.createElement('img');
+ img.src = imageUrl;
+ img.alt = 'DAM Image';
+
+ // Add authentication headers when the image is requested
+ img.crossOrigin = 'use-credentials';
+
+ return img;
+}
+```
+
+### Axios Library
+
+```javascript
+// Using Axios library
+import axios from 'axios';
+
+/**
+ * Fetch media assets using Axios
+ */
+async function fetchDamAssetsAxios(baseUrl, username, password) {
+ const endpoint = `${baseUrl}/jsonapi/media/image`;
+
+ const response = await axios.get(endpoint, {
+ headers: {
+ 'Accept': 'application/vnd.api+json',
+ 'Authorization': 'Basic ' + btoa(`${username}:${password}`)
+ }
+ });
+
+ return response.data;
+}
+```
+
+## Drupal Module Integration
+
+### Custom Module Structure
+
+```
+my_dam_integration/
+ ├── my_dam_integration.info.yml
+ ├── my_dam_integration.module
+ ├── my_dam_integration.services.yml
+ └── src/
+ ├── DamOpenClient.php
+ ├── Form/
+ │ └── DamOpenSettingsForm.php
+ └── Plugin/
+ └── Field/
+ └── DamOpenMediaField.php
+```
+
+### Service Configuration
+
+```yaml
+# my_dam_integration.services.yml
+services:
+ my_dam_integration.client:
+ class: Drupal\my_dam_integration\DamOpenClient
+ arguments:
+ - '@config.factory'
+ - '@http_client'
+```
+
+### Client Implementation
+
+```php
+configFactory = $configFactory;
+ $this->httpClient = $httpClient;
+ }
+
+ /**
+ * Get media assets from DAM Open.
+ */
+ public function getMediaAssets() {
+ $config = $this->configFactory->get('my_dam_integration.settings');
+ $baseUrl = $config->get('base_url');
+ $username = $config->get('username');
+ $password = $config->get('password');
+
+ $response = $this->httpClient->request('GET', $baseUrl . '/jsonapi/media/image', [
+ 'headers' => [
+ 'Accept' => 'application/vnd.api+json',
+ 'Authorization' => 'Basic ' . base64_encode("$username:$password"),
+ ],
+ ]);
+
+ return json_decode($response->getBody(), TRUE);
+ }
+
+ /**
+ * Get image URL with style.
+ */
+ public function getImageUrl($imageUri, $style = 'medium') {
+ $config = $this->configFactory->get('my_dam_integration.settings');
+ $baseUrl = $config->get('base_url');
+
+ return $baseUrl . '/image-style/' . $style . '/private/' . $imageUri;
+ }
+}
+```
diff --git a/docs/api/overview.md b/docs/api/overview.md
new file mode 100644
index 0000000..6947629
--- /dev/null
+++ b/docs/api/overview.md
@@ -0,0 +1,39 @@
+# DAM Open API Overview
+
+## Introduction
+
+DAM Open provides a RESTful API for integrating with Drupal-based systems. The API is built on top of Drupal's JSON:API and REST modules, allowing for standardized access to digital assets stored in the DAM system.
+
+## Architecture
+
+The DAM Open API follows REST principles and uses JSON:API specification for structured responses. It provides access to media assets, taxonomies, and file operations through standardized endpoints.
+
+## Key Features
+
+- **Media Asset Access**: Retrieve and manage digital assets
+- **Taxonomy Integration**: Access categories and keywords
+- **Image Style Processing**: Generate different image styles on demand
+- **Authentication**: Secure access through Basic Authentication
+- **Standardized Responses**: Consistent JSON:API formatted responses
+
+## Prerequisites
+
+To integrate with DAM Open, your Drupal installation needs:
+
+- Drupal 9.1+ or Drupal 10.0+
+- JSON:API module enabled
+- REST module enabled
+- Basic Auth module enabled
+
+## Module Dependencies
+
+The API functionality is provided by the following modules:
+- `damopen_assets_api`: Core API functionality
+- `damopen_common`: Common utilities and services
+- `jsonapi_extras`: Enhanced JSON:API functionality
+
+## Getting Started
+
+1. Ensure you have proper authentication credentials
+2. Explore the available endpoints
+3. Test API calls using the examples provided in the [Integration Examples](integration-examples.md) document
diff --git a/modules/damopen_common/damopen_common.module b/modules/damopen_common/damopen_common.module
index 60f9164..3e244ff 100644
--- a/modules/damopen_common/damopen_common.module
+++ b/modules/damopen_common/damopen_common.module
@@ -1,5 +1,7 @@
id() !== 'media') {
+ return [];
+ }
+
+ // Check if user has permission to view unpublished media
+ if ($account->hasPermission('view any unpublished image media') ||
+ $account->hasPermission('administer media')) {
+ return [
+ JSONAPI_FILTER_AMONG_ALL => AccessResult::allowed(),
+ ];
+ }
+
+ // Check if user has permission to view own unpublished media
+ if ($account->hasPermission('view own unpublished media')) {
+ return [
+ JSONAPI_FILTER_AMONG_OWN => AccessResult::allowed(),
+ ];
+ }
+
+ return [];
+}
+
+/**
+ * Implements hook_jsonapi_entity_access().
+ *
+ * Grant access to unpublished media entities for authorized users.
+ */
+function damopen_common_jsonapi_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
+ // Only handle media entities
+ if ($entity->getEntityTypeId() !== 'media') {
+ return \Drupal\Core\Access\AccessResult::neutral();
+ }
+
+ // Only handle view operations
+ if ($operation !== 'view') {
+ return \Drupal\Core\Access\AccessResult::neutral();
+ }
+
+ // Check if media is unpublished
+ if ($entity->isPublished()) {
+ return \Drupal\Core\Access\AccessResult::neutral();
+ }
+
+ // Check permissions for unpublished media
+ if ($account->hasPermission('view any unpublished image media') ||
+ $account->hasPermission('administer media')) {
+ return \Drupal\Core\Access\AccessResult::allowed()
+ ->cachePerPermissions()
+ ->addCacheableDependency($entity);
+ }
+
+ // Check if user owns the media
+ if ($entity->hasField('uid') &&
+ $entity->get('uid')->target_id == $account->id() &&
+ $account->hasPermission('view own unpublished media')) {
+ return \Drupal\Core\Access\AccessResult::allowed()
+ ->cachePerPermissions()
+ ->cachePerUser()
+ ->addCacheableDependency($entity);
+ }
+
+ return \Drupal\Core\Access\AccessResult::neutral();
+}
diff --git a/modules/damopen_common/src/Plugin/Block/HeaderMenuBlock.php b/modules/damopen_common/src/Plugin/Block/HeaderMenuBlock.php
index de905ae..73b285c 100644
--- a/modules/damopen_common/src/Plugin/Block/HeaderMenuBlock.php
+++ b/modules/damopen_common/src/Plugin/Block/HeaderMenuBlock.php
@@ -64,7 +64,7 @@ public function build() {
$count = $query->count()->execute();
$menu['manage_assets'] = [
'title' => new TranslatableMarkup('My Assets waiting for approval'),
- 'url' => Url::fromRoute('view.unpublished_assets.user_unpublished_assets')->toString(),
+ 'url' => Url::fromRoute('damopen_wfa.assets')->toString(),
'class' => '',
'count' => $count,
];
diff --git a/modules/damopen_upload/damopen_upload.routing.yml b/modules/damopen_upload/damopen_upload.routing.yml
index 896a538..4f309e2 100644
--- a/modules/damopen_upload/damopen_upload.routing.yml
+++ b/modules/damopen_upload/damopen_upload.routing.yml
@@ -5,6 +5,7 @@ damopen_upload.upload:
_title: 'Image upload'
requirements:
_permission: 'access content'
+
damopen_upload.image_form:
path: '/upload-image-form'
defaults:
@@ -13,3 +14,11 @@ damopen_upload.image_form:
requirements:
_permission: 'access content'
_access: 'TRUE'
+
+damopen_wfa.assets:
+ path: '/waiting-for-approval'
+ defaults:
+ _controller: '\Drupal\damopen_upload\Controller\DamopenWfaController::waitingForApproval'
+ _title: 'Assets Waiting for Approval'
+ requirements:
+ _permission: 'access media asset library'
diff --git a/modules/damopen_upload/src/Controller/DamopenWfaController.php b/modules/damopen_upload/src/Controller/DamopenWfaController.php
new file mode 100644
index 0000000..69b355c
--- /dev/null
+++ b/modules/damopen_upload/src/Controller/DamopenWfaController.php
@@ -0,0 +1,32 @@
+ '