From 148d1b58828176e806ff10e88d9a75dd0c0134ce Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Fri, 26 Jan 2018 15:38:07 +0100
Subject: [PATCH] api allows to give remove reason

---
 .../lcsb/mapviewer/api/BaseController.java    | 112 ++++---
 .../projects/comments/CommentController.java  | 304 +++++++++---------
 .../projects/comments/CommentRestImpl.java    |   7 +-
 .../services/impl/CommentService.java         |   3 +-
 .../services/interfaces/ICommentService.java  |   2 +-
 5 files changed, 225 insertions(+), 203 deletions(-)

diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java b/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java
index e210ca049b..7e9ca40a76 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/BaseController.java
@@ -23,62 +23,70 @@ import lcsb.mapviewer.common.Configuration;
 import lcsb.mapviewer.common.exception.InvalidStateException;
 
 public abstract class BaseController {
-	private Logger			 logger	= Logger.getLogger(BaseController.class);
+  private Logger logger = Logger.getLogger(BaseController.class);
 
-	private ObjectMapper mapper	= new ObjectMapper();
+  private ObjectMapper mapper = new ObjectMapper();
 
-	@ExceptionHandler({ Exception.class })
-	public ResponseEntity<Object> handleException(Exception e, WebRequest request) {
-		if (e instanceof lcsb.mapviewer.services.SecurityException) {
-			return new ResponseEntity<Object>("{\"error\" : \"Access denied.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(), HttpStatus.FORBIDDEN);
-		} else if (e instanceof ObjectNotFoundException) {
-			return new ResponseEntity<Object>("{\"error\" : \"Object not found.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(), HttpStatus.NOT_FOUND);
-		} else if (e instanceof ObjectExistsException) {
-			return new ResponseEntity<Object>(
-					"{\"error\" : \"Object already exists.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(), HttpStatus.CONFLICT);
-		} else if (e instanceof QueryException) {
-			logger.error(e, e);
-			return new ResponseEntity<Object>(
-					"{\"error\" : \"Query server error.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(), HttpStatus.BAD_REQUEST);
-		} else if (e instanceof ServletRequestBindingException && e.getMessage().indexOf(Configuration.AUTH_TOKEN) >= 0) {
-			return new ResponseEntity<Object>("{\"error\" : \"Access denied.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(), HttpStatus.FORBIDDEN);
-		} else {
-			logger.error(e, e);
-			return new ResponseEntity<Object>(
-					"{\"error\" : \"Internal server error.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
-		}
-	}
+  @ExceptionHandler({ Exception.class })
+  public ResponseEntity<Object> handleException(Exception e, WebRequest request) {
+    if (e instanceof lcsb.mapviewer.services.SecurityException) {
+      return new ResponseEntity<Object>("{\"error\" : \"Access denied.\",\"reason\":\"" + e.getMessage() + "\"}",
+          new HttpHeaders(), HttpStatus.FORBIDDEN);
+    } else if (e instanceof ObjectNotFoundException) {
+      return new ResponseEntity<Object>("{\"error\" : \"Object not found.\",\"reason\":\"" + e.getMessage() + "\"}",
+          new HttpHeaders(), HttpStatus.NOT_FOUND);
+    } else if (e instanceof ObjectExistsException) {
+      return new ResponseEntity<Object>(
+          "{\"error\" : \"Object already exists.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(),
+          HttpStatus.CONFLICT);
+    } else if (e instanceof QueryException) {
+      logger.error(e, e);
+      return new ResponseEntity<Object>("{\"error\" : \"Query server error.\",\"reason\":\"" + e.getMessage() + "\"}",
+          new HttpHeaders(), HttpStatus.BAD_REQUEST);
+    } else if (e instanceof ServletRequestBindingException && e.getMessage().indexOf(Configuration.AUTH_TOKEN) >= 0) {
+      return new ResponseEntity<Object>("{\"error\" : \"Access denied.\",\"reason\":\"" + e.getMessage() + "\"}",
+          new HttpHeaders(), HttpStatus.FORBIDDEN);
+    } else {
+      logger.error(e, e);
+      return new ResponseEntity<Object>(
+          "{\"error\" : \"Internal server error.\",\"reason\":\"" + e.getMessage() + "\"}", new HttpHeaders(),
+          HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+  }
 
-	public Map<String, Object> parseBody(String body) throws IOException, JsonParseException, JsonMappingException {
-		ObjectNode result = mapper.readValue(body, ObjectNode.class);
-		return mapper.convertValue(result, Map.class);
-	}
+  public Map<String, Object> parseBody(String body) throws IOException, JsonParseException, JsonMappingException {
+    if (body == null || body.isEmpty()) {
+      return new HashMap<>();
+    }
+    ObjectNode result = mapper.readValue(body, ObjectNode.class);
+    return mapper.convertValue(result, Map.class);
+  }
 
-	protected Map<String, Object> getData(Map<String, Object> node, String objectName) {
-		return (Map<String, Object>) node.get(objectName);
-	}
+  protected Map<String, Object> getData(Map<String, Object> node, String objectName) {
+    return (Map<String, Object>) node.get(objectName);
+  }
 
-	protected Map<String, String> parsePostBody(String body) {
-		Map<String, String> result = new HashMap<>();
-		String[] parameters = body.split("&");
-		for (String string : parameters) {
-			int position = string.indexOf("=");
-			if (position < 0) {
-				position = string.length();
-			}
-			String key = string.substring(0, position);
-			String value = null;
-			if (position < string.length()) {
-				value = string.substring(position + 1, string.length());
-				try {
-					value = URLDecoder.decode(value, "UTF-8");
-				} catch (UnsupportedEncodingException e) {
-					throw new InvalidStateException("Cannot decode input", e);
-				}
-			}
-			result.put(key, value);
-		}
-		return result;
-	}
+  protected Map<String, String> parsePostBody(String body) {
+    Map<String, String> result = new HashMap<>();
+    String[] parameters = body.split("&");
+    for (String string : parameters) {
+      int position = string.indexOf("=");
+      if (position < 0) {
+        position = string.length();
+      }
+      String key = string.substring(0, position);
+      String value = null;
+      if (position < string.length()) {
+        value = string.substring(position + 1, string.length());
+        try {
+          value = URLDecoder.decode(value, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+          throw new InvalidStateException("Cannot decode input", e);
+        }
+      }
+      result.put(key, value);
+    }
+    return result;
+  }
 
 }
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentController.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentController.java
index 2617a3c3f8..c941446e66 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentController.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentController.java
@@ -1,6 +1,7 @@
 package lcsb.mapviewer.api.projects.comments;
 
 import java.awt.geom.Point2D;
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -8,11 +9,15 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.CookieValue;
 import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
 import lcsb.mapviewer.api.BaseController;
 import lcsb.mapviewer.api.QueryException;
 import lcsb.mapviewer.common.Configuration;
@@ -22,152 +27,157 @@ import lcsb.mapviewer.services.search.data.ElementIdentifier.ElementIdentifierTy
 @RestController
 public class CommentController extends BaseController {
 
-	@Autowired
-	private CommentRestImpl commentController;
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public List<Map<String, Object>> getComments(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@RequestParam(value = "columns", defaultValue = "") String columns, //
-			@RequestParam(value = "removed", defaultValue = "") String removed //
-	) throws SecurityException, QueryException {
-		return commentController.getCommentList(token, projectId, columns, "", "", removed);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/{commentId}/", method = { RequestMethod.DELETE }, produces = { MediaType.APPLICATION_JSON_VALUE })
-	public Map<String, Object> removeComment(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@PathVariable(value = "commentId") String commentId //
-	) throws SecurityException, QueryException {
-		return commentController.removeComment(token, projectId, commentId);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/reactions/{reactionId}", method = { RequestMethod.GET },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public List<Map<String, Object>> getCommentsByReaction(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@RequestParam(value = "columns", defaultValue = "") String columns, //
-			@PathVariable(value = "reactionId") String reactionId, //
-			@RequestParam(value = "removed", defaultValue = "") String removed //
-	) throws SecurityException, QueryException {
-		return commentController.getCommentList(token, projectId, columns, reactionId, ElementIdentifierType.REACTION.getJsName(), removed);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/elements/{elementId}", method = { RequestMethod.GET },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public List<Map<String, Object>> getCommentsByElement(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@RequestParam(value = "columns", defaultValue = "") String columns, //
-			@PathVariable(value = "elementId") String elementId, //
-			@RequestParam(value = "removed", defaultValue = "") String removed //
-	) throws SecurityException, QueryException {
-		return commentController.getCommentList(token, projectId, columns, elementId, ElementIdentifierType.ALIAS.getJsName(), removed);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/points/{coordinates:.+}", method = { RequestMethod.GET },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public List<Map<String, Object>> getCommentsByPoint(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@RequestParam(value = "columns", defaultValue = "") String columns, //
-			@PathVariable(value = "coordinates") String coordinates, //
-			@RequestParam(value = "removed", defaultValue = "") String removed //
-	) throws SecurityException, QueryException {
-		return commentController.getCommentList(token, projectId, columns, coordinates, ElementIdentifierType.POINT.getJsName(), removed);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/elements/{elementId}", method = { RequestMethod.POST },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public Map<String, Object> addCommentForElement(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@PathVariable(value = "elementId") String elementId, //
-			@RequestParam(value = "name") String name, //
-			@RequestParam(value = "email") String email, //
-			@RequestParam(value = "content") String content, //
-			@RequestParam(value = "pinned", defaultValue = "true") String pinned, //
-			@RequestParam(value = "coordinates") String coordinates, //
-			@PathVariable(value = "modelId") String modelId //
-	) throws SecurityException, QueryException {
-		Point2D pointCoordinates = parseCoordinates(coordinates);
-		return commentController.addComment(
-				token, projectId, ElementIdentifierType.ALIAS.getJsName(), elementId, name, email, content, pinned.toLowerCase().equals("true"), pointCoordinates,
-				modelId);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/reactions/{reactionId}", method = { RequestMethod.POST },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public Map<String, Object> addCommentForReaction(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@PathVariable(value = "reactionId") String reactionId, //
-			@RequestParam(value = "name") String name, //
-			@RequestParam(value = "email") String email, //
-			@RequestParam(value = "content") String content, //
-			@RequestParam(value = "pinned", defaultValue = "true") String pinned, //
-			@RequestParam(value = "coordinates") String coordinates, //
-			@PathVariable(value = "modelId") String modelId //
-	) throws SecurityException, QueryException {
-		Point2D pointCoordinates = parseCoordinates(coordinates);
-		return commentController.addComment(
-				token, projectId, ElementIdentifierType.REACTION.getJsName(), reactionId, name, email, content, pinned.toLowerCase().equals("true"), pointCoordinates,
-				modelId);
-	}
-
-	@RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/points/{coordinates}", method = { RequestMethod.POST },
-			produces = { MediaType.APPLICATION_JSON_VALUE })
-	public Map<String, Object> addCommentForPoint(//
-			@CookieValue(value = Configuration.AUTH_TOKEN) String token, //
-			@PathVariable(value = "projectId") String projectId, //
-			@RequestParam(value = "name") String name, //
-			@RequestParam(value = "email") String email, //
-			@RequestParam(value = "content") String content, //
-			@RequestParam(value = "pinned", defaultValue = "true") String pinned, //
-			@PathVariable(value = "coordinates") String coordinates, //
-			@PathVariable(value = "modelId") String modelId //
-	) throws SecurityException, QueryException {
-		Point2D pointCoordinates = parseCoordinates(coordinates);
-		return commentController.addComment(
-				token, projectId, ElementIdentifierType.POINT.getJsName(), coordinates, name, email, content, pinned.toLowerCase().equals("true"), pointCoordinates,
-				modelId);
-	}
-
-	private Point2D parseCoordinates(String coordinates) throws QueryException {
-		String[] tmp = coordinates.split(",");
-		if (tmp.length != 2) {
-			throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'");
-		}
-		Double x = null;
-		Double y = null;
-		try {
-			x = Double.valueOf(tmp[0]);
-			y = Double.valueOf(tmp[1]);
-		} catch (NumberFormatException e) {
-			throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'", e);
-		}
-		Point2D pointCoordinates = new Point2D.Double(x, y);
-		return pointCoordinates;
-	}
-
-	/**
-	 * @return the overlayController
-	 * @see #commentController
-	 */
-	public CommentRestImpl getOverlayController() {
-		return commentController;
-	}
-
-	/**
-	 * @param overlayController
-	 *          the overlayController to set
-	 * @see #commentController
-	 */
-	public void setOverlayController(CommentRestImpl overlayController) {
-		this.commentController = overlayController;
-	}
+  @Autowired
+  private CommentRestImpl commentController;
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/", method = {
+      RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public List<Map<String, Object>> getComments(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @RequestParam(value = "columns", defaultValue = "") String columns, //
+      @RequestParam(value = "removed", defaultValue = "") String removed //
+  ) throws SecurityException, QueryException {
+    return commentController.getCommentList(token, projectId, columns, "", "", removed);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/{commentId}/", method = { RequestMethod.DELETE }, produces = {
+      MediaType.APPLICATION_JSON_VALUE })
+  public Map<String, Object> removeComment(//
+      @RequestBody(required = false) String body, //
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @PathVariable(value = "commentId") String commentId //
+  ) throws SecurityException, QueryException, JsonParseException, JsonMappingException, IOException {
+    Map<String, Object> node = parseBody(body);
+    String reason = (String) node.get("reason");
+    return commentController.removeComment(token, projectId, commentId, reason);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/reactions/{reactionId}", method = {
+      RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public List<Map<String, Object>> getCommentsByReaction(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @RequestParam(value = "columns", defaultValue = "") String columns, //
+      @PathVariable(value = "reactionId") String reactionId, //
+      @RequestParam(value = "removed", defaultValue = "") String removed //
+  ) throws SecurityException, QueryException {
+    return commentController.getCommentList(token, projectId, columns, reactionId,
+        ElementIdentifierType.REACTION.getJsName(), removed);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/elements/{elementId}", method = {
+      RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public List<Map<String, Object>> getCommentsByElement(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @RequestParam(value = "columns", defaultValue = "") String columns, //
+      @PathVariable(value = "elementId") String elementId, //
+      @RequestParam(value = "removed", defaultValue = "") String removed //
+  ) throws SecurityException, QueryException {
+    return commentController.getCommentList(token, projectId, columns, elementId,
+        ElementIdentifierType.ALIAS.getJsName(), removed);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/points/{coordinates:.+}", method = {
+      RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public List<Map<String, Object>> getCommentsByPoint(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @RequestParam(value = "columns", defaultValue = "") String columns, //
+      @PathVariable(value = "coordinates") String coordinates, //
+      @RequestParam(value = "removed", defaultValue = "") String removed //
+  ) throws SecurityException, QueryException {
+    return commentController.getCommentList(token, projectId, columns, coordinates,
+        ElementIdentifierType.POINT.getJsName(), removed);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/elements/{elementId}", method = {
+      RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public Map<String, Object> addCommentForElement(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @PathVariable(value = "elementId") String elementId, //
+      @RequestParam(value = "name") String name, //
+      @RequestParam(value = "email") String email, //
+      @RequestParam(value = "content") String content, //
+      @RequestParam(value = "pinned", defaultValue = "true") String pinned, //
+      @RequestParam(value = "coordinates") String coordinates, //
+      @PathVariable(value = "modelId") String modelId //
+  ) throws SecurityException, QueryException {
+    Point2D pointCoordinates = parseCoordinates(coordinates);
+    return commentController.addComment(token, projectId, ElementIdentifierType.ALIAS.getJsName(), elementId, name,
+        email, content, pinned.toLowerCase().equals("true"), pointCoordinates, modelId);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/bioEntities/reactions/{reactionId}", method = {
+      RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public Map<String, Object> addCommentForReaction(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @PathVariable(value = "reactionId") String reactionId, //
+      @RequestParam(value = "name") String name, //
+      @RequestParam(value = "email") String email, //
+      @RequestParam(value = "content") String content, //
+      @RequestParam(value = "pinned", defaultValue = "true") String pinned, //
+      @RequestParam(value = "coordinates") String coordinates, //
+      @PathVariable(value = "modelId") String modelId //
+  ) throws SecurityException, QueryException {
+    Point2D pointCoordinates = parseCoordinates(coordinates);
+    return commentController.addComment(token, projectId, ElementIdentifierType.REACTION.getJsName(), reactionId, name,
+        email, content, pinned.toLowerCase().equals("true"), pointCoordinates, modelId);
+  }
+
+  @RequestMapping(value = "/projects/{projectId}/comments/models/{modelId}/points/{coordinates}", method = {
+      RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE })
+  public Map<String, Object> addCommentForPoint(//
+      @CookieValue(value = Configuration.AUTH_TOKEN) String token, //
+      @PathVariable(value = "projectId") String projectId, //
+      @RequestParam(value = "name") String name, //
+      @RequestParam(value = "email") String email, //
+      @RequestParam(value = "content") String content, //
+      @RequestParam(value = "pinned", defaultValue = "true") String pinned, //
+      @PathVariable(value = "coordinates") String coordinates, //
+      @PathVariable(value = "modelId") String modelId //
+  ) throws SecurityException, QueryException {
+    Point2D pointCoordinates = parseCoordinates(coordinates);
+    return commentController.addComment(token, projectId, ElementIdentifierType.POINT.getJsName(), coordinates, name,
+        email, content, pinned.toLowerCase().equals("true"), pointCoordinates, modelId);
+  }
+
+  private Point2D parseCoordinates(String coordinates) throws QueryException {
+    String[] tmp = coordinates.split(",");
+    if (tmp.length != 2) {
+      throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'");
+    }
+    Double x = null;
+    Double y = null;
+    try {
+      x = Double.valueOf(tmp[0]);
+      y = Double.valueOf(tmp[1]);
+    } catch (NumberFormatException e) {
+      throw new QueryException("Coordinates must be in the format: 'xxx.xx,yyy.yy'", e);
+    }
+    Point2D pointCoordinates = new Point2D.Double(x, y);
+    return pointCoordinates;
+  }
+
+  /**
+   * @return the overlayController
+   * @see #commentController
+   */
+  public CommentRestImpl getOverlayController() {
+    return commentController;
+  }
+
+  /**
+   * @param overlayController
+   *          the overlayController to set
+   * @see #commentController
+   */
+  public void setOverlayController(CommentRestImpl overlayController) {
+    this.commentController = overlayController;
+  }
 
 }
\ No newline at end of file
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentRestImpl.java
index 192efa5d6d..b35bf06117 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentRestImpl.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/comments/CommentRestImpl.java
@@ -130,6 +130,8 @@ public class CommentRestImpl extends BaseRestImpl {
         value = comment.isDeleted();
       } else if (column.equals("coord")) {
         value = getCoordinates(comment);
+      } else if (column.equals("removereason")) {
+        value = comment.getRemoveReason();
       } else if (column.equals("type")) {
         value = getType(comment);
       } else if (column.equals("icon")) {
@@ -240,6 +242,7 @@ public class CommentRestImpl extends BaseRestImpl {
         columnsSet.add("pinned");
         columnsSet.add("author");
         columnsSet.add("email");
+        columnsSet.add("removeReason");
       }
     } else {
       for (String str : columns.split(",")) {
@@ -328,7 +331,7 @@ public class CommentRestImpl extends BaseRestImpl {
     return preparedComment(comment, createCommentColumnSet("", isAdmin), isAdmin);
   }
 
-  public Map<String, Object> removeComment(String token, String projectId, String commentId)
+  public Map<String, Object> removeComment(String token, String projectId, String commentId, String reason)
       throws SecurityException, QueryException {
     AuthenticationToken authenticationToken = getUserService().getToken(token);
     Project project = getProjectService().getProjectByProjectId(projectId, authenticationToken);
@@ -340,7 +343,7 @@ public class CommentRestImpl extends BaseRestImpl {
       throw new ObjectNotFoundException("Comment with given id doesn't exist");
     }
 
-    commentService.deleteComment(comment, authenticationToken);
+    commentService.deleteComment(comment, authenticationToken, reason);
     return okStatus();
   }
 
diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java b/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java
index 08f6daf43d..faf8f766ae 100644
--- a/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java
+++ b/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java
@@ -426,11 +426,12 @@ public class CommentService implements ICommentService {
   }
 
   @Override
-  public void deleteComment(Comment comment, AuthenticationToken token) throws UserAccessException {
+  public void deleteComment(Comment comment, AuthenticationToken token, String reason) throws UserAccessException {
     Project project = comment.getModelData().getProject();
     boolean editComments = userService.userHasPrivilege(token, PrivilegeType.EDIT_COMMENTS_PROJECT, project);
     if (editComments || userService.getUserByToken(token).equals(comment.getUser())) {
       comment.setDeleted(true);
+      comment.setRemoveReason(reason);
       commentDao.update(comment);
     } else {
       throw new UserAccessException("You have no privileges to remove the comment");
diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java
index 06d32a16cc..dd3db2c6be 100644
--- a/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java
+++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java
@@ -60,7 +60,7 @@ public interface ICommentService {
 	 */
 	void deleteComment(User loggedUser, String commentId, String reason);
 	
-	void deleteComment(Comment comment, AuthenticationToken token) throws UserAccessException;
+	void deleteComment(Comment comment, AuthenticationToken token, String reason) throws UserAccessException;
 
 	/**
 	 * Method returns all comments for a given map.
-- 
GitLab