media_acquiadam-8.x-1.46/media_acquiadam.module
media_acquiadam.module
<?php
/**
* @file
* Integrates Drupal with Acquia DAM.
*/
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\media\MediaInterface;
use Drupal\media_acquiadam\AcquiadamAuthService;
/**
* Implements hook_form_FORM_ID_alter().
*
* Adds Acquia DAM authorization field on user profile form. A link will be
* provided if user has not authenticated yet. On the other hand if the
* current user account has already been associated with a username and token,
* an option (a checkbox) to remove such credentials is given.
*/
function media_acquiadam_form_user_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// We don't want to alter the user creation form.
$account = $form_state->getFormObject()->getEntity();
if (!$account->id()) {
return;
}
global $base_url;
// Get the editing user.
$user = \Drupal::routeMatch()->getParameter('user');
$acquiadam_account = \Drupal::service('user.data')
->get('media_acquiadam', $user->id(), 'account');
$form['media_acquiadam'] = [
'#type' => 'details',
'#title' => t('Acquia DAM Authorization'),
'#weight' => 7,
'#open' => TRUE,
];
$domain = \Drupal::config('media_acquiadam.settings')->get('domain');
if (!empty($domain)) {
if (isset($acquiadam_account['acquiadam_token'])) {
$acquiadam_username = $acquiadam_account['acquiadam_username'];
$form['media_acquiadam']['remove_auth'] = [
'#type' => 'checkbox',
'#title' => t('Remove Acquia DAM authorization'),
'#prefix' => t('Currently authorized with Acquia DAM as "@username".', ['@username' => $acquiadam_username]) . '<br />',
];
$form['actions']['submit']['#submit'][] = 'media_acquiadam_unauthorize';
}
else {
$return_link = $base_url . Url::fromRoute('media_acquiadam.user_auth')->toString() . '?uid=' . $user->id();
$form['media_acquiadam']['auth_link'] = [
'#markup' => '<a href="' . AcquiadamAuthService::generateAuthUrl($return_link) . '">' . t('Authorize with Acquia DAM.') . '</a>',
];
}
}
else {
$form['media_acquiadam']['setup_message'] = [
'#markup' => t('You must configure your Acquia DAM domain first. @link', ['@link' => Link::createFromRoute(t('Click here to configure.'), 'media_acquiadam.config')->toString()]),
];
}
}
/**
* Custom user form submit handler to unauthorize the user on Acquia DAM.
*/
function media_acquiadam_unauthorize($form, FormStateInterface $form_state) {
if ($form_state->getValue('remove_auth')) {
// Get the editing user.
$user = \Drupal::routeMatch()->getParameter('user');
$acquiadam_account = \Drupal::service('user.data')
->get('media_acquiadam', $user->id(), 'account');
// Cancel the user token on Acquia DAM.
try {
$cancelled = AcquiadamAuthService::cancel($acquiadam_account['acquiadam_token']);
}
catch (exception $e) {
// Check if we got an 'Unauthorized' response.
if ($e->getCode() == '401') {
// The access token we are trying to cancel is the same token used to
// make the cancel() call to the API. If the token is already cancelled
// elsewhere, or has expired, our cancel call will throw this error.
// If we receive a 401 here, we should note the token as cancelled and
// proceed to the code below so the token is still removed from the
// database.
// If uncaught, this error will cause a WSOD and prevent us from
// unlinking tokens that produce a 401.
$cancelled = TRUE;
}
else {
// If this was any other error, rethrow it.
throw $e;
}
}
// Remove the Acquia DAM data (mainly the token) from the user account.
if ($cancelled) {
\Drupal::service('user.data')
->set('media_acquiadam', $user->id(), 'account', []);
}
}
}
/**
* A form submission handler callback to initialize the media type processing.
*/
function _set_redirect_migration_confirm_page($form, FormStateInterface $form_state) {
if ($form_state->getTriggeringElement()['#parents'][0] === 'save_and_migrate') {
$form_state->setRedirectUrl(Url::fromRoute('media_acquiadam.migration_confirm'));
}
}
/**
* 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_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 ($asset === NULL) {
\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_insert().
*
* Register integration link on Acquia DAM when media gets created.
*/
function media_acquiadam_media_insert(MediaInterface $entity) {
// Don't process if the config is set to not report asset usage.
$config = \Drupal::configFactory()->get('media_acquiadam.settings');
if (!$config->get('report_asset_usage')) {
return;
}
// Only process the media using the Acquia DAM source.
/** @var \Drupal\media_acquiadam\Plugin\media\Source\AcquiadamAsset $source */
$source = $entity->getSource();
if ($source->getPluginId() !== 'acquiadam_asset') {
return;
}
// Only process the media which have an asset id (uuid).
/** @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;
}
// Queue the registration of integration link for later process.
\Drupal::queue('media_acquiadam_integration_link_report')->createItem([
'mid' => $entity->id(),
'asset_uuid' => $assetID,
'url' => \Drupal::request()->getSchemeAndHttpHost(),
]);
}
/**
* 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() {
$config = \Drupal::configFactory()->get('media_acquiadam.settings');
$interval = $config->get('sync_interval') ?? 3600;
$last_execution = \Drupal::state()->get('media_acquiadam.last_sync') ?? 0;
$request_time = \Drupal::time()->getRequestTime();
// Check if we need to execute.
if ($interval !== -1 && $last_execution + $interval >= $request_time) {
return;
}
if ($config->get('sync_method') === 'updated_date') {
$new_items = media_acquiadam_refresh_asset_sync_updated_date_queue();
}
else {
$new_items = media_acquiadam_refresh_asset_sync_queue();
}
$count = \Drupal::queue('media_acquiadam_asset_refresh')->numberOfItems();
/** @var \Drupal\Core\Logger\LoggerChannelInterface $logger */
$logger = \Drupal::service('logger.factory')->get('media_acquiadam');
$logger->info('New items added: @new_items. Total items in the queue: @count.', [
'@new_items' => $new_items,
'@count' => $count,
]
);
\Drupal::state()->set('media_acquiadam.last_sync', $request_time);
}
/**
* Adds media items to the asset sync queue for later processing.
*
* Uses the Search API to get affected asset ids. Determines which assets
* where changed within the given period of time, and adds them to the queue.
*
* @return int
* The number of items currently in the queue.
*/
function media_acquiadam_refresh_asset_sync_updated_date_queue() {
$asset_id_fields = media_acquiadam_get_bundle_asset_id_fields();
if (empty($asset_id_fields)) {
return FALSE;
}
/** @var \Drupal\media_acquiadam\Service\AssetRefreshManagerInterface $asset_refresh_manager */
$asset_refresh_manager = \Drupal::service('media_acquiadam.asset_refresh.manager');
return $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) {
$message = '';
$messenger = \Drupal::messenger();
$account = \Drupal::currentUser();
$mapping_field_min_weight = 0;
$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.
$mappings[$key]['#weight'] = 0;
if (!empty($mappings[$key]['#default_value']) && $mappings[$key]['#default_value'] !== '_none') {
$mappings[$key]['#weight'] -= 10;
}
// Get the minimum weight of the mapping items.
if ($mapping_field_min_weight > $mappings[$key]['#weight']) {
$mapping_field_min_weight = $mappings[$key]['#weight'];
}
$options = &$mappings[$key]['#options'];
if (!isset($options['status'])) {
$options['status'] = t('Publishing status');
}
// Remove Asset ID field from mapping list.
unset($options['field_acquiadam_asset_id']);
}
}
// Assign minimum weight to the description, so it shows at the top of the
// other mapping fields.
if (isset($mappings['description']) && ($mapping_field_min_weight != 0)) {
$mappings['description']['#weight'] = $mapping_field_min_weight - 10;
}
// Check whether user is authenticated or not.
$client = \Drupal::service('media_acquiadam.client');
$user_authenticated = $client->checkAuth();
$return_link = Url::fromRoute('media_acquiadam.user_auth', ['uid' => $account->id()], ['absolute' => TRUE])->toString();
// Fetch Auth URL.
$auth_url = AcquiadamAuthService::generateAuthUrl($return_link);
// Add a warning if module is not configured.
if (!$auth_url) {
// If Acquia Dam module is not configured yet, display an warning to
// Configure the module first.
$message = t('Acquia DAM module is not configured yet. Please contact your administrator to do so.');
if ($account->hasPermission('administer site configuration')) {
$message = t('Acquia DAM module is not configured yet. Please %config it to start using Acquia DAM assets.', [
'%config' => Link::createFromRoute(t('configure'), 'media_acquiadam.config', [], ['attributes' => ['target' => '_blank']])->toString(),
]);
}
}
// Add a warning user is not authenticated and module is not configured.
elseif (!$user_authenticated && $auth_url) {
$auth_link = Url::fromUri($auth_url, ['attributes' => ['target' => '_blank']]);
$message = t('You are not authenticated on Acquia DAM. Please %authenticate to see and map all metadata fields.', [
'%authenticate' => Link::fromTextAndUrl("Authenticate", $auth_link)->toString(),
]);
}
if ($message) {
$messenger->addWarning($message);
}
}
/**
* 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() {
$total = 0;
$queue = \Drupal::queue('media_acquiadam_asset_refresh');
foreach (media_acquiadam_get_active_media_ids() as $media_id) {
if ($queue->createItem(['media_id' => $media_id]) !== FALSE) {
$total++;
}
}
return $total;
}
/**
* 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')
->accessCheck(FALSE)
->condition('bundle', $bundle);
$media_ids = array_merge($media_ids, $media_id_query->execute());
}
return $media_ids;
}
/**
* 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'),
],
];
}
/**
* Helper to parse the reference updation CSV.
*
* The CSV contains the association between the legacy asset_ids and the
* new asset_ids to be updated in medias.
*
* @param string $filename
* The full path + filename of the updation file to process.
* @param string $delimiter
* The csv delimiter.
*
* @return array
* An associative array. Keys are the legacy asset_ids, values are the new
* asset_ids.
*/
function _media_acquiadam_parse_reference_updation_csv(string $filename, string $delimiter = ',') {
$data = [];
if (($handle = fopen($filename, 'r')) !== FALSE) {
$counter = 0;
while (($row = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
$counter++;
if ($counter != 1) {
$data[trim($row[0])] = trim($row[1]);
}
}
fclose($handle);
}
return $data;
}
/**
* Helper to get the list of Acquia Dam's medias entity ids.
*
* @return array
* A list of media entity ids keyed by their respective asset ids.
*/
function media_acquiadam_get_active_media_ids_by_asset_ids() {
// Get the list of media bundle which are using the acquiadam media source.
// For each bundle, we get the asset_id field name.
$asset_id_fields = media_acquiadam_get_bundle_asset_id_fields();
$ids = [];
// Build an associative array of all the existing AcquiaDam medias. The
// key is the legacy asset_id, the value is an array of entity (media) ids.
foreach ($asset_id_fields as $bundle => $field) {
$query = \Drupal::database()->select('media__' . $field, 'asset')
->fields('asset', ['entity_id', $field . '_value'])
->condition('bundle', $bundle);
$items = $query->execute()->fetchAll();
foreach ($items as $item) {
$ids[$item->{$field . '_value'}][] = $item->entity_id;
}
}
return $ids;
}
/**
* Helper to build the batch list of operations to update the assets ids.
*
* @param array $legacy_ids_to_new_ids
* The array mapping the legacy asset_ids and the new asset_ids.
*
* @return array
* The batch array.
*/
function _media_acquiadam_build_reference_updation_batch(array $legacy_ids_to_new_ids) {
// Fetch a mapping btw legacy asset ids and their respective entity (media)
// ids.
$legacy_ids_to_entity_ids = media_acquiadam_get_active_media_ids_by_asset_ids();
// Loop through all the medias. If the asset id is present in
// the csv list, we add the info to the batch queue to update the media.
$operations = [];
foreach ($legacy_ids_to_entity_ids as $legacy_id => $entity_ids) {
if (array_key_exists($legacy_id, $legacy_ids_to_new_ids)) {
foreach ($entity_ids as $entity_id) {
$operations[] = [
'\Drupal\media_acquiadam\Batch\AcquiadamUpdateAssetsReference::updateTable',
[$entity_id, $legacy_id, $legacy_ids_to_new_ids[$legacy_id]],
];
}
}
}
$batch = [
'title' => t('Migrating AcquiaDam medias'),
'operations' => $operations,
'finished' => '\Drupal\media_acquiadam\Batch\AcquiadamUpdateAssetsReference::finishBatchOperation',
];
return $batch;
}
