Skip to content

Commit

Permalink
chore(content) #28183 : Edit Content: Expose Date and User Informatio…
Browse files Browse the repository at this point in the history
…n in Content API Response (#28603)

### Proposed Changes
* Exposes audit information to all Contentlets in dotCMS.

Here's the list of audit properties that are being exposed, and how they
can be accessed by the different consumers:

| Attribute | REST Endpoint | Elasticsearch | GraphQL |
| -------- | ------- | ------- | ------- |
| `creationDate` | `creationDate` | `creationDate` | `creationDate` |
| `owner` | `owner` | `owner` | `owner` |
| `ownerName` | `ownerName` | Only the User ID is available | Reachable
inside the "owner" object |
| `modDate` | `modDate` | `modDate` | `modDate` |
| `modUser` | `modUser` | `modUser` | `modUser` |
| `modUserName` | `modUserName` | Only the User ID is available |
Reachable inside the "modUser" object |
| `publishDate` | `publishDate` | `sysPublishDate` | `publishDate` |
| `publishUser` | `publishUser` | `sysPublishUser` | `publishUser` |
| `publishUserName` | `publishUserName` | Only the User ID is available
| Reachable inside the "publishUser" object |
  • Loading branch information
jcastro-dotcms authored May 20, 2024
1 parent de8117a commit 4c3a898
Show file tree
Hide file tree
Showing 10 changed files with 1,995 additions and 828 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
package com.dotcms.content.elasticsearch.business;

import static com.dotcms.content.elasticsearch.business.ESIndexAPI.INDEX_OPERATIONS_TIMEOUT_IN_MS;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.PERSONA_KEY_TAG;
import static com.dotcms.contenttype.model.field.LegacyFieldTypes.CUSTOM_FIELD;
import static com.dotcms.contenttype.model.type.PersonaContentType.PERSONA_KEY_TAG_FIELD_VAR;
import static com.dotmarketing.business.PermissionAPI.PERMISSION_PUBLISH;
import static com.dotmarketing.business.PermissionAPI.PERMISSION_READ;
import static com.dotmarketing.business.PermissionAPI.PERMISSION_WRITE;
import static com.dotmarketing.util.UtilMethods.isNotSet;
import static com.liferay.util.StringPool.BLANK;
import static com.liferay.util.StringPool.COMMA;
import static com.liferay.util.StringPool.PERIOD;

import com.dotcms.business.CloseDBIfOpened;
import com.dotcms.content.business.ContentMappingAPI;
import com.dotcms.content.business.DotMappingException;
Expand Down Expand Up @@ -42,6 +30,7 @@
import com.dotmarketing.business.VersionableAPI;
import com.dotmarketing.cache.FieldsCache;
import com.dotmarketing.common.db.DotConnect;
import com.dotmarketing.exception.DotCorruptedDataException;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.categories.business.CategoryAPI;
Expand Down Expand Up @@ -76,6 +65,19 @@
import com.liferay.portal.model.User;
import io.vavr.Lazy;
import io.vavr.control.Try;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.GetFieldMappingsRequest;
import org.elasticsearch.client.indices.GetFieldMappingsResponse;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;

import java.io.IOException;
import java.io.StringWriter;
import java.text.DecimalFormat;
Expand All @@ -97,18 +99,21 @@
import java.util.TimeZone;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.GetFieldMappingsRequest;
import org.elasticsearch.client.indices.GetFieldMappingsResponse;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;

import static com.dotcms.content.elasticsearch.business.ESIndexAPI.INDEX_OPERATIONS_TIMEOUT_IN_MS;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.CREATION_DATE;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.PERSONA_KEY_TAG;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.SYS_PUBLISH_USER;
import static com.dotcms.contenttype.model.field.LegacyFieldTypes.CUSTOM_FIELD;
import static com.dotcms.contenttype.model.type.PersonaContentType.PERSONA_KEY_TAG_FIELD_VAR;
import static com.dotcms.util.DotPreconditions.checkNotEmpty;
import static com.dotmarketing.business.PermissionAPI.PERMISSION_PUBLISH;
import static com.dotmarketing.business.PermissionAPI.PERMISSION_READ;
import static com.dotmarketing.business.PermissionAPI.PERMISSION_WRITE;
import static com.dotmarketing.util.UtilMethods.isNotSet;
import static com.liferay.util.StringPool.BLANK;
import static com.liferay.util.StringPool.COMMA;
import static com.liferay.util.StringPool.PERIOD;

/**
* Implementation class for the {@link ContentMappingAPI}.
Expand Down Expand Up @@ -382,6 +387,8 @@ public Map<String,Object> toMap(final Contentlet contentlet) throws DotMappingEx
contentletMap.put(ESMappingConstants.MOD_DATE + TEXT, datetimeFormat.format(contentlet.getModDate()));
contentletMap.put(ESMappingConstants.OWNER, contentlet.getOwner()==null ? "0" : contentlet.getOwner());
contentletMap.put(ESMappingConstants.MOD_USER, contentlet.getModUser());
contentletMap.put(CREATION_DATE, elasticSearchDateTimeFormat.format(contentIdentifier.getCreateDate()));
contentletMap.put(CREATION_DATE + TEXT, datetimeFormat.format(contentIdentifier.getCreateDate()));
contentletMap.put(ESMappingConstants.LIVE, contentlet.isLive());
contentletMap.put(ESMappingConstants.LIVE + TEXT, Boolean.toString(contentlet.isLive()));
contentletMap.put(ESMappingConstants.WORKING, contentlet.isWorking());
Expand All @@ -396,7 +403,7 @@ public Map<String,Object> toMap(final Contentlet contentlet) throws DotMappingEx
contentletMap.put(ESMappingConstants.IDENTIFIER, contentIdentifier.getId());
contentletMap.put(ESMappingConstants.CONTENTLET_HOST, contentIdentifier.getHostId());
contentletMap.put(ESMappingConstants.CONTENTLET_HOSTNAME, contentSite.getHostname());
contentletMap.put(ESMappingConstants.CONTENTLET_FOLER, contentFolder!=null && InodeUtils.isSet(contentFolder.getInode()) ? contentFolder.getInode() : contentlet.getFolder());
contentletMap.put(ESMappingConstants.CONTENTLET_FOLER, InodeUtils.isSet(contentFolder.getInode()) ? contentFolder.getInode() : contentlet.getFolder());
contentletMap.put(ESMappingConstants.PARENT_PATH, contentIdentifier.getParentPath());
contentletMap.put(ESMappingConstants.PATH, contentIdentifier.getPath());
// makes shorties searchable regardless of length
Expand All @@ -417,7 +424,9 @@ public Map<String,Object> toMap(final Contentlet contentlet) throws DotMappingEx
contentlet.getDateProperty(expireDateVar) : null;
loadDateTimeFieldValue(contentletMap,
ESMappingConstants.EXPIRE_DATE, expireDate);

if (contentlet.isLive()) {
contentletMap.put(SYS_PUBLISH_USER, contentlet.getModUser());
}
loadDateTimeFieldValue(contentletMap,
ESMappingConstants.SYS_PUBLISH_DATE, versionInfo.get().getPublishDate());

Expand All @@ -431,7 +440,7 @@ public Map<String,Object> toMap(final Contentlet contentlet) throws DotMappingEx
}
} catch (final Exception e) {
Logger.warn(this.getClass(), "Cannot get URLMap for Content Type: " + contentType.name() + " and " +
"contentlet.id : " + ((contentIdentifier != null) ? contentIdentifier.getId() : contentlet) +
"contentlet.id : " + contentIdentifier.getId() +
" , reason: " + e.getMessage());
}

Expand Down Expand Up @@ -480,6 +489,9 @@ public Map<String,Object> toMap(final Contentlet contentlet) throws DotMappingEx

//The url is now stored under the identifier for html pages, so we need to index that also.
if (contentlet.getContentType().baseType().getType() == BaseContentType.HTMLPAGE.getType()) {
checkNotEmpty(contentlet.getContentType().variable(),
DotCorruptedDataException.class, "Contentlet '%s' " +
"points to a Content Type with an empty Velocity Var Name", contentlet.getIdentifier());
mapLowered.put(contentlet.getContentType().variable().toLowerCase() + ".url", contentIdentifier.getAssetName());
mapLowered.put(contentlet.getContentType().variable().toLowerCase() + ".url_dotraw", contentIdentifier.getAssetName());
sw.append(contentIdentifier.getAssetName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ private ESMappingConstants(){}
public static final String SYSTEM_TYPE = "systemType";

public static final String MOD_DATE = "modDate";
public static final String CREATION_DATE = "creationDate";
public static final String OWNER = "owner";
public static final String MOD_USER = "modUser";
public static final String LIVE = "live";
Expand Down Expand Up @@ -81,6 +82,7 @@ private ESMappingConstants(){}
public static final String EXPIRE_DATE = "expdate";
public static final String VERSION_TS = "versionTs";
public static final String SYS_PUBLISH_DATE = "sysPublishDate";
public static final String SYS_PUBLISH_USER = "sysPublishUser";
public static final String URL_MAP = "urlMap";
public static final String VANITY_URL = "vanityUrl";
public static final String CATEGORIES = "categories";
Expand Down
46 changes: 29 additions & 17 deletions dotCMS/src/main/java/com/dotcms/graphql/ContentFields.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
package com.dotcms.graphql;

import com.dotcms.graphql.datafetcher.ContentMapDataFetcher;
import com.dotcms.graphql.datafetcher.FolderFieldDataFetcher;
import com.dotcms.graphql.datafetcher.LanguageDataFetcher;
import com.dotcms.graphql.datafetcher.SiteFieldDataFetcher;
import com.dotcms.graphql.datafetcher.TitleImageFieldDataFetcher;
import com.dotcms.graphql.datafetcher.UserDataFetcher;
import com.dotcms.graphql.util.TypeUtil.TypeFetcher;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.util.UtilMethods;
import graphql.scalars.ExtendedScalars;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLTypeReference;
import graphql.schema.PropertyDataFetcher;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.BASE_TYPE;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.CONTENT_TYPE;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.CREATION_DATE;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.IDENTIFIER;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.INODE;
import static com.dotcms.content.elasticsearch.constants.ESMappingConstants.LIVE;
Expand All @@ -20,29 +39,14 @@
import static com.dotmarketing.portlets.contentlet.model.Contentlet.LOCKED_KEY;
import static com.dotmarketing.portlets.contentlet.model.Contentlet.MOD_USER_KEY;
import static com.dotmarketing.portlets.contentlet.model.Contentlet.OWNER_KEY;
import static com.dotmarketing.portlets.contentlet.model.Contentlet.PUBLISH_DATE_KEY;
import static com.dotmarketing.portlets.contentlet.model.Contentlet.PUBLISH_USER_KEY;
import static com.dotmarketing.portlets.contentlet.model.Contentlet.TITLE_IMAGE_KEY;
import static graphql.Scalars.GraphQLBoolean;
import static graphql.Scalars.GraphQLID;
import static graphql.Scalars.GraphQLInt;
import static graphql.Scalars.GraphQLString;

import com.dotcms.graphql.datafetcher.ContentMapDataFetcher;
import com.dotcms.graphql.datafetcher.FolderFieldDataFetcher;
import com.dotcms.graphql.datafetcher.LanguageDataFetcher;
import com.dotcms.graphql.datafetcher.SiteFieldDataFetcher;
import com.dotcms.graphql.datafetcher.TitleImageFieldDataFetcher;
import com.dotcms.graphql.datafetcher.UserDataFetcher;
import com.dotcms.graphql.util.TypeUtil.TypeFetcher;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.util.UtilMethods;
import graphql.scalars.ExtendedScalars;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLTypeReference;
import graphql.schema.PropertyDataFetcher;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
* Utility class that defines and returns the available fields for the {@link InterfaceType#CONTENTLET}
*/
Expand All @@ -52,6 +56,7 @@ private ContentFields() {}

public static Map<String, TypeFetcher> getContentFields() {
final Map<String, TypeFetcher> contentFields = new HashMap<>();
contentFields.put(CREATION_DATE, new TypeFetcher(GraphQLString));
contentFields.put(MOD_DATE, new TypeFetcher(GraphQLString));
contentFields.put(TITLE, new TypeFetcher(GraphQLString));
contentFields.put(TITLE_IMAGE_KEY, new TypeFetcher(GraphQLTypeReference.typeRef(BINARY.getTypeName()),
Expand Down Expand Up @@ -84,6 +89,13 @@ public static Map<String, TypeFetcher> getContentFields() {
GraphQLArgument.newArgument().name("depth").defaultValue(0).type(GraphQLInt).build(),
GraphQLArgument.newArgument().name("render").defaultValue(false)
.type(GraphQLBoolean).build()));
contentFields.put(PUBLISH_DATE_KEY, new TypeFetcher(GraphQLString, PropertyDataFetcher
.fetching((Function<Contentlet, String>) contentlet ->
UtilMethods.isSet(contentlet.getStringProperty("publishDate"))
? contentlet.getStringProperty("publishDate")
: "")));
contentFields.put(PUBLISH_USER_KEY, new TypeFetcher(GraphQLTypeReference.typeRef(USER.getTypeName()),
new UserDataFetcher()));
return contentFields;
}

Expand Down
Loading

0 comments on commit 4c3a898

Please sign in to comment.