org.apache.roller.weblogger.ui.struts2.editor.WeblogExport.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.roller.weblogger.ui.struts2.editor.WeblogExport.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  The ASF licenses this file to You
 * under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.roller.weblogger.ui.struts2.editor;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
// import java.net.URL;
// import java.net.MalformedURLException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

// import org.apache.abdera.Abdera;
// import org.apache.abdera.ext.thread.ThreadHelper;
// import org.apache.abdera.model.Entry;
// import org.apache.abdera.model.Feed;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.RollerException;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.MediaFileManager;
import org.apache.roller.weblogger.business.URLStrategy;
import org.apache.roller.weblogger.business.WeblogEntryManager;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.apache.roller.weblogger.pojos.MediaFile;
import org.apache.roller.weblogger.pojos.MediaFileDirectory;
import org.apache.roller.weblogger.pojos.WeblogEntry;
import org.apache.roller.weblogger.pojos.WeblogPermission;
import org.apache.roller.weblogger.pojos.wrapper.WeblogEntryCommentWrapper;
// import org.apache.roller.weblogger.pojos.wrapper.WeblogEntryTagWrapper;
import org.apache.roller.weblogger.pojos.wrapper.WeblogEntryWrapper;
import org.apache.roller.weblogger.ui.struts2.util.UIAction;
import org.apache.struts2.interceptor.ServletResponseAware;

/**
 * Provides export functionality for the author of a weblog.
 */
public final class WeblogExport extends UIAction implements ServletResponseAware {

    // Static Variables --------------------------------------------------------
    private static final Log log = LogFactory.getLog(WeblogExport.class);

    private static final Pattern SC_TAG_PATTERN = Pattern.compile("(([\\S])(/>))");

    private static final Pattern PRE_TAG_PATTERN = Pattern.compile("<pre>[\\s\\S]+?</pre>");

    private static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r\\n|\\r|\\n");

    // TODO: Perhaps add enum to manage the different MT constants
    private static final String MT_SECTION_DIVIDER = "-----\n";
    private static final String MT_ENTRY_DIVIDER = "--------\n";

    private static final SimpleDateFormat MT_DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    private static final SimpleDateFormat ATOM_ID_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private static final String MT_FORMAT = "mtimport";
    private static final String MT_PLUS_FORMAT = "mtimportplus";
    private static final String ATOM_FORMAT = "atom";

    // private static final Abdera abdera = new Abdera();

    // Instance Variables ------------------------------------------------------
    private Pattern baseUrlPattern;

    private HttpServletResponse response;
    private String baseUrl;
    private String format;

    // Constructors ------------------------------------------------------------
    public WeblogExport() {
        this.actionName = "weblogExport";
        this.desiredMenu = "editor";
        this.pageTitle = "weblogExport.title";

        // Set the default format
        this.format = MT_FORMAT;
    }

    // Public Methods ----------------------------------------------------------
    /**
     * Keeps a reference to the current HTTP servlet response object.
     *
     * @param httpServletResponse The HTTP servlet response.
     */
    public void setServletResponse(HttpServletResponse httpServletResponse) {
        this.response = httpServletResponse;
    }

    /**
     * Sets the base URL to be used when replacing references to resource files.
     *
     * @param baseUrl The desired base URL.
     */
    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     * Get the current format.
     *
     * @return The current format.
     */
    public String getFormat() {
        return format;
    }

    /**
     * Sets the desired export format.
     *
     * @param format The desired export format.
     */
    public void setFormat(String format) {
        this.format = format;
    }

    /**
     * Gets the list of supported export formats.
     *
     * @return A list of string made of of supported export formats.
     */
    public Map<String, String> getFormatOptions() {
        Map<String, String> options;
        options = new LinkedHashMap<String, String>();

        options.put(MT_FORMAT, getText("weblogExport.format.mtimport"));
        options.put(MT_PLUS_FORMAT, getText("weblogExport.format.mtimportplus"));
        // options.put(ATOM_FORMAT, getText("weblogExport.format.atom"));

        return options;
    }

    /**
     * Require the author role before allowing export functionality.
     */
    @Override
    public List<String> requiredWeblogPermissionActions() {
        return Collections.singletonList(WeblogPermission.ADMIN);
    }

    /**
     * Simply triggers the display of the export options UI.
     */
    @Override
    public String execute() throws WebloggerException {

        if (!WebloggerConfig.getBooleanProperty("weblog.export.enabled")) {
            throw new WebloggerException("ERROR: export is disabled");
        }

        // We need to gather some more info before we can attempt an export
        return INPUT;
    }

    /**
     * Returns an output stream to the client containing a text file of all
     * entries and comments. This will include draft entries as well.
     *
     * Currently the only file format supported is mtimport.
     */
    public void exportEntries() throws WebloggerException {

        if (!WebloggerConfig.getBooleanProperty("weblog.export.enabled")) {
            throw new WebloggerException("ERROR: export is disabled");
        }

        try {
            WeblogEntryManager wmgr = WebloggerFactory.getWeblogger().getWeblogEntryManager();

            URLStrategy urlStrategy;
            urlStrategy = WebloggerFactory.getWeblogger().getUrlStrategy();

            List rawEntries;
            rawEntries = wmgr.getWeblogEntries(getActionWeblog(), null, null, null, null, null, null, null, null,
                    null, null, 0, -1);

            List<WeblogEntryWrapper> entries;
            entries = new ArrayList<WeblogEntryWrapper>();

            for (Object entry : rawEntries) {
                entries.add(WeblogEntryWrapper.wrap((WeblogEntry) entry, urlStrategy));
            }

            // Compile the resource URL pattern using the weblog handle
            baseUrlPattern = Pattern
                    .compile("(<[\\s\\S]+?=[\"'])(http[s]*?://[\\S]+/" + getActionWeblog().getHandle()
                            + "/resource/|/" + getActionWeblog().getHandle() + "/resource/)");

            // Produce the selected output format
            String output;
            output = formatAsMoveableType(entries);
            /*
            if (format.equals(ATOM_FORMAT)) {
            output = formatAsAtom(entries);
            }
            else {
            output = formatAsMoveableType(entries);
            }
            */

            if (!response.isCommitted()) {
                response.reset();

                SimpleDateFormat dateFormat;
                dateFormat = new SimpleDateFormat("MMddyyyy'T'HHmmss");

                StringBuilder fileName;
                fileName = new StringBuilder();
                fileName.append(getActionWeblog().getHandle());
                fileName.append("-entries-");
                fileName.append(dateFormat.format(System.currentTimeMillis()));

                if (format.equals(ATOM_FORMAT)) {
                    fileName.append(".xml");
                } else {
                    fileName.append(".txt");
                }

                // Force the browser to download the export file
                response.setContentType("application/octet-stream; charset=utf-8");
                response.setContentLength(output.getBytes("UTF-8").length);
                response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName.toString() + "\"");

                ServletOutputStream outputStream;
                outputStream = response.getOutputStream();
                outputStream.print(output);
                outputStream.flush();
                outputStream.close();
            }
        } catch (WebloggerException e) {
            log.error("Error looking up entries: ", e);
        } catch (IOException e) {
            log.error("Error getting output stream: ", e);
        }
    }

    /**
     * Returns an output stream to the client of all uploaded resource files as
     * a ZIP archive.
     */
    public void exportResources() {
        SimpleDateFormat dateFormat;
        dateFormat = new SimpleDateFormat("MMddyyyy'T'HHmmss");

        StringBuilder fileName;
        fileName = new StringBuilder();
        fileName.append(getActionWeblog().getHandle());
        fileName.append("-resources-");
        fileName.append(dateFormat.format(System.currentTimeMillis()));
        fileName.append(".zip");

        if (!response.isCommitted()) {
            response.reset();

            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName.toString() + "\"");

            try {
                MediaFileManager fmgr = WebloggerFactory.getWeblogger().getMediaFileManager();

                List<MediaFile> resources = new ArrayList<MediaFile>();

                // Load the contents of any sub-directories
                for (MediaFileDirectory mdir : fmgr.getMediaFileDirectories(getActionWeblog())) {
                    loadResources(resources, mdir);
                }

                // Load the files at the root of the specific upload directory
                loadResources(resources, null);

                // Create a buffer for reading the files
                byte[] buffer;
                buffer = new byte[1024];

                ServletOutputStream servletOutput;
                servletOutput = response.getOutputStream();

                ZipOutputStream zipOutput;
                zipOutput = new ZipOutputStream(servletOutput);

                for (MediaFile resource : resources) {
                    InputStream input;
                    input = resource.getInputStream();

                    // Add a new ZIP entry to output stream
                    zipOutput.putNextEntry(new ZipEntry(resource.getPath()));

                    int length;
                    while ((length = input.read(buffer)) > 0) {
                        zipOutput.write(buffer, 0, length);
                    }

                    // Cleanup the entry
                    input.close();
                    zipOutput.closeEntry();
                }

                // Cleanup the output stream
                zipOutput.flush();
                zipOutput.close();
            } catch (Exception e) {
                log.error("Error exporting resources: " + e.getMessage());
            }
        }
    }

    // Private Methods ---------------------------------------------------------
    /**
     * Formats all entries and comments, including draft entries, in the
     * Atom Syndication Format.
     *
     * @param entries A collection of entries to format.
     * @return A String of all entries and comments formatted as Atom
     */
    /*
    private String formatAsAtom(List<WeblogEntryWrapper> entries) {
    Weblog weblog;
    weblog = getActionWeblog();
        
    String hostname;
    URL absoluteUrl;
    try {
        absoluteUrl = new URL(weblog.getAbsoluteURL());
        hostname = absoluteUrl.getHost();
    }
    catch (MalformedURLException e) {
        log.error("Unable to parse the absolute URL: " + e.getMessage());
        hostname = "unknown";
    }
        
    // Feed
    StringBuilder feedId;
    feedId = new StringBuilder();
        
    feedId.append("tag:");
    feedId.append(hostname);
    feedId.append(",");
    feedId.append(ATOM_ID_DATE_FORMAT.format(weblog.getDateCreated()));
    feedId.append(":");
    feedId.append(weblog.getId());
        
    Feed feed;
    feed = abdera.newFeed();
        
    feed.setId(feedId.toString());
    feed.setTitle(weblog.getName());
        
    if (weblog.getDescription() != null &&
            !weblog.getDescription().equals("")) {
        feed.setSubtitle(weblog.getDescription());
    }
        
    // TODO: Maybe want to add all authors
    feed.addAuthor(weblog.getCreator().getScreenName());
        
    // TODO: Really need a "self" link, but what to use?
    feed.addLink(weblog.getAbsoluteURL(), "self");
    feed.addLink(weblog.getAbsoluteURL(), "alternate");
        
    feed.setUpdated(weblog.getLastModified());
        
    feed.setGenerator("http://roller.apache.org",
            WebloggerFactory.getWeblogger().getVersion(), "Apache Roller");
        
    // Entries
    for (WeblogEntryWrapper entryWrapper : entries) {
        StringBuilder entryId;
        entryId = new StringBuilder();
        
        entryId.append("tag:");
        entryId.append(hostname);
        entryId.append(",");
        entryId.append(ATOM_ID_DATE_FORMAT.format(
                (entryWrapper.getPubTime() != null) ?
                        entryWrapper.getPubTime() :
                        entryWrapper.getUpdateTime()));
        entryId.append(":");
        entryId.append(entryWrapper.getId());
        
        Entry entry;
        entry = feed.addEntry();
        
        entry.setId(entryId.toString());
        entry.setTitle(entryWrapper.getTitle());
        entry.addAuthor(entryWrapper.getCreator().getScreenName());
        entry.addLink(entryWrapper.getPermalink(), "alternate");
        entry.setPublished(entryWrapper.getPubTime());
        entry.setUpdated(entryWrapper.getUpdateTime());
        
        // Category
        entry.addCategory(null, entryWrapper.getCategory().getPath(),
                entryWrapper.getCategory().getName());
        
        // Tags
        for (Object tagWrapperObj : entryWrapper.getTags()) {
            WeblogEntryTagWrapper tagWrapper;
            tagWrapper = (WeblogEntryTagWrapper) tagWrapperObj;
        
            entry.addCategory("http://roller.apache.org/ns/tags/",
                    tagWrapper.getName(), tagWrapper.getName());
        }
        
        // Enclosure
        String enclosureUrl;
        enclosureUrl = entryWrapper.findEntryAttribute(
                "att_mediacast_url");
        
        if (enclosureUrl != null && !enclosureUrl.equals("")) {
            String enclosureType;
            enclosureType = entryWrapper.findEntryAttribute(
                    "att_mediacast_type");
        
            Long enclosureLength;
            try {
                enclosureLength = Long.parseLong(
                        entryWrapper.findEntryAttribute(
                                "att_mediacast_length"));
            }
            catch (NumberFormatException e) {
                log.error("Unable to parse 'att_mediacast_length': " +
                        e.getMessage());
                enclosureLength = (long) 0;
            }
        
            entry.addLink(enclosureUrl, "enclosure", enclosureType, null,
                    null, enclosureLength);
        }
        
        // Summary
        if (entryWrapper.getSummary() != null &&
                !entryWrapper.getSummary().equals("")) {
            entry.setSummaryAsHtml(processEntry(
                    entryWrapper.getSummary().trim()));
        }
        
        // Content
        if (entryWrapper.getText() != null &&
                !entryWrapper.getText().equals("")) {
            entry.setContentAsHtml(processEntry(
                    entryWrapper.getText().trim()));
        }
        
        // Comments in reply to the entry
        for (Object commentObj : entryWrapper.getComments()) {
            WeblogEntryCommentWrapper commentEntryWrapper;
            commentEntryWrapper = (WeblogEntryCommentWrapper) commentObj;
        
            StringBuilder commentEntryId;
            commentEntryId = new StringBuilder();
        
            commentEntryId.append("tag:");
            commentEntryId.append(hostname);
            commentEntryId.append(",");
            commentEntryId.append(ATOM_ID_DATE_FORMAT.format(
                    commentEntryWrapper.getPostTime()));
            commentEntryId.append(":");
            commentEntryId.append(commentEntryWrapper.getId());
        
            Entry commentEntry;
            commentEntry = feed.addEntry();
        
            commentEntry.setId(commentEntryId.toString());
            commentEntry.setTitle("Re: " + entryWrapper.getTitle());
        
            // Author
            if (commentEntryWrapper.getName() != null &&
                    ! commentEntryWrapper.getName().equals("")) {
                commentEntry.addAuthor(commentEntryWrapper.getName());
            }
            else {
                commentEntry.addAuthor("Anonymous");
            }
        
            commentEntry.addLink(entryWrapper.getPermalink() +
                    "#comment-" + commentEntryWrapper.getTimestamp(),
                    "alternate");
            commentEntry.setPublished(commentEntryWrapper.getPostTime());
            commentEntry.setUpdated(commentEntryWrapper.getPostTime());
            commentEntry.setContentAsHtml(commentEntryWrapper.getContent());
        
            // The important bit
            ThreadHelper.addInReplyTo(commentEntry, entry);
        }
    }
        
    return feed.toString();
    }
    */

    /**
     * Formats all entries and comments, including draft entries, in the
     * Moveable Type Import Format (mtimport). This format can be imported
     * into both Moveable Type and WordPress blogging platforms.
     *
     * @param entries A collection of entries to format.
     * @return A String of all entries and comments formatted as mtimport
     */
    private String formatAsMoveableType(List<WeblogEntryWrapper> entries) {
        StringBuilder result;
        result = new StringBuilder();

        for (WeblogEntryWrapper entry : entries) {
            // Author
            result.append("AUTHOR: ");
            result.append(entry.getCreator().getScreenName());
            result.append("\n");

            // Title
            result.append("TITLE: ");
            result.append(entry.getTitle());
            result.append("\n");

            // Date
            result.append("DATE: ");
            if (entry.getStatus().equals(WeblogEntry.PUBLISHED)) {
                result.append(MT_DATE_FORMAT.format(entry.getPubTime()));
            } else {
                result.append(MT_DATE_FORMAT.format(entry.getUpdateTime()));
            }
            result.append("\n");

            // Primary category
            result.append("PRIMARY CATEGORY: ");
            result.append(entry.getCategory().getName());
            result.append("\n");

            // Status
            result.append("STATUS: ");
            if (entry.getStatus().equals(WeblogEntry.PUBLISHED)) {
                result.append("publish");
            } else {
                result.append("draft");
            }
            result.append("\n");

            // Allow comments
            result.append("ALLOW COMMENTS: ");
            if (entry.getAllowComments()) {
                result.append("1");
            } else {
                result.append("0");
            }
            result.append("\n");

            result.append(MT_SECTION_DIVIDER);

            // Body
            // TODO: May want to use transformed text here
            result.append("BODY: \n");
            result.append(processEntry(entry.getText().trim()));
            result.append("\n");

            result.append(MT_SECTION_DIVIDER);

            // Excerpt
            if (entry.getSummary() != null && !entry.getSummary().equals("")) {
                // TODO: May want to use transformed summary here
                result.append("EXCERPT: \n");
                result.append(processEntry(entry.getSummary().trim()));
                result.append("\n");

                result.append(MT_SECTION_DIVIDER);
            }

            for (Object commentObj : entry.getComments()) {
                WeblogEntryCommentWrapper comment;
                comment = (WeblogEntryCommentWrapper) commentObj;
                result.append("COMMENT: \n");

                result.append("AUTHOR: ");
                result.append(comment.getName());
                result.append("\n");

                result.append("EMAIL: ");
                result.append(comment.getEmail());
                result.append("\n");

                result.append("URL: ");
                result.append(comment.getUrl());
                result.append("\n");

                result.append("DATE: ");
                result.append(MT_DATE_FORMAT.format(comment.getPostTime()));
                result.append("\n");

                result.append(comment.getContent());
                result.append("\n");

                result.append(MT_SECTION_DIVIDER);
            }

            result.append(MT_ENTRY_DIVIDER);
        }

        return result.toString();
    }

    /**
     * Performs some pre-processing of entry text. It fixes a problem when
     * WordPress imports a self-closing HTML tag that does not have a space
     * preceding the "/>" characters. It also provides a replacment base URL
     * for all referenced resource files if requested.
     *
     * @param text The entry text to process.
     * @return The resulting String after processing has taken place.
     */
    private String processEntry(String text) {
        String result;
        result = text;

        // Some special processing is needed for mtimport
        if (format.startsWith(MT_FORMAT)) {
            // Fix self closing tags that are missing a space,
            // replaceing <foo bar="foobar"/> with <foo bar="foobar" />
            Matcher badSelfClosingTagMatcher;
            badSelfClosingTagMatcher = SC_TAG_PATTERN.matcher(result);

            result = badSelfClosingTagMatcher.replaceAll("$2 />");

            if (format.equals(MT_PLUS_FORMAT)) {
                // Replace all newlines with spaces leaving "<pre>" blocks
                // alone. WordPress will automatically convert newlines to
                // "<br />" which alters the intended formatting.
                Matcher preTagMatcher;
                preTagMatcher = PRE_TAG_PATTERN.matcher(result);

                StringBuilder replacedNewLines;
                replacedNewLines = new StringBuilder();

                int index;
                index = 0;

                while (preTagMatcher.find()) {
                    replacedNewLines.append(NEWLINE_PATTERN.matcher(result.substring(index, preTagMatcher.start()))
                            .replaceAll(" "));
                    replacedNewLines.append(preTagMatcher.group());
                    index = preTagMatcher.end();
                }

                replacedNewLines
                        .append(NEWLINE_PATTERN.matcher(result.substring(index, result.length())).replaceAll(" "));

                result = replacedNewLines.toString();
            }
        }

        // Replace all /weblog-handle/resource/ links with a specified base URL
        if (baseUrl != null && !baseUrl.equals("")) {
            Matcher baseUrlMatcher;
            baseUrlMatcher = baseUrlPattern.matcher(result);

            try {
                result = baseUrlMatcher.replaceAll("$1" + baseUrl);
            } catch (IllegalArgumentException e) {
                log.error("Invalid base URL submitted: " + baseUrl + ": " + e.getMessage());
            }
        }

        return result;
    }

    /**
     * Adds all the non-directory files for the specified path to the provided
     * List.
     *
     * @param mfiles The List in which to add the resource objects.
     * @param mdir The path from which to load. If null, the root path is used.
     */
    private void loadResources(List<MediaFile> mfiles, MediaFileDirectory mdir) {
        try {
            // Load the non-directory files
            mfiles.addAll(mdir.getMediaFiles());
        } catch (Exception e) {
            log.error("Error loading resources: " + e.getMessage());
        }
    }
}