media_acquiadam.module in Media: Acquia DAM 8
Same filename and directory in other branches
Integrates Drupal with Acquia DAM.
File
media_acquiadam.moduleView source
<?php
/**
* @file
* Integrates Drupal with Acquia DAM.
*/
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\media\MediaInterface;
/**
* Implements hook_theme().
*/
function media_acquiadam_theme($existing, $type, $theme, $path) {
return [
'asset_details' => [
'variables' => [
'asset_data' => [],
'asset_preview' => NULL,
'asset_link' => NULL,
],
],
'checkboxes__acquiadam_assets' => [
'render element' => 'element',
'template' => 'checkboxes--acquiadam-assets',
],
'asset_browser_message' => [
'variables' => [
'message' => [],
],
],
];
}
/**
* Implements hook_field_formatter_info_alter().
*/
function media_acquiadam_field_formatter_info_alter(&$info) {
// Allow using the image formatter on a file field.
if (isset($info['image']) && !in_array('file', $info['image']['field_types'])) {
$info['image']['field_types'][] = 'file';
}
// Allow using the responsive image formatter on a file field.
if (isset($info['responsive_image']) && !in_array('file', $info['responsive_image']['field_types'])) {
$info['responsive_image']['field_types'][] = 'file';
}
}
/**
* Implements hook_entity_type_alter().
*/
function media_acquiadam_entity_type_alter(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
$entity_types['user']
->setLinkTemplate('acquiadam-auth-form', '/user/{user}/acquiadam');
}
/**
* Implements hook_menu_local_tasks_alter().
*
* Hides the 'Acquia DAM' tab on the user profile if the user is not viewing
* their own profile.
*/
function media_acquiadam_menu_local_tasks_alter(&$data, $route_name) {
if ($route_name === 'entity.user.canonical') {
foreach ($data['tabs'][0] as $key => $link) {
$url = $link['#link']['url'];
if ($url
->getRouteName() === 'entity.user.acquiadam_auth') {
if ($url
->getRouteParameters()['user'] != Drupal::currentUser()
->id()) {
unset($data['tabs'][0][$key]);
}
}
}
}
}
/**
* Implements hook_preprocess_HOOK().
*
* Allow custom markup for acquiadam asset checkboxes.
*/
function media_acquiadam_preprocess_checkboxes__acquiadam_assets(&$variables) {
$element = $variables['element'];
$variables['children'] = $element['#children'];
$variables['element']['#theme'] = 'checkboxes__acquiadam_assets';
}
/**
* Implements hook_ENTITY_TYPE_presave().
*
* Override the preSave function from \Drupal\media\Entity\Media because it
* ignores empty values (e.g. status = 0) and doesn't set date values for
* created/changed because they are already set by Drupal core.
*/
function media_acquiadam_media_presave(MediaInterface $entity) {
// Only go for a resync when updating existing entities.
if ($entity
->isNew()) {
return;
}
/** @var \Drupal\media_acquiadam\Plugin\media\Source\AcquiadamAsset $source */
$source = $entity
->getSource();
if ($source
->getPluginId() !== 'acquiadam_asset') {
return;
}
/** @var \Drupal\media_acquiadam\Service\AssetMediaFactory $asset_media_factory */
$asset_media_factory = Drupal::service('media_acquiadam.asset_media.factory');
$media_helper = $asset_media_factory
->get($entity);
$assetID = $media_helper
->getAssetId();
if (empty($assetID)) {
return;
}
$asset = $media_helper
->getAsset();
if (empty($asset)) {
Drupal::logger('media_acquiadam')
->warning('Unable to retrieve asset @assetID.', [
'@assetID' => $assetID,
]);
return;
}
foreach ($entity->bundle->entity
->getFieldMap() as $source_field => $destination_field) {
if ($entity
->hasField($destination_field)) {
$entity
->set($destination_field, $source
->getMetadata($entity, $source_field));
}
}
/** @var \Drupal\media\Entity\Media $entity */
$entity
->updateQueuedThumbnail();
}
/**
* Implements hook_ENTITY_TYPE_delete().
*/
function media_acquiadam_media_delete(MediaInterface $entity) {
if ($entity
->getSource()
->getPluginId() !== 'acquiadam_asset') {
return;
}
/** @var \Drupal\media_acquiadam\Service\AssetMediaFactory $asset_media_factory */
$asset_media_factory = Drupal::service('media_acquiadam.asset_media.factory');
$asset_id = $asset_media_factory
->get($entity)
->getAssetId();
if (!empty($asset_id)) {
$usages = $asset_media_factory
->getAssetUsage($asset_id);
// If usages is empty then that means no media entities are referencing the
// asset ID and it should be safe to delete the stored data.
if (empty($usages)) {
Drupal::service('media_acquiadam.asset_data')
->delete($asset_id);
}
}
}
/**
* Implements hook_cron().
*
* Refresh acquiadam metadata and sync to media entities.
*/
function media_acquiadam_cron() {
$cron_config = Drupal::configFactory()
->get('media_acquiadam.settings');
$interval = $cron_config
->get('sync_interval');
// 3600 seconds = 1 hour.
$interval = !empty($interval) ? $interval : 3600;
// Rate limit so we're not syncing every minute even if cron is configured to.
$next_execution = Drupal::state()
->get('media_acquiadam.next_sync');
$next_execution = !empty($next_execution) ? $next_execution : 0;
$request_time = Drupal::time()
->getRequestTime();
// -1 interval means run on every cron.
$process_queues = $interval == -1 || $request_time >= $next_execution;
if (!$process_queues) {
return;
}
media_acquiadam_purge_expired_tokens();
if ($cron_config
->get('notifications_sync')) {
media_acquiadam_refresh_asset_sync_notifications_queue();
}
else {
media_acquiadam_refresh_asset_sync_queue();
}
$total_queue_items = Drupal::queue('media_acquiadam_asset_refresh')
->numberOfItems();
/** @var \Drupal\Core\Logger\LoggerChannelInterface $logger */
$logger = \Drupal::service('logger.factory')
->get('media_acquiadam');
$logger
->info('Total items in the queue: @items.', [
'@items' => $total_queue_items,
]);
Drupal::state()
->set('media_acquiadam.next_sync', $request_time + $interval);
}
/**
* Adds media items to the asset sync queue for later processing.
*
* Uses the Notifications API to get affected asset ids. Determines which assets
* where changed within the given period of time, and adds them to the queue.
*/
function media_acquiadam_refresh_asset_sync_notifications_queue() {
$asset_id_fields = media_acquiadam_get_bundle_asset_id_fields();
if (empty($asset_id_fields)) {
return;
}
$queue = Drupal::queue('media_acquiadam_asset_refresh');
// We only want to re-queue everything when the queue is totally empty. This
// should help minimize the number of duplicate syncs we perform on assets.
if ($queue
->numberOfItems() > 0) {
return;
}
/** @var \Drupal\media_acquiadam\Service\AssetRefreshManagerInterface $asset_refresh_manager */
$asset_refresh_manager = \Drupal::service('media_acquiadam.asset_refresh.manager');
$asset_refresh_manager
->updateQueue($asset_id_fields);
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function media_acquiadam_form_media_form_alter(&$form, FormStateInterface &$form_state) {
/** @var \Drupal\media\Entity\Media $entity */
$entity = $form_state
->getFormObject()
->getEntity();
/** @var \Drupal\media_acquiadam\Plugin\media\Source\AcquiadamAsset $source */
$source = $entity
->getSource();
if ($entity
->isNew() || $source
->getPluginId() !== 'acquiadam_asset') {
return;
}
// Disables Asset ID field on edit.
$form[$source
->getSourceFieldDefinition($entity
->get('bundle')->entity)
->getName()]['#disabled'] = TRUE;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function media_acquiadam_form_media_type_edit_form_alter(&$form, FormStateInterface &$form_state) {
$form['#attached']['library'][] = 'media_acquiadam/asset_form';
// Add a field mapping to the entity publishing status if one doesn't exist.
$mappings =& $form['source_dependent']['field_map'];
if (!empty($mappings)) {
foreach (Element::children($mappings) as $key) {
// Sort fields with assigned mappings to the top to make it easier to
// identify what is and is not mapped at a glance.
// We need to set different weights based on if we're dealing with an
// XMP field or not.
$mappings[$key]['#weight'] = strpos($key, 'xmp_') === FALSE ? 0 : 100;
if (!empty($mappings[$key]['#default_value']) && $mappings[$key]['#default_value'] !== '_none') {
$mappings[$key]['#weight'] -= 10;
}
$options =& $mappings[$key]['#options'];
if (!isset($options['status'])) {
$options['status'] = t('Publishing status');
}
}
}
// Insert headline before first XMP field in the fieldset.
$mapping_keys = array_keys($mappings);
$xmp_keys = preg_grep('/xmp_.+/i', $mapping_keys);
reset($xmp_keys);
$offset = key($xmp_keys);
if ($offset) {
$xmp_header = [
'xmp_header' => [
'#type' => 'html_tag',
'#tag' => 'h4',
'#value' => t('Field Mapping: XMP Metadata'),
'#attributes' => [
'class' => 'fieldset-subhead',
],
],
];
array_splice($mappings, $offset, 0, $xmp_header);
}
}
/**
* Adds media items to the asset sync queue for later processing.
*
* Adds all existing active media entities to the queue.
*
* @return int
* The number of items currently in the queue.
*/
function media_acquiadam_refresh_asset_sync_queue() {
$queue = Drupal::queue('media_acquiadam_asset_refresh');
// We only want to re-queue everything when the queue is totally empty. This
// should help minimize the number of duplicate syncs we perform on assets.
if ($queue
->numberOfItems() > 0) {
return $queue
->numberOfItems();
}
foreach (media_acquiadam_get_active_media_ids() as $media_id) {
$queue
->createItem([
'media_id' => $media_id,
]);
}
return $queue
->numberOfItems();
}
/**
* Returns the list of all active media entity ids.
*
* @return array
* The list of media entity ids.
*/
function media_acquiadam_get_active_media_ids() : array {
$asset_id_fields = media_acquiadam_get_bundle_asset_id_fields();
if (empty($asset_id_fields)) {
return [];
}
$media_ids = [];
foreach ($asset_id_fields as $bundle => $field) {
$media_id_query = Drupal::entityQuery('media')
->condition('bundle', $bundle);
$media_ids = array_merge($media_ids, $media_id_query
->execute());
}
return $media_ids;
}
/**
* Purge Acquia DAM authentication tokens for inactive Drupal users.
*/
function media_acquiadam_purge_expired_tokens() {
$cookie_params = session_get_cookie_params();
// 2000000 is the Drupal default value for cookie lifetime (approx 23 days).
$lifetime = $cookie_params['lifetime'] ?: 2000000;
// The user data keys associated with DAM authentication.
$purgable_keys = [
'acquiadam_access_token',
'acquiadam_refresh_token',
'acquiadam_access_token_expiration',
];
try {
$db = Drupal::database();
$expired_tokens_query = $db
->select('users_data', 'ud_expired')
->fields('ud_expired', [
'uid',
])
->condition('ud_expired.module', 'media_acquiadam')
->condition('ud_expired.name', 'acquiadam_access_token_expiration')
->condition('ud_expired.value', \Drupal::time()
->getRequestTime() - $lifetime, '<=');
// Select our UID list into a temporary table so we can delete without
// involving PHP. queryTemporary requires a string-based query at this time.
$temporary_table = $db
->queryTemporary((string) $expired_tokens_query, $expired_tokens_query
->getArguments());
$temporary_query = $db
->select($temporary_table, 'tt')
->fields('tt', [
'uid',
]);
$deleted_count = $db
->delete('users_data')
->condition('module', 'media_acquiadam')
->condition('name', $purgable_keys, 'IN')
->condition('uid', $temporary_query, 'IN')
->execute();
if ($deleted_count > 0) {
Drupal::logger('media_acquiadam')
->info('Deleted @count records (approx. @estimate inactive users).', [
'@count' => $deleted_count,
'@estimate' => intval($deleted_count / count($purgable_keys)),
]);
}
} catch (Exception $x) {
Drupal::logger('media_acquiadam')
->error('Unable to purge old authentication tokens.');
watchdog_exception('media_acquiadam', $x);
}
}
/**
* Get a list of asset ID fields related to their bundle.
*
* @return array
* An array of media bundles and associated asset ID fields
*/
function media_acquiadam_get_bundle_asset_id_fields() {
return Drupal::service('media_acquiadam.asset_media.factory')
->getAssetIdFields();
}
/**
* Implements hook_views_data().
*/
function media_acquiadam_views_data() {
// Expose acquiadam_assets_data table to views.
$data['acquiadam_assets_data']['table']['group'] = t('Acquia DAM');
$data['acquiadam_assets_data']['name'] = [
'title' => t('Acquia DAM asset data name'),
'help' => t('The name of the asset data.'),
'field' => [
'id' => 'standard',
],
'sort' => [
'id' => 'standard',
],
'filter' => [
'id' => 'string',
],
'argument' => [
'id' => 'string',
],
];
return $data;
}
/**
* Implements hook_views_data_alter().
*/
function media_acquiadam_views_data_alter(array &$data) {
// The default source_field defined in MediaSource plugin.
$source_field = Drupal::service('plugin.manager.media.source')
->createInstance('acquiadam_asset')
->defaultConfiguration()['source_field'];
// The source field table contains the media item's asset id.
$source_field_table = 'media__' . $source_field;
// The asset id.
$source_field_asset_id = $source_field . '_value';
// Grouping display in Views UI.
$data[$source_field_table]['table']['group'] = t('Acquia DAM');
// Provide a relationship between source field and asset data tables.
$data[$source_field_table]['acquiadam_source_to_asset_data'] = [
'title' => t('Asset field to data'),
'help' => t('Creates a relationship between the media source field to the asset data.'),
'relationship' => [
'base' => 'acquiadam_assets_data',
'base field' => 'asset_id',
'field' => $source_field_asset_id,
'id' => 'standard',
'label' => t('Asset data'),
],
];
}