org.silverpeas.core.webapi.attachment.SimpleDocumentResource.java Source code

Java tutorial

Introduction

Here is the source code for org.silverpeas.core.webapi.attachment.SimpleDocumentResource.java

Source

/*
 * Copyright (C) 2000 - 2018 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.silverpeas.core.webapi.attachment;

import org.apache.commons.io.FileUtils;
import org.silverpeas.core.ResourceReference;
import org.silverpeas.core.annotation.RequestScoped;
import org.silverpeas.core.annotation.Service;
import org.silverpeas.core.contribution.attachment.ActifyDocumentProcessor;
import org.silverpeas.core.contribution.attachment.AttachmentServiceProvider;
import org.silverpeas.core.contribution.attachment.WebdavServiceProvider;
import org.silverpeas.core.contribution.attachment.model.SimpleDocument;
import org.silverpeas.core.contribution.attachment.model.SimpleDocumentPK;
import org.silverpeas.core.contribution.attachment.model.UnlockContext;
import org.silverpeas.core.contribution.attachment.model.UnlockOption;
import org.silverpeas.core.i18n.I18NHelper;
import org.silverpeas.core.importexport.versioning.DocumentVersion;
import org.silverpeas.core.io.file.SilverpeasFile;
import org.silverpeas.core.io.file.SilverpeasFileProvider;
import org.silverpeas.core.notification.user.UserSubscriptionNotificationSendingHandler;
import org.silverpeas.core.util.StringUtil;
import org.silverpeas.core.util.file.FileUtil;
import org.silverpeas.core.web.attachment.SimpleDocumentUploadData;
import org.silverpeas.core.web.attachment.WebDavTokenProducer;
import org.silverpeas.core.web.http.FileResponse;
import org.silverpeas.core.web.http.RequestParameterDecoder;
import org.silverpeas.core.web.mvc.webcomponent.WebMessager;
import org.silverpeas.core.webapi.base.UserPrivilegeValidation;
import org.silverpeas.core.webapi.base.UserPrivilegeValidator;
import org.silverpeas.core.webapi.base.annotation.Authorized;
import org.silverpeas.core.webapi.media.EmbedMediaPlayerDispatcher;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.silverpeas.core.i18n.I18NHelper.defaultLanguage;
import static org.silverpeas.core.web.util.IFrameAjaxTransportUtil.*;

@Service
@RequestScoped
@Path(AbstractSimpleDocumentResource.PATH + "/{componentId}/document/{id}")
@Authorized
public class SimpleDocumentResource extends AbstractSimpleDocumentResource {

    private static final String DOCUMENT_PATH_NODE = "document";

    @PathParam("id")
    private String simpleDocumentId;

    public String getSimpleDocumentId() {
        return simpleDocumentId;
    }

    /**
     * Returns the specified document in the specified lang.
     *
     * @param lang the wanted language.
     * @return the specified document in the specified lang.
     */
    @GET
    @Path("{lang}")
    @Produces(MediaType.APPLICATION_JSON)
    public SimpleDocumentEntity getDocument(final @PathParam("lang") String lang) {
        SimpleDocument attachment = getSimpleDocument(lang);
        URI attachmentUri = getUri().getRequestUriBuilder().path(DOCUMENT_PATH_NODE).path(attachment.getLanguage())
                .build();
        return SimpleDocumentEntity.fromAttachment(attachment).withURI(attachmentUri);
    }

    /**
     * Deletes the the specified document.
     */
    @DELETE
    @Produces(MediaType.APPLICATION_JSON)
    public void deleteDocument() {
        UserSubscriptionNotificationSendingHandler.verifyRequest(getHttpRequest());
        SimpleDocument document = getSimpleDocument(null);
        document.setUpdatedBy(getUser().getId());
        AttachmentServiceProvider.getAttachmentService().deleteAttachment(document);
    }

    /**
     * Deletes the the specified document.
     *
     * @param lang the lang of the content to be deleted.
     */
    @DELETE
    @Path("content/{lang}")
    @Produces(MediaType.APPLICATION_JSON)
    public void deleteContent(final @PathParam("lang") String lang) {
        UserSubscriptionNotificationSendingHandler.verifyRequest(getHttpRequest());
        SimpleDocument document = getSimpleDocument(lang);
        AttachmentServiceProvider.getAttachmentService().removeContent(document, lang, false);
    }

    /**
     * Updates the document identified by the requested URI.
     *
     * A {@link SimpleDocumentUploadData} is extracted from request parameters.
     *
     * @return an HTTP response embodied an entity in a format expected by the client (that is
     * identified by the <code>xRequestedWith</code> parameter).
     * @throws IOException if an error occurs while updating the document.
     */
    @POST
    @Path("{filename}")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response updateDocument(final @PathParam("filename") String filename) throws IOException {
        SimpleDocumentUploadData uploadData = RequestParameterDecoder.decode(getHttpRequest(),
                SimpleDocumentUploadData.class);

        try {

            // Update the attachment
            String normalizedFileName = StringUtil.normalize(filename);
            SimpleDocumentEntity entity = updateSimpleDocument(uploadData, normalizedFileName);

            if (AJAX_IFRAME_TRANSPORT.equals(uploadData.getXRequestedWith())) {

                // In case of file upload performed by Ajax IFrame transport way,
                // the expected response type is text/html
                // (when FormData API doesn't exist on client side)
                return Response.ok().type(MediaType.TEXT_HTML_TYPE)
                        .entity(packObjectToJSonDataWithHtmlContainer(entity)).build();
            } else {

                // Otherwise JSON response type is expected
                return Response.ok().type(MediaType.APPLICATION_JSON_TYPE).entity(entity).build();
            }

        } catch (WebApplicationException wae) {
            WebApplicationException finalWae = wae;
            if (AJAX_IFRAME_TRANSPORT.equals(uploadData.getXRequestedWith())
                    && wae.getResponse().getStatus() == Response.Status.PRECONDITION_FAILED.getStatusCode()) {

                // In case of file upload performed by Ajax IFrame transport way,
                // the exception must also be returned into a text/html response.
                finalWae = createWebApplicationExceptionWithJSonErrorInHtmlContainer(wae);
            }
            throw finalWae;
        }
    }

    protected SimpleDocumentEntity updateSimpleDocument(SimpleDocumentUploadData uploadData, String filename)
            throws IOException {
        try {
            UserSubscriptionNotificationSendingHandler.verifyRequest(getHttpRequest());

            SimpleDocument document = getSimpleDocument(uploadData.getLanguage());
            boolean isPublic = false;
            if (uploadData.getVersionType() != null) {
                isPublic = uploadData.getVersionType() == DocumentVersion.TYPE_PUBLIC_VERSION;
                document.setPublicDocument(isPublic);
            }
            document.setUpdatedBy(getUser().getId());
            document.setLanguage(uploadData.getLanguage());
            document.setTitle(uploadData.getTitle());
            document.setDescription(uploadData.getDescription());
            document.setComment(uploadData.getComment());
            String uploadedFilename = filename;
            if (StringUtil.isNotDefined(filename)) {
                uploadedFilename = uploadData.getRequestFile().getName();
            }
            boolean isWebdav = false;
            InputStream uploadedInputStream;
            if (uploadData.getRequestFile() != null
                    && (uploadedInputStream = uploadData.getRequestFile().getInputStream()) != null
                    && StringUtil.isDefined(uploadedFilename) && !"no_file".equalsIgnoreCase(uploadedFilename)) {
                uploadFileAndUpdate(document, uploadedFilename, uploadedInputStream);
            } else {
                // In case of update and if no content has been uploaded whereas the physical file does
                // not exist, sending HTTP error 412 because file is mandatory.
                if (!FileUtils.getFile(document.getAttachmentPath()).exists()) {
                    String errorMessage = getBundle().getString("attachment.dialog.error.file.mandatory");
                    WebMessager.getInstance().addError(errorMessage);
                    throw new WebApplicationException(
                            Response.status(Response.Status.PRECONDITION_FAILED).entity(errorMessage).build());
                }
                isWebdav = document.isOpenOfficeCompatible() && document.isReadOnly();
                updateWithAlreadyUploadedFile(document, isWebdav);
            }
            UnlockContext unlockContext = new UnlockContext(document.getId(), getUser().getId(),
                    uploadData.getLanguage(), uploadData.getComment());
            unlockDocument(document, isPublic, isWebdav, unlockContext);
            document = getSimpleDocument(uploadData.getLanguage());
            URI attachmentUri = getUri().getRequestUriBuilder().path(DOCUMENT_PATH_NODE)
                    .path(document.getLanguage()).build();
            return SimpleDocumentEntity.fromAttachment(document).withURI(attachmentUri);
        } catch (RuntimeException re) {
            performRuntimeException(re);
        }
        throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
    }

    private void unlockDocument(final SimpleDocument document, final boolean isPublic, final boolean isWebdav,
            final UnlockContext unlockContext) {
        if (isWebdav) {
            unlockContext.addOption(UnlockOption.WEBDAV);
        } else {
            unlockContext.addOption(UnlockOption.UPLOAD);
        }
        if (!isPublic) {
            unlockContext.addOption(UnlockOption.PRIVATE_VERSION);
        }
        AttachmentServiceProvider.getAttachmentService().unlock(unlockContext);
        if (isWebdav) {
            WebDavTokenProducer.deleteToken(getUser(), document.getId());
        }
    }

    private void updateWithAlreadyUploadedFile(final SimpleDocument document, final boolean isWebdav) {
        if (document.isVersioned()) {
            File content = new File(document.getAttachmentPath());
            AttachmentServiceProvider.getAttachmentService().lock(document.getId(), getUser().getId(),
                    document.getLanguage());
            AttachmentServiceProvider.getAttachmentService().updateAttachment(document, content, true, true);
        } else {
            if (isWebdav) {
                WebdavServiceProvider.getWebdavService().updateDocumentContent(document);
            }
            AttachmentServiceProvider.getAttachmentService().updateAttachment(document, true, true);
        }
    }

    private void uploadFileAndUpdate(final SimpleDocument document, final String uploadedFilename,
            final InputStream uploadedInputStream) throws IOException {
        File tempFile = File.createTempFile("silverpeas_", uploadedFilename);
        FileUtils.copyInputStreamToFile(uploadedInputStream, tempFile);
        document.setFilename(uploadedFilename);
        document.setContentType(FileUtil.getMimeType(tempFile.getPath()));

        //check the file
        checkUploadedFile(tempFile);

        document.setSize(tempFile.length());
        InputStream content = new BufferedInputStream(new FileInputStream(tempFile));
        if (!StringUtil.isDefined(document.getEditedBy())) {
            document.edit(getUser().getId());
        }

        AttachmentServiceProvider.getAttachmentService().updateAttachment(document, content, true, true);
        content.close();
        FileUtils.deleteQuietly(tempFile);
        // in the case the document is a CAD one, process it for Actify
        ActifyDocumentProcessor.getProcessor().process(document);
    }

    /**
     * Returns all the existing translation of a SimpleDocument.
     *
     * @return all the existing translation of a SimpleDocument.
     */
    @GET
    @Path("translations")
    @Produces(MediaType.APPLICATION_JSON)
    public SimpleDocumentEntity[] getDocumentTanslations() {
        List<SimpleDocumentEntity> result = new ArrayList<>(I18NHelper.getNumberOfLanguages());
        for (String lang : I18NHelper.getAllSupportedLanguages()) {
            SimpleDocument attachment = getSimpleDocument(lang);
            if (lang.equals(attachment.getLanguage())) {
                URI attachmentUri = getUri().getRequestUriBuilder().path(DOCUMENT_PATH_NODE).path(lang).build();
                result.add(SimpleDocumentEntity.fromAttachment(attachment).withURI(attachmentUri));
            }
        }
        return result.toArray(new SimpleDocumentEntity[result.size()]);
    }

    /**
     * Validates the authorization of the user to request this web service. For doing, the user must
     * have the rights to access the component instance that manages this web resource. The validation
     * is actually delegated to the validation service by passing it the required information.
     *
     * This method should be invoked for web service requiring an authorized access. For doing, the
     * authentication of the user must be first valdiated. Otherwise, the annotation Authorized can be
     * also used instead at class level for both authentication and authorization.
     *
     * @see UserPrivilegeValidator
     * @param validation the validation instance to use.
     * @throws WebApplicationException if the rights of the user are not enough to access this web
     * resource.
     */
    @Override
    public void validateUserAuthorization(final UserPrivilegeValidation validation) {
        validation.validateUserAuthorizationOnAttachment(getHttpServletRequest(), getUser(),
                getSimpleDocument(null));
    }

    /**
     * Returns the content of the specified document in the specified language.
     *
     * @param language the language of the document's content to get.
     * @return the content of the specified document in the specified language.
     */
    @GET
    @Path("content/{lang}")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    public Response getFileContent(@PathParam("lang") final String language) {
        SimpleDocument document = getSimpleDocument(language);
        SilverpeasFile file = SilverpeasFileProvider.getFile(document.getAttachmentPath());
        Response.ResponseBuilder responseBuilder = EmbedMediaPlayerDispatcher
                .from((HttpServletRequest) getHttpRequest().getRequest(), getHttpServletResponse())
                .seeOtherWithSilverpeasFile(file);
        if (responseBuilder == null) {
            responseBuilder = FileResponse.fromRest(getHttpRequest(), getHttpServletResponse())
                    .forceMimeType(document.getContentType()).silverpeasFile(file);
        }
        return responseBuilder.build();
    }

    /**
     * Locks the specified document for exclusive edition.
     *
     * @return JSON status to true if the document was locked successfully - JSON status to false
     * otherwise..
     */
    @PUT
    @Path("lock/{lang}")
    @Produces(MediaType.APPLICATION_JSON)
    public String lock(@PathParam("lang") final String language) {
        boolean result = AttachmentServiceProvider.getAttachmentService().lock(getSimpleDocumentId(),
                getUser().getId(), language);
        return MessageFormat.format("'{'\"status\":{0}}", result);
    }

    /**
     * @param document
     * @return
     */
    private List<SimpleDocument> getListDocuments(SimpleDocument document) {
        return AttachmentServiceProvider.getAttachmentService().listDocumentsByForeignKeyAndType(
                new ResourceReference(document.getForeignId(), getComponentId()), document.getDocumentType(),
                defaultLanguage);
    }

    /**
     * @param docs
     * @return
     */
    private String reorderDocuments(List<SimpleDocument> docs) {
        AttachmentServiceProvider.getAttachmentService().reorderDocuments(docs);
        return MessageFormat.format("'{'\"status\":{0}}", true);
    }

    /**
     * Moves the specified document up in the list.
     *
     * @return JSON status to true if the document was locked successfully - JSON status to false
     * otherwise..
     */
    @PUT
    @Path("moveUp")
    @Produces(MediaType.APPLICATION_JSON)
    public String moveSimpleDocumentUp() {
        return moveSimpleDocument(true);
    }

    /**
     * Moves the specified document down in the list.
     *
     * @return JSON status to true if the document was locked successfully - JSON status to false
     * otherwise..
     */
    @PUT
    @Path("moveDown")
    @Produces(MediaType.APPLICATION_JSON)
    public String moveSimpleDocumentDown() {
        return moveSimpleDocument(false);
    }

    private String moveSimpleDocument(boolean up) {
        SimpleDocument document = getSimpleDocument(defaultLanguage);
        List<SimpleDocument> docs = getListDocuments(document);
        int position = docs.indexOf(document);
        Collections.swap(docs, position, up ? (position - 1) : (position + 1));
        return reorderDocuments(docs);
    }

    /**
     * Unlocks the specified document for exclusive edition.
     *
     * @param force if the unlocking has to be forced.
     * @param webdav if the unlock is performed while a WebDAV access.
     * @param privateVersion if the document is a private version.
     * @param comment a comment about the unlock.
     * @return JSON status to true if the document was locked successfully - JSON status to false
     * otherwise..
     */
    @POST
    @Path("unlock")
    @Produces(MediaType.APPLICATION_JSON)
    public String unlockDocument(@FormParam("force") final boolean force, @FormParam("webdav") final boolean webdav,
            @FormParam("private") final boolean privateVersion, @FormParam("comment") final String comment) {
        UserSubscriptionNotificationSendingHandler.verifyRequest(getHttpRequest());
        SimpleDocument document = getSimpleDocument(defaultLanguage);
        UnlockContext unlockContext = new UnlockContext(getSimpleDocumentId(), getUser().getId(), defaultLanguage,
                comment);
        if (force) {
            unlockContext.addOption(UnlockOption.FORCE);
        }
        if (webdav) {
            unlockContext.addOption(UnlockOption.WEBDAV);
        }
        if (privateVersion) {
            unlockContext.addOption(UnlockOption.PRIVATE_VERSION);
        }
        boolean result = AttachmentServiceProvider.getAttachmentService().unlock(unlockContext);
        if (result) {
            WebDavTokenProducer.deleteToken(getUser(), document.getId());
        }
        return MessageFormat.format("'{'\"status\":{0}, \"id\":{1,number,#}, \"attachmentId\":\"{2}\"}", result,
                document.getOldSilverpeasId(), document.getId());
    }

    /**
     * Changes the document version state.
     *
     * @param comment comment about the version state switching.
     * @param version the new version state.
     * @return JSON status to true if the document was locked successfully - JSON status to false
     * otherwise..
     */
    @PUT
    @Path("switchState")
    @Produces(MediaType.APPLICATION_JSON)
    public String switchDocumentVersionState(@FormParam("switch-version-comment") final String comment,
            @FormParam("switch-version") final String version) {
        boolean useMajor = "lastMajor".equalsIgnoreCase(version);
        SimpleDocument document = getSimpleDocument(defaultLanguage);
        SimpleDocumentPK pk = new SimpleDocumentPK(getSimpleDocumentId());
        if (document.isVersioned() && useMajor) {
            final SimpleDocument lastPublicVersion = document.getLastPublicVersion();
            if (lastPublicVersion == null) {
                throw new WebApplicationException(Status.NOT_FOUND);
            }
            pk = lastPublicVersion.getPk();
        }
        pk = AttachmentServiceProvider.getAttachmentService().changeVersionState(pk, comment);
        document = AttachmentServiceProvider.getAttachmentService().searchDocumentById(pk, defaultLanguage);
        return MessageFormat.format("'{'\"status\":{0}, \"id\":{1,number,#}, \"attachmentId\":\"{2}\"}", true,
                document.getOldSilverpeasId(), document.getId());
    }

    /**
     * Forbid or allow the download of the document for readers.
     * @return JSON download state for readers. allowedDownloadForReaders = true or false.
     */
    @POST
    @Path("switchDownloadAllowedForReaders")
    @Produces(MediaType.APPLICATION_JSON)
    public String switchDownloadAllowedForReaders(@FormParam("allowed") final boolean allowed) {

        // Performing the request
        SimpleDocument document = getSimpleDocument(null);
        AttachmentServiceProvider.getAttachmentService().switchAllowingDownloadForReaders(document.getPk(),
                allowed);

        // JSON Response.
        return MessageFormat.format(
                "'{'\"allowedDownloadForReaders\":{0}, \"id\":{1,number,#}, \"attachmentId\":\"{2}\"}", allowed,
                document.getOldSilverpeasId(), document.getId());
    }

    /**
     * Enable or not the display as content of an attachment.
     * @return JSON display as content state. displayableAsContent = true or false.
     */
    @POST
    @Path("switchDisplayAsContentEnabled")
    @Produces(MediaType.APPLICATION_JSON)
    public String switchDisplayAsContentEnabled(@FormParam("enabled") final boolean enabled) {

        // Performing the request
        SimpleDocument document = getSimpleDocument(null);
        AttachmentServiceProvider.getAttachmentService().switchEnableDisplayAsContent(document.getPk(), enabled);

        // JSON Response.
        return MessageFormat.format(
                "'{'\"displayableAsContent\":{0}, \"id\":{1,number,#}, \"attachmentId\":\"{2}\"}", enabled,
                document.getOldSilverpeasId(), document.getId());
    }

    /**
     * Return the current document
     * @param lang
     * @return SimpleDocument
     */
    private SimpleDocument getSimpleDocument(String lang) {
        String language = (lang == null ? defaultLanguage : lang);
        SimpleDocument attachment = AttachmentServiceProvider.getAttachmentService()
                .searchDocumentById(new SimpleDocumentPK(getSimpleDocumentId()), language);
        if (attachment == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }
        return attachment;
    }
}