org.olat.modules.fo.archiver.formatters.ForumRTFFormatter.java Source code

Java tutorial

Introduction

Here is the source code for org.olat.modules.fo.archiver.formatters.ForumRTFFormatter.java

Source

/**
 * OLAT - Online Learning and Training<br>
 * http://www.olat.org
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); <br>
 * you may not use this file except in compliance with the License.<br>
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing,<br>
 * software distributed under the License is distributed on an "AS IS" BASIS, <br>
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
 * See the License for the specific language governing permissions and <br>
 * limitations under the License.
 * <p>
 * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
 * University of Zurich, Switzerland.
 * <p>
 */

package org.olat.modules.fo.archiver.formatters;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringEscapeUtils;
import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
import org.olat.core.id.Identity;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Formatter;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.ZipUtil;
import org.olat.core.util.filter.FilterFactory;
import org.olat.core.util.nodes.INode;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.LocalFolderImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSItem;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.modules.fo.ForumManager;
import org.olat.modules.fo.MessageNode;

/**
 * Initial Date: Nov 09, 2005 <br>
 * 
 * @author Patrick Brunner, Alexander Schneider
 */

public class ForumRTFFormatter extends ForumFormatter {

    private final VFSContainer container;
    private VFSItem vfsFil = null;
    private final ForumManager fm = ForumManager.getInstance();
    private VFSContainer tempContainer;

    final Pattern PATTERN_HTML_BOLD = Pattern.compile("<strong>(.*?)</strong>", Pattern.CASE_INSENSITIVE);
    final Pattern PATTERN_HTML_ITALIC = Pattern.compile("<em>(.*?)</em>", Pattern.CASE_INSENSITIVE);
    final Pattern PATTERN_HTML_BREAK = Pattern.compile("<br />", Pattern.CASE_INSENSITIVE);
    final Pattern PATTERN_HTML_PARAGRAPH = Pattern.compile("<p>(.*?)</p>", Pattern.CASE_INSENSITIVE);
    final Pattern PATTERN_HTML_AHREF = Pattern.compile("<a href=\"([^\"]+)\"[^>]*>(.*?)</a>",
            Pattern.CASE_INSENSITIVE);
    final Pattern PATTERN_HTML_LIST = Pattern.compile("<li>(.*?)</li>", Pattern.CASE_INSENSITIVE);
    final Pattern HTML_SPACE_PATTERN = Pattern.compile("&nbsp;");

    final Pattern PATTERN_CSS_O_FOQUOTE = Pattern.compile(
            "<div class=\"b_quote_wrapper\">\\s*<div class=\"b_quote_author mceNonEditable\">(.*?)</div>\\s*<blockquote class=\"b_quote\">\\s*(.*?)\\s*</blockquote>\\s*</div>",
            Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

    final Pattern PATTERN_THREEPOINTS = Pattern.compile("&#8230;", Pattern.CASE_INSENSITIVE);
    final String THREEPOINTS = "...";

    // TODO: (LD) translate this!
    private final String HIDDEN_STR = "VERBORGEN";

    /**
     * @param container
     * @param filePerThread
     */

    public ForumRTFFormatter(final VFSContainer container, final boolean filePerThread) {
        // init String Buffer in ForumFormatter
        super();
        // where to write
        this.container = container;
        this.filePerThread = filePerThread;
        //
        /*
         * Translator translator = new PackageTranslator(PACKAGE, locale); HIDDEN_STR = translator.translate("fo_hidden");
         */
    }

    /**
     * @see org.olat.core.util.tree.Visitor#visit(org.olat.core.util.nodes.INode)
     */
    @Override
    public void visit(final INode node) {
        final MessageNode mn = (MessageNode) node;

        if (isTopThread) {
            if (filePerThread) {
                // make a file per thread
                // to have a meaningful filename we create the file here
                final String filName = "Thread_" + mn.getKey().toString();
                tempContainer = makeTempVFSContainer();
                this.vfsFil = tempContainer.resolve(filName + ".rtf");
                if (vfsFil == null) {
                    tempContainer.createChildLeaf(filName + ".rtf");
                    this.vfsFil = tempContainer.resolve(filName + ".rtf");
                }
            }
            // important!
            isTopThread = false;
        }
        // Message Title
        sb.append("{\\pard \\brdrb\\brdrs\\brdrw10 \\f1\\fs30\\b ");
        sb.append(getImageRTF(mn));
        sb.append(getTitlePrefix(mn));
        sb.append(mn.getTitle());
        sb.append("\\par}");
        // Message Body
        sb.append("{\\pard \\f0");
        sb.append(convertHTMLMarkupToRTF(mn.getBody()));
        sb.append("\\par}");
        // Message key
        sb.append("{\\pard \\f0\\fs15 Message key: ");
        sb.append(mn.getKey());
        sb.append("} \\line ");
        sb.append("{\\pard \\f0\\fs15 created: ");
        // Creator and creation date
        sb.append(mn.getCreator().getUser().getProperty(UserConstants.FIRSTNAME, null));
        sb.append(", ");
        sb.append(mn.getCreator().getUser().getProperty(UserConstants.LASTNAME, null));
        sb.append(" ");
        sb.append(mn.getCreationDate().toString());
        // Modifier and modified date
        final Identity modifier = mn.getModifier();
        if (modifier != null) {
            sb.append(" \\line modified: ");
            sb.append(modifier.getUser().getProperty(UserConstants.FIRSTNAME, null));
            sb.append(", ");
            sb.append(modifier.getUser().getProperty(UserConstants.LASTNAME, null));
            sb.append(" ");
            sb.append(mn.getModifiedDate().toString());
        }
        sb.append(" \\par}");
        // attachment(s)
        final OlatRootFolderImpl msgContainer = fm
                .getMessageContainer((Long) (getMetainfo(ForumFormatter.MANDATORY_METAINFO_KEY)), mn.getKey());
        final List attachments = msgContainer.getItems();
        if (attachments != null && attachments.size() > 0) {
            VFSItem item = container.resolve("attachments");
            if (item == null) {
                item = container.createChildContainer("attachments");
            }
            final VFSContainer attachmentContainer = (VFSContainer) item;
            attachmentContainer.copyFrom(msgContainer);

            sb.append("{\\pard \\f0\\fs15 Attachment(s): ");
            boolean commaFlag = false;
            for (final Iterator iter = attachments.iterator(); iter.hasNext();) {
                final VFSItem attachment = (VFSItem) iter.next();
                if (commaFlag) {
                    sb.append(", ");
                }
                sb.append(attachment.getName());
                commaFlag = true;
            }
            sb.append("} \\line");
        }
        sb.append("{\\pard \\brdrb\\brdrs\\brdrw10 \\par}");

    }

    /**
     * @see org.olat.modules.fo.archiver.formatters.ForumFormatter#openThread()
     */
    @Override
    public void openThread() {
        super.openThread();
        if (filePerThread) {
            sb.append("{\\rtf1\\ansi\\deff0");
            sb.append("{\\fonttbl {\\f0\\fswiss Arial;}} ");
            sb.append("\\deflang1033\\plain");
        }
        sb.append("{\\pard \\brdrb \\brdrs \\brdrdb \\brsp20 \\par}{\\pard\\par}");
    }

    /**
     * @see org.olat.modules.fo.archiver.formatters.ForumFormatter#getThreadResult()
     */
    @Override
    public StringBuilder closeThread() {
        final boolean append = !filePerThread;
        final String footerThread = "{\\pard \\brdrb \\brdrs \\brdrw20 \\brsp20 \\par}{\\pard\\par}";
        sb.append(footerThread);
        if (filePerThread) {
            sb.append("}");
        }
        writeToFile(append, sb);
        if (this.filePerThread) {
            zipContainer(tempContainer);
            tempContainer.delete();
        }
        return super.closeThread();
    }

    /**
     * @see org.olat.modules.fo.archiver.formatters.ForumFormatter#openForum()
     */
    @Override
    public void openForum() {
        if (!filePerThread) {
            // make one ForumFile
            final Long forumKey = (Long) metaInfo.get(ForumFormatter.MANDATORY_METAINFO_KEY);
            String filName = forumKey.toString();
            filName = "Threads_" + filName + ".rtf";

            tempContainer = makeTempVFSContainer();
            this.vfsFil = tempContainer.resolve(filName);
            if (vfsFil == null) {
                tempContainer.createChildLeaf(filName);
                this.vfsFil = tempContainer.resolve(filName);
            }
            sb.append("{\\rtf1\\ansi\\deff0");
            sb.append("{\\fonttbl {\\f0\\fswiss Arial;}} ");
            sb.append("\\deflang1033\\plain");
        }
    }

    /**
     * @see org.olat.modules.fo.archiver.formatters.ForumFormatter#closeForum()
     */
    @Override
    public StringBuilder closeForum() {
        if (!filePerThread) {
            final boolean append = !filePerThread;
            final String footerForum = "}";
            sb.append(footerForum);
            writeToFile(append, sb);
            zipContainer(tempContainer);
            tempContainer.delete();
        }
        return sb;
    }

    /**
     * @param append
     * @param buff
     */
    private void writeToFile(final boolean append, final StringBuilder buff) {
        final BufferedOutputStream bos = new BufferedOutputStream(((VFSLeaf) vfsFil).getOutputStream(append));
        OutputStreamWriter w;
        try {
            w = new OutputStreamWriter(bos, "utf-8");
            final BufferedWriter bw = new BufferedWriter(w);
            final String s = buff.toString();
            final StringBuilder out = new StringBuilder();
            final int len = s.length();
            for (int i = 0; i < len; i++) {
                final char c = s.charAt(i);
                final int val = c;
                if (val > 127) {
                    out.append("\\u").append(String.valueOf(val)).append("?");
                } else {
                    out.append(c);
                }
            }

            final String encoded = out.toString();
            bw.write(encoded);
            bw.close();
            bos.close();
        } catch (final UnsupportedEncodingException ueEx) {
            throw new AssertException("could not encode stream from forum export file: " + ueEx);
        } catch (final IOException e) {
            throw new AssertException("could not write to forum export file: " + e);
        }
    }

    /**
     * @param originalText
     * @return
     */
    private String convertHTMLMarkupToRTF(final String originalText) {
        String htmlText = originalText;

        final Matcher mb = PATTERN_HTML_BOLD.matcher(htmlText);
        final StringBuffer bolds = new StringBuffer();
        while (mb.find()) {
            mb.appendReplacement(bolds, "{\\\\b $1} ");
        }
        mb.appendTail(bolds);
        htmlText = bolds.toString();

        final Matcher mi = PATTERN_HTML_ITALIC.matcher(htmlText);
        final StringBuffer italics = new StringBuffer();
        while (mi.find()) {
            mi.appendReplacement(italics, "{\\\\i $1} ");
        }
        mi.appendTail(italics);
        htmlText = italics.toString();

        final Matcher mbr = PATTERN_HTML_BREAK.matcher(htmlText);
        final StringBuffer breaks = new StringBuffer();
        while (mbr.find()) {
            mbr.appendReplacement(breaks, "\\\\line ");
        }
        mbr.appendTail(breaks);
        htmlText = breaks.toString();

        final Matcher mofo = PATTERN_CSS_O_FOQUOTE.matcher(htmlText);
        final StringBuffer foquotes = new StringBuffer();
        while (mofo.find()) {
            mofo.appendReplacement(foquotes, "\\\\line {\\\\i $1} {\\\\pard $2\\\\par}");
        }
        mofo.appendTail(foquotes);
        htmlText = foquotes.toString();

        final Matcher mp = PATTERN_HTML_PARAGRAPH.matcher(htmlText);
        final StringBuffer paragraphs = new StringBuffer();
        while (mp.find()) {
            mp.appendReplacement(paragraphs, "\\\\line $1 \\\\line");
        }
        mp.appendTail(paragraphs);
        htmlText = paragraphs.toString();

        final Matcher mahref = PATTERN_HTML_AHREF.matcher(htmlText);
        final StringBuffer ahrefs = new StringBuffer();
        while (mahref.find()) {
            mahref.appendReplacement(ahrefs,
                    "{\\\\field{\\\\*\\\\fldinst{HYPERLINK\"$1\"}}{\\\\fldrslt{\\\\ul $2}}}");
        }
        mahref.appendTail(ahrefs);
        htmlText = ahrefs.toString();

        final Matcher mli = PATTERN_HTML_LIST.matcher(htmlText);
        final StringBuffer lists = new StringBuffer();
        while (mli.find()) {
            mli.appendReplacement(lists, "$1\\\\line ");
        }
        mli.appendTail(lists);
        htmlText = lists.toString();

        final Matcher mtp = PATTERN_THREEPOINTS.matcher(htmlText);
        final StringBuffer tps = new StringBuffer();
        while (mtp.find()) {
            mtp.appendReplacement(tps, THREEPOINTS);
        }
        mtp.appendTail(tps);
        htmlText = tps.toString();

        // strip all other html-fragments, because not convertable that easy
        htmlText = FilterFactory.getHtmlTagsFilter().filter(htmlText);
        // Remove all &nbsp;
        final Matcher tmp = HTML_SPACE_PATTERN.matcher(htmlText);
        htmlText = tmp.replaceAll(" ");
        htmlText = StringEscapeUtils.unescapeHtml(htmlText);

        return htmlText;
    }

    /**
     * @param messageNode
     * @return title prefix for hidden forum threads.
     */
    private String getTitlePrefix(final MessageNode messageNode) {
        final StringBuffer stringBuffer = new StringBuffer();
        if (messageNode.isHidden()) {
            stringBuffer.append(HIDDEN_STR);
        }
        if (stringBuffer.length() > 1) {
            stringBuffer.append(": ");
        }
        return stringBuffer.toString();
    }

    /**
     * Gets the RTF image section for the input messageNode.
     * 
     * @param messageNode
     * @return the RTF image section for the input messageNode.
     */
    private String getImageRTF(final MessageNode messageNode) {

        final StringBuffer stringBuffer = new StringBuffer();
        final List<String> fileNameList = addImagesToVFSContainer(messageNode, tempContainer);
        final Iterator<String> listIterator = fileNameList.iterator();
        while (listIterator.hasNext()) {
            final String fileName = listIterator.next();

            stringBuffer.append("{\\field\\fldedit{\\*\\fldinst { INCLUDEPICTURE ");
            stringBuffer.append("\"").append(fileName).append("\"");
            stringBuffer.append(" \\\\d }}{\\fldrslt {}}}");
        }
        return stringBuffer.toString();
    }

    /**
     * Retrieves the appropriate images for the input messageNode, if any, and adds it to the input container.
     * 
     * @param messageNode
     * @param container
     * @return
     */
    private List<String> addImagesToVFSContainer(final MessageNode messageNode, final VFSContainer container) {
        final List<String> fileNameList = new ArrayList<String>();
        String iconPath = null;
        if (messageNode.isClosed() && messageNode.isSticky()) {
            iconPath = getImagePath("fo_sticky_closed");
        } else if (messageNode.isClosed()) {
            iconPath = getImagePath("fo_closed");
        } else if (messageNode.isSticky()) {
            iconPath = getImagePath("fo_sticky");
        }
        if (iconPath != null) {
            final File file = new File(iconPath);
            if (file.exists()) {
                final LocalFileImpl imgFile = new LocalFileImpl(file);
                container.copyFrom(imgFile);
                fileNameList.add(file.getName());
            } else {
                Tracing.logError("Could not find image for forum RTF formatter::" + iconPath, ForumFormatter.class);
            }
        }
        return fileNameList;
    }

    /**
     * TODO: LD: to clarify whether there it a better way to get the image path? Gets the image path.
     * 
     * @param val
     * @return the path of the static icon image.
     */
    private String getImagePath(final Object val) {
        return WebappHelper.getContextRoot() + "/static/images/forum/" + val.toString() + ".png";
    }

    /**
     * Generates a new temporary VFSContainer.
     * 
     * @return the temp container.
     */
    private VFSContainer makeTempVFSContainer() {
        final Long forumKey = (Long) metaInfo.get(ForumFormatter.MANDATORY_METAINFO_KEY);
        final String dateStamp = String.valueOf(System.currentTimeMillis());
        // TODO: (LD) could this filename regarded as unique or use System.nanoTime() instead?
        final String fileName = "forum" + forumKey.toString() + "_" + dateStamp;
        final LocalFolderImpl tempFolder = new OlatRootFolderImpl("/tmp/" + fileName, null);
        return tempFolder;
    }

    /**
     * Zips the input vFSContainer into the container.
     * 
     * @param vFSContainer
     */
    private void zipContainer(final VFSContainer vFSContainer) {
        final String dateStamp = Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis()));
        final VFSLeaf zipVFSLeaf = container.createChildLeaf("forum_archive-" + dateStamp + ".zip");
        ZipUtil.zip(vFSContainer.getItems(), zipVFSLeaf, true);
    }

}