1326 lines
42 KiB
Java
1326 lines
42 KiB
Java
package org.gcube.gcat.persistence.ckan;
|
|
|
|
import java.io.StringWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import javax.ws.rs.BadRequestException;
|
|
import javax.ws.rs.ForbiddenException;
|
|
import javax.ws.rs.InternalServerErrorException;
|
|
import javax.ws.rs.NotAllowedException;
|
|
import javax.ws.rs.WebApplicationException;
|
|
import javax.ws.rs.core.MultivaluedMap;
|
|
import javax.ws.rs.core.Response.Status;
|
|
|
|
import org.apache.http.MethodNotSupportedException;
|
|
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
|
|
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
|
|
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
|
|
import org.gcube.common.scope.impl.ScopeBean;
|
|
import org.gcube.common.scope.impl.ScopeBean.Type;
|
|
import org.gcube.gcat.api.GCatConstants;
|
|
import org.gcube.gcat.api.moderation.CMItemStatus;
|
|
import org.gcube.gcat.api.moderation.CMItemVisibility;
|
|
import org.gcube.gcat.api.moderation.Moderated;
|
|
import org.gcube.gcat.api.moderation.ModerationContent;
|
|
import org.gcube.gcat.api.roles.Role;
|
|
import org.gcube.gcat.moderation.thread.ModerationThread;
|
|
import org.gcube.gcat.oldutils.Validator;
|
|
import org.gcube.gcat.profile.MetadataUtility;
|
|
import org.gcube.gcat.social.PortalUser;
|
|
import org.gcube.gcat.social.SocialPost;
|
|
import org.gcube.gcat.utils.URIResolver;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
/**
|
|
* @author Luca Frosini (ISTI - CNR)
|
|
*/
|
|
public class CKANPackage extends CKAN implements Moderated {
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(CKANPackage.class);
|
|
|
|
/*
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_list
|
|
public static final String ITEM_LIST = CKAN.CKAN_API_PATH + "package_list";
|
|
*/
|
|
// see https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.package_search
|
|
public static final String ITEM_LIST = CKAN.CKAN_API_PATH + "package_search";
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.create.package_create
|
|
public static final String ITEM_CREATE = CKAN.CKAN_API_PATH + "package_create";
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_show
|
|
public static final String ITEM_SHOW = CKAN.CKAN_API_PATH + "package_show";
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.update.package_update
|
|
public static final String ITEM_UPDATE = CKAN.CKAN_API_PATH + "package_update";
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.package_patch
|
|
public static final String ITEM_PATCH = CKAN.CKAN_API_PATH + "package_patch";
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.package_delete
|
|
public static final String ITEM_DELETE = CKAN.CKAN_API_PATH + "package_delete";
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.dataset_purge
|
|
public static final String ITEM_PURGE = CKAN.CKAN_API_PATH + "dataset_purge";
|
|
|
|
|
|
// limit in https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.package_search
|
|
protected static final String ROWS_KEY = "rows";
|
|
// offset in https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.package_search
|
|
protected static final String START_KEY = "start";
|
|
|
|
protected static final String ORGANIZATION_FILTER_TEMPLATE = GCatConstants.ORGANIZATION_PARAMETER + ":%s";
|
|
|
|
protected static final String ORGANIZATION_REGEX = GCatConstants.ORGANIZATION_PARAMETER + ":[a-zA-Z0-9_\"]*";
|
|
|
|
protected static final Pattern ORGANIZATION_REGEX_PATTERN;
|
|
|
|
static {
|
|
ORGANIZATION_REGEX_PATTERN = Pattern.compile(ORGANIZATION_REGEX);
|
|
}
|
|
|
|
protected static final String LICENSE_KEY = "license_id";
|
|
|
|
protected static final String EXTRAS_ITEM_URL_KEY = "Item URL";
|
|
|
|
protected static final String AUTHOR_KEY = "author";
|
|
protected static final String AUTHOR_EMAIL_KEY = "author_email";
|
|
|
|
protected static final String MAINTAINER_KEY = "maintainer";
|
|
protected static final String MAINTAINER_EMAIL_KEY = "maintainer_email";
|
|
|
|
protected static final String OWNER_ORG_KEY = "owner_org";
|
|
protected static final String RESOURCES_KEY = "resources";
|
|
protected static final String TITLE_KEY = "title";
|
|
|
|
public static final String EXTRAS_KEY = "extras";
|
|
public static final String EXTRAS_KEY_KEY = "key";
|
|
public static final String EXTRAS_KEY_VALUE_SYSTEM_TYPE = "system:type";
|
|
public static final String EXTRAS_VALUE_KEY = "value";
|
|
|
|
// The 'results' array is included in the 'result' object for package_search
|
|
private static final String RESULTS_KEY = "results";
|
|
|
|
protected static final String PRIVATE_KEY = "private";
|
|
protected static final String SEARCHABLE_KEY = "searchable";
|
|
protected static final String CAPACITY_KEY = "capacity";
|
|
|
|
protected static final String CM_STATUS_QUERY_FILTER_KEY = "extras_systemcm_item_status";
|
|
|
|
protected static final String INCLUDE_PRIVATE_KEY = "include_private";
|
|
// protected static final String INCLUDE_DRAFTS_KEY = "include_drafts";
|
|
|
|
public static final String GROUPS_KEY = "groups";
|
|
public static final String TAGS_KEY = "tags";
|
|
|
|
protected final List<CKANResource> managedResources;
|
|
|
|
protected String itemID;
|
|
|
|
protected final CKANUser ckanUser;
|
|
|
|
protected final CKANInstance ckanInstance;
|
|
protected final Set<String> supportedOrganizations;
|
|
|
|
protected ModerationThread moderationThread;
|
|
|
|
public CKANPackage() {
|
|
super();
|
|
|
|
LIST = ITEM_LIST;
|
|
CREATE = ITEM_CREATE;
|
|
READ = ITEM_SHOW;
|
|
UPDATE = ITEM_UPDATE;
|
|
PATCH = ITEM_PATCH;
|
|
DELETE = ITEM_DELETE;
|
|
PURGE = ITEM_PURGE;
|
|
managedResources = new ArrayList<CKANResource>();
|
|
|
|
ckanUser = CKANUserCache.getCurrrentCKANUser();
|
|
|
|
ckanInstance = CKANInstance.getInstance();
|
|
supportedOrganizations = ckanInstance.getSupportedOrganizations();
|
|
|
|
for(String supportedOrganization : supportedOrganizations) {
|
|
ckanUser.addUserToOrganization(supportedOrganization);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Return the CKAN organization name using the current context name
|
|
*/
|
|
protected static String getOrganizationName(ScopeBean scopeBean) {
|
|
String contextName = scopeBean.name();
|
|
return contextName.toLowerCase().replace(" ", "_");
|
|
}
|
|
|
|
protected CKANOrganization checkGotOrganization(String gotOrganization) throws ForbiddenException {
|
|
if(!supportedOrganizations.contains(gotOrganization)) {
|
|
String error = String.format(
|
|
"IS Configuration does not allow to publish in %s organizations. Allowed organization are: %s",
|
|
gotOrganization, supportedOrganizations);
|
|
throw new ForbiddenException(error);
|
|
}
|
|
|
|
CKANOrganization ckanOrganization = new CKANOrganization();
|
|
ckanOrganization.setName(gotOrganization);
|
|
ckanOrganization.read();
|
|
|
|
return ckanOrganization;
|
|
}
|
|
|
|
protected CKANOrganization getPublishingOrganization(ObjectNode objectNode) throws ForbiddenException {
|
|
CKANOrganization ckanOrganization = null;
|
|
if(objectNode.has(OWNER_ORG_KEY)) {
|
|
String gotOrganizationName = objectNode.get(OWNER_ORG_KEY).asText();
|
|
ckanOrganization = checkGotOrganization(gotOrganizationName);
|
|
}
|
|
|
|
if(ckanOrganization == null) {
|
|
// owner organization must be specified if the token belongs to a VRE
|
|
String organizationFromContext = ckanInstance.getCurrentOrganizationName();
|
|
ckanOrganization = checkGotOrganization(organizationFromContext);
|
|
objectNode.put(OWNER_ORG_KEY, organizationFromContext);
|
|
}
|
|
|
|
return ckanOrganization;
|
|
}
|
|
|
|
public ObjectNode checkBaseInformation(String json) throws Exception {
|
|
return checkBaseInformation(json, false);
|
|
}
|
|
|
|
public JsonNode cleanResult(JsonNode jsonNode) {
|
|
if(jsonNode.has(OWNER_ORG_KEY)) {
|
|
((ObjectNode) jsonNode).remove(OWNER_ORG_KEY);
|
|
}
|
|
|
|
// Removing all Content Moderation Keys
|
|
if(jsonNode.has(EXTRAS_KEY)) {
|
|
ArrayNode extras = (ArrayNode) jsonNode.get(EXTRAS_KEY);
|
|
// It is not possible to remove the element of an array while iterating it.
|
|
// We need to create a new array only with valie elements;
|
|
ArrayNode newExtras = mapper.createArrayNode();
|
|
boolean foundOne = false;
|
|
for(int i=0; i<extras.size(); i++) {
|
|
JsonNode extra = extras.get(i);
|
|
if(extra.has(EXTRAS_KEY_KEY) &&
|
|
extra.get(EXTRAS_KEY_KEY)!=null &&
|
|
extra.get(EXTRAS_KEY_KEY).asText().startsWith(Moderated.SYSTEM_CM_PREFIX)) {
|
|
foundOne = true;
|
|
}else {
|
|
newExtras.add(extra.deepCopy());
|
|
}
|
|
}
|
|
|
|
if(foundOne) {
|
|
((ObjectNode) jsonNode).replace(EXTRAS_KEY, newExtras);
|
|
}
|
|
}
|
|
return jsonNode;
|
|
}
|
|
|
|
protected String getAsCleanedString(JsonNode node) {
|
|
JsonNode jsonNode = cleanResult(node);
|
|
return getAsString(jsonNode);
|
|
}
|
|
|
|
/**
|
|
* @param json The json to check
|
|
* @param allowPartialInfo used for patch method which provide only partial information (i.e. the info to patch)
|
|
* @return ObjectNode from json
|
|
* @throws Exception
|
|
*/
|
|
public ObjectNode checkBaseInformation(String json, boolean allowPartialInfo) throws Exception {
|
|
ObjectNode objectNode = (ObjectNode) mapper.readTree(json);
|
|
|
|
try {
|
|
objectNode = (ObjectNode) checkName(objectNode);
|
|
}catch (Exception e) {
|
|
if(!allowPartialInfo) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
// We need to enforce the itemID to properly manage resource persistence
|
|
if(objectNode.has(ID_KEY)) {
|
|
itemID = objectNode.get(ID_KEY).asText();
|
|
}
|
|
|
|
// To include private item in search result (e.g. listing) a private package must be searchable
|
|
// The package it is just included in the search and listing results but remain private and cannot be accessed
|
|
// if not authorized
|
|
if(objectNode.has(PRIVATE_KEY)) {
|
|
boolean privatePackage = objectNode.get(PRIVATE_KEY).asBoolean();
|
|
if(privatePackage) {
|
|
objectNode.put(SEARCHABLE_KEY, true);
|
|
}
|
|
}
|
|
|
|
// check license
|
|
String licenseId = null;
|
|
if(objectNode.has(LICENSE_KEY)) {
|
|
licenseId = objectNode.get(LICENSE_KEY).asText();
|
|
}
|
|
|
|
if(licenseId!=null && !licenseId.isEmpty()) {
|
|
try {
|
|
CKANLicense.checkLicenseId(licenseId);
|
|
} catch(Exception e) {
|
|
throw new BadRequestException(
|
|
"You must specify an existing license identifier for the item. License list can be retrieved using licence collection");
|
|
}
|
|
}else if(!allowPartialInfo) {
|
|
throw new BadRequestException(
|
|
"You must specify a license identifier for the item. License list can be retrieved using licence collection");
|
|
}
|
|
|
|
if(objectNode.has(CAPACITY_KEY)) {
|
|
/*
|
|
* When a client provides the 'capacity' field as 'private', the item is not counted in the
|
|
* total number of items in the GUI. We want to avoid such a behavior
|
|
* See https://support.d4science.org/issues/16410
|
|
*/
|
|
objectNode.remove(CAPACITY_KEY);
|
|
}
|
|
|
|
PortalUser portalUser = ckanUser.getPortalUser();
|
|
String authorName = String.format("%s %s", portalUser.getSurname(), portalUser.getName());
|
|
if(authorName==null || authorName.compareTo("")==0) {
|
|
authorName = ckanUser.getName();
|
|
}
|
|
objectNode.put(AUTHOR_KEY, authorName);
|
|
String authorEmail = portalUser.getEMail();
|
|
objectNode.put(AUTHOR_EMAIL_KEY, authorEmail);
|
|
|
|
|
|
if(!objectNode.has(MAINTAINER_KEY)) {
|
|
if(!objectNode.has(MAINTAINER_EMAIL_KEY)) {
|
|
objectNode.put(MAINTAINER_KEY, authorName);
|
|
objectNode.put(MAINTAINER_EMAIL_KEY, authorEmail);
|
|
}else {
|
|
objectNode.put(MAINTAINER_KEY, objectNode.get(MAINTAINER_EMAIL_KEY).toString());
|
|
}
|
|
}
|
|
|
|
getPublishingOrganization(objectNode);
|
|
|
|
return objectNode;
|
|
}
|
|
|
|
protected JsonNode validateJson(String json) {
|
|
try {
|
|
// check base information (and set them if needed)
|
|
ObjectNode objectNode = checkBaseInformation(json);
|
|
|
|
// Validating against profiles if any
|
|
MetadataUtility metadataUtility = new MetadataUtility();
|
|
if(!metadataUtility.getMetadataProfiles().isEmpty()) {
|
|
Validator validator = new Validator(mapper);
|
|
objectNode = validator.validateAgainstProfile(objectNode, metadataUtility);
|
|
}
|
|
|
|
return objectNode;
|
|
} catch(BadRequestException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new BadRequestException(e);
|
|
}
|
|
}
|
|
|
|
protected Map<String,String> getListCountParameters(int limit, int offset) {
|
|
Map<String,String> parameters = new HashMap<>();
|
|
if(limit <= 0) {
|
|
// According to CKAN documentation
|
|
// the number of matching rows to return. There is a hard limit of 1000 datasets per query.
|
|
// see https://docs.ckan.org/en/2.6/api/index.html#ckan.logic.action.get.package_search
|
|
limit = 1000;
|
|
}
|
|
parameters.put(ROWS_KEY, String.valueOf(limit));
|
|
|
|
if(offset < 0) {
|
|
offset = 0;
|
|
}
|
|
parameters.put(START_KEY, String.valueOf(offset * limit));
|
|
|
|
if(uriInfo != null) {
|
|
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
|
parameters = checkListParameters(queryParameters, parameters);
|
|
}
|
|
|
|
if(!parameters.containsKey(GCatConstants.Q_KEY)) {
|
|
String filter = getFilterForOrganizations();
|
|
parameters.put(GCatConstants.Q_KEY, filter);
|
|
}
|
|
|
|
parameters = addModerationStatusFilter(parameters);
|
|
|
|
return parameters;
|
|
}
|
|
|
|
public String list(Map<String,String> parameters) {
|
|
sendGetRequest(LIST, parameters);
|
|
|
|
ArrayNode results = (ArrayNode) result.get(RESULTS_KEY);
|
|
|
|
ArrayNode arrayNode = mapper.createArrayNode();
|
|
for(JsonNode node : results) {
|
|
try {
|
|
String name = node.get(NAME_KEY).asText();
|
|
arrayNode.add(name);
|
|
} catch(Exception e) {
|
|
try {
|
|
logger.error("Unable to get the ID of {}. the result will not be included in the result",
|
|
mapper.writeValueAsString(node));
|
|
} catch(Exception ex) {
|
|
logger.error("", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
return getAsString(arrayNode);
|
|
}
|
|
|
|
@Override
|
|
public String list(int limit, int offset) {
|
|
Map<String,String> parameters = getListCountParameters(limit, offset);
|
|
return list(parameters);
|
|
}
|
|
|
|
public int count() {
|
|
Map<String,String> parameters = getListCountParameters(1, 0);
|
|
|
|
sendGetRequest(LIST, parameters);
|
|
|
|
int count = result.get(GCatConstants.COUNT_KEY).asInt();
|
|
return count;
|
|
}
|
|
|
|
protected Set<String> checkOrganizationFilter(String q) {
|
|
Matcher m = ORGANIZATION_REGEX_PATTERN.matcher(q);
|
|
|
|
Set<String> matches = new HashSet<>();
|
|
while(m.find()) {
|
|
matches.add(q.substring(m.start(), m.end()).replace(GCatConstants.ORGANIZATION_PARAMETER+":", ""));
|
|
}
|
|
|
|
return matches;
|
|
}
|
|
|
|
protected static String[] allowedListQueryParameters = new String[] {"q", "fq", "fq_list", "sort",
|
|
/* "facet", "facet.mincount", "facet.limit", "facet.field", */
|
|
"include_drafts", "include_private", "ext_bbox"};
|
|
|
|
protected String getFilterForOrganizations() {
|
|
StringWriter stringWriter = new StringWriter();
|
|
int i=1;
|
|
for(String organizationName : supportedOrganizations) {
|
|
stringWriter.append(String.format(GCatConstants.ORGANIZATION_FILTER_TEMPLATE, organizationName));
|
|
if(i!=supportedOrganizations.size()) {
|
|
// Please note that an item can only belong to a single organization.
|
|
// Hence the query must put supported organizations in OR.
|
|
stringWriter.append(" OR ");
|
|
}
|
|
i++;
|
|
}
|
|
return stringWriter.toString();
|
|
}
|
|
|
|
protected Map<String,String> checkListParameters(MultivaluedMap<String,String> queryParameters,
|
|
Map<String,String> parameters) {
|
|
|
|
String q = null;
|
|
if(queryParameters.containsKey(GCatConstants.Q_KEY)) {
|
|
q = queryParameters.getFirst(GCatConstants.Q_KEY);
|
|
}
|
|
|
|
if(q != null) {
|
|
Set<String> organizations = checkOrganizationFilter(q);
|
|
|
|
if(organizations.size()==0) {
|
|
// Adding organization filter to q
|
|
String filter = getFilterForOrganizations();
|
|
parameters.put(GCatConstants.Q_KEY, String.format("%s AND %s", q, filter));
|
|
}else {
|
|
organizations.removeAll(this.supportedOrganizations);
|
|
if(organizations.size()>0) {
|
|
String error = String.format("It is not possible to query the following organizations %s. Supported organization in this context are %s", organizations.toString(), supportedOrganizations.toString());
|
|
throw new ForbiddenException(error);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
String filter = getFilterForOrganizations();
|
|
parameters.put(GCatConstants.Q_KEY, filter);
|
|
}
|
|
|
|
|
|
for(String key : allowedListQueryParameters) {
|
|
if(queryParameters.containsKey(key)) {
|
|
parameters.put(key, queryParameters.getFirst(key));
|
|
}
|
|
}
|
|
|
|
// parameters.put(INCLUDE_PRIVATE_KEY, String.valueOf(true));
|
|
|
|
// By default not including draft
|
|
// parameters.put(INCLUDE_DRAFTS_KEY, String.valueOf(false));
|
|
|
|
return parameters;
|
|
}
|
|
|
|
protected void rollbackManagedResources() {
|
|
for(CKANResource ckanResource : managedResources) {
|
|
try {
|
|
ckanResource.rollback();
|
|
} catch(Exception e) {
|
|
logger.error("Unable to rollback resource {} to the original value", ckanResource.getResourceID());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
protected ArrayNode createResources(ArrayNode resourcesToBeCreated) {
|
|
ArrayNode created = mapper.createArrayNode();
|
|
for(JsonNode resourceNode : resourcesToBeCreated) {
|
|
CKANResource ckanResource = new CKANResource(itemID);
|
|
String json = ckanResource.create(getAsString(resourceNode));
|
|
created.add(getAsJsonNode(json));
|
|
managedResources.add(ckanResource);
|
|
}
|
|
return created;
|
|
}
|
|
|
|
protected JsonNode addExtraField(JsonNode jsonNode, String key, String value) {
|
|
ArrayNode extras = null;
|
|
boolean found = false;
|
|
if(jsonNode.has(EXTRAS_KEY)) {
|
|
extras = (ArrayNode) jsonNode.get(EXTRAS_KEY);
|
|
for(JsonNode extra : extras) {
|
|
if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY)!=null && extra.get(EXTRAS_KEY_KEY).asText().compareTo(key) == 0) {
|
|
((ObjectNode) extra).put(EXTRAS_VALUE_KEY, value);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
extras = ((ObjectNode) jsonNode).putArray(EXTRAS_KEY);
|
|
}
|
|
|
|
if(!found) {
|
|
ObjectNode extra = mapper.createObjectNode();
|
|
extra.put(EXTRAS_KEY_KEY, key);
|
|
extra.put(EXTRAS_VALUE_KEY, value);
|
|
extras.add(extra);
|
|
}
|
|
|
|
return jsonNode;
|
|
}
|
|
|
|
protected JsonNode getExtraField(JsonNode jsonNode, String key) {
|
|
if(jsonNode.has(EXTRAS_KEY)) {
|
|
ArrayNode extras = (ArrayNode) jsonNode.get(EXTRAS_KEY);
|
|
for(JsonNode extra : extras) {
|
|
if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY)!=null && extra.get(EXTRAS_KEY_KEY).asText().compareTo(key) == 0) {
|
|
return extra.get(EXTRAS_VALUE_KEY);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
protected String addItemURLViaResolver(JsonNode jsonNode) {
|
|
// Adding Item URL via Resolver
|
|
URIResolver uriResolver = new URIResolver();
|
|
String catalogueItemURL = uriResolver.getCatalogueItemURL(name);
|
|
addExtraField(jsonNode, EXTRAS_ITEM_URL_KEY, catalogueItemURL);
|
|
return catalogueItemURL;
|
|
}
|
|
|
|
protected void sendSocialPost(String title, String catalogueItemURL) {
|
|
try {
|
|
boolean makePost = false;
|
|
try {
|
|
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
|
if(queryParameters.containsKey(GCatConstants.SOCIAL_POST_QUERY_PARAMETER)) {
|
|
makePost = Boolean.parseBoolean(queryParameters.getFirst(GCatConstants.SOCIAL_POST_QUERY_PARAMETER));
|
|
}
|
|
} catch(Exception e) {
|
|
makePost = false;
|
|
}
|
|
|
|
if(makePost) {
|
|
ArrayNode arrayNode = (ArrayNode) result.get(TAGS_KEY);
|
|
SocialPost socialPost = new SocialPost();
|
|
socialPost.setItemID(itemID);
|
|
socialPost.setItemURL(catalogueItemURL);
|
|
socialPost.setTags(arrayNode);
|
|
socialPost.setItemTitle(title);
|
|
|
|
Boolean notification = null;
|
|
try {
|
|
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
|
if(queryParameters.containsKey(GCatConstants.SOCIAL_POST_NOTIFICATION_QUERY_PARAMETER)) {
|
|
notification = Boolean.parseBoolean(queryParameters.getFirst(GCatConstants.SOCIAL_POST_NOTIFICATION_QUERY_PARAMETER));
|
|
}
|
|
} catch(Exception e) {
|
|
|
|
}
|
|
socialPost.setNotification(notification);
|
|
socialPost.start();
|
|
} else {
|
|
logger.info("The request explicitly disabled the Social Post.");
|
|
}
|
|
} catch(Exception e) {
|
|
logger.warn(
|
|
"error dealing with Social Post. The service will not raise the exception belove. Please contact the administrator to let him know about this message.",
|
|
e);
|
|
}
|
|
}
|
|
|
|
protected boolean isItemCreator() {
|
|
return result.get(AUTHOR_EMAIL_KEY).asText().compareTo(ckanUser.getPortalUser().getEMail())==0;
|
|
}
|
|
|
|
protected void readItem() throws Exception {
|
|
String ret = super.read();
|
|
result = mapper.readTree(ret);
|
|
this.itemID = result.get(ID_KEY).asText();
|
|
}
|
|
|
|
|
|
@Override
|
|
public String read() {
|
|
try {
|
|
readItem();
|
|
checkModerationRead();
|
|
return getAsCleanedString(result);
|
|
} catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.create.package_create
|
|
@Override
|
|
public String create(String json) {
|
|
try {
|
|
logger.debug("Going to create Item {}", json);
|
|
|
|
if(ckanUser.getRole().ordinal() < Role.EDITOR.ordinal()) {
|
|
StringBuffer stringBuffer = new StringBuffer();
|
|
stringBuffer.append("Only ");
|
|
stringBuffer.append(Role.EDITOR.getPortalRole());
|
|
stringBuffer.append(" and ");
|
|
stringBuffer.append(Role.ADMIN.getPortalRole());
|
|
stringBuffer.append(" are entitled to create items. ");
|
|
stringBuffer.append("Please contact the VRE Manager to request your grant.");
|
|
throw new ForbiddenException(stringBuffer.toString());
|
|
}
|
|
|
|
JsonNode jsonNode = validateJson(json);
|
|
|
|
setItemToPending(jsonNode);
|
|
|
|
ArrayNode resourcesToBeCreated = mapper.createArrayNode();
|
|
if(jsonNode.has(RESOURCES_KEY)) {
|
|
resourcesToBeCreated = (ArrayNode) jsonNode.get(RESOURCES_KEY);
|
|
((ObjectNode) jsonNode).remove(RESOURCES_KEY);
|
|
}
|
|
|
|
String catalogueItemURL = "";
|
|
if(ckanInstance.getCurrentScopeBean().is(Type.VRE)) {
|
|
catalogueItemURL = addItemURLViaResolver(jsonNode);
|
|
}
|
|
|
|
super.create(getAsString(jsonNode));
|
|
|
|
this.itemID = result.get(ID_KEY).asText();
|
|
ArrayNode created = createResources(resourcesToBeCreated);
|
|
((ObjectNode) result).replace(RESOURCES_KEY, created);
|
|
|
|
postItemCreated();
|
|
|
|
if(!isModerationEnabled()) {
|
|
if(ckanInstance.getCurrentScopeBean().is(Type.VRE)) {
|
|
// Actions performed after a package has been correctly created on ckan.
|
|
String title = result.get(TITLE_KEY).asText();
|
|
sendSocialPost(title, catalogueItemURL);
|
|
}
|
|
}
|
|
|
|
return getAsCleanedString(result);
|
|
} catch(WebApplicationException e) {
|
|
rollbackManagedResources();
|
|
throw e;
|
|
} catch(Exception e) {
|
|
rollbackManagedResources();
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.update.package_update
|
|
@Override
|
|
public String update(String json) {
|
|
try {
|
|
JsonNode jsonNode = validateJson(json);
|
|
|
|
readItem();
|
|
|
|
jsonNode = checkModerationUpdate(jsonNode);
|
|
|
|
Map<String,CKANResource> originalResources = new HashMap<>();
|
|
ArrayNode originalResourcesarrayNode = (ArrayNode) result.get(RESOURCES_KEY);
|
|
if(originalResources != null) {
|
|
for(JsonNode resourceNode : originalResourcesarrayNode) {
|
|
CKANResource ckanResource = new CKANResource(itemID);
|
|
ckanResource.setPreviousRepresentation(resourceNode);
|
|
String resourceID = ckanResource.getResourceID();
|
|
originalResources.put(resourceID, ckanResource);
|
|
}
|
|
}
|
|
|
|
if(jsonNode.has(RESOURCES_KEY)) {
|
|
ArrayNode resourcesToBeSend = mapper.createArrayNode();
|
|
ArrayNode receivedResources = (ArrayNode) jsonNode.get(RESOURCES_KEY);
|
|
for(JsonNode resourceNode : receivedResources) {
|
|
CKANResource ckanResource = new CKANResource(itemID);
|
|
String resourceId = CKANResource.extractResourceID(resourceNode);
|
|
if(resourceId != null) {
|
|
if(originalResources.containsKey(resourceId)) {
|
|
ckanResource = originalResources.get(resourceId);
|
|
originalResources.remove(resourceId);
|
|
} else {
|
|
throw new BadRequestException(
|
|
"The content contains a resource with id " + resourceId + " which does not exists");
|
|
}
|
|
}
|
|
if(originalResources.get(resourceId) != null
|
|
&& (!originalResources.get(resourceId).getPreviousRepresentation().equals(resourceNode))) {
|
|
resourceNode = ckanResource.createOrUpdate(resourceNode);
|
|
}
|
|
resourcesToBeSend.add(resourceNode);
|
|
managedResources.add(ckanResource);
|
|
|
|
}
|
|
((ObjectNode) jsonNode).replace(RESOURCES_KEY, resourcesToBeSend);
|
|
}
|
|
|
|
addItemURLViaResolver(jsonNode);
|
|
|
|
sendPostRequest(ITEM_UPDATE, getAsString(jsonNode));
|
|
|
|
for(String resourceId : originalResources.keySet()) {
|
|
CKANResource ckanResource = originalResources.get(resourceId);
|
|
ckanResource.deleteFile();
|
|
}
|
|
|
|
postItemUpdated();
|
|
|
|
return getAsCleanedString(result);
|
|
} catch(WebApplicationException e) {
|
|
rollbackManagedResources();
|
|
throw e;
|
|
} catch(Exception e) {
|
|
rollbackManagedResources();
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.package_patch
|
|
@Override
|
|
public String patch(String json) {
|
|
try {
|
|
readItem();
|
|
|
|
JsonNode jsonNode = checkBaseInformation(json, true);
|
|
((ObjectNode)jsonNode).put(ID_KEY, this.itemID);
|
|
|
|
jsonNode = checkModerationUpdate(jsonNode);
|
|
|
|
Map<String,CKANResource> originalResources = new HashMap<>();
|
|
ArrayNode originalResourcesarrayNode = (ArrayNode) result.get(RESOURCES_KEY);
|
|
if(originalResources != null) {
|
|
for(JsonNode resourceNode : originalResourcesarrayNode) {
|
|
CKANResource ckanResource = new CKANResource(itemID);
|
|
ckanResource.setPreviousRepresentation(resourceNode);
|
|
String resourceID = ckanResource.getResourceID();
|
|
originalResources.put(resourceID, ckanResource);
|
|
}
|
|
}
|
|
|
|
if(jsonNode.has(RESOURCES_KEY)) {
|
|
ArrayNode resourcesToBeSend = mapper.createArrayNode();
|
|
ArrayNode receivedResources = (ArrayNode) jsonNode.get(RESOURCES_KEY);
|
|
for(JsonNode resourceNode : receivedResources) {
|
|
CKANResource ckanResource = new CKANResource(itemID);
|
|
String resourceId = CKANResource.extractResourceID(resourceNode);
|
|
if(resourceId != null) {
|
|
if(originalResources.containsKey(resourceId)) {
|
|
ckanResource = originalResources.get(resourceId);
|
|
originalResources.remove(resourceId);
|
|
} else {
|
|
throw new BadRequestException(
|
|
"The content contains a resource with id " + resourceId + " which does not exists");
|
|
}
|
|
}
|
|
if(originalResources.get(resourceId) != null
|
|
&& (!originalResources.get(resourceId).getPreviousRepresentation().equals(resourceNode))) {
|
|
resourceNode = ckanResource.createOrUpdate(resourceNode);
|
|
}
|
|
resourcesToBeSend.add(resourceNode);
|
|
managedResources.add(ckanResource);
|
|
|
|
}
|
|
((ObjectNode) jsonNode).replace(RESOURCES_KEY, resourcesToBeSend);
|
|
}
|
|
|
|
addItemURLViaResolver(jsonNode);
|
|
|
|
sendPostRequest(ITEM_PATCH, getAsString(jsonNode));
|
|
|
|
for(String resourceId : originalResources.keySet()) {
|
|
CKANResource ckanResource = originalResources.get(resourceId);
|
|
ckanResource.deleteFile();
|
|
}
|
|
|
|
postItemUpdated();
|
|
|
|
return getAsCleanedString(result);
|
|
} catch(WebApplicationException e) {
|
|
rollbackManagedResources();
|
|
throw e;
|
|
} catch(Exception e) {
|
|
rollbackManagedResources();
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void delete() {
|
|
checkModerationDelete();
|
|
super.delete();
|
|
}
|
|
|
|
@Override
|
|
public void purge() {
|
|
try {
|
|
delete();
|
|
} catch(WebApplicationException e) {
|
|
// If the item was deleted but not purged we obtain Not Found. This is accepted. The item has to be purged
|
|
// with SysAdmin right.
|
|
Status status = Status.fromStatusCode(e.getResponse().getStatusInfo().getStatusCode());
|
|
if(status != Status.NOT_FOUND) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
try {
|
|
setApiKey(CKANUtility.getSysAdminAPI());
|
|
readItem();
|
|
|
|
if(ckanUser.getRole()!=Role.ADMIN && !isItemCreator()) {
|
|
throw new ForbiddenException("Only " + Role.ADMIN.getPortalRole() + "s and item creator are entitled to purge an the item");
|
|
}
|
|
|
|
if(result.has(RESOURCES_KEY)) {
|
|
itemID = result.get(ID_KEY).asText();
|
|
ArrayNode arrayNode = (ArrayNode) result.get(RESOURCES_KEY);
|
|
for(JsonNode jsonNode : arrayNode) {
|
|
CKANResource ckanResource = new CKANResource(itemID);
|
|
ckanResource.setPreviousRepresentation(jsonNode);
|
|
ckanResource.deleteFile(); // Only delete file is required because the item has been deleted
|
|
}
|
|
}
|
|
super.purge();
|
|
} catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used for bulk purging. Internal use only
|
|
*/
|
|
protected void purgeNoCheckNoDeleteFiles() {
|
|
// setApiKey(CKANUtility.getSysAdminAPI());
|
|
// super.purge();
|
|
}
|
|
|
|
/*
|
|
* --------------------------------------------------------------------------------------------------------
|
|
* Moderation Related functions
|
|
* --------------------------------------------------------------------------------------------------------
|
|
*
|
|
*/
|
|
|
|
protected CMItemStatus getCMItemStatus() {
|
|
|
|
String cmItemStatusString = CMItemStatus.APPROVED.getValue();
|
|
boolean found = false;
|
|
if(result.has(EXTRAS_KEY)) {
|
|
ArrayNode extras = (ArrayNode) result.get(EXTRAS_KEY);
|
|
for(JsonNode extra : extras) {
|
|
if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY).asText().compareTo(Moderated.SYSTEM_CM_ITEM_STATUS) == 0) {
|
|
cmItemStatusString = extra.get(EXTRAS_VALUE_KEY).asText();
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CMItemStatus cmItemStatus = CMItemStatus.getCMItemStatusFromValue(cmItemStatusString);
|
|
|
|
if(!found) {
|
|
// The item was published before activating the moderation.
|
|
// The item is considered as approved and the item representation must be updated
|
|
setToApproved(result);
|
|
String ret = sendPostRequest(ITEM_PATCH, getAsString(result));
|
|
try {
|
|
result = mapper.readTree(ret);
|
|
}catch (Exception e) {
|
|
new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
return cmItemStatus;
|
|
}
|
|
|
|
protected CMItemStatus getRequestedCMItemStatus() {
|
|
CMItemStatus cmItemStatus = null;
|
|
try {
|
|
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
|
if(queryParameters.containsKey(Moderated.CM_ITEM_STATUS_QUERY_PARAMETER)) {
|
|
String cmItemStatusString = queryParameters.getFirst(Moderated.CM_ITEM_STATUS_QUERY_PARAMETER);
|
|
cmItemStatus = CMItemStatus.getCMItemStatusFromValue(cmItemStatusString);
|
|
}
|
|
}catch (Exception e) {
|
|
cmItemStatus = null;
|
|
}
|
|
return cmItemStatus;
|
|
}
|
|
|
|
protected boolean isModerationEnabled() {
|
|
boolean moderationEnabled = ckanInstance.isModerationEnabled();
|
|
if(moderationEnabled && moderationThread==null) {
|
|
moderationThread = ModerationThread.getDefaultInstance();
|
|
moderationThread.setCKANUser(ckanUser);
|
|
}
|
|
return moderationEnabled;
|
|
}
|
|
|
|
protected Map<String,String> addModerationStatusFilter(Map<String,String> parameters){
|
|
if(isModerationEnabled()) {
|
|
String q = parameters.get(GCatConstants.Q_KEY);
|
|
|
|
StringBuffer stringBuffer = new StringBuffer();
|
|
stringBuffer.append("(");
|
|
stringBuffer.append(CM_STATUS_QUERY_FILTER_KEY);
|
|
stringBuffer.append(":");
|
|
|
|
CMItemStatus cmItemStatus = getRequestedCMItemStatus();
|
|
|
|
if(!ckanUser.getPortalUser().isCatalogueModerator()) {
|
|
|
|
switch (ckanUser.getRole()) {
|
|
case ADMIN:
|
|
break;
|
|
|
|
case EDITOR:
|
|
if(cmItemStatus!=null && cmItemStatus!=CMItemStatus.APPROVED) {
|
|
q = String.format("%s AND %s:%s", q, AUTHOR_EMAIL_KEY, ckanUser.getPortalUser().getEMail());
|
|
}else{
|
|
cmItemStatus = CMItemStatus.APPROVED;
|
|
}
|
|
break;
|
|
|
|
case MEMBER:
|
|
if(cmItemStatus!=null && cmItemStatus!=CMItemStatus.APPROVED) {
|
|
throw new ForbiddenException("You are only authorized to list " + CMItemStatus.APPROVED.getValue() + " items");
|
|
}
|
|
cmItemStatus = CMItemStatus.APPROVED;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if(cmItemStatus!=null) {
|
|
stringBuffer.append(cmItemStatus.getValue());
|
|
if(cmItemStatus == CMItemStatus.APPROVED) {
|
|
stringBuffer.append(" OR (*:* -");
|
|
stringBuffer.append(CM_STATUS_QUERY_FILTER_KEY);
|
|
stringBuffer.append(":[* TO *])");
|
|
}
|
|
stringBuffer.append(")");
|
|
q = String.format("%s AND %s", q, stringBuffer.toString());
|
|
parameters.put(GCatConstants.Q_KEY, q);
|
|
}
|
|
|
|
parameters.put(INCLUDE_PRIVATE_KEY, String.valueOf(true));
|
|
}
|
|
|
|
return parameters;
|
|
}
|
|
|
|
protected void checkModerationRead() {
|
|
if(isModerationEnabled()) {
|
|
CMItemStatus cmItemStatus = getCMItemStatus();
|
|
if(cmItemStatus == CMItemStatus.APPROVED) {
|
|
return;
|
|
}
|
|
|
|
PortalUser portalUser = ckanUser.getPortalUser();
|
|
if(isItemCreator()) {
|
|
// The author is entitled to read its own items independently from the status
|
|
return;
|
|
}
|
|
|
|
if(ckanUser.getRole() == Role.ADMIN || portalUser.isCatalogueModerator()) {
|
|
// Catalogue-Admin and Catalogue-Moderator are entitled to read items with any statues
|
|
return;
|
|
}
|
|
|
|
throw new ForbiddenException("You are not entitled to read the item");
|
|
}
|
|
}
|
|
|
|
protected JsonNode checkModerationUpdate(JsonNode jsonNode) {
|
|
if(isModerationEnabled()) {
|
|
CMItemStatus cmItemStatus = getCMItemStatus();
|
|
|
|
boolean setToPending = true;
|
|
|
|
PortalUser portalUser = ckanUser.getPortalUser();
|
|
|
|
switch (cmItemStatus) {
|
|
case APPROVED:
|
|
if(ckanUser.getRole() != Role.ADMIN && !isItemCreator()) {
|
|
throw new ForbiddenException("Only " + Role.ADMIN.getPortalRole() + "s and item creator are entitled to update an " + cmItemStatus.getValue() + " item");
|
|
}
|
|
if(ckanUser.getRole() == Role.ADMIN) {
|
|
setToApproved(jsonNode);
|
|
setToPending = false;
|
|
}
|
|
break;
|
|
|
|
case PENDING:
|
|
if(isItemCreator()) {
|
|
break;
|
|
}
|
|
if(portalUser.isCatalogueModerator()) {
|
|
break;
|
|
}
|
|
throw new ForbiddenException("You are not entitled to update a " + cmItemStatus.getValue() + " item");
|
|
|
|
case REJECTED:
|
|
if(isItemCreator()) {
|
|
break;
|
|
}
|
|
if(portalUser.isCatalogueModerator()) {
|
|
break;
|
|
}
|
|
throw new ForbiddenException("You are not entitled to update a " + cmItemStatus.getValue() + " item");
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(setToPending) {
|
|
setItemToPending(jsonNode);
|
|
}
|
|
|
|
}
|
|
|
|
return jsonNode;
|
|
}
|
|
|
|
protected void checkModerationDelete() {
|
|
try {
|
|
if(isModerationEnabled()) {
|
|
readItem();
|
|
|
|
PortalUser portalUser = ckanUser.getPortalUser();
|
|
|
|
if(ckanUser.getRole() == Role.ADMIN) {
|
|
// Ad Admin can delete any item independently from the status
|
|
return;
|
|
}
|
|
|
|
CMItemStatus cmItemStatus = getCMItemStatus();
|
|
|
|
switch (cmItemStatus) {
|
|
case APPROVED:
|
|
if(isItemCreator()) {
|
|
break;
|
|
}
|
|
throw new ForbiddenException("Only " + Role.ADMIN.getPortalRole() + "s and item creator are entitled to delete an " + cmItemStatus.getValue() + " item");
|
|
|
|
case REJECTED:
|
|
if(isItemCreator()) {
|
|
break;
|
|
}
|
|
if(portalUser.isCatalogueModerator()) {
|
|
break;
|
|
}
|
|
throw new ForbiddenException("You are not entitled to delete a " + cmItemStatus.getValue() + " item");
|
|
|
|
case PENDING:
|
|
if(isItemCreator()) {
|
|
break;
|
|
}
|
|
if(portalUser.isCatalogueModerator()) {
|
|
break;
|
|
}
|
|
throw new ForbiddenException("You are not entitled to update a " + cmItemStatus.getValue() + " item");
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
protected void setToRejected(JsonNode jsonNode) {
|
|
addExtraField(jsonNode, Moderated.SYSTEM_CM_ITEM_STATUS, CMItemStatus.REJECTED.getValue());
|
|
}
|
|
|
|
protected void setItemToPending(JsonNode jsonNode) {
|
|
if(isModerationEnabled()) {
|
|
addExtraField(jsonNode, Moderated.SYSTEM_CM_ITEM_STATUS, CMItemStatus.PENDING.getValue());
|
|
|
|
CMItemVisibility cmItemVisibility = CMItemVisibility.PUBLIC;
|
|
|
|
if(jsonNode.has(PRIVATE_KEY)) {
|
|
boolean privatePackage = jsonNode.get(PRIVATE_KEY).asBoolean();
|
|
if(privatePackage) {
|
|
cmItemVisibility = CMItemVisibility.RESTRICTED;
|
|
}
|
|
}
|
|
addExtraField(jsonNode, Moderated.SYSTEM_CM_ITEM_VISIBILITY, cmItemVisibility.getValue());
|
|
|
|
((ObjectNode) jsonNode).put(PRIVATE_KEY, true);
|
|
((ObjectNode) jsonNode).put(SEARCHABLE_KEY, false);
|
|
}
|
|
}
|
|
|
|
protected void setToApproved(JsonNode jsonNode) {
|
|
ArrayNode extras = (ArrayNode) jsonNode.get(EXTRAS_KEY);
|
|
|
|
boolean approvedSet = false;
|
|
CMItemVisibility cmItemVisibility = null;
|
|
|
|
for(JsonNode extra : extras) {
|
|
if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY)!=null && extra.get(EXTRAS_KEY_KEY).asText().compareTo(Moderated.SYSTEM_CM_ITEM_STATUS) == 0) {
|
|
((ObjectNode) extra).put(EXTRAS_VALUE_KEY, CMItemStatus.APPROVED.getValue());
|
|
approvedSet = true;
|
|
}
|
|
|
|
if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY)!=null && extra.get(EXTRAS_KEY_KEY).asText().compareTo(Moderated.SYSTEM_CM_ITEM_VISIBILITY) == 0) {
|
|
cmItemVisibility = CMItemVisibility.getCMItemStatusFromValue(extra.get(EXTRAS_VALUE_KEY).asText());
|
|
}
|
|
}
|
|
|
|
if(!approvedSet) {
|
|
addExtraField(jsonNode, Moderated.SYSTEM_CM_ITEM_STATUS, CMItemStatus.APPROVED.getValue());
|
|
}
|
|
|
|
if(cmItemVisibility==null) {
|
|
cmItemVisibility = CMItemVisibility.PUBLIC;
|
|
addExtraField(jsonNode, Moderated.SYSTEM_CM_ITEM_VISIBILITY, cmItemVisibility.getValue());
|
|
}
|
|
|
|
|
|
((ObjectNode) jsonNode).put(PRIVATE_KEY, cmItemVisibility == CMItemVisibility.RESTRICTED ? true :false);
|
|
((ObjectNode) jsonNode).put(SEARCHABLE_KEY, true);
|
|
}
|
|
|
|
private void postItemCreated() throws Exception {
|
|
try {
|
|
if(isModerationEnabled()) {
|
|
moderationThread.setItemCoordinates(itemID, name);
|
|
moderationThread.postItemCreated();
|
|
}
|
|
} catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
private void postItemUpdated() {
|
|
try {
|
|
if(isModerationEnabled()) {
|
|
moderationThread.setItemCoordinates(itemID, name);
|
|
moderationThread.postItemUpdated();
|
|
}
|
|
} catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String approve(String moderatorMessage) {
|
|
try {
|
|
if(isModerationEnabled()) {
|
|
readItem();
|
|
PortalUser portalUser = ckanUser.getPortalUser();
|
|
CMItemStatus cmItemStatus = getCMItemStatus();
|
|
switch (cmItemStatus) {
|
|
case APPROVED:
|
|
// Nothing TO DO
|
|
break;
|
|
|
|
case REJECTED:
|
|
throw new MethodNotSupportedException("You can't approve a rejected item. The item must be updated first. The update will set the item in pending, than it can be approved/rejected.");
|
|
|
|
case PENDING:
|
|
if(!portalUser.isCatalogueModerator()) {
|
|
throw new MethodNotSupportedException("Only catalogue moderator can approve a pending item.");
|
|
}
|
|
setToApproved(result);
|
|
String ret = sendPostRequest(ITEM_PATCH, getAsString(result));
|
|
result = mapper.readTree(ret);
|
|
|
|
moderationThread.setItemCoordinates(itemID, name);
|
|
moderationThread.postItemApproved(moderatorMessage);
|
|
|
|
if(ckanInstance.getCurrentScopeBean().is(Type.VRE)) {
|
|
// Actions performed after a package has been correctly created on ckan.
|
|
String title = result.get(TITLE_KEY).asText();
|
|
String catalogueItemURL = getExtraField(result, EXTRAS_ITEM_URL_KEY).asText();
|
|
sendSocialPost(title, catalogueItemURL);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return getAsCleanedString(result);
|
|
}
|
|
throw new MethodNotSupportedException("The approve operation is available only in moderation mode");
|
|
}catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String reject(String moderatorMessage) {
|
|
try {
|
|
if(isModerationEnabled()) {
|
|
readItem();
|
|
CMItemStatus cmItemStatus = getCMItemStatus();
|
|
switch (cmItemStatus) {
|
|
case APPROVED:
|
|
throw new MethodNotSupportedException("You can't rejected an approved item. The item must be updated first. The update will set the item in pending, than it can be approved/rejected.");
|
|
|
|
case REJECTED:
|
|
// Nothing TO DO
|
|
break;
|
|
|
|
case PENDING:
|
|
PortalUser portalUser = ckanUser.getPortalUser();
|
|
if(!portalUser.isCatalogueModerator()) {
|
|
throw new MethodNotSupportedException("Only catalogue moderator can reject a pending item.");
|
|
}
|
|
|
|
setToRejected(result);
|
|
String ret = sendPostRequest(ITEM_PATCH, getAsString(result));
|
|
result = mapper.readTree(ret);
|
|
|
|
moderationThread.setItemCoordinates(itemID, name);
|
|
moderationThread.postItemRejected(moderatorMessage);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return getAsCleanedString(result);
|
|
}
|
|
throw new MethodNotSupportedException("The reject operation is available only in moderation mode");
|
|
}catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void message(String message) {
|
|
try {
|
|
if(isModerationEnabled()) {
|
|
if(message==null || message.compareTo("")==0) {
|
|
return;
|
|
}
|
|
|
|
readItem();
|
|
|
|
// Catalogue Moderators are allowed to post message to the dedicated Stream
|
|
if(!ckanUser.getPortalUser().isCatalogueModerator()) {
|
|
// Users that are not
|
|
if(!isItemCreator()) {
|
|
throw new NotAllowedException("Only item creator and " + Moderated.CATALOGUE_MODERATOR + "s are entitled to partecipate to the moderation discussion thread.");
|
|
}
|
|
}
|
|
|
|
CMItemStatus cmItemStatus = getCMItemStatus();
|
|
moderationThread.setItemCoordinates(itemID, name);
|
|
moderationThread.postUserMessage(cmItemStatus, message);
|
|
return;
|
|
}
|
|
throw new MethodNotSupportedException("The message operation is available only in moderation mode");
|
|
}catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
public String moderate(String json) {
|
|
try {
|
|
ModerationContent moderationContent = mapper.readValue(json, ModerationContent.class);
|
|
String message = moderationContent.getMessage();
|
|
if(moderationContent.getCMItemStatus() !=null) {
|
|
CMItemStatus cmItemStatus = moderationContent.getCMItemStatus();
|
|
switch (cmItemStatus) {
|
|
case APPROVED:
|
|
return approve(message);
|
|
case REJECTED:
|
|
return reject(message);
|
|
default:
|
|
throw new BadRequestException("Allowed moderation operations are approve, reject and message");
|
|
}
|
|
}else {
|
|
if(message==null || message.compareTo("")==0) {
|
|
throw new BadRequestException("Allowed moderation operations are approve, reject and message");
|
|
}
|
|
message(message);
|
|
return null;
|
|
}
|
|
}catch(WebApplicationException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new InternalServerErrorException(e);
|
|
}
|
|
}
|
|
|
|
}
|