/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.dynamic.data.lists.service.impl;
import com.liferay.document.library.kernel.util.DLUtil;
import com.liferay.dynamic.data.lists.exception.NoSuchRecordException;
import com.liferay.dynamic.data.lists.exception.RecordGroupIdException;
import com.liferay.dynamic.data.lists.model.DDLFormRecord;
import com.liferay.dynamic.data.lists.model.DDLRecord;
import com.liferay.dynamic.data.lists.model.DDLRecordConstants;
import com.liferay.dynamic.data.lists.model.DDLRecordSet;
import com.liferay.dynamic.data.lists.model.DDLRecordSetConstants;
import com.liferay.dynamic.data.lists.model.DDLRecordVersion;
import com.liferay.dynamic.data.lists.service.base.DDLRecordLocalServiceBaseImpl;
import com.liferay.dynamic.data.lists.util.DDL;
import com.liferay.dynamic.data.mapping.exception.StorageException;
import com.liferay.dynamic.data.mapping.model.DDMStructure;
import com.liferay.dynamic.data.mapping.service.DDMStructureLocalService;
import com.liferay.dynamic.data.mapping.storage.DDMFormValues;
import com.liferay.dynamic.data.mapping.storage.Field;
import com.liferay.dynamic.data.mapping.storage.Fields;
import com.liferay.dynamic.data.mapping.storage.StorageEngine;
import com.liferay.dynamic.data.mapping.util.DDM;
import com.liferay.dynamic.data.mapping.util.DDMFormValuesToFieldsConverter;
import com.liferay.dynamic.data.mapping.util.FieldsToDDMFormValuesConverter;
import com.liferay.expando.kernel.model.ExpandoBridge;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.language.LanguageUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.SystemEventConstants;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.search.BaseModelSearchResult;
import com.liferay.portal.kernel.search.Document;
import com.liferay.portal.kernel.search.Hits;
import com.liferay.portal.kernel.search.Indexable;
import com.liferay.portal.kernel.search.IndexableType;
import com.liferay.portal.kernel.search.Indexer;
import com.liferay.portal.kernel.search.IndexerRegistry;
import com.liferay.portal.kernel.search.IndexerRegistryUtil;
import com.liferay.portal.kernel.search.SearchContext;
import com.liferay.portal.kernel.service.ServiceContext;
import com.liferay.portal.kernel.systemevent.SystemEvent;
import com.liferay.portal.kernel.util.Constants;
import com.liferay.portal.kernel.util.ContentTypes;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.OrderByComparator;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
import com.liferay.portal.spring.extender.service.ServiceReference;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
/**
* Provides the local service for accessing, adding, deleting, and updating
* dynamic data lists (DDL) records.
*
* @author Marcellus Tavares
* @author Eduardo Lundgren
*/
public class DDLRecordLocalServiceImpl extends DDLRecordLocalServiceBaseImpl {
/**
* Adds a record referencing the record set.
*
* @param userId the primary key of the record's creator/owner
* @param groupId the primary key of the record's group
* @param recordSetId the primary key of the record set
* @param displayIndex the index position in which the record is displayed
* in the spreadsheet view
* @param ddmFormValues the record values. See <code>DDMFormValues</code>
* in the <code>dynamic.data.mapping.api</code> module.
* @param serviceContext the service context to be applied. This can set
* the UUID, guest permissions, and group permissions for the
* record.
* @return the record
* @throws PortalException if a portal exception occurred
*/
@Indexable(type = IndexableType.REINDEX)
@Override
public DDLRecord addRecord(
long userId, long groupId, long recordSetId, int displayIndex,
DDMFormValues ddmFormValues, ServiceContext serviceContext)
throws PortalException {
// Record
User user = userLocalService.getUser(userId);
DDLRecordSet recordSet = ddlRecordSetPersistence.findByPrimaryKey(
recordSetId);
validate(groupId, recordSet);
long recordId = counterLocalService.increment();
DDLRecord record = ddlRecordPersistence.create(recordId);
record.setUuid(serviceContext.getUuid());
record.setGroupId(groupId);
record.setCompanyId(user.getCompanyId());
record.setUserId(user.getUserId());
record.setUserName(user.getFullName());
record.setVersionUserId(user.getUserId());
record.setVersionUserName(user.getFullName());
long ddmStorageId = storageEngine.create(
recordSet.getCompanyId(), recordSet.getDDMStructureId(),
ddmFormValues, serviceContext);
record.setDDMStorageId(ddmStorageId);
record.setRecordSetId(recordSetId);
record.setRecordSetVersion(recordSet.getVersion());
record.setVersion(DDLRecordConstants.VERSION_DEFAULT);
record.setDisplayIndex(displayIndex);
ddlRecordPersistence.update(record);
// Record version
int status = GetterUtil.getInteger(
serviceContext.getAttribute("status"),
WorkflowConstants.STATUS_DRAFT);
DDLRecordVersion recordVersion = addRecordVersion(
user, record, ddmStorageId, DDLRecordConstants.VERSION_DEFAULT,
displayIndex, status);
// Asset
Locale locale = serviceContext.getLocale();
updateAsset(
userId, record, recordVersion, serviceContext.getAssetCategoryIds(),
serviceContext.getAssetTagNames(), locale,
serviceContext.getAssetPriority());
// Workflow
if (serviceContext.getWorkflowAction() ==
WorkflowConstants.ACTION_PUBLISH) {
WorkflowHandlerRegistryUtil.startWorkflowInstance(
user.getCompanyId(), groupId, userId,
getWorkflowAssetClassName(recordSet),
recordVersion.getRecordVersionId(), recordVersion,
serviceContext);
}
return record;
}
/**
* Adds a record that's based on the fields object and that references the
* record set.
*
* @param userId the primary key of the record's creator/owner
* @param groupId the primary key of the record's group
* @param recordSetId the primary key of the record set
* @param displayIndex the index position in which the record is
* displayed in the spreadsheet view.
* @param fields the record values. See the dynamic-data-mapping-api
* module's Fields class for more information.
* @param serviceContext the service context to be applied. This can
* set the UUID, guest permissions, and group permissions for
* the record.
* @return the record
* @throws PortalException if a portal exception occurred
* @deprecated As of 1.1.0, replaced by {@link #addRecord(long, long, int,
* DDMFormValues, ServiceContext)}
*/
@Deprecated
@Override
public DDLRecord addRecord(
long userId, long groupId, long recordSetId, int displayIndex,
Fields fields, ServiceContext serviceContext)
throws PortalException {
DDLRecordSet recordSet = ddlRecordSetPersistence.findByPrimaryKey(
recordSetId);
DDMFormValues ddmFormValues = fieldsToDDMFormValuesConverter.convert(
recordSet.getDDMStructure(), fields);
return ddlRecordLocalService.addRecord(
userId, groupId, recordSetId, displayIndex, ddmFormValues,
serviceContext);
}
/**
* Adds a record that's based on the fields map and that references the
* record set.
*
* @param userId the primary key of the record's creator/owner
* @param groupId the primary key of the record's group
* @param recordSetId the primary key of the record set
* @param displayIndex the index position in which the record is
* displayed in the spreadsheet view
* @param fieldsMap the record values. The fieldsMap is a map of field
* names and their serializable values.
* @param serviceContext the service context to be applied. This can
* set the UUID, guest permissions, and group permissions for
* the record.
* @return the record
* @throws PortalException if a portal exception occurred
* @deprecated As of 1.1.0, replaced by {@link #addRecord(long, long, int,
* DDMFormValues, ServiceContext)}
*/
@Deprecated
@Override
public DDLRecord addRecord(
long userId, long groupId, long recordSetId, int displayIndex,
Map<String, Serializable> fieldsMap, ServiceContext serviceContext)
throws PortalException {
DDLRecordSet recordSet = ddlRecordSetPersistence.findByPrimaryKey(
recordSetId);
DDMStructure ddmStructure = recordSet.getDDMStructure();
Fields fields = toFields(
ddmStructure.getStructureId(), fieldsMap,
serviceContext.getLocale(), LocaleUtil.getSiteDefault());
return ddlRecordLocalService.addRecord(
userId, groupId, recordSetId, displayIndex, fields, serviceContext);
}
/**
* Deletes the record and its resources.
*
* @param record the record to be deleted
* @return the record
* @throws PortalException if a portal exception occurred
*/
@Indexable(type = IndexableType.DELETE)
@Override
@SystemEvent(
action = SystemEventConstants.ACTION_SKIP,
type = SystemEventConstants.TYPE_DELETE
)
public DDLRecord deleteRecord(DDLRecord record) throws PortalException {
// Record
ddlRecordPersistence.remove(record);
// Record Versions
List<DDLRecordVersion> recordVersions =
ddlRecordVersionPersistence.findByRecordId(record.getRecordId());
for (DDLRecordVersion recordVersion : recordVersions) {
ddlRecordVersionPersistence.remove(recordVersion);
// Dynamic data mapping storage
storageEngine.deleteByClass(recordVersion.getDDMStorageId());
// Workflow
deleteWorkflowInstanceLink(
record.getCompanyId(), record.getGroupId(),
recordVersion.getPrimaryKey());
}
// Asset
deleteAssetEntry(record.getRecordId());
// Ratings
deleteRatingsStats(record.getRecordId());
return record;
}
/**
* Deletes the record and its resources.
*
* @param recordId the primary key of the record to be deleted
* @throws PortalException if a portal exception occurred
*/
@Override
public void deleteRecord(long recordId) throws PortalException {
DDLRecord record = ddlRecordPersistence.findByPrimaryKey(recordId);
ddlRecordLocalService.deleteRecord(record);
}
/**
* Disassociates the locale from the record.
*
* @param recordId the primary key of the record
* @param locale the locale of the record values to be removed
* @param serviceContext the service context to be applied. This can
* set the record modified date.
* @return the affected record
* @throws PortalException if a portal exception occurred
* @deprecated As of 1.1.0, replaced by {@link #updateRecord(long, boolean,
* int, DDMFormValues, ServiceContext)}
*/
@Deprecated
@Override
public DDLRecord deleteRecordLocale(
long recordId, Locale locale, ServiceContext serviceContext)
throws PortalException {
DDLRecord record = ddlRecordPersistence.findByPrimaryKey(recordId);
DDLRecordSet recordSet = record.getRecordSet();
DDMFormValues ddmFormValues = storageEngine.getDDMFormValues(
record.getDDMStorageId());
Fields fields = ddmFormValuesToFieldsConverter.convert(
recordSet.getDDMStructure(), ddmFormValues);
for (Field field : fields) {
Map<Locale, List<Serializable>> valuesMap = field.getValuesMap();
valuesMap.remove(locale);
}
return ddlRecordLocalService.updateRecord(
serviceContext.getUserId(), recordId, false,
DDLRecordConstants.DISPLAY_INDEX_DEFAULT, fields, false,
serviceContext);
}
/**
* Deletes all the record set's records.
*
* @param recordSetId the primary key of the record set from which to
* delete records
* @throws PortalException if a portal exception occurred
*/
@Override
public void deleteRecords(long recordSetId) throws PortalException {
List<DDLRecord> records = ddlRecordPersistence.findByRecordSetId(
recordSetId);
for (DDLRecord record : records) {
ddlRecordLocalService.deleteRecord(record);
}
}
/**
* Returns the record with the ID.
*
* @param recordId the primary key of the record
* @return the record with the ID, or <code>null</code> if a matching record
* could not be found
*/
@Override
public DDLRecord fetchRecord(long recordId) {
return ddlRecordPersistence.fetchByPrimaryKey(recordId);
}
/**
* Returns an ordered range of all the records matching the company,
* workflow status, and scope.
*
* <p>
* Useful when paginating results. Returns a maximum of <code>end -
* start</code> instances. <code>start</code> and <code>end</code> are not
* primary keys, they are indexes in the result set. Thus, <code>0</code>
* refers to the first result in the set. Setting both <code>start</code>
* and <code>end</code> to <code>QueryUtil.ALL_POS</code> will return the
* full result set.
* </p>
*
* @param companyId the primary key of the record's company
* @param status the record's workflow status. For more information search
* the portal kernel's WorkflowConstants class for constants
* starting with the "STATUS_" prefix.
* @param scope the record's scope. For more information search the
* dynamic-data-lists-api module's DDLRecordSetConstants class for
* constants starting with the "SCOPE_" prefix.
* @param start the lower bound of the range of records to return
* @param end the upper bound of the range of records to return (not
* inclusive)
* @param orderByComparator the comparator to order the records
* @return the range of matching records ordered by the comparator
*/
@Override
public List<DDLRecord> getCompanyRecords(
long companyId, int status, int scope, int start, int end,
OrderByComparator<DDLRecord> orderByComparator) {
return ddlRecordFinder.findByC_S_S(
companyId, status, scope, start, end, orderByComparator);
}
/**
* Returns the number of records matching the company, workflow status, and
* scope.
*
* @param companyId the primary key of the record's company
* @param status the record's workflow status. For more information search
* the portal kernel's WorkflowConstants class for constants
* starting with the "STATUS_" prefix.
* @param scope the record's scope. For more information search the
* dynamic-data-lists-api module's DDLRecordSetConstants class for
* constants starting with the "SCOPE_" prefix.
* @return the number of matching records
*/
@Override
public int getCompanyRecordsCount(long companyId, int status, int scope) {
return ddlRecordFinder.countByC_S_S(companyId, status, scope);
}
/**
* Returns the DDM form values object associated with the record storage ID
* See <code>DDLRecord#getDDMFormValues</code> in the
* <code>com.liferay.dynamic.data.lists.api</code> module.
*
* @param ddmStorageId the storage ID associated with the record
* @return the DDM form values
* @throws StorageException
*/
@Override
public DDMFormValues getDDMFormValues(long ddmStorageId)
throws StorageException {
return storageEngine.getDDMFormValues(ddmStorageId);
}
/**
* @deprecated As of 1.1.0, replaced by {@link
* DDLRecordVersionLocalService#getLatestRecordVersion(long)}
*/
@Deprecated
@Override
@SuppressWarnings("deprecation")
public DDLRecordVersion getLatestRecordVersion(long recordId)
throws PortalException {
return ddlRecordVersionLocalService.getLatestRecordVersion(recordId);
}
@Override
public Long[] getMinAndMaxCompanyRecordIds(
long companyId, int status, int scope) {
return ddlRecordFinder.findByC_S_S_MinAndMax(companyId, status, scope);
}
@Override
public List<DDLRecord> getMinAndMaxCompanyRecords(
long companyId, int status, int scope, long minRecordId,
long maxRecordId) {
return ddlRecordFinder.findByC_S_S_MinAndMax(
companyId, status, scope, minRecordId, maxRecordId);
}
/**
* Returns the record with the ID.
*
* @param recordId the primary key of the record
* @return the record with the ID
* @throws PortalException if a portal exception occurred
*/
@Override
public DDLRecord getRecord(long recordId) throws PortalException {
return ddlRecordPersistence.findByPrimaryKey(recordId);
}
/**
* Returns all the records matching the record set ID
*
* @param recordSetId the record's record set ID
* @return the matching records
*/
@Override
public List<DDLRecord> getRecords(long recordSetId) {
return ddlRecordPersistence.findByRecordSetId(recordSetId);
}
/**
* Returns an ordered range of all the records matching the record set ID
* and workflow status.
*
* <p>
* Useful when paginating results. Returns a maximum of <code>end -
* start</code> instances. <code>start</code> and <code>end</code> are not
* primary keys, they are indexes in the result set. Thus, <code>0</code>
* refers to the first result in the set. Setting both <code>start</code>
* and <code>end</code> to <code>QueryUtil.ALL_POS</code> will return the
* full result set.
* </p>
*
* @param recordSetId the record's record set ID
* @param status the record's workflow status. For more information search
* the portal kernel's WorkflowConstants class for constants
* starting with the "STATUS_" prefix.
* @param start the lower bound of the range of records to return
* @param end the upper bound of the range of records to return (not
* inclusive)
* @param orderByComparator the comparator to order the records
* @return the range of matching records ordered by the comparator
*/
@Override
public List<DDLRecord> getRecords(
long recordSetId, int status, int start, int end,
OrderByComparator<DDLRecord> orderByComparator) {
return ddlRecordFinder.findByR_S(
recordSetId, status, start, end, orderByComparator);
}
@Override
public List<DDLRecord> getRecords(
long recordSetId, int start, int end,
OrderByComparator<DDLRecord> obc) {
return ddlRecordPersistence.findByRecordSetId(
recordSetId, start, end, obc);
}
/**
* Returns all the records matching the record set ID and user ID.
*
* @param recordSetId the record's record set ID
* @param userId the user ID the records belong to
* @return the list of matching records ordered by the comparator
*/
@Override
public List<DDLRecord> getRecords(long recordSetId, long userId) {
return ddlRecordPersistence.findByR_U(recordSetId, userId);
}
@Override
public List<DDLRecord> getRecords(
long recordSetId, long userId, int start, int end,
OrderByComparator<DDLRecord> obc) {
return ddlRecordPersistence.findByR_U(
recordSetId, userId, start, end, obc);
}
@Override
public int getRecordsCount(long recordSetId) {
return ddlRecordPersistence.countByRecordSetId(recordSetId);
}
/**
* Returns the number of records matching the record set ID and workflow
* status.
*
* @param recordSetId the record's record set ID
* @param status the record's workflow status. For more information search
* the portal kernel's WorkflowConstants class for constants
* starting with the "STATUS_" prefix.
* @return the number of matching records
*/
@Override
public int getRecordsCount(long recordSetId, int status) {
return ddlRecordFinder.countByR_S(recordSetId, status);
}
@Override
public int getRecordsCount(long recordSetId, long userId) {
return ddlRecordPersistence.countByR_U(recordSetId, userId);
}
/**
* @deprecated As of 1.1.0, replaced by {@link
* com.liferay.dynamic.data.lists.service.DDLRecordVersionLocalService#getRecordVersion(
* long)}
*/
@Deprecated
@Override
@SuppressWarnings("deprecation")
public DDLRecordVersion getRecordVersion(long recordVersionId)
throws PortalException {
return ddlRecordVersionPersistence.findByPrimaryKey(recordVersionId);
}
/**
* @deprecated As of 1.1.0, replaced by {@link
* com.liferay.dynamic.data.lists.service.DDLRecordVersionLocalService#getRecordVersion(
* long, String)}
*/
@Deprecated
@Override
@SuppressWarnings("deprecation")
public DDLRecordVersion getRecordVersion(long recordId, String version)
throws PortalException {
return ddlRecordVersionPersistence.findByR_V(recordId, version);
}
/**
* @deprecated As of 1.1.0, replaced by {@link
* com.liferay.dynamic.data.lists.service.DDLRecordVersionLocalService#getRecordVersions(
* long, int, int, OrderByComparator)}
*/
@Deprecated
@Override
@SuppressWarnings("deprecation")
public List<DDLRecordVersion> getRecordVersions(
long recordId, int start, int end,
OrderByComparator<DDLRecordVersion> orderByComparator) {
return ddlRecordVersionPersistence.findByRecordId(
recordId, start, end, orderByComparator);
}
/**
* @deprecated As of 1.1.0, replaced by {@link
* com.liferay.dynamic.data.lists.service.DDLRecordVersionLocalService#getRecordVersionsCount(
* long)}
*/
@Deprecated
@Override
@SuppressWarnings("deprecation")
public int getRecordVersionsCount(long recordId) {
return ddlRecordVersionPersistence.countByRecordId(recordId);
}
/**
* Reverts the record to the given version.
*
* @param userId the primary key of the user who is reverting the record
* @param recordId the primary key of the record
* @param version the version to revert to
* @param serviceContext the service context to be applied. This can set
* the record modified date.
* @throws PortalException if a portal exception occurred
*/
@Override
public void revertRecord(
long userId, long recordId, String version,
ServiceContext serviceContext)
throws PortalException {
DDLRecordVersion recordVersion =
ddlRecordVersionLocalService.getRecordVersion(recordId, version);
if (!recordVersion.isApproved()) {
return;
}
DDMFormValues ddmFormValues = storageEngine.getDDMFormValues(
recordVersion.getDDMStorageId());
serviceContext.setCommand(Constants.REVERT);
ddlRecordLocalService.updateRecord(
userId, recordId, true, recordVersion.getDisplayIndex(),
ddmFormValues, serviceContext);
}
/**
* @deprecated As of 1.1.0, replaced by {@link #revertRecord(long, long,
* String, ServiceContext)}
*/
@Deprecated
@Override
@SuppressWarnings("deprecation")
public void revertRecordVersion(
long userId, long recordId, String version,
ServiceContext serviceContext)
throws PortalException {
revertRecord(userId, recordId, version, serviceContext);
}
/**
* Returns hits to all the records indexed by the search engine matching the
* search context.
*
* @param searchContext the search context to be applied for searching
* records. For more information, see <code>SearchContext</code> in
* the <code>portal-kernel</code> module.
* @return the hits of the records that matched the search criteria.
*/
@Override
public Hits search(SearchContext searchContext) {
try {
Indexer<DDLRecord> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
DDLRecord.class);
return indexer.search(searchContext);
}
catch (Exception e) {
throw new SystemException(e);
}
}
/**
* Searches for records documents indexed by the search engine.
*
* @param searchContext the search context to be applied for searching
* documents. For more information, see <code>SearchContext</code>
* in the <code>portal-kernel</code> module.
* @return BaseModelSearchResult containing the list of records that matched
* the search criteria
*/
@Override
public BaseModelSearchResult<DDLRecord> searchDDLRecords(
SearchContext searchContext) {
try {
Indexer<DDLRecord> indexer = getDDLRecordIndexer();
Hits hits = indexer.search(searchContext, DDL.SELECTED_FIELD_NAMES);
List<DDLRecord> records = getRecords(hits);
return new BaseModelSearchResult<>(records, hits.getLength());
}
catch (Exception e) {
throw new SystemException(e);
}
}
/**
* Updates the record's asset with new asset categories, tag names, and link
* entries, removing and adding them as necessary.
*
* @param userId the primary key of the user updating the record's asset
* @param record the record
* @param recordVersion the record version
* @param assetCategoryIds the primary keys of the new asset categories
* @param assetTagNames the new asset tag names
* @param locale the locale to apply to the asset
* @param priority the new priority
* @throws PortalException if a portal exception occurred
*/
@Override
public void updateAsset(
long userId, DDLRecord record, DDLRecordVersion recordVersion,
long[] assetCategoryIds, String[] assetTagNames, Locale locale,
Double priority)
throws PortalException {
DDLRecordSet recordSet = record.getRecordSet();
int scope = recordSet.getScope();
if ((scope != DDLRecordSetConstants.SCOPE_DYNAMIC_DATA_LISTS) &&
(scope != DDLRecordSetConstants.SCOPE_FORMS) &&
(scope != DDLRecordSetConstants.SCOPE_KALEO_FORMS)) {
return;
}
boolean addDraftAssetEntry = false;
boolean visible = true;
if ((recordVersion != null) && !recordVersion.isApproved()) {
String version = recordVersion.getVersion();
if (!version.equals(DDLRecordConstants.VERSION_DEFAULT)) {
int approvedRecordVersionsCount =
ddlRecordVersionPersistence.countByR_S(
record.getRecordId(),
WorkflowConstants.STATUS_APPROVED);
if (approvedRecordVersionsCount > 0) {
addDraftAssetEntry = true;
}
}
visible = false;
}
if (scope == DDLRecordSetConstants.SCOPE_FORMS) {
visible = false;
}
DDMStructure ddmStructure = recordSet.getDDMStructure();
String ddmStructureName = ddmStructure.getName(locale);
String recordSetName = recordSet.getName(locale);
String title = LanguageUtil.format(
locale, "new-x-for-list-x",
new Object[] {ddmStructureName, recordSetName}, false);
if (addDraftAssetEntry) {
assetEntryLocalService.updateEntry(
userId, record.getGroupId(), record.getCreateDate(),
record.getModifiedDate(),
DDLRecordConstants.getClassName(scope),
recordVersion.getRecordVersionId(), record.getUuid(), 0,
assetCategoryIds, assetTagNames, true, false, null, null, null,
null, ContentTypes.TEXT_HTML, title, null, StringPool.BLANK,
null, null, 0, 0, priority);
}
else {
Date publishDate = null;
if (visible) {
publishDate = record.getCreateDate();
}
assetEntryLocalService.updateEntry(
userId, record.getGroupId(), record.getCreateDate(),
record.getModifiedDate(),
DDLRecordConstants.getClassName(scope), record.getRecordId(),
record.getUuid(), 0, assetCategoryIds, assetTagNames, true,
visible, null, null, publishDate, null, ContentTypes.TEXT_HTML,
title, null, StringPool.BLANK, null, null, 0, 0, priority);
}
}
/**
* Updates the record, replacing its display index and values.
*
* @param userId the primary key of the user updating the record
* @param recordId the primary key of the record
* @param majorVersion whether this update is a major change. A major
* change increments the record's major version number.
* @param displayIndex the index position in which the record is displayed
* in the spreadsheet view
* @param ddmFormValues the record values. See <code>DDMFormValues</code>
* in the <code>dynamic.data.mapping.api</code> module.
* @param serviceContext the service context to be applied. This can set
* the record modified date.
* @return the record
* @throws PortalException if a portal exception occurred
*/
@Indexable(type = IndexableType.REINDEX)
@Override
public DDLRecord updateRecord(
long userId, long recordId, boolean majorVersion, int displayIndex,
DDMFormValues ddmFormValues, ServiceContext serviceContext)
throws PortalException {
// Record
User user = userLocalService.getUser(userId);
DDLRecord record = ddlRecordPersistence.findByPrimaryKey(recordId);
record.setModifiedDate(serviceContext.getModifiedDate(null));
ddlRecordPersistence.update(record);
// Record version
DDLRecordVersion recordVersion = record.getLatestRecordVersion();
if (recordVersion.isApproved()) {
DDLRecordSet recordSet = record.getRecordSet();
long ddmStorageId = storageEngine.create(
recordSet.getCompanyId(), recordSet.getDDMStructureId(),
ddmFormValues, serviceContext);
String version = getNextVersion(
recordVersion.getVersion(), majorVersion,
serviceContext.getWorkflowAction());
recordVersion = addRecordVersion(
user, record, ddmStorageId, version, displayIndex,
WorkflowConstants.STATUS_DRAFT);
}
else {
storageEngine.update(
recordVersion.getDDMStorageId(), ddmFormValues, serviceContext);
String version = recordVersion.getVersion();
updateRecordVersion(
user, recordVersion, version, displayIndex,
recordVersion.getStatus(), serviceContext);
}
if (isKeepRecordVersionLabel(
record.getRecordVersion(), recordVersion, serviceContext)) {
ddlRecordVersionPersistence.remove(recordVersion);
// Dynamic data mapping storage
storageEngine.deleteByClass(recordVersion.getDDMStorageId());
return record;
}
// Workflow
if (serviceContext.getWorkflowAction() ==
WorkflowConstants.ACTION_PUBLISH) {
WorkflowHandlerRegistryUtil.startWorkflowInstance(
user.getCompanyId(), record.getGroupId(), userId,
DDLRecord.class.getName(), recordVersion.getRecordVersionId(),
recordVersion, serviceContext);
}
return record;
}
/**
* Updates a record, replacing its display index and values.
*
* @param userId the primary key of the user updating the record
* @param recordId the primary key of the record
* @param majorVersion whether this update is a major change. A major
* change increments the record's major version number
* @param displayIndex the index position in which the record is
* displayed in the spreadsheet view.
* @param fields the record values. See <code>Fields</code> in the
* <code>dynamic.data.mapping.api</code> module.
* @param mergeFields whether to merge the new fields with the existing
* ones; otherwise replace the existing fields
* @param serviceContext the service context to be applied. This can
* set the record modified date.
* @return the record
* @throws PortalException if a portal exception occurred
* @deprecated As of 1.1.0, replaced by {@link #updateRecord(long, long,
* boolean, int, DDMFormValues, ServiceContext)}
*/
@Deprecated
@Override
public DDLRecord updateRecord(
long userId, long recordId, boolean majorVersion, int displayIndex,
Fields fields, boolean mergeFields, ServiceContext serviceContext)
throws PortalException {
DDLRecord record = ddlRecordPersistence.findByPrimaryKey(recordId);
DDLRecordSet recordSet = record.getRecordSet();
DDLRecordVersion recordVersion = record.getLatestRecordVersion();
if (mergeFields) {
DDMFormValues existingDDMFormValues =
storageEngine.getDDMFormValues(recordVersion.getDDMStorageId());
Fields existingFields = ddmFormValuesToFieldsConverter.convert(
recordSet.getDDMStructure(), existingDDMFormValues);
fields = ddm.mergeFields(fields, existingFields);
}
DDMFormValues ddmFormValues = fieldsToDDMFormValuesConverter.convert(
recordSet.getDDMStructure(), fields);
return ddlRecordLocalService.updateRecord(
userId, recordId, majorVersion, displayIndex, ddmFormValues,
serviceContext);
}
/**
* Updates a record, replacing its display index and values.
*
* @param userId the primary key of the user updating the record
* @param recordId the primary key of the record
* @param displayIndex the index position in which the record is
* displayed in the spreadsheet view
* @param fieldsMap the record values. The fieldsMap is a map of field
* names and its Serializable values.
* @param mergeFields whether to merge the new fields with the existing
* ones; otherwise replace the existing fields
* @param serviceContext the service context to be applied. This can
* set the record modified date.
* @return the record
* @throws PortalException if a portal exception occurred
* @deprecated As of 1.1.0, replaced by {@link #updateRecord(long, long,
* boolean, int, DDMFormValues, ServiceContext)}
*/
@Deprecated
@Override
public DDLRecord updateRecord(
long userId, long recordId, int displayIndex,
Map<String, Serializable> fieldsMap, boolean mergeFields,
ServiceContext serviceContext)
throws PortalException {
DDLRecord record = ddlRecordPersistence.findByPrimaryKey(recordId);
DDMFormValues oldDDMFormValues = record.getDDMFormValues();
DDLRecordSet recordSet = record.getRecordSet();
DDMStructure ddmStructure = recordSet.getDDMStructure();
Fields fields = toFields(
ddmStructure.getStructureId(), fieldsMap,
serviceContext.getLocale(), oldDDMFormValues.getDefaultLocale());
return ddlRecordLocalService.updateRecord(
userId, recordId, false, displayIndex, fields, mergeFields,
serviceContext);
}
/**
* Updates the workflow status of the record version.
*
* @param userId the primary key of the user updating the record's workflow
* status
* @param recordVersionId the primary key of the record version
* @param status
* @param serviceContext the service context to be applied. This can set
* the modification date and status date.
* @return the record
* @throws PortalException if a portal exception occurred
*/
@Indexable(type = IndexableType.REINDEX)
@Override
public DDLRecord updateStatus(
long userId, long recordVersionId, int status,
ServiceContext serviceContext)
throws PortalException {
// Record version
User user = userLocalService.getUser(userId);
DDLRecordVersion recordVersion =
ddlRecordVersionPersistence.findByPrimaryKey(recordVersionId);
recordVersion.setStatus(status);
recordVersion.setStatusByUserId(user.getUserId());
recordVersion.setStatusByUserName(user.getFullName());
recordVersion.setStatusDate(new Date());
ddlRecordVersionPersistence.update(recordVersion);
// Record
DDLRecord record = ddlRecordPersistence.findByPrimaryKey(
recordVersion.getRecordId());
if (status == WorkflowConstants.STATUS_APPROVED) {
if (DLUtil.compareVersions(
record.getVersion(), recordVersion.getVersion()) <= 0) {
record.setDDMStorageId(recordVersion.getDDMStorageId());
record.setVersion(recordVersion.getVersion());
record.setRecordSetId(recordVersion.getRecordSetId());
record.setDisplayIndex(recordVersion.getDisplayIndex());
record.setVersion(recordVersion.getVersion());
record.setVersionUserId(recordVersion.getUserId());
record.setVersionUserName(recordVersion.getUserName());
ddlRecordPersistence.update(record);
}
}
else {
if (Objects.equals(
record.getVersion(), recordVersion.getVersion())) {
String newVersion = DDLRecordConstants.VERSION_DEFAULT;
List<DDLRecordVersion> approvedRecordVersions =
ddlRecordVersionPersistence.findByR_S(
record.getRecordId(),
WorkflowConstants.STATUS_APPROVED);
if (!approvedRecordVersions.isEmpty()) {
newVersion = approvedRecordVersions.get(0).getVersion();
}
record.setVersion(newVersion);
ddlRecordPersistence.update(record);
}
}
// Asset
Locale locale = serviceContext.getLocale();
updateAsset(
userId, record, recordVersion, serviceContext.getAssetCategoryIds(),
serviceContext.getAssetTagNames(), locale,
serviceContext.getAssetPriority());
return record;
}
protected DDLRecordVersion addRecordVersion(
User user, DDLRecord record, long ddmStorageId, String version,
int displayIndex, int status) {
long recordVersionId = counterLocalService.increment();
DDLRecordVersion recordVersion = ddlRecordVersionPersistence.create(
recordVersionId);
recordVersion.setGroupId(record.getGroupId());
recordVersion.setCompanyId(record.getCompanyId());
recordVersion.setUserId(user.getUserId());
recordVersion.setUserName(user.getFullName());
recordVersion.setCreateDate(record.getModifiedDate());
recordVersion.setDDMStorageId(ddmStorageId);
recordVersion.setRecordSetId(record.getRecordSetId());
recordVersion.setRecordSetVersion(record.getRecordSetVersion());
recordVersion.setRecordId(record.getRecordId());
recordVersion.setVersion(version);
recordVersion.setDisplayIndex(displayIndex);
recordVersion.setStatus(status);
recordVersion.setStatusByUserId(user.getUserId());
recordVersion.setStatusByUserName(user.getFullName());
recordVersion.setStatusDate(record.getModifiedDate());
ddlRecordVersionPersistence.update(recordVersion);
return recordVersion;
}
protected void deleteAssetEntry(long recordId) throws PortalException {
assetEntryLocalService.deleteEntry(
DDLFormRecord.class.getName(), recordId);
assetEntryLocalService.deleteEntry(DDLRecord.class.getName(), recordId);
}
protected void deleteRatingsStats(long recordId) throws PortalException {
ratingsStatsLocalService.deleteStats(
DDLFormRecord.class.getName(), recordId);
ratingsStatsLocalService.deleteStats(
DDLRecord.class.getName(), recordId);
}
protected void deleteWorkflowInstanceLink(
long companyId, long groupId, long recordVersionId)
throws PortalException {
workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(
companyId, groupId, DDLFormRecord.class.getName(), recordVersionId);
workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(
companyId, groupId, DDLRecord.class.getName(), recordVersionId);
}
protected Indexer<DDLRecord> getDDLRecordIndexer() {
Indexer<DDLRecord> indexer = indexerRegistry.nullSafeGetIndexer(
DDLRecord.class);
return indexer;
}
protected String getNextVersion(
String version, boolean majorVersion, int workflowAction) {
if (workflowAction == WorkflowConstants.ACTION_SAVE_DRAFT) {
majorVersion = false;
}
int[] versionParts = StringUtil.split(version, StringPool.PERIOD, 0);
if (majorVersion) {
versionParts[0]++;
versionParts[1] = 0;
}
else {
versionParts[1]++;
}
return versionParts[0] + StringPool.PERIOD + versionParts[1];
}
protected List<DDLRecord> getRecords(Hits hits) throws PortalException {
List<DDLRecord> records = new ArrayList<>();
for (Document document : hits.toList()) {
long recordId = GetterUtil.getLong(
document.get(
com.liferay.portal.kernel.search.Field.ENTRY_CLASS_PK));
try {
DDLRecord record = getRecord(recordId);
records.add(record);
}
catch (NoSuchRecordException nsre) {
if (_log.isWarnEnabled()) {
_log.warn(
"DDL record index is stale and contains record " +
recordId,
nsre);
}
long companyId = GetterUtil.getLong(
document.get(
com.liferay.portal.kernel.search.Field.COMPANY_ID));
Indexer<DDLRecord> indexer = getDDLRecordIndexer();
indexer.delete(companyId, document.getUID());
}
}
return records;
}
protected String getWorkflowAssetClassName(DDLRecordSet recordSet) {
if (recordSet.getScope() == DDLRecordSetConstants.SCOPE_FORMS) {
return DDLFormRecord.class.getName();
}
return DDLRecord.class.getName();
}
/**
* See <code>DLFileVersionPolicyImpl#isKeepFileVersionLabel</code> in the
* <code>com.liferay.document.library.service</code> module.
*/
protected boolean isKeepRecordVersionLabel(
DDLRecordVersion lastRecordVersion,
DDLRecordVersion latestRecordVersion, ServiceContext serviceContext)
throws PortalException {
if (Objects.equals(serviceContext.getCommand(), Constants.REVERT)) {
return false;
}
if (serviceContext.getWorkflowAction() ==
WorkflowConstants.ACTION_SAVE_DRAFT) {
return false;
}
if (Objects.equals(
lastRecordVersion.getVersion(),
latestRecordVersion.getVersion())) {
return false;
}
DDMFormValues lastDDMFormValues = storageEngine.getDDMFormValues(
lastRecordVersion.getDDMStorageId());
DDMFormValues latestDDMFormValues = storageEngine.getDDMFormValues(
latestRecordVersion.getDDMStorageId());
if (!lastDDMFormValues.equals(latestDDMFormValues)) {
return false;
}
ExpandoBridge lastExpandoBridge = lastRecordVersion.getExpandoBridge();
ExpandoBridge latestExpandoBridge =
latestRecordVersion.getExpandoBridge();
Map<String, Serializable> lastAttributes =
lastExpandoBridge.getAttributes();
Map<String, Serializable> latestAttributes =
latestExpandoBridge.getAttributes();
if (!lastAttributes.equals(latestAttributes)) {
return false;
}
return true;
}
protected Fields toFields(
long ddmStructureId, Map<String, Serializable> fieldsMap, Locale locale,
Locale defaultLocale) {
Fields fields = new Fields();
for (Map.Entry<String, Serializable> entry : fieldsMap.entrySet()) {
Field field = new Field();
field.setDDMStructureId(ddmStructureId);
field.setName(entry.getKey());
Serializable value = entry.getValue();
List<Serializable> serializableValues = _getSerializableValues(
value);
if (serializableValues != null) {
field.addValues(locale, serializableValues);
if (!locale.equals(defaultLocale)) {
field.addValues(defaultLocale, serializableValues);
}
}
else {
field.addValue(locale, value);
if (!locale.equals(defaultLocale)) {
field.addValue(defaultLocale, value);
}
}
field.setDefaultLocale(defaultLocale);
fields.put(field);
}
Field fieldsDisplayField = fields.get(_FIELDS_DISPLAY_NAME);
if (fieldsDisplayField == null) {
StringBundler fieldsDisplayFieldSB = new StringBundler(
fieldsMap.size() * 4 - 1);
for (String fieldName : fields.getNames()) {
fieldsDisplayFieldSB.append(fieldName);
fieldsDisplayFieldSB.append(_INSTANCE_SEPARATOR);
fieldsDisplayFieldSB.append(StringUtil.randomString());
fieldsDisplayFieldSB.append(StringPool.COMMA);
}
if (fieldsDisplayFieldSB.index() > 0) {
fieldsDisplayFieldSB.setIndex(fieldsDisplayFieldSB.index() - 1);
}
fieldsDisplayField = new Field(
ddmStructureId, _FIELDS_DISPLAY_NAME,
fieldsDisplayFieldSB.toString());
fields.put(fieldsDisplayField);
}
return fields;
}
protected void updateRecordVersion(
User user, DDLRecordVersion recordVersion, String version,
int displayIndex, int status, ServiceContext serviceContext) {
recordVersion.setUserId(user.getUserId());
recordVersion.setUserName(user.getFullName());
recordVersion.setVersion(version);
recordVersion.setDisplayIndex(displayIndex);
recordVersion.setStatus(status);
recordVersion.setStatusByUserId(user.getUserId());
recordVersion.setStatusByUserName(user.getFullName());
recordVersion.setStatusDate(serviceContext.getModifiedDate(null));
ddlRecordVersionPersistence.update(recordVersion);
}
protected void validate(long groupId, DDLRecordSet recordSet)
throws PortalException {
if (recordSet.getGroupId() != groupId) {
throw new RecordGroupIdException(
"Record group ID is not the same as the record set group ID");
}
}
@ServiceReference(type = DDM.class)
protected DDM ddm;
@ServiceReference(type = DDMFormValuesToFieldsConverter.class)
protected DDMFormValuesToFieldsConverter ddmFormValuesToFieldsConverter;
@ServiceReference(type = DDMStructureLocalService.class)
protected DDMStructureLocalService ddmStructureLocalService;
@ServiceReference(type = FieldsToDDMFormValuesConverter.class)
protected FieldsToDDMFormValuesConverter fieldsToDDMFormValuesConverter;
@ServiceReference(type = IndexerRegistry.class)
protected IndexerRegistry indexerRegistry;
@ServiceReference(type = StorageEngine.class)
protected StorageEngine storageEngine;
private List<Serializable> _getSerializableValues(Serializable value) {
List<Serializable> serializableValues = null;
if (value instanceof Collection) {
Collection<Serializable> values = (Collection<Serializable>)value;
serializableValues = new ArrayList<>(values);
}
else if (value instanceof Serializable[]) {
Serializable[] values = (Serializable[])value;
serializableValues = ListUtil.toList(values);
}
else if (value instanceof boolean[]) {
boolean[] values = (boolean[])value;
serializableValues = new ArrayList<>(values.length);
for (boolean serializableValue : values) {
serializableValues.add(serializableValue);
}
}
else if (value instanceof double[]) {
double[] values = (double[])value;
serializableValues = new ArrayList<>(values.length);
for (double serializableValue : values) {
serializableValues.add(serializableValue);
}
}
else if (value instanceof float[]) {
float[] values = (float[])value;
serializableValues = new ArrayList<>(values.length);
for (float serializableValue : values) {
serializableValues.add(serializableValue);
}
}
else if (value instanceof int[]) {
int[] values = (int[])value;
serializableValues = new ArrayList<>(values.length);
for (int serializableValue : values) {
serializableValues.add(serializableValue);
}
}
else if (value instanceof long[]) {
long[] values = (long[])value;
serializableValues = new ArrayList<>(values.length);
for (long serializableValue : values) {
serializableValues.add(serializableValue);
}
}
else if (value instanceof short[]) {
short[] values = (short[])value;
serializableValues = new ArrayList<>(values.length);
for (short serializableValue : values) {
serializableValues.add(serializableValue);
}
}
return serializableValues;
}
private static final String _FIELDS_DISPLAY_NAME = "_fieldsDisplay";
private static final String _INSTANCE_SEPARATOR = "_INSTANCE_";
private static final Log _log = LogFactoryUtil.getLog(
DDLRecordLocalServiceImpl.class);
}