net.riezebos.thoth.commands.CommentCommand.java Source code

Java tutorial

Introduction

Here is the source code for net.riezebos.thoth.commands.CommentCommand.java

Source

/* Copyright (c) 2016 W.T.J. Riezebos
 *
 * Licensed 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.
 */
package net.riezebos.thoth.commands;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;

import org.apache.commons.lang3.StringUtils;
import org.parboiled.Parboiled;
import org.pegdown.LinkRenderer;
import org.pegdown.Parser;
import org.pegdown.RelaxedParser;
import org.pegdown.ast.RootNode;
import org.pegdown.plugins.PegDownPlugins;

import net.riezebos.thoth.beans.MarkDownDocument;
import net.riezebos.thoth.configuration.Configuration;
import net.riezebos.thoth.configuration.ThothEnvironment;
import net.riezebos.thoth.content.ContentManager;
import net.riezebos.thoth.content.comments.Comment;
import net.riezebos.thoth.content.comments.CommentManager;
import net.riezebos.thoth.content.sections.Section;
import net.riezebos.thoth.content.skinning.Skin;
import net.riezebos.thoth.exceptions.ContentManagerException;
import net.riezebos.thoth.exceptions.ContextNotFoundException;
import net.riezebos.thoth.exceptions.RenderException;
import net.riezebos.thoth.renderers.RenderResult;
import net.riezebos.thoth.renderers.RendererBase;
import net.riezebos.thoth.renderers.RendererProvider;
import net.riezebos.thoth.renderers.util.CustomHtmlSerializer;
import net.riezebos.thoth.user.Identity;
import net.riezebos.thoth.user.Permission;
import net.riezebos.thoth.util.ThothUtil;

public class CommentCommand extends RendererBase implements Command {

    public static final String TYPE_CODE = "comment";

    public static final String CREATE = "create";
    public static final String COPY = "copy";
    public static final String DELETE = "delete";
    public static final String DOCPATH_ARGUMENT = "docpath";
    public static final String COMMENTTEXT_ARGUMENT = "commenttext";
    public static final String COMMENTID_ARGUMENT = "commentid";
    public static final String EDIT_TEXT = "editText";

    private static final String MARKER = "@@@";
    private static final String DETAILSTART = MARKER + "detailstart" + MARKER;
    private static final String DETAILEND = MARKER + "detailend" + MARKER;

    public CommentCommand(ThothEnvironment thothEnvironment, RendererProvider rendererProvider) {
        super(thothEnvironment, rendererProvider);
    }

    @Override
    public String getTypeCode() {
        return TYPE_CODE;
    }

    @Override
    public RenderResult execute(Identity identity, String contextName, String path, CommandOperation operation,
            Map<String, Object> arguments, Skin skin, OutputStream outputStream) throws RenderException {

        try {
            RenderResult result = RenderResult.OK;
            ContentManager contentManager = getContentManager(contextName);
            if (!contentManager.getAccessManager().hasPermission(identity, path, Permission.COMMENT))
                return RenderResult.FORBIDDEN;

            String operationCode = (String) arguments.get(OPERATION_ARGUMENT);
            if (StringUtils.isNotBlank(operationCode))
                result = handleOperation(operationCode, identity, contextName, path, arguments, skin, outputStream,
                        contentManager);
            else
                result = handleRender(identity, contextName, path, arguments, skin, outputStream, contentManager);

            return result;
        } catch (Exception e) {
            throw new RenderException(e);
        }
    }

    protected RenderResult handleRender(Identity identity, String contextName, String path,
            Map<String, Object> arguments, Skin skin, OutputStream outputStream, ContentManager contentManager)
            throws IOException, ContextNotFoundException, ContentManagerException, ServletException,
            UnsupportedEncodingException {
        MarkDownDocument markDownDocument = contentManager.getMarkDownDocument(path, suppressErrors(arguments),
                getCriticProcessingMode(arguments));
        String markdownSource = annotateSections(markDownDocument.getMarkdown());

        Configuration configuration = getConfiguration();
        int extensions = configuration.getMarkdownOptions();
        long parseTimeOut = configuration.getParseTimeOut();
        RelaxedParser parser = Parboiled.createParser(RelaxedParser.class, extensions, parseTimeOut,
                Parser.DefaultParseRunnerProvider, PegDownPlugins.NONE);
        RootNode ast = parser.parse(ThothUtil.wrapWithNewLines(markdownSource.toCharArray()));

        CustomHtmlSerializer serializer = new CustomHtmlSerializer(new LinkRenderer());
        String body = serializer.toHtml(ast);

        Section section = parseSections(body, contextName, markDownDocument.getPath());

        Map<String, String> metatags = markDownDocument.getMetatags();
        List<String> metaTagKeys = new ArrayList<>(metatags.keySet());
        Collections.sort(metaTagKeys);

        Map<String, Object> variables = new HashMap<>(arguments);
        variables.put("document", markDownDocument);
        variables.put("metatagKeys", metaTagKeys);
        variables.put("metatags", metatags);
        variables.put("mainsection", section);

        render(skin.getSkinBaseFolder(), skin.getCommentTemplate(), contextName, arguments, variables,
                outputStream);
        return RenderResult.OK;
    }

    protected RenderResult handleOperation(String operationCode, Identity identity, String contextName, String path,
            Map<String, Object> arguments, Skin skin, OutputStream outputStream, ContentManager contentManager)
            throws ContextNotFoundException, UnsupportedEncodingException, IOException, ContentManagerException,
            ServletException {

        RenderResult result = RenderResult.OK;

        switch (operationCode) {
        case CREATE:
            result = createComment(identity, contextName, path, arguments, contentManager);
            break;
        case DELETE:
            result = deleteComment(identity, path, arguments, contentManager);
            break;
        case COPY:
            result = copyComment(identity, path, arguments, contentManager);
            break;
        default:
            break;
        }

        if (!result.equals(RenderResult.OK) || silent(arguments))
            return result;
        else
            return handleRender(identity, contextName, path, arguments, skin, outputStream, contentManager);
    }

    protected RenderResult createComment(Identity identity, String contextName, String path,
            Map<String, Object> arguments, ContentManager contentManager) throws ContentManagerException {
        String text = (String) arguments.get(COMMENTTEXT_ARGUMENT);
        String docpath = (String) arguments.get(DOCPATH_ARGUMENT);
        if (!StringUtils.isBlank(text) && !StringUtils.isBlank(docpath)) {
            CommentManager commentManager = getThothEnvironment().getCommentManager();
            Comment comment = new Comment();
            comment.setContextName(contextName);
            comment.setDocumentPath(docpath);
            comment.setUserName(identity.getIdentifier());
            comment.setBody(text);
            commentManager.createComment(comment);
        }
        return RenderResult.OK;
    }

    protected RenderResult deleteComment(Identity identity, String path, Map<String, Object> arguments,
            ContentManager contentManager) throws ContentManagerException {

        String text = (String) arguments.get(COMMENTID_ARGUMENT);
        if (!StringUtils.isBlank(text)) {
            long id = Long.parseLong(text);
            CommentManager commentManager = getThothEnvironment().getCommentManager();
            Comment comment = commentManager.getComment(id);
            if (comment != null) {
                boolean isCreator = comment != null && comment.getUserName().equals(identity.getIdentifier());

                if (isCreator || contentManager.getAccessManager().hasPermission(identity, path,
                        Permission.DELETE_ANY_COMMENT))
                    commentManager.deleteComment(comment);
                else
                    return RenderResult.FORBIDDEN;
            }
        }
        return RenderResult.OK;
    }

    protected RenderResult copyComment(Identity identity, String path, Map<String, Object> arguments,
            ContentManager contentManager) throws ContentManagerException {

        String text = (String) arguments.get(COMMENTID_ARGUMENT);
        if (!StringUtils.isBlank(text)) {
            long id = Long.parseLong(text);
            CommentManager commentManager = getThothEnvironment().getCommentManager();
            Comment comment = commentManager.getComment(id);
            boolean isCreator = comment != null && comment.getUserName().equals(identity.getIdentifier());

            if (isCreator || contentManager.getAccessManager().hasPermission(identity, path,
                    Permission.EDIT_ANY_COMMENT)) {
                String body = comment.getBody();
                arguments.put(EDIT_TEXT, body);
            } else
                return RenderResult.FORBIDDEN;
        }
        return RenderResult.OK;
    }

    protected Section parseSections(String body, String contextName, String fileName)
            throws ContentManagerException {
        CommentManager commentManager = getThothEnvironment().getCommentManager();
        Pattern sectionStartPattern = Pattern.compile(DETAILSTART + "(.*?)" + MARKER);
        Pattern sectionEndPattern = Pattern.compile(DETAILEND);

        Stack<Section> sections = new Stack<>();
        Section main = new Section(fileName);
        main.setComments(commentManager.getComments(contextName, fileName, null));
        sections.push(main);

        for (String line : body.split("\n")) {
            Matcher matcher = sectionStartPattern.matcher(line);
            if (matcher.find()) {
                String path = matcher.group(1);
                Section subSection = new Section(path);
                List<Comment> comments = commentManager.getComments(contextName, path, null);
                subSection.setComments(comments);
                sections.peek().addSection(subSection);
                sections.push(subSection);
            } else if (sectionEndPattern.matcher(line).find()) {
                sections.pop();
            } else
                sections.peek().addSection(line);
        }

        return main;
    }

    protected String annotateSections(String markdown) {
        String result = markdown.replaceAll("\\[//\\]\\: \\# \"Include begin\\: ([^\"]*)\"",
                DETAILSTART + "$1" + MARKER + "\n");
        result = result.replaceAll("\\[//\\]\\: \\# \"Include end\\: .*?\"", DETAILEND + "\n");
        return result;
    }

}