From ff647d1ae9f5b56b72266372693f0ca536696c52 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Tue, 13 Aug 2019 14:25:21 +0200 Subject: [PATCH] Ported AbstractTextUploadForm from legacy code --- .../contenttypes/AbstractTextUploadForm.java | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/contenttypes/AbstractTextUploadForm.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/contenttypes/AbstractTextUploadForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contenttypes/AbstractTextUploadForm.java new file mode 100644 index 000000000..4839dbaae --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contenttypes/AbstractTextUploadForm.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2019 LibreCCM Foundation. + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.contenttypes; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.SaveCancelSection; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormValidationListener; +import com.arsdigita.bebop.form.FormErrorDisplay; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.bebop.util.GlobalizationUtil; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.ui.FileUploadSection; +import com.arsdigita.globalization.Globalization; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.util.UncheckedWrapperException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.awt.image.Kernel; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +import javax.activation.MimeType; + +/** + * + * @author Jens Pelzetter + */ +/** + *

+ * An abstract base form for uploading Texts. The code of this class has been + * extracted from {@link TextAssetBody}, the form for editing an TextAsset (used + * for example for Article). The TextAsset has the disadvantage of storing its + * information in a separate table, so that information of a content item is + * spread over several tables. + *

+ *

+ * To use this form, define a property for your object which has the Java type + * String and the database type CLOB, like this: + *

+ *
+ * String[0..1] text = ct_yourContenttype.text CLOB
+ * 
+ *

+ * To use this form your have to overwrite three methods: + *

+ * + *
  • + * Of course, you have to add your form to a property step also, and write a + * simple constructor, which takes an {@link ItemSelectionModel} as parameter + * and passes it to the constructor of this class. + *
  • + * + * + * @author Jens Pelzetter + */ +public abstract class AbstractTextUploadForm + extends Form + implements FormInitListener, + FormProcessListener, + FormValidationListener { + + private static final Logger LOGGER = LogManager + .getLogger(AbstractTextUploadForm.class); + + private ItemSelectionModel itemModel; + private FileUploadSection fileUploadSection; + private SaveCancelSection saveCancelSection; + private RequestLocal fileUploadContent; + private RequestLocal fileUploadContentUsedInso; + + public AbstractTextUploadForm(final ItemSelectionModel itemModel) { + super("sciprojectUploadDescFrom", new BoxPanel(BoxPanel.VERTICAL)); + this.itemModel = itemModel; + setMethod(Form.POST); + setEncType("multipart/form-data"); + this.fileUploadContent = new RequestLocal(); + this.fileUploadContentUsedInso = new RequestLocal(); + addWidgets(); + } + + //Abstract methods to overwrite + /** + * The return value of this method is used as label for the upload form. + * + * @return The label for the upload form. + */ + public abstract GlobalizedMessage getLabelText(); + + /** + * The return value of this method is used as label for the MimeType field. + * + * @return The label for the MimeType field. + */ + public abstract GlobalizedMessage getMimeTypeLabel(); + + /** + *

    + * This method is called to pass the uploaded text to the edited object. In + * the method, you have to retrieve the current selected object from the + * itemModel parameter and call the appropriate + * set of your class, and its save method. An simple example: + *

    + *
    +     * @Override
    +     * public void setText(ItemSelectionModel itemModel,
    +     *                     PageState state,
    +     *                     String text) {
    +     *   YourContentType obj = (YourContentType) itemModel.getSelectedObject(state);
    +     *   obj.setText(text);
    +     *   obj.save();
    +     * }
    +     * 
    + * + * @param itemModel The {@link ItemSelectionModel} used by the form. + * @param state The current {@link PageState}. + * @param text The uploaded text. + */ + public abstract void setText(ItemSelectionModel itemModel, + PageState state, + String text); + + protected void addWidgets() { + add(new Label(getLabelText())); + fileUploadSection = new FileUploadSection( + getMimeTypeLabel(), + "mime", + "text/plain"); + fileUploadSection.getFileUploadWidget().addValidationListener( + new NotNullValidationListener()); + fileUploadSection.getMimeTypeWidget().setDefaultValue( + FileUploadSection.GUESS_MIME); + add(fileUploadSection); + + saveCancelSection = new SaveCancelSection(); + add(saveCancelSection); + + add(new FormErrorDisplay(this)); + + addValidationListener(this); + addProcessListener(this); + } + + /** + * @return the save/cancel section for this form + */ + public SaveCancelSection getSaveCancelSection() { + return saveCancelSection; + } + + @Override + public void init(final FormSectionEvent event) throws FormProcessException { + final PageState state = event.getPageState(); + + setVisible(state, true); + } + + /** + * Validate file upload + * + * @param event + * + * @throws FormProcessException + */ + @Override + public void validate(final FormSectionEvent event) throws + FormProcessException { + + final MimeType mime = fileUploadSection.getMimeType(event); + boolean textType = mime.getPrimaryType().equals("text"); + + validateFileType(mime, textType); + + // Convert the file to HTML, if possible + final File file = fileUploadSection.getFile(event); + byte[] file_bytes = readFileBytes(file); + boolean[] used_inso = new boolean[1]; + String file_content = convertBytes(file_bytes, textType, used_inso); + + if ("text/html".equals(mime.toString())) { + file_content = extractHTMLBody(file_content); + } + + final PageState state = event.getPageState(); + fileUploadContent.set(state, file_content); + fileUploadContentUsedInso.set(state, used_inso[0]); + } + + @Override + public void process(final FormSectionEvent event) + throws FormProcessException { + LOGGER.debug("Processing upload..."); + final PageState state = event.getPageState(); + //File file = fileUploadSection.getFile(fse); + //SciProject project = (SciProject) itemModel.getSelectedObject(state); + + final String uploadContent = (String) fileUploadContent.get(state); + //boolean usedInso = (Boolean) fileUploadContentUsedInso.get(state); + + LOGGER.debug(String.format("Setting project description to: %s", + uploadContent)); + //project.setProjectDescription(uploadContent); + LOGGER.debug("Saving project."); + //project.save(); + setText(itemModel, state, uploadContent); + } + + private void validateFileType(final MimeType mime, final boolean textType) + throws FormProcessException { + + boolean validType = textType; + + if (!validType) { + throw new FormProcessException(GlobalizationUtil.globalize( + "cms.ui.authoring.invalid_file_type")); + } + } + + /** + * read in the content of the file (in bytes). + */ + private byte[] readFileBytes(final File file) throws FormProcessException { + final byte[] fileBytes; + try (FileInputStream fileInputStream = new FileInputStream(file)) { + fileBytes = new byte[fileInputStream.available()]; + fileInputStream.read(fileBytes); + } catch (IOException ex) { + throw new FormProcessException(GlobalizationUtil.globalize( + "cms.ui.authoring.unable_to_load_file")); + } + return fileBytes; + } + + /** + * Convert bytes to String, possibly using INSO filter to convert to HTML + * type + */ + private String convertBytes(final byte[] fileBytes, + final boolean textType, + final boolean[] usedInso) + throws FormProcessException { + + return new String(fileBytes, StandardCharsets.UTF_8); + } + + /** + * Extract the contents of the html Body tag. (Done to prevent base and + * other header tags from interfering with page display). + */ + private String extractHTMLBody(final String htmlText) + throws FormProcessException { + + final String lc = htmlText.toLowerCase(); + final int bodyStart = lc.indexOf("", bodyStart); + final int bodyEnd = lc.indexOf("", bodyStart_v); + if (bodyStart == -1 || bodyEnd == -1) { + throw new FormProcessException(GlobalizationUtil.globalize( + "cms.ui.authoring.html_file_missing_body_tags")); + } + return htmlText.substring(bodyStart_v + 1, bodyEnd); + } + +}