/*
*
* This is a simple Content Management System (CMS)
* Copyright (C) 2010 Imran M Yousuf (imyousuf@smartitengineering.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.smartitengineering.cms.ws.resources.type;
import com.smartitengineering.cms.api.factory.SmartContentAPI;
import com.smartitengineering.cms.api.factory.type.WritableContentType;
import com.smartitengineering.cms.api.type.CollectionDataType;
import com.smartitengineering.cms.api.type.CompositeDataType;
import com.smartitengineering.cms.api.type.ContentDataType;
import com.smartitengineering.cms.api.type.ContentStatus;
import com.smartitengineering.cms.api.type.ContentType;
import com.smartitengineering.cms.api.type.ContentTypeId;
import com.smartitengineering.cms.api.type.DataType;
import com.smartitengineering.cms.api.type.EnumDataType;
import com.smartitengineering.cms.api.type.FieldDef;
import com.smartitengineering.cms.api.type.OtherDataType;
import com.smartitengineering.cms.ws.common.domains.CollectionFieldDefImpl;
import com.smartitengineering.cms.ws.common.domains.CompositeFieldDefImpl;
import com.smartitengineering.cms.ws.common.domains.ContentFieldDefImpl;
import com.smartitengineering.cms.ws.common.domains.EnumFieldDefImpl;
import com.smartitengineering.cms.ws.common.domains.FieldDefImpl;
import com.smartitengineering.cms.ws.common.domains.OtherFieldDefImpl;
import com.smartitengineering.cms.ws.common.utils.SimpleFeedExtensions;
import com.smartitengineering.cms.ws.resources.content.searcher.ContentSearcherResource;
import com.smartitengineering.util.rest.atom.server.AbstractResource;
import com.smartitengineering.util.rest.server.ServerResourceInjectables;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Link;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author imyousuf
*/
public class ContentTypeResource extends AbstractResource {
private final ContentType type;
private final Date lastModified;
private final EntityTag tag;
private final Logger logger = LoggerFactory.getLogger(getClass());
public static transient final String PATH_TO_SEARCH = "search";
public static final String PATH_TO_REINDEX = "reindex";
public static final String PATH_TO_CHILDREN = "children";
public static final String PATH_TO_INSTANCES = "instances";
public static final String PATH_TO_STATUSES = "statuses";
public ContentTypeResource(ServerResourceInjectables injectables, ContentType type) {
super(injectables);
if (type == null) {
throw new WebApplicationException(Response.status(Status.NOT_FOUND).build());
}
this.type = type;
lastModified = type.getLastModifiedDate();
tag = new EntityTag(type.getEntityTagValue());
}
@GET
@Produces(MediaType.APPLICATION_XML)
public Response get() {
ResponseBuilder builder = getContext().getRequest().evaluatePreconditions(lastModified, tag);
if (builder == null) {
builder = Response.ok(type.getRepresentations().get(
com.smartitengineering.cms.api.common.MediaType.APPLICATION_XML));
builder.lastModified(lastModified);
builder.tag(tag);
builder.header(HttpHeaders.VARY, HttpHeaders.ACCEPT);
}
return builder.build();
}
@Path(PATH_TO_SEARCH)
public ContentSearcherResource search() {
ContentSearcherResource resource = new ContentSearcherResource(getInjectables());
resource.setContentTypeId(Collections.singletonList(type.getContentTypeID().toString()));
return resource;
}
@Path(PATH_TO_REINDEX)
public ContentTypeReIndexResource reIndex() {
ContentTypeReIndexResource resource = new ContentTypeReIndexResource(getInjectables());
resource.setTypeId(type.getContentTypeID());
return resource;
}
@GET
@Produces(MediaType.APPLICATION_ATOM_XML)
public Response getAtomFeed() {
ResponseBuilder builder = getContext().getRequest().evaluatePreconditions(lastModified, tag);
if (builder == null) {
final String id = type.getContentTypeID().toString();
Feed feed = getFeed(id, StringUtils.isBlank(type.getDisplayName()) ? id : type.getDisplayName(), lastModified);
if (type.getParent() != null) {
feed.addLink(getLink(getContentTypeRelativeURI(getUriInfo(), type.getParent()), "parent",
MediaType.APPLICATION_ATOM_XML));
}
feed.addLink(getLink(getRelativeURIBuilder().path(ContentTypesResource.class).path(
ContentTypesResource.PATH_TO_CONTENT_TYPE).path(PATH_TO_SEARCH).build(type.getContentTypeID().getWorkspace().
getGlobalNamespace(), type.getContentTypeID().getWorkspace().getName(), type.getContentTypeID().getNamespace(),
type.getContentTypeID().getName()),
"search",
com.smartitengineering.util.opensearch.jaxrs.MediaType.APPLICATION_OPENSEARCHDESCRIPTION_XML));
feed.addLink(getLink(getRelativeURIBuilder().path(ContentTypesResource.class).path(
ContentTypesResource.PATH_TO_CONTENT_TYPE).path(PATH_TO_REINDEX).build(type.getContentTypeID().getWorkspace().
getGlobalNamespace(), type.getContentTypeID().getWorkspace().getName(), type.getContentTypeID().getNamespace(),
type.getContentTypeID().getName()),
PATH_TO_REINDEX, MediaType.TEXT_PLAIN));
final URI childrenUri = getRelativeURIBuilder().path(ContentTypesResource.class).path(
ContentTypesResource.PATH_TO_CONTENT_TYPE).path(PATH_TO_CHILDREN).build(type.getContentTypeID().getWorkspace().
getGlobalNamespace(), type.getContentTypeID().getWorkspace().getName(), type.getContentTypeID().getNamespace(),
type.getContentTypeID().getName());
final URI instancesUri = getRelativeURIBuilder().path(ContentTypesResource.class).path(
ContentTypesResource.PATH_TO_CONTENT_TYPE).path(PATH_TO_INSTANCES).build(type.getContentTypeID().getWorkspace().
getGlobalNamespace(), type.getContentTypeID().getWorkspace().getName(), type.getContentTypeID().getNamespace(),
type.getContentTypeID().getName());
feed.addLink(getLink(childrenUri, PATH_TO_CHILDREN, MediaType.APPLICATION_ATOM_XML));
feed.addLink(getLink(instancesUri, PATH_TO_INSTANCES, MediaType.APPLICATION_ATOM_XML));
feed.addLink(getLink(childrenUri, PATH_TO_CHILDREN,
com.smartitengineering.util.opensearch.jaxrs.MediaType.APPLICATION_OPENSEARCHDESCRIPTION_XML));
feed.addLink(getLink(instancesUri, PATH_TO_INSTANCES,
com.smartitengineering.util.opensearch.jaxrs.MediaType.APPLICATION_OPENSEARCHDESCRIPTION_XML));
final URI typeUri = getRelativeURIBuilder().path(ContentTypesResource.class).path(
ContentTypesResource.PATH_TO_CONTENT_TYPE).build(type.getContentTypeID().getWorkspace().getGlobalNamespace(),
type.getContentTypeID().getWorkspace().getName(), type.
getContentTypeID().getNamespace(), type.getContentTypeID().getName());
feed.addLink(getLink(typeUri, Link.REL_ALTERNATE, MediaType.APPLICATION_XML));
final URI statusesUri = getRelativeURIBuilder().path(ContentTypesResource.class).path(
ContentTypesResource.PATH_TO_CONTENT_TYPE).path(PATH_TO_STATUSES).build(type.getContentTypeID().getWorkspace().
getGlobalNamespace(), type.getContentTypeID().getWorkspace().getName(), type.getContentTypeID().getNamespace(),
type.getContentTypeID().getName());
feed.addLink(getLink(statusesUri, "statuses", MediaType.APPLICATION_ATOM_XML));
Map<String, FieldDef> fieldDefs = type.getFieldDefs();
feed.addSimpleExtension(SimpleFeedExtensions.WORKSPACE_NAME_SPACE, type.getContentTypeID().getWorkspace().
getGlobalNamespace());
feed.addSimpleExtension(SimpleFeedExtensions.WORKSPACE_NAME, type.getContentTypeID().getWorkspace().getName());
feed.addSimpleExtension(SimpleFeedExtensions.CONTENT_TYPE_NAME_SPACE, type.getContentTypeID().getNamespace());
feed.addSimpleExtension(SimpleFeedExtensions.CONTENT_TYPE_NAME, type.getContentTypeID().getName());
if (fieldDefs != null && !fieldDefs.isEmpty()) {
ObjectMapper objectMapper = new ObjectMapper();
for (Entry<String, FieldDef> fieldDef : fieldDefs.entrySet()) {
org.apache.abdera.model.Entry entry = getEntry(fieldDef.getKey(), fieldDef.getValue().getDisplayName(),
lastModified);
StringWriter writer = new StringWriter();
final FieldDefImpl def;
def = convertFieldDef(fieldDef.getValue());
try {
objectMapper.writeValue(writer, def);
entry.setContent(writer.toString(), MediaType.APPLICATION_JSON);
feed.addEntry(entry);
}
catch (Exception ex) {
logger.error("Could not add field entry", ex);
}
}
}
builder = Response.ok(feed);
builder.lastModified(lastModified);
builder.tag(tag);
builder.header(HttpHeaders.VARY, HttpHeaders.ACCEPT);
}
return builder.build();
}
protected FieldDefImpl convertFieldDef(FieldDef fieldDef) {
final FieldDefImpl def;
final DataType dataType = fieldDef.getValueDef();
switch (dataType.getType()) {
case OTHER:
case STRING:
def = new OtherFieldDefImpl();
((OtherFieldDefImpl) def).setMimeType(((OtherDataType) fieldDef.getValueDef()).getMIMEType());
break;
case COLLECTION:
def = new CollectionFieldDefImpl();
CollectionDataType collectionDataType = ((CollectionDataType) fieldDef.getValueDef());
FieldDefImpl itemFieldDef;
switch (collectionDataType.getItemDataType().getType()) {
case OTHER:
case STRING:
itemFieldDef = new OtherFieldDefImpl();
((OtherFieldDefImpl) itemFieldDef).setMimeType(((OtherDataType) collectionDataType.getItemDataType()).
getMIMEType());
break;
case CONTENT:
itemFieldDef = new ContentFieldDefImpl();
if (logger.isDebugEnabled()) {
logger.debug("Content inner data type (" + fieldDef.getName() + "): " +
collectionDataType.getItemDataType());
if (collectionDataType.getItemDataType() != null) {
logger.debug("Content inner data type type def (" + fieldDef.getName() + "): " +
((ContentDataType) collectionDataType.getItemDataType()).getTypeDef());
}
}
((ContentFieldDefImpl) itemFieldDef).setInstanceOfId(((ContentDataType) collectionDataType.getItemDataType()).
getTypeDef().toString());
break;
case ENUM:
EnumDataType enumDataType = (EnumDataType) collectionDataType.getItemDataType();
EnumFieldDefImpl enumFieldDefImpl = new EnumFieldDefImpl();
itemFieldDef = enumFieldDefImpl;
if (logger.isDebugEnabled()) {
logger.debug("Enum choices " + enumDataType.getChoices());
}
enumFieldDefImpl.setChoices(enumDataType.getChoices());
break;
case COMPOSITE: {
itemFieldDef = new CompositeFieldDefImpl();
CompositeFieldDefImpl defImpl = (CompositeFieldDefImpl) itemFieldDef;
CompositeDataType compositeDataType = (CompositeDataType) collectionDataType.getItemDataType();
final Collection<FieldDef> composition = compositeDataType.getComposition();
if (composition != null && !composition.isEmpty()) {
List<com.smartitengineering.cms.ws.common.domains.FieldDef> composedFields =
new ArrayList<com.smartitengineering.cms.ws.common.domains.FieldDef>(composition.
size());
for (FieldDef composedField : composition) {
composedFields.add(convertFieldDef(composedField));
}
defImpl.setComposedFields(composedFields);
}
break;
}
default:
itemFieldDef = new FieldDefImpl();
break;
}
itemFieldDef.setType(collectionDataType.getItemDataType().getType().name());
itemFieldDef.setName(fieldDef.getName());
itemFieldDef.setRequired(fieldDef.isRequired());
((CollectionFieldDefImpl) def).setItemDef(itemFieldDef);
((CollectionFieldDefImpl) def).setMaxSize(collectionDataType.getMaxSize());
((CollectionFieldDefImpl) def).setMinSize(collectionDataType.getMinSize());
break;
case CONTENT:
def = new ContentFieldDefImpl();
((ContentFieldDefImpl) def).setInstanceOfId(((ContentDataType) fieldDef.getValueDef()).getTypeDef().
toString());
break;
case ENUM: {
EnumDataType enumDataType = (EnumDataType) fieldDef.getValueDef();
EnumFieldDefImpl enumFieldDefImpl = new EnumFieldDefImpl();
def = enumFieldDefImpl;
if (logger.isDebugEnabled()) {
logger.debug("Enum choices " + enumDataType.getChoices());
}
enumFieldDefImpl.setChoices(enumDataType.getChoices());
break;
}
case COMPOSITE: {
def = new CompositeFieldDefImpl();
CompositeFieldDefImpl defImpl = (CompositeFieldDefImpl) def;
CompositeDataType compositeDataType = (CompositeDataType) dataType;
final Collection<FieldDef> composition = compositeDataType.getComposition();
if (composition != null && !composition.isEmpty()) {
List<com.smartitengineering.cms.ws.common.domains.FieldDef> composedFields =
new ArrayList<com.smartitengineering.cms.ws.common.domains.FieldDef>(composition.
size());
for (FieldDef composedField : composition) {
composedFields.add(convertFieldDef(composedField));
}
defImpl.setComposedFields(composedFields);
}
break;
}
default:
def = new FieldDefImpl();
break;
}
def.setName(fieldDef.getName());
def.setDisplayName(fieldDef.getDisplayName());
def.setRequired(fieldDef.isRequired());
def.setParameters(fieldDef.getParameters());
def.setType(fieldDef.getValueDef().getType().name());
return def;
}
@DELETE
public Response delete(@HeaderParam(HttpHeaders.IF_MATCH) EntityTag etag) {
if (logger.isDebugEnabled()) {
logger.debug("Delete content type with id " + type.getContentTypeID().toString() + " with last-modified " +
lastModified + " " + tag.getValue());
}
if (etag == null) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
ResponseBuilder builder = getContext().getRequest().evaluatePreconditions(lastModified, tag);
if (builder != null) {
return builder.build();
}
WritableContentType contentType = SmartContentAPI.getInstance().getContentTypeLoader().getWritableContentType(type);
try {
contentType.delete();
return Response.ok().build();
}
catch (Exception ex) {
logger.error(ex.getMessage(), ex);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
@Path(PATH_TO_CHILDREN)
public ContentTypeSearcherResource getChildren() {
ContentTypeSearcherResource resource = getResourceContext().getResource(ContentTypeSearcherResource.class);
resource.setWorkspaceId(type.getContentTypeID().getWorkspace().toString());
resource.setParentId(type.getContentTypeID().toString());
resource.setIncludeFriendlies(false);
return resource;
}
@Path(PATH_TO_INSTANCES)
public ContentTypeSearcherResource getInstances() {
ContentTypeSearcherResource resource = getResourceContext().getResource(ContentTypeSearcherResource.class);
resource.setWorkspaceId(type.getContentTypeID().getWorkspace().toString());
resource.setContentTypeId(Collections.singletonList(type.getContentTypeID().toString()));
resource.setIncludeFriendlies(false);
return resource;
}
@GET
@Path(PATH_TO_STATUSES)
@Produces(MediaType.APPLICATION_ATOM_XML)
public Response getStatuses() {
ResponseBuilder builder = getContext().getRequest().evaluatePreconditions(lastModified, tag);
if (builder == null) {
Map<String, ContentStatus> statuses = type.getStatuses();
Feed feed = getFeed("Statuses", lastModified);
for (String name : statuses.keySet()) {
if (StringUtils.isNotBlank(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Adding status " + name);
}
org.apache.abdera.model.Entry entry = getEntry(name, name, lastModified);
entry.setContent(name);
feed.addEntry(entry);
}
}
builder = Response.ok(feed);
builder.lastModified(lastModified);
builder.tag(tag);
builder.header(HttpHeaders.VARY, HttpHeaders.ACCEPT);
}
return builder.build();
}
public static URI getContentTypeRelativeURI(UriInfo info, ContentTypeId typeId) {
UriBuilder builder = UriBuilder.fromPath(info.getBaseUri().getPath());
builder.path(ContentTypesResource.class);
builder.path(ContentTypesResource.PATH_TO_CONTENT_TYPE);
return builder.build(typeId.getWorkspace().getGlobalNamespace(), typeId.getWorkspace().getName(), typeId.
getNamespace(), typeId.getName());
}
public ContentType getType() {
return type;
}
@Override
protected String getAuthor() {
return "Smart CMS";
}
}