Skip to content

Commit

Permalink
FEATURE: Allow filtering by media type
Browse files Browse the repository at this point in the history
Resolves: #186
  • Loading branch information
Sebobo committed Apr 8, 2023
1 parent bd3f008 commit 8d2b6f6
Show file tree
Hide file tree
Showing 25 changed files with 250 additions and 86 deletions.
76 changes: 44 additions & 32 deletions Classes/Domain/Model/AssetSource/NeosAssetProxyRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Neos\Media\Domain\Model\AssetSource\SupportsCollectionsInterface;
use Neos\Media\Domain\Model\AssetSource\SupportsSortingInterface;
use Neos\Media\Domain\Model\AssetSource\SupportsTaggingInterface;
use Neos\Media\Domain\Model\ImageVariant;
use Neos\Media\Domain\Model\Tag;
use Neos\Media\Domain\Repository\AssetRepository;
use Neos\Media\Domain\Repository\AudioRepository;
Expand Down Expand Up @@ -74,6 +75,7 @@ final class NeosAssetProxyRepository implements AssetProxyRepositoryInterface, S
private $activeAssetCollection;

private string $assetTypeFilter = 'All';
private string $mediaTypeFilter = '';

private array $assetRepositoryClassNames = [
'All' => AssetRepository::class,
Expand Down Expand Up @@ -115,6 +117,11 @@ public function filterByType(AssetTypeFilter $assetType = null): void
$this->initializeObject();
}

public function filterByMediaType(string $mediaType): void
{
$this->mediaTypeFilter = $mediaType;
}

/**
* NOTE: This needs to be refactored to use an asset collection identifier instead of Media's domain model before
* it can become a public API for other asset sources.
Expand All @@ -124,6 +131,19 @@ public function filterByCollection(AssetCollection $assetCollection = null): voi
$this->activeAssetCollection = $assetCollection;
}

private function filterQuery(QueryInterface $query, bool $filterOtherCollections = false): QueryInterface
{
$query = $this->filterOutImportedAssetsFromOtherAssetSources($query);
$query = $this->filterOutImageVariants($query);
if ($filterOtherCollections) {
$query = $this->filterOutAssetsFromOtherAssetCollections($query);
}
if ($this->mediaTypeFilter) {
$query = $this->filterOutAssetsWithOtherMediaTypes($query);
}
return $query;
}

/**
* @throws NeosAssetNotFoundException
*/
Expand All @@ -138,73 +158,55 @@ public function getAssetProxy(string $identifier): AssetProxyInterface

public function findAll(): AssetProxyQueryResultInterface
{
$queryResult = $this->assetRepository->findAll($this->activeAssetCollection);
$query = $this->filterOutImportedAssetsFromOtherAssetSources($queryResult->getQuery());
$query = $this->filterOutImageVariants($query);
$query = $this->filterQuery($this->assetRepository->findAll($this->activeAssetCollection)->getQuery());
return new NeosAssetProxyQueryResult($query->execute(), $this->assetSource);
}

public function findBySearchTerm(string $searchTerm): AssetProxyQueryResultInterface
{
$queryResult = $this->assetRepository->findBySearchTermOrTags($searchTerm, [], $this->activeAssetCollection);
$query = $this->filterOutImportedAssetsFromOtherAssetSources($queryResult->getQuery());
$query = $this->filterOutImageVariants($query);
$query = $this->filterQuery($this->assetRepository->findBySearchTermOrTags($searchTerm, [],
$this->activeAssetCollection)->getQuery());
return new NeosAssetProxyQueryResult($query->execute(), $this->assetSource);
}

public function findByTag(Tag $tag): AssetProxyQueryResultInterface
{
$queryResult = $this->assetRepository->findByTag($tag, $this->activeAssetCollection);
$query = $this->filterOutImportedAssetsFromOtherAssetSources($queryResult->getQuery());
$query = $this->filterOutImageVariants($query);
$query = $this->filterQuery($this->assetRepository->findByTag($tag, $this->activeAssetCollection)->getQuery());
return new NeosAssetProxyQueryResult($query->execute(), $this->assetSource);
}

public function findUntagged(): AssetProxyQueryResultInterface
{
$queryResult = $this->assetRepository->findUntagged($this->activeAssetCollection);
$query = $this->filterOutImportedAssetsFromOtherAssetSources($queryResult->getQuery());
$query = $this->filterOutImageVariants($query);
$query = $this->filterQuery($this->assetRepository->findUntagged($this->activeAssetCollection)->getQuery());
return new NeosAssetProxyQueryResult($query->execute(), $this->assetSource);
}

public function findUnassigned(): AssetProxyQueryResultInterface
{
$query = $this->assetRepository->createQuery();
$query = $this->filterOutImportedAssetsFromOtherAssetSources($query);
$query = $this->filterQuery($this->assetRepository->createQuery());
$query = $this->filterOutAssetsWithAssetCollections($query);
$query = $this->filterOutImageVariants($query);
return new NeosAssetProxyQueryResult($query->execute(), $this->assetSource);
}

public function countAll(): int
{
$query = $this->filterOutImportedAssetsFromOtherAssetSources($this->assetRepository->createQuery());
$query = $this->filterOutAssetsFromOtherAssetCollections($query);
$query = $this->filterOutImageVariants($query);
return $query->count();
return $this->filterQuery($this->assetRepository->createQuery(), true)->count();
}

public function countUntagged(): int
{
$query = $this->assetRepository->createQuery();
$query = $this->filterQuery($this->assetRepository->createQuery(), true);
try {
$query->matching($query->isEmpty('tags'));
} catch (InvalidQueryException $e) {
}

$query = $this->filterOutImportedAssetsFromOtherAssetSources($query);
$query = $this->filterOutAssetsFromOtherAssetCollections($query);
$query = $this->filterOutImageVariants($query);
return $query->count();
}

public function countByTag(Tag $tag): int
{
$queryResult = $this->assetRepository->findByTag($tag, $this->activeAssetCollection);
$query = $this->filterOutImportedAssetsFromOtherAssetSources($queryResult->getQuery());
$query = $this->filterOutImageVariants($query);
return $query->count();
return $this->filterQuery($this->assetRepository->findByTag($tag, $this->activeAssetCollection)->getQuery(),
true)->count();
}

private function filterOutImportedAssetsFromOtherAssetSources(QueryInterface $query): QueryInterface
Expand All @@ -224,8 +226,7 @@ private function filterOutImageVariants(QueryInterface $query): QueryInterface
if (!method_exists($query, 'getQueryBuilder')) {
return $query;
}
$queryBuilder = $query->getQueryBuilder();
$queryBuilder->andWhere('e NOT INSTANCE OF Neos\Media\Domain\Model\ImageVariant');
$query->getQueryBuilder()->andWhere('e NOT INSTANCE OF ' . ImageVariant::class);
return $query;
}

Expand All @@ -236,7 +237,7 @@ private function filterOutAssetsFromOtherAssetCollections(QueryInterface $query)
$query->matching(
$query->logicalAnd([
$constraints,
$query->contains('assetCollections', $this->activeAssetCollection)
$query->contains('assetCollections', $this->activeAssetCollection),
])
);
} catch (InvalidQueryException $e) {
Expand All @@ -251,11 +252,22 @@ private function filterOutAssetsWithAssetCollections(QueryInterface $query): Que
$query->matching(
$query->logicalAnd([
$constraints,
$query->isEmpty('assetCollections')
$query->isEmpty('assetCollections'),
])
);
} catch (InvalidQueryException $e) {
}
return $query;
}

private function filterOutAssetsWithOtherMediaTypes(QueryInterface $query)
{
$constraints = $query->getConstraint();
return $query->matching(
$query->logicalAnd([
$constraints,
$query->equals('resource.mediaType', $this->mediaTypeFilter),
])
);
}
}
16 changes: 13 additions & 3 deletions Classes/GraphQL/Resolver/Type/QueryResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ protected function createAssetProxyIterator(
'tagId' => $tagId,
'assetCollectionId' => $assetCollectionId,
'mediaType' => $mediaType,
'assetType' => $assetType,
'searchTerm' => $searchTerm,
'sortBy' => $sortBy,
'sortDirection' => $sortDirection
Expand All @@ -144,6 +145,7 @@ protected function createAssetProxyIterator(
'tagId' => null,
'assetCollectionId' => null,
'mediaType' => null,
'assetType' => null,
'searchTerm' => null,
'sortBy' => null,
'sortDirection' => null,
Expand All @@ -161,12 +163,20 @@ protected function createAssetProxyIterator(
$assetProxyRepository = $activeAssetSource->getAssetProxyRepository();
}

if (is_string($mediaType) && !empty($mediaType)) {
if (is_string($assetType) && !empty($assetType)) {
try {
$assetTypeFilter = new AssetTypeFilter(ucfirst($mediaType));
$assetTypeFilter = new AssetTypeFilter(ucfirst($assetType));
$assetProxyRepository->filterByType($assetTypeFilter);
} catch (\InvalidArgumentException $e) {
$this->systemLogger->warning('Ignoring invalid mediatype when filtering assets ' . $mediaType);
$this->systemLogger->warning('Ignoring invalid asset type when filtering assets ' . $assetType);
}
}

if (is_string($mediaType) && !empty($mediaType)) {
try {
$assetProxyRepository->filterByMediaType($mediaType);
} catch (\InvalidArgumentException $e) {
$this->systemLogger->warning('Ignoring invalid media-type when filtering assets ' . $mediaType);
}
}

Expand Down
7 changes: 7 additions & 0 deletions Resources/Private/GraphQL/schema.root.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Query {
assetSourceId: AssetSourceId
tagId: TagId
assetCollectionId: AssetCollectionId
assetType: AssetType
mediaType: MediaType
searchTerm: String
limit: Int
Expand All @@ -26,6 +27,7 @@ type Query {
assetSourceId: AssetSourceId
tagId: TagId
assetCollectionId: AssetCollectionId
assetType: AssetType
mediaType: MediaType
searchTerm: String
): Int!
Expand Down Expand Up @@ -454,6 +456,11 @@ IANA media type of an Asset (e.g. "image/jpeg")
"""
scalar MediaType

"""
Neos type of an Asset, can be "All", "Image", "Document", "Video" or "Audio" (see `Neos\Media\Domain\Model\AssetSource\AssetTypeFilter`)
"""
scalar AssetType

"""
A File extension (e.g. "pdf")
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const ReplaceAssetDialog: React.FC = () => {
const completeMediaType = selectedAsset?.file.mediaType;
const regex = /^(?<type>(?:[.!#%&'`^~$*+\-|\w]+))\//;
const mainType = completeMediaType.match(regex)?.groups?.type;
return mainType ? mainType + '/*' : '';
return mainType ? (`${mainType}/*` as MediaType) : '';
}, [selectedAsset]);

const handleUpload = useCallback(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface UploadSectionProps {
loading: boolean;
onSetFiles: (files: UploadedFile[]) => void;
maxFiles?: number;
acceptedFileTypes?: string | string[];
acceptedFileTypes?: MediaType | MediaType[] | '';
}

const UploadSection: React.FC<UploadSectionProps> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const ClipboardActions: React.FC = () => {
icon="trash"
size="regular"
style="transparent"
hoverStyle="warn"
hoverStyle="danger"
onClick={onDeleteClipboard}
/>
<IconButton
Expand Down
14 changes: 12 additions & 2 deletions Resources/Private/JavaScript/core/src/hooks/useAssetCountQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { useRecoilValue } from 'recoil';
import { selectedAssetSourceState } from '@media-ui/feature-asset-sources';

import { ASSET_COUNT } from '../queries';
import { searchTermState, selectedAssetCollectionAndTagState } from '../state';
import {
searchTermState,
selectedAssetCollectionAndTagState,
selectedAssetTypeState,
selectedMediaTypeState,
} from '../state';

interface AssetCountQueryResult {
assetCount: number;
Expand All @@ -14,18 +19,23 @@ interface AssetCountVariables {
assetCollectionId?: string;
assetSourceId?: string;
tagId?: string;
mediaType?: string;
mediaType?: MediaType | '';
assetType?: AssetType | '';
searchTerm?: string;
}

export default function useAssetCountQuery(total = false) {
const searchTerm = useRecoilValue(searchTermState);
const { tagId, assetCollectionId } = useRecoilValue(selectedAssetCollectionAndTagState);
const assetSourceId = useRecoilValue(selectedAssetSourceState);
const mediaType = useRecoilValue(selectedMediaTypeState);
const assetType = useRecoilValue(selectedAssetTypeState);
const { data, loading } = useQuery<AssetCountQueryResult, AssetCountVariables>(ASSET_COUNT, {
variables: {
assetCollectionId: total ? undefined : assetCollectionId,
assetSourceId,
mediaType,
assetType,
tagId: total ? undefined : tagId,
searchTerm: searchTerm.toString(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
initialLoadCompleteState,
loadingState,
searchTermState,
selectedAssetTypeState,
selectedMediaTypeState,
selectedSortOrderState,
} from '../state';
Expand All @@ -32,7 +33,8 @@ interface AssetsQueryVariables {
searchTerm: string;
assetSourceId: string;
assetCollectionId: string;
mediaType: string;
mediaType: MediaType | '';
assetType: AssetType | '';
tagId: string;
limit: number;
offset: number;
Expand All @@ -49,6 +51,7 @@ const useAssetsQuery = () => {
const assetSourceId = useRecoilValue(selectedAssetSourceState);
const selectedTagId = useRecoilValue(selectedTagIdState);
const mediaType = useRecoilValue(selectedMediaTypeState);
const assetType = useRecoilValue(selectedAssetTypeState);
const sortOrderState = useRecoilValue(selectedSortOrderState);
const currentPage = useRecoilValue(currentPageState);
const [isLoading, setIsLoading] = useRecoilState(loadingState);
Expand All @@ -63,6 +66,7 @@ const useAssetsQuery = () => {
searchTerm: searchTerm.toString(),
assetSourceId,
assetCollectionId,
assetType,
mediaType,
tagId: selectedTagId,
limit: assetsPerPage,
Expand All @@ -79,6 +83,7 @@ const useAssetsQuery = () => {
searchTerm: searchTerm.toString(),
assetSourceId,
assetCollectionId,
assetType,
mediaType,
tagId: selectedTagId,
limit: assetsPerPage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type AssetFileType = 'AssetFile';
export default interface AssetFile extends GraphQlEntity {
__typename: AssetFileType;
extension: string;
mediaType: string;
mediaType: MediaType;
typeIcon: Image;
size?: number;
url: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default interface SelectionConstraints {
assetSources?: string[];
mediaTypes?: string[];
mediaTypes?: MediaType[];
}
Loading

0 comments on commit 8d2b6f6

Please sign in to comment.