Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(core): Expose the editableAsText attribute, even if the metadata file doesn't have it yet #27393 #27398

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 3 additions & 82 deletions dotCMS/src/curl-test/GraphQLTests.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
{
"info": {
"_postman_id": "ec9b06ea-0e55-44b2-8364-6c503499f792",
"_postman_id": "611b379f-e7c5-4732-8b92-2d34993515d4",
"name": "GraphQL",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "5403727",
"_collection_link": "https://cloudy-robot-285072.postman.co/workspace/JCastro-Workspace~5bfa586e-54db-429b-b7d5-c4ff997e3a0d/collection/5403727-ec9b06ea-0e55-44b2-8364-6c503499f792?action=share&source=collection_link&creator=5403727"
"_exporter_id": "5403727"
},
"item": [
{
Expand Down Expand Up @@ -4562,7 +4561,7 @@
" pm.expect(imageFieldJson.fileAsset.mime).to.eql(\"image/jpeg\");",
" ",
" // metaData",
" pm.expect(imageFieldJson.metaData.length).to.eql(12);",
" pm.expect(imageFieldJson.metaData.length).to.eql(13, \"Returned Metadata attributes is \" + imageFieldJson.metaData.length);",
" pm.expect(hasProperty(imageFieldJson.metaData, \"fileSize\", \"5494\"), 'FAILED:[fileSize]').to.be.true;",
" pm.expect(hasProperty(imageFieldJson.metaData, \"length\", \"5494\"), 'FAILED:[length]').to.be.true;",
" pm.expect(hasProperty(imageFieldJson.metaData, \"width\", \"139\"), 'FAILED:[width]').to.be.true;",
Expand Down Expand Up @@ -10034,83 +10033,5 @@
]
}
}
],
"variable": [
{
"key": "languageId",
"value": ""
},
{
"key": "contentTypeVariable",
"value": ""
},
{
"key": "contentIdentifier",
"value": ""
},
{
"key": "imageTypeInfo",
"value": ""
},
{
"key": "videoTypeInfo",
"value": ""
},
{
"key": "fileImageContentTypeVariable",
"value": ""
},
{
"key": "imageFieldVariable",
"value": ""
},
{
"key": "fileFieldVariable",
"value": ""
},
{
"key": "fileContentIdentifier",
"value": ""
},
{
"key": "imageContentIdentifier",
"value": ""
},
{
"key": "childContentTypeVariable",
"value": ""
},
{
"key": "parentContentTypeVariable",
"value": ""
},
{
"key": "relFieldVariable",
"value": ""
},
{
"key": "childContentIdentifier",
"value": ""
},
{
"key": "parentContentIdentifier",
"value": ""
},
{
"key": "contentTypeIdWithStoryBlock",
"value": ""
},
{
"key": "contentTypeVarNameWithStoryBlock",
"value": ""
},
{
"key": "contentletIdWithEmptyStoryBlock",
"value": ""
},
{
"key": "dateFieldContentTypeVariable",
"value": ""
}
]
}
26 changes: 22 additions & 4 deletions dotCMS/src/main/java/com/dotcms/storage/FileStorageAPI.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dotcms.storage;

import com.dotcms.storage.model.BasicMetadataFields;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.util.Config;

Expand All @@ -8,8 +9,11 @@
import java.util.Map;

/**
* This class is in charge of resolve File (on diff repositories), metadata, etc.
* This Storage API is in charge of generating, removing and storing file metadata in dotCMS using
* the File System as its storage volume.
*
* @author jsanca
* @since Oct 19th, 2020
*/
public interface FileStorageAPI {

Expand Down Expand Up @@ -56,9 +60,23 @@ Map<String, Serializable> generateMetaData(final File binary, final GenerateMeta
throws DotDataException;

/**
* Retrieve the metadata
* @param requestMetaData {@link FetchMetadataParams}
* @return Map with the metadata
* Retrieves the metadata object from the configured Storage Provider. There are a couple of
* aspects to take into consideration when calling this method:
* <ul>
* <li>Depending on the configuration of the {@link FetchMetadataParams} parameter, the
* resulting metadata object may be cached.</li>
* <li>For performance reasons, the {@link BasicMetadataFields#EDITABLE_AS_TEXT} property
* is always calculated <b>UNLESS</b> it's already present in the metadata Map. This way,
* Files don't need to be re-indexed for it to be available.</li>
* </ul>
*
* @param requestMetaData The {@link FetchMetadataParams} object specifying how the metadata
* should be retrieved.
*
* @return A key/value Map with the expected metadata properties.
*
* @throws DotDataException An error occurred when retrieving the metadata from the Storage
* Provider.
*/
Map<String, Serializable> retrieveMetaData(final FetchMetadataParams requestMetaData) throws DotDataException;

Expand Down
81 changes: 30 additions & 51 deletions dotCMS/src/main/java/com/dotcms/storage/FileStorageAPIImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import com.dotmarketing.business.CacheLocator;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.portlets.contentlet.business.MetadataCache;
import com.dotmarketing.util.FileUtil;
import com.dotmarketing.util.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.liferay.util.StringPool;
import io.vavr.control.Try;

import java.io.File;
Expand All @@ -26,6 +28,7 @@

import static com.dotcms.storage.StoragePersistenceAPI.HASH_OBJECT;
import static com.dotcms.storage.model.BasicMetadataFields.CONTENT_TYPE_META_KEY;
import static com.dotcms.storage.model.BasicMetadataFields.EDITABLE_AS_TEXT;
import static com.dotcms.storage.model.BasicMetadataFields.LENGTH_META_KEY;
import static com.dotcms.storage.model.BasicMetadataFields.SIZE_META_KEY;
import static com.dotcms.storage.model.BasicMetadataFields.VERSION_KEY;
Expand Down Expand Up @@ -75,22 +78,11 @@ public FileStorageAPIImpl() {
CacheLocator.getMetadataCache());
}

/**
* {@inheritDoc}
* @param binary {@link File} file to get the information
* @return
*/
@Override
public Map<String, Serializable> generateRawBasicMetaData(final File binary) {
return this.generateBasicMetaData(binary, s -> true); // raw = no filter
}

/**
* {@inheritDoc}
* @param binary {@link File} file to get the information
* @param maxLength {@link Long} max length is used when parse the content, how many bytes do you want to parse.
* @return
*/
@Override
public Map<String, Serializable> generateRawFullMetaData(final File binary, long maxLength) {
return this.generateFullMetaData(binary, s -> true, maxLength); // raw = no filter
Expand Down Expand Up @@ -195,12 +187,6 @@ private TreeMap<String, Serializable> ensureTypes(TreeMap<String, Serializable>
return metadataMap;
}

/**
* {@inheritDoc}
* @param binary {@link File} file to get the information
* @param configuration {@link GenerateMetadataConfig}
* @return
*/
@Override
public Map<String, Serializable> generateMetaData(final File binary,
final GenerateMetadataConfig configuration) throws DotDataException {
Expand Down Expand Up @@ -387,50 +373,59 @@ private void checkOverride(final StoragePersistenceAPI storage, final StorageKey
}
}

/**
* {@inheritDoc}
* @param requestMetaData {@link FetchMetadataParams}
* @return
*/
@Override
public Map<String, Serializable> retrieveMetaData(final FetchMetadataParams requestMetaData)
throws DotDataException {
if (requestMetaData.isCache()) {
final Map<String, Serializable> metadataMap = metadataCache
final Map<String, Serializable> metadataMap = this.metadataCache
.getMetadataMap(requestMetaData.getCacheKeySupplier().get());
if (null != metadataMap) {
checkEditableAsText(metadataMap);
putIntoCache(requestMetaData.getCacheKeySupplier().get(), metadataMap);
return metadataMap;
}
}

Map<String, Serializable> metadataMap = null;
final StorageKey storageKey = requestMetaData.getStorageKey();
final StoragePersistenceAPI storage = persistenceProvider
final StoragePersistenceAPI storage = this.persistenceProvider
.getStorage(storageKey.getStorage());

this.checkBucket(storageKey, storage);
if (storage.existsObject(storageKey.getGroup(), storageKey.getPath())) {

metadataMap = retrieveMetadata(storageKey, storage);
Logger.debug(FileStorageAPIImpl.class,
"Retrieve the meta data from storage, path: " + storageKey.getPath());
() -> "Retrieve the meta data from storage path: " + storageKey.getPath());
checkEditableAsText(metadataMap);
if (null != requestMetaData.getCacheKeySupplier()) {
final Map<String, Serializable> projection = requestMetaData.getProjectionMapForCache().apply(metadataMap);
putIntoCache(requestMetaData.getCacheKeySupplier().get(), projection);
return projection;
}

}

return metadataMap;
}

/***
* {@inheritDoc}
* @param requestMetaData {@link FetchMetadataParams}
* @return
* @throws DotDataException
/**
* Performs a simple check that verifies whether the File that the metadata belongs to can be
* editable as a text file. If it can, the {@link BasicMetadataFields#EDITABLE_AS_TEXT} property
* will be added.
* <p>This is particularly useful in the File's edit mode in the back-end for dotCMS to allow
* content authors to edit the contents directly in the Code Editor field. If, for any reason,
* the MIME Type cannot be detected, the value of the
* the {@link BasicMetadataFields#EDITABLE_AS_TEXT} property will be set to {@code false}.</p>
*
* @param metadataMap The File's metadata Map.
*/
private void checkEditableAsText(final Map<String, Serializable> metadataMap) {
if (!metadataMap.containsKey(BasicMetadataFields.EDITABLE_AS_TEXT.key())) {
final String mimeType =
Try.of(() -> metadataMap.get(CONTENT_TYPE_META_KEY.key()).toString()).getOrElse(StringPool.BLANK);
metadataMap.put(EDITABLE_AS_TEXT.key(), FileUtil.isFileEditableAsText(mimeType));
}
}

@Override
public boolean removeMetaData(final FetchMetadataParams requestMetaData) throws DotDataException{
boolean deleteSucceeded = false;
final StorageKey storageKey = requestMetaData.getStorageKey();
Expand All @@ -443,12 +438,7 @@ public boolean removeMetaData(final FetchMetadataParams requestMetaData) throws
return deleteSucceeded;
}

/***
* {@inheritDoc}
* @param requestMetaData {@link FetchMetadataParams}
* @return
* @throws DotDataException
*/
@Override
public boolean removeVersionMetaData(final FetchMetadataParams requestMetaData) throws DotDataException{
boolean deleteSucceeded = false;
final StorageKey storageKey = requestMetaData.getStorageKey();
Expand All @@ -461,12 +451,6 @@ public boolean removeVersionMetaData(final FetchMetadataParams requestMetaData)
return deleteSucceeded;
}

/**
* {@inheritDoc}
* @param fetchMetadataParams
* @param customAttributes
* @throws DotDataException
*/
@Override
public void putCustomMetadataAttributes(
final FetchMetadataParams fetchMetadataParams,
Expand Down Expand Up @@ -527,12 +511,7 @@ public void putCustomMetadataAttributes(

}

/**
* {@inheritDoc}
* @param requestMetadata
* @param metadata
* @throws DotDataException
*/
@Override
public boolean setMetadata(final FetchMetadataParams requestMetadata,
final Map<String, Serializable> metadata) throws DotDataException {
final StorageKey storageKey = requestMetadata.getStorageKey();
Expand Down
Loading