nl.tricode.magnolia.blogs.dialog.action.SaveWordpressBlogDialogAction.java Source code

Java tutorial

Introduction

Here is the source code for nl.tricode.magnolia.blogs.dialog.action.SaveWordpressBlogDialogAction.java

Source

/**
 *      Tricode Blog module
 *      Is a Blog module for Magnolia CMS.
 *      Copyright (C) 2015  Tricode Business Integrators B.V.
 *
 *      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 nl.tricode.magnolia.blogs.dialog.action;

import com.vaadin.data.Item;
import info.magnolia.cms.core.Path;
import info.magnolia.cms.util.QueryUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.jcr.util.NodeUtil;
import info.magnolia.ui.api.action.AbstractAction;
import info.magnolia.ui.api.action.ActionExecutionException;
import info.magnolia.ui.api.shell.Shell;
import info.magnolia.ui.form.EditorCallback;
import info.magnolia.ui.form.EditorValidator;
import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
import nl.tricode.magnolia.blogs.BlogsNodeTypes;
import nl.tricode.magnolia.blogs.util.BlogWorkspaceUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.xmlrpc.XmlRpcClient;
import org.apache.xmlrpc.XmlRpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Vector;

public class SaveWordpressBlogDialogAction extends AbstractAction<SaveWordpressBlogDialogActionDefinition> {
    private static final Logger log = LoggerFactory.getLogger(SaveWordpressBlogDialogAction.class);

    private static final String FIRSTNAME = "first_name";
    private static final String LASTNAME = "last_name";
    private static final String EMAIL = "email";
    private static final String URL = "url";

    private final Item item;
    private final EditorValidator validator;
    private final EditorCallback callback;
    private final Shell shell;
    private Session blogSession;
    private Session contactSession;
    private Session damSession;
    private XmlRpcClient client;
    private boolean shouldImportImages;
    private boolean shouldImportContacts;
    private HashMap<String, String> recentlyCreatedMagnoliaContacts;

    public SaveWordpressBlogDialogAction(SaveWordpressBlogDialogActionDefinition definition, Item item,
            EditorValidator validator, EditorCallback callback, Shell shell) throws ActionExecutionException {
        super(definition);
        this.item = item;
        this.validator = validator;
        this.callback = callback;
        this.shell = shell;
        this.shouldImportImages = (Boolean) item.getItemProperty("shouldImportImages").getValue();
        this.shouldImportContacts = (Boolean) item.getItemProperty("shouldImportContacts").getValue();
        this.validator.showValidation(true);

        if (this.validator.isValid()) {
            try {
                this.blogSession = MgnlContext.getJCRSession(BlogWorkspaceUtil.COLLABORATION);

                /** Only open contacts session when needed.*/
                if (this.shouldImportContacts) {
                    this.recentlyCreatedMagnoliaContacts = new HashMap<String, String>();
                    this.contactSession = MgnlContext.getJCRSession(BlogWorkspaceUtil.CONTACTS);
                }

                /** Only open DAM session when needed. */
                if (this.shouldImportImages) {
                    this.damSession = MgnlContext.getJCRSession(BlogWorkspaceUtil.DAM);
                }

                this.client = new XmlRpcClient((String) this.item.getItemProperty("endpoint").getValue());
            } catch (RepositoryException e) {
                log.error("Error getting session: " + e.getMessage(), e);
                shell.showError("Error getting session: " + e.getMessage(), e);
                callback.onCancel();
                throw new ActionExecutionException(e);
            } catch (MalformedURLException e) {
                log.error("Bad URL entered: " + e.getMessage(), e);
                shell.showError("Bad URL entered: " + e.getMessage(), e);
                callback.onCancel();
                throw new ActionExecutionException(e);
            }
        }
    }

    @Override
    public void execute() throws ActionExecutionException {
        if (validator.isValid()) {
            try {
                log.debug("Start wordPress import.");
                log.debug("Import into DAM [" + this.shouldImportImages + "].");
                log.debug("Import into Contacts [" + this.shouldImportContacts + "].");
                Vector<Hashtable<String, Object>> blog = getWordpressPosts();
                if (!blog.isEmpty()) {
                    log.debug("Blog size [" + blog.size() + "]");
                    for (Hashtable<String, Object> blogPost : blog) {
                        processPost(blogPost);
                    }
                    finishImport();
                } else {
                    shell.openNotification(MessageStyleTypeEnum.INFO, false, "No WordPress posts were found.");
                    callback.onCancel();
                }
            } catch (ActionExecutionException e) {
                cancelImport();
                throw new ActionExecutionException(e);
            }
        } else {
            log.error("Validation error(s). Import cancelled.");
        }
    }

    /**
     * Gets all the posts from WordPress for a given blog ID (obtained through the Magnolia dialog).
     *
     * @return all posts
     * @throws info.magnolia.ui.api.action.ActionExecutionException if an IOException or XmlRpcException occurs
     */
    private Vector<Hashtable<String, Object>> getWordpressPosts() throws ActionExecutionException {
        try {
            return (Vector<Hashtable<String, Object>>) client.execute("wp.getPosts", buildGetPostsRequest());
        } catch (IOException e) {
            log.error("Error while getting the blog posts from WordPress: " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        } catch (XmlRpcException e) {
            log.error("Error while getting the blog posts from WordPress: " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        }
    }

    /**
     * Builds the getPosts request for the WordPress XML-RPC call.
     *
     * @return the request
     */
    private Vector<Object> buildGetPostsRequest() {
        Integer blogID = Integer.valueOf((String) item.getItemProperty("blogID").getValue());
        String username = (String) item.getItemProperty("username").getValue();
        String password = (String) item.getItemProperty("password").getValue();

        Vector<Object> request = new Vector<Object>();
        Vector<String> expectedReturnValues = new Vector<String>();
        expectedReturnValues.addElement("post_content");
        expectedReturnValues.addElement("post_title");
        expectedReturnValues.addElement("post_author");
        expectedReturnValues.addElement("post_date");
        expectedReturnValues.addElement("post_modified");
        expectedReturnValues.addElement("post_name");
        expectedReturnValues.addElement("terms");

        Hashtable hashTable = new Hashtable();
        hashTable.put("number", 254);

        request.addElement(blogID);
        request.addElement(username);
        request.addElement(password);
        request.addElement(hashTable);
        request.addElement(expectedReturnValues);

        return request;
    }

    /**
     * Adds a blog post.
     *
     * @param blogPost The blog post in Hashtable format
     * @throws info.magnolia.ui.api.action.ActionExecutionException if a RepositoryException occurs
     */
    private void processPost(Hashtable<String, Object> blogPost) throws ActionExecutionException {
        String name = (String) blogPost.get("post_name");
        String title = (String) blogPost.get("post_title");
        Calendar date = Calendar.getInstance();
        date.setTime((Date) blogPost.get("post_date"));
        Calendar dateModified = Calendar.getInstance();
        dateModified.setTime((Date) blogPost.get("post_modified"));
        String message = (String) blogPost.get("post_content");

        /** Encoding for readable text in the blogpost. */
        title = encodeString(title);
        message = encodeString(message);

        try {
            if (shouldImportImages) {
                BlogPostImageImporter contentProcessor = new BlogPostImageImporter(message, damSession);
                message = contentProcessor.startImporting();
            }

            log.debug("Process blog [" + title + "].");
            Node blogPostNode = blogSession.getRootNode().addNode("temporaryBlogPostNodeName",
                    BlogsNodeTypes.Blog.NAME);
            blogPostNode.setProperty(BlogsNodeTypes.Blog.PROPERTY_TITLE, title);
            blogPostNode.setProperty(BlogsNodeTypes.Blog.PROPERTY_MESSAGE, message);

            if (shouldImportContacts) {
                String author = getMagnoliaContact((String) blogPost.get("post_author"));
                blogPostNode.setProperty(BlogsNodeTypes.Blog.PROPERTY_AUTHOR, author);
            }

            blogPostNode.setProperty(BlogsNodeTypes.Blog.PROPERTY_COMMENTS_ENABLED, true);
            blogPostNode.setProperty("mgnl:created", date);
            blogPostNode.setProperty("mgnl:lastModified", dateModified);

            if (name.isEmpty()) {
                NodeUtil.renameNode(blogPostNode,
                        BlogWorkspaceUtil.generateUniqueNodeName(blogPostNode, BlogsNodeTypes.Blog.PROPERTY_TITLE));
            } else {
                NodeUtil.renameNode(blogPostNode, name);
            }
        } catch (RepositoryException e) {
            log.error("Error while adding post: " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        }
    }

    /**
     * Encode strings to UTF-8.
     * @param stringToEncode
     * @return
     */
    private String encodeString(String stringToEncode) {
        String result = stringToEncode;
        try {
            result = new String(stringToEncode.getBytes(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("Error occurred when encoding [" + stringToEncode + "] ", e);
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Gets the magnolia contact reference for a given Wordpress User ID.
     * NOTE: If no existing contact is found, a new one will be created.
     *
     * @param wordpressUserID the WordPress user ID
     * @return UUID of the Magnolia contact
     * @throws info.magnolia.ui.api.action.ActionExecutionException if an XmlRpcException or IOException occurs
     */
    private String getMagnoliaContact(String wordpressUserID) throws ActionExecutionException {
        try {
            Hashtable<String, Object> author = (Hashtable<String, Object>) client.execute("wp.getUser",
                    buildGetUserRequest(wordpressUserID));
            String magnoliaContactReference = findMagnoliaContact((String) author.get("email"));
            if (magnoliaContactReference == null) {
                magnoliaContactReference = createMagnoliaContact(author);
            }
            return magnoliaContactReference;
        } catch (IOException e) {
            log.error("Error retrieving the author from WordPress " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        } catch (XmlRpcException e) {
            log.error("Error retrieving the author from WordPress " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        }
    }

    /**
     * Builds the getUser request for the WordPress XML-RPC call.
     *
     * @param wordpressUserID the user to get
     * @return the request
     */
    private Vector<Object> buildGetUserRequest(String wordpressUserID) {
        Vector<Object> request = new Vector<Object>();
        Vector<String> expectedReturnValues = new Vector<String>();
        expectedReturnValues.addElement(FIRSTNAME);
        expectedReturnValues.addElement(LASTNAME);
        expectedReturnValues.addElement(EMAIL);
        expectedReturnValues.addElement(URL);

        request.addElement(item.getItemProperty("blogID").getValue());
        request.addElement(item.getItemProperty("username").getValue());
        request.addElement(item.getItemProperty("password").getValue());
        request.addElement(wordpressUserID);
        request.addElement(expectedReturnValues);

        return request;
    }

    private String findRecentlyCreatedMagnoliaContact(String email) {
        if (recentlyCreatedMagnoliaContacts.containsKey(email)) {
            return recentlyCreatedMagnoliaContacts.get(email);
        } else {
            return null;
        }
    }

    /**
     * Searches for an existing Magnolia contact.
     * NOTE: Will return the first result only.
     *
     * @param email Used to match the contact
     * @return UUID of the Magnolia contact or null if no contact was found
     * @throws info.magnolia.ui.api.action.ActionExecutionException if a RepositoryException occurs
     */
    private String findMagnoliaContact(String email) throws ActionExecutionException {
        try {
            NodeIterator nodes = QueryUtil.search(BlogWorkspaceUtil.CONTACTS,
                    "SELECT * FROM [mgnl:contact] WHERE email  = '" + email + "'");
            if (nodes.hasNext()) {
                return nodes.nextNode().getIdentifier();
            } else {
                return findRecentlyCreatedMagnoliaContact(email);
            }
        } catch (RepositoryException e) {
            log.error("Error while searching for contact " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        }
    }

    /**
     * Creates a new Magnolia contact with a first name, last name, email address and website.
     *
     * @param contactDetails contact details
     * @return UUID of the Magnolia contact
     * @throws info.magnolia.ui.api.action.ActionExecutionException if a RepositoryException occurs
     */
    private String createMagnoliaContact(Hashtable<String, Object> contactDetails) throws ActionExecutionException {
        try {
            String firstname = (String) contactDetails.get(FIRSTNAME);
            String lastname = (String) contactDetails.get(LASTNAME);

            /** Encoded first and lastname to readable text. */
            firstname = encodeString(firstname);
            lastname = encodeString(lastname);

            Node newContactNode = contactSession.getRootNode().addNode("temporaryContactNodeName", "mgnl:contact");
            newContactNode.setProperty("firstName", firstname);
            newContactNode.setProperty("lastName", lastname);
            newContactNode.setProperty("email", (String) contactDetails.get(EMAIL));
            newContactNode.setProperty("website", (String) contactDetails.get(URL));
            NodeUtil.renameNode(newContactNode, generateUniqueNodeNameForContact(newContactNode));
            recentlyCreatedMagnoliaContacts.put((String) contactDetails.get(EMAIL), newContactNode.getIdentifier());
            return newContactNode.getIdentifier();
        } catch (RepositoryException e) {
            log.error("Error while creating contact " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        }
    }

    /**
     * Create a new Node Unique NodeName.
     */
    private String generateUniqueNodeNameForContact(final Node node) throws RepositoryException {
        String newNodeName = defineNodeName(node);
        return Path.getUniqueLabel(node.getSession(), node.getParent().getPath(), newNodeName);
    }

    /**
     * Define the Node Name. Node Name = First Char of the lastName + the full
     * firstName. lastName = eric firstName = tabli The node name is etabli
     */
    private String defineNodeName(final Node node) throws RepositoryException {
        String result;
        String intitialFirstName = node.getProperty("firstName").getString();
        String firstName = StringUtils.isNotBlank(intitialFirstName) ? intitialFirstName.trim() : intitialFirstName;

        if (StringUtils.isNotBlank(firstName)) {
            String lastName = node.getProperty("lastName").getString().trim();
            result = Path.getValidatedLabel((firstName.charAt(0) + lastName.replaceAll("\\s+", "")).toLowerCase());
        } else {
            log.debug("Incomplete wordpress userdetails");
            result = "Anonymous";
        }
        return result;
    }

    /**
     * Persists the changes made to the repository and closes the dialog.
     *
     * @throws info.magnolia.ui.api.action.ActionExecutionException if a RepositoryException occurs
     */
    private void finishImport() throws ActionExecutionException {
        try {
            blogSession.save();

            if (shouldImportContacts) {
                contactSession.save();
            }

            if (shouldImportImages) {
                damSession.save();
            }
            BlogPostImageImporter.cleanRecentUrlList();
            callback.onSuccess(getDefinition().getName());
            shell.openNotification(MessageStyleTypeEnum.INFO, true, "Import completed successfully.");
        } catch (RepositoryException e) {
            log.error("Error while saving sessions: " + e.getMessage(), e);
            throw new ActionExecutionException(e);
        }
    }

    /**
     * Will discard all changes made to the repository and closes the dialog.
     *
     * @throws info.magnolia.ui.api.action.ActionExecutionException if a RepositoryException occurs
     */
    private void cancelImport() throws ActionExecutionException {
        try {
            blogSession.refresh(false);

            if (shouldImportContacts) {
                contactSession.refresh(false);
            }

            if (shouldImportImages) {
                damSession.refresh(false);
            }
            BlogPostImageImporter.cleanRecentUrlList();
            callback.onCancel();
        } catch (RepositoryException e) {
            log.error("Error while discarding changes", e);
            throw new ActionExecutionException(e);
        }
    }
}