Commenting example JSP webpage
The following sample JSP code is a simple example of a webpage featuring the display of, and forms for submitting, comments. Also included is the CSS stylesheet that defines its styles.
The JSP file is as follows:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="com.sdl.delivery.ugc.client.comment.Ugcapi" %>
<%@ page import="com.sdl.delivery.ugc.client.comment.impl.DefaultUgcapi" %>
<%@ page import="com.sdl.delivery.ugc.client.odata.edm.Comment" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.net.URI" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="static com.tridion.ambientdata.AmbientDataContext.getCurrentClaimStore" %>
<%@ page import="static java.util.Arrays.asList" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Map" %>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!-- This sample JSP page demonstrates the following UGC functionality:
-Displaying comments
-Controls to add a new comment
-Controls to edit or remove a comment submitted by the person accessing the Web page.
Notes:
-Internet Explorer is not supported
-Code is based on Bootstrap, JQuery, and ES6
-->
<head>
<title>Sample UGC demo page</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1'>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!-- Specify your own personal CSS stylesheet for comments -->
<link rel="stylesheet" href="comments-example.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jqeury.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- Specify your own personal JavaScript form for submitting comments -->
<script src="comments-form.js"></script>
</head>
<body>
<%
String cmuri = "tcm:1-1-64";
getCurrentClaimStore().clearReadOnly();
URI userURI = URI.create("taf:claim:contentdelivery:webservice:user");
String currentUserId = (String) getCurrentClaimStore().get(userURI);
UgcCommentApi api = new DefaultUgcCommentApi();
// Submitting a new or modified comment
if (request.getParameter("submitComment") != null) {
String username = request.getParameter("username");
String email = request.getParameter("email");
String comment = request.getParameter("comment");
// If this is a modified existing comment, get the ID of the comment being modified.
int editCommentId = 0;
if (request.getParameter("edit-comment-id") != null) {
editCommentId = Integer.ParseInt(request.getParameter("edit-comment-id"));
}
// If this is a (new) reply to another comment, get the ID of the comment being replied to.
int replyCommentId = 0;
if (request.getParameter("reply-comment-id") != null) {
replyCommentId = Integer.ParseInt(request.getParameter("reply-comment-id"));
}
// Retrieve any metadata key-value pairs included in the request.
String metaKeys = request.getParameterValues("MetaKey[]");
String metaValues = request.getParameterValues("MetaValue[]");
Map<String, String> metaMap = createMetaMap(metaKeys, metaValues);
// Get the URI of the item being commented on.
cmuri = request.getParameter("cmuri");
if (replyCommentId != 0) {
// Post a new reply to a comment.
api.postComment(cmuri, username, email, comment, replyCommentId, metaMap);
} else if (editCommentId != 0) {
// Modify an existing comment.
api.editComment(editCommentId, comment, null, metaMap);
} else {
// Post a new comment on the item itself (top level).
api.postComment(cmuri, username, email, comment, 0, metaMap);
}
// Removing a comment
} else if (request.getParameter("removeComment") != null) {
String commentId = request.getParameter("commentId");
api.removeComment(Long.parseLong(commentId));
}
// Loading the comments
// This call retrieves all comment threads for an item in one go,
// to full thread depth, sorted in ascending order, with metadata fetched.
List<Comment> comments = api.retrieveThreadedComments(cmuri, false, -1, -1, -1, true);
%>
<%!
private Map<String, String> createMetaMap(String[] metaKeys, String[] metaValues) {
Map<String, String> metaMap = new HashMap<>();
Iterator<String> keys = asList(metaKeys).iterator();
Iterator<String> values = asList(metaValues).iterator();
while (keys.hasNext() && values.hasNext()) {
String key = keys.next();
String value = values.next();
if (key != null && !key.isEmpty() && value != null && !value.isEmpty()) {
metaMap.put(key, value);
}
}
return metaMap;
}
private void buildRootComment(List<Comment> comments, String currentUserId, JspWriter out) throws IOException {
out.print("<ul class=\"media-list\">");
for (Comment comment : comments) {
out.print("<li class=\"media\">");
buildComment(comment, currentUserId, out);
out.print("</li>");
}
out.print("</ul>");
}
private void buildNestedComments(List<Comment> comments, String currentUserId, JspWriter out) throws IOException {
out.print("<ul class=\"media-list\">");
for (Comment comment : comments) {
out.print("<li class=\"media media-replied\">");
buildComment(comment, currentUserId, out);
out.print("</li>");
}
out.print("</ul>");
}
private void buildComment(Comment comment, String currentUserId, JspWriter out) throws IOException {
writeComment(comment, currentUserId, out);
List<Comment> children = comment.getChildren();
if (!children.isEmpty()) {
buildNestedComments(children, currentUserId, out);
}
}
private void writeComment(Comment comment, String currentUserId, JspWriter out) throws IOException {
// Check if the comment being displayed was submitted by the user who is currently logged in.
boolean sameUser = comment.getUser().getId().equals(currentUserId);
out.print("<div class=\"media-body\">");
out.print("<div class=\"well well-lg\">");
// Show the name of the commenter.
out.print("<h4 class=\"media-heading text-uppercase reviews username\">");
if (comment.getParent() != null) {
out.print("<span class=\"glyphicon glyphicon-share-alt\"></span>");
}
out.print(comment.getUser().getName());
out.print("</h4>");
// Get, but hide, the email address of the commenter and the ID of the comment.
out.print("<input type=\"hidden\" class=\"form-control\" name=\"email\" value=\"");
out.print(comment.getUser().getEmailAddress());
out.print("\"/>");
out.print("<input type=\"hidden\" class=\"form-control\" name=\"comment-id\" value=\"");
out.print(comment.getId());
out.print("\"/>");
// Show the comment itself.
out.print("<p class=\"media-comment\">");
out.print(comment.getContent());
out.print("</p>");
// Get, but hide, the metadata key-value pairs
for (Map.Entry<String, String> meta : comment.getMetadata().entrySet()) {
out.print("<input type=\"hidden\" class=\"form-control\" name=\"MetaKey[]\" value=\"");
out.print(meta.getKey());
out.print("\"/>");
out.print("<input type=\"hidden\" class=\"form-control\" name=\"MetaValue[]\" value=\"");
out.print(meta.getValue());
out.print("\"/>");
}
// Show a Reply button.
out.print("<a class=\"btn btn-default btn-md reply-btn\" href=\"#add-comment\">");
out.print("<span class=\"glyphicon glyphicon-share-alt\"></span>Reply</a>");
// Show an Edit button (disabled if the currently logged-in user is not the commenter).
out.print("<a class=\"btn btn-default btn-md edit-btn\" href=\"#edit-comment\">" +
(sameUser ? ">" : " disabled=\"disabled\" >"));
out.print("<span class=\"glyphicon glyphicon-edit\"></span>Edit</a>");
// Show a Meta button.
out.print("<a class=\"btn btn-default btn-md meta-btn\" href=\"#meta-comment\">");
out.print("<span class=\"glyphicon glyphicon-info-sign\"></span>Meta</a>");
// Show a Remove button (disabled if the currently logged-in user is not the commenter).
out.print("<a class=\"btn btn-default btn-md remove-btn\" href=\"#remove-comment\">" +
(sameUser ? ">" : " disabled=\"disabled\" >"));
out.print("<span class=\"glyphicon glyphicon-remove\"></span>Remove</a>");
out.print("</div>");
out.print("</div>");
}
%>
<div class="container">
<div class="row">
<div class="col-sm-10 col-sm-offset-1" id="logout">
<div class="page-header">
<h3 class="reviews">Leave your comment</h3>
</div>
<div class="comment-tabs">
<ul class="nav nav-tabs" role="tablist">
<li class="active">
<a href="#comments-logout" role="tab" data-toggle="tab" class="logout-comments-btn">
<h4 class="reviews text-capitalize">Comments</h4>
</a>
</li>
<li>
<a href="#add-comment" role="tab" data-toggle="tab" class="add-comment-btn">
<h4 class="reviews text-capitalize">Post comment</h4>
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="comments-logout">
<% buildRootComments(comments, currentUserId, out); %>
</div>
<div class="tab-pane" id="add-comment" />
</div>
</div>
</div>
</div>
</div>
<div id="show-meta-modal" class="modal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Metadata</h4>
</div>
<div class="show-meta modal-body">
<table class="table">
<thead>
<tr>
<th>Meta Key</th>
<th>Meta Value</th>
</tr>
</thead>
<tbody/>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script>
$(function () {
$('.add-comment-btn').click(function () {
COMMENT_FORM.init({
'edit-comment-id': 0,
'reply-comment-id': 0
});
COMMENT_FORM.loadCommentsForm();
$('.nav-tabs a[href="#add-comment"]').tab('show')
});
$('.reply-btn').click(function () {
var comment = getComment($(this));
COMMENT_FORM.init({
'edit-comment-id': 0,
'reply-comment-id': comment['comment-id'],
'username': comment['username'],
'email': comment['email']
});
COMMENT_FORM.loadCommentsForm();
$('.nav-tabs a[href="#add-comment"]').tab('show')
});
$('.edit-btn').click(function (e) {
e.preventDefault();
if (!$(this).is("[disabled]")) {
COMMENT_FORM.init(getComment($(this)));
COMMENT_FORM.loadCommentsForm();
$('.nav-tabs a[href="#add-comment"]').tab('show')
}
});
$('.remove-btn').click(function (e) {
e.preventDefault();
if (!$(this).is("[disabled]")) {
return false;
}
$.post('comments-example.jsp',
{removeComment: '', commentId: getComment($(this))['comment-id']})
.done(function (msg) {
windows.location.replace('comments-example-jsp');
})
.fail(function (xhr, status, error) {
document.open();
document.write(xhr.responseText);
document.close();
});
return true;
});
$('.meta-btn').click(function () {
$('#show-meta-model tbody').remove();
var tbody = $('#show-meta modal tabl').append('<tbody>');
$.each(getMeta($(this)), function(key, value) {
var trow = $('<tr>');
tbody.append(trow);
trow.append($('<td>').html(key));
trow.append($('<td>').html(value));
});
$('#show-meta-modal').modal();
});
var getComment = function (sibling) {
var result = {};
result['reply-comment-id'] = 0;
result['comment-id'] = sibling.siblings('input[name="comment-id"]').first().val();
result['edit-comment-id'] = sibling.siblings('input[name="comment-id"]').first().val();
result['username'] = sibling.siblings('h4.username').first().text();
result['email'] = sibling.siblings('input[name="email"]').first().val();
result['comment'] = sibling.siblings('p.media-comment').first().text();
result['metadata'] = getMeta(sibling);
return result;
}
var getMeta = function (sibling) {
var keyArr = [];
sibling.siblings('input[name^="MetaKey"]').each(function () {
keyArr.push($(this).val());
});
var valueArr = [];
sibling.siblings('input[name^="MetaValue"]').each(function () {
valueArr.push($(this).val());
});
return ((keys, values) => keys.reduce((result, key, value) => {
result[key] = values[value];
return result;
}, {}))(keyArr, valueArr);
}
});
</script>
</body>
</html>
The CSS file is as follows:
.page-header { position:relative; }
.reviews {
color: #555;
font-weight: bold;
margin: 10px auto 20px;
}
.media .media-object { max-width: 120px; }
.media-body { position: relative; }
.media-body .btn { margin-right: 5px; }
.media-comment { margin-bottom: 20px; }
.media-replied { margin: 0 0 0px 50px; }
.media-replied .media-heading { padding-left: 0px; }
.btn-circle {
font-weight: bold;
font-size: 12px;
padding: 6px 15px;
border-radius: 20px;
}
.btn-circle span { padding-right: 6px; }
.tab-content {
padding: 50px 15px;
border: 1px sold #ddd;
border-top: 0;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
.meta-value-col {margin-left: 25px; }