brandfolder-8.x-1.x-dev/src/Controller/BrandfolderBrowserController.php
src/Controller/BrandfolderBrowserController.php
<?php
namespace Drupal\brandfolder\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\brandfolder\Service\BrandfolderGatekeeper;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Core\TempStore\SharedTempStore;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Controller for Brandfolder browser data requests.
*/
class BrandfolderBrowserController extends ControllerBase {
/**
* Shared storage.
*
* @var SharedTempStore
*/
protected SharedTempStore $bfBrowserDataStore;
/**
* Brandfolder Gatekeeper service.
*
* @var \Drupal\brandfolder\Service\BrandfolderGatekeeper
*/
protected BrandfolderGatekeeper $brandfolderGatekeeper;
/**
* Constructs a new BrandfolderBrowserController object.
*/
public function __construct(SharedTempStoreFactory $shared_temp_store_factory, BrandfolderGatekeeper $brandfolder_gatekeeper) {
$this->bfBrowserDataStore = $shared_temp_store_factory->get('brandfolder_browser_data');
$this->brandfolderGatekeeper = $brandfolder_gatekeeper;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): BrandfolderBrowserController|static {
return new static(
$container->get('tempstore.shared'),
$container->get('brandfolder.gatekeeper')
);
}
/**
* Handler for data update requests from Brandfolder browsers.
*/
public function bfBrowserUpdate(Request $request) : JsonResponse {
$request_data = [];
$content = $request->getContent();
if (!empty($content)) {
$request_data = Json::decode($content);
}
$required_request_fields = [
'bfBrowserId',
];
foreach ($required_request_fields as $required_key) {
if (empty($request_data[$required_key])) {
return new JsonResponse(FALSE);
}
}
$bf_browser_id = $request_data['bfBrowserId'];
// Testing.
if ($bf_browser_id == 'abc123') {
$gatekeeper_criteria = [
'approved' => TRUE,
'expired' => FALSE,
'unpublished' => FALSE,
'allowed' => [
'filetype' => [
'jpg',
'png',
'gif',
'tiff',
'svg',
'webp',
],
],
'disallowed' => [
'section' => [],
],
];
}
else {
$bf_browser_data = $this->bfBrowserDataStore->get($bf_browser_id);
if (empty($bf_browser_data)) {
return new JsonResponse(FALSE);
}
$required_browser_data_fields = [
'gatekeeper_criteria',
];
foreach ($required_browser_data_fields as $required_key) {
if (empty($bf_browser_data[$required_key])) {
return new JsonResponse(FALSE);
}
}
$gatekeeper_criteria = $bf_browser_data['gatekeeper_criteria'];
}
// Default to fetching the first page, unless the user has requested
// another page.
$page_to_fetch = $request_data['requestedPage'] ?? 1;
// @todo: Config option, or centralized default.
$assets_per_page = $request_data['assetsPerPage'] ?? 100;
$query_params = [
'per' => $assets_per_page,
'page' => $page_to_fetch,
];
// Process user search text and all filters.
$user_input_mapping = [
'collection_key' => 'collections',
'section_key' => 'sections',
];
$user_criteria = [
'collection_key' => [],
'section_key' => [],
'aspect' => [],
'filetype' => [],
'tags' => [],
];
if (!empty($request_data['userInput'])) {
foreach (array_keys($user_criteria) as $criterion_type) {
$user_input_key = $user_input_mapping[$criterion_type] ?? $criterion_type;
if (!empty($request_data['userInput'][$user_input_key])) {
$user_criteria[$criterion_type] = $request_data['userInput'][$user_input_key];
}
}
}
$search_query_components = [];
$user_search_query = $request_data['userInput']['searchText'] ?? '';
if (!empty($user_search_query)) {
$search_query_components[] = $user_search_query;
}
foreach ($user_criteria as $criterion => $allowed_values) {
if (count($allowed_values) > 0) {
array_walk($allowed_values, function(&$value) {
$value = "\"$value\"";
});
if ($criterion == 'tags' && $request_data['userInput']['tagFilterMode'] == 'all') {
$separator = ' AND ';
}
else {
$separator = ' OR ';
}
$search_query_components[] = "$criterion:(" . implode($separator, $allowed_values) . ')';
}
}
// Labels.
if (!empty($request_data['userInput']['labels'])) {
$selected_label_names = $request_data['userInput']['labels'];
array_walk($selected_label_names, function(&$value) {
$value = "\"$value\"";
});
$search_query_components[] = "labels:(" . implode(' OR ', $selected_label_names) . ')';
}
// Date range options.
$date_range_control_mapping = [
'creationDate' => 'created_at',
'modificationDate' => 'updated_at',
'publicationDate' => 'published_at',
];
foreach ($date_range_control_mapping as $control_key => $date_field) {
if (!empty($request_data['userInput'][$control_key])) {
$date_input = $request_data['userInput'][$control_key];
if ($date_input != 'all') {
$search_query_components[] = "$date_field:>now-$date_input";
}
}
}
// Assemble the search query string.
if (!empty($search_query_components)) {
array_walk($search_query_components, function(&$subquery) {
$subquery = "($subquery)";
});
$query_params['search'] = implode(' AND ', $search_query_components);
}
// Sorting.
$query_params['sort_by'] = $request_data['userInput']['sortCriterion'] ?? 'created_at';
$query_params['order'] = $request_data['userInput']['sortOrder'] ?? 'desc';
$gatekeeper = $this->brandfolderGatekeeper;
$gatekeeper->setCriteria($gatekeeper_criteria);
$query_params['include'] = 'attachments';
$query_params['fields'] = 'cdn_url,availability,created_at,updated_at,availability_start,availability_end';
$result = $gatekeeper->fetchAssets($query_params);
$response_data = FALSE;
if ($result) {
// Assemble data related to browser controls (user search/filtering/etc.).
// @todo: Consider proactively customizing/winnowing this list based on which filter combinations will yield results.
$control_schema = [
'searchText' => '',
'tags' => [],
];
$collections = $gatekeeper->getCollections();
if (count($collections) > 1) {
$control_schema['collections'] = $collections;
}
$sections = $gatekeeper->getSections();
if (count($sections) > 1) {
$control_schema['sections'] = $sections;
}
$labels = $gatekeeper->getLabels();
if (count($labels) > 1) {
$control_schema['labels'] = $labels;
}
// @todo: maintain a registry of file types that are actually used and popular for the given Brandfolder and use those.
// @todo: Also consider whether filetype is really a useful filter, since assets often have attachments of multiple file types, and we encourage CDN delivery using auto/optimized format options regardless of original image format.
$filetype_options = [
'avif' => 'AVIF',
'gif' => 'GIF',
'png' => 'PNG',
'jpg' => 'JPG/JPEG',
'svg' => 'SVG',
'tiff' => 'TIFF',
'webp' => 'WebP',
// 'mp4' => 'MP4',
];
if (isset($gatekeeper_criteria['allowed']['filetype']) && is_array($gatekeeper_criteria['allowed']['filetype'])) {
$filetype_options = array_intersect_key($filetype_options, array_flip($gatekeeper_criteria['allowed']['filetype']));
}
if (isset($gatekeeper_criteria['disallowed']['filetype']) && is_array($gatekeeper_criteria['disallowed']['filetype'])) {
$filetype_options = array_diff_key($filetype_options, array_flip($gatekeeper_criteria['disallowed']['filetype']));
}
$predefined_date_range_options = [
'all' => t('All'),
'30m' => t('Last 30 Minutes'),
'1d' => t('Last 24 Hours'),
'7d' => t('Last 7 Days'),
'30d' => t('Last 30 Days'),
'60d' => t('Last 60 Days'),
'90d' => t('Last 90 Days'),
];
$control_schema += [
'aspect' => [
'landscape' => t('Horizontal'),
'portrait' => t('Vertical'),
'square' => t('Square'),
'panorama' => t('Panoramic'),
],
'creationDate' => $predefined_date_range_options,
'modificationDate' => $predefined_date_range_options,
'publicationDate' => $predefined_date_range_options,
'sortCriteria' => [
'name' => t('Name'),
'score' => t('Score'),
'position' => t('Position'),
'updated_at' => t('Date Last Updated'),
'created_at' => t('Date Uploaded/Created'),
],
'sortOrder' => [
'asc' => t('Ascending'),
'desc' => t('Descending'),
],
];
if (!empty($filetype_options)) {
$control_schema['filetype'] = $filetype_options;
}
$response_data = [
'assets' => $result->data,
'meta' => $result->meta,
'controlSchema' => $control_schema,
];
}
return new JsonResponse($response_data);
}
/**
* Handler for Brandfolder browsers requesting attachment data.
*/
public function bfBrowserGetAttachmentsById(Request $request) : JsonResponse {
$request_data = [];
$content = $request->getContent();
if (!empty($content)) {
$request_data = Json::decode($content);
}
$required_request_fields = [
'attachmentIds',
];
foreach ($required_request_fields as $required_key) {
if (empty($request_data[$required_key])) {
return new JsonResponse(FALSE);
}
}
$attachment_ids = $request_data['attachmentIds'];
$result = $this->brandfolderGatekeeper->fetchAttachmentsById($attachment_ids);
$response_data = FALSE;
if ($result) {
$response_data = [
'attachments' => $result->data,
'meta' => $result->meta,
];
}
return new JsonResponse($response_data);
}
}
