Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r4f7ee8239da980f9bdb403845dfff1aa2e39ed64 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 Binary files differ Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rb3fa6a0350e2b024a94c20d83c7d242a54e6b10b -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision b3fa6a0350e2b024a94c20d83c7d242a54e6b10b) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -661,4 +661,25 @@ ckeditor.math.accents =Accents ckeditor.math.formatting =Formatting -#======= End labels: Exported 438 labels for en AU ===== +heading.comments=Comments +label.hidden=Comment Hidden +label.reply =Reply +label.hide=Hide +label.show=Show +label.post =Post +label.refresh = Refresh Comments +error.cannot.redisplay.please.refresh =Your changes have been saved but cannot be redisplayed. Please select refresh to reload the comments. +error.please.refresh =An error has occurred. Your post may not have been saved. Please select refresh to start again. +label.showhide.prompt =Show/Hide Replies +message.complete.or.cancel.reply =Please complete or cancel the current reply before starting a new reply. +message.complete.or.cancel.edit =Please complete or cancel the current edit before starting a new edit. +label.show.more.messages =More Comments +label.likes=Likes +label.comment.body.validation=The comment must be between 1 and 5000 characters long. +label.edited=Edited +label.like=Like +label.dislike=Dislike +label.no.comments=No Comments +label.newest.first=Newest First +label.top.comments=Top Comments +#======= End labels: Exported 439 labels for en AU ===== Index: lams_central/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -re485a7d6bab86fd056839aafaea13368afb6fb10 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision e485a7d6bab86fd056839aafaea13368afb6fb10) +++ lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -644,5 +644,25 @@ authoring.fla.page.svg.generator.title =SVG Generator authoring.fla.tool.groups.all =All - -#======= End labels: Exported 438 labels for en AU ===== +heading.comments=Comments +label.hidden=Comment Hidden +label.reply =Reply +label.hide=Hide +label.show=Show +label.post =Post +label.refresh = Refresh Comments +error.cannot.redisplay.please.refresh =Your changes have been saved but cannot be redisplayed. Please select refresh to reload the comments. +error.please.refresh =An error has occurred. Your post may not have been saved. Please select refresh to start again. +label.showhide.prompt =Show/Hide Replies +message.complete.or.cancel.reply =Please complete or cancel the current reply before starting a new reply. +message.complete.or.cancel.edit =Please complete or cancel the current edit before starting a new edit. +label.show.more.messages =More Comments +label.likes=Likes +label.comment.body.validation=The comment must be between 1 and 5000 characters long. +label.edited=Edited +label.like=Like +label.dislike=Dislike +label.no.comments=No Comments +label.newest.first=Newest First +label.top.comments=Top Comments +#======= End labels: Exported 439 labels for en AU ===== Index: lams_central/conf/xdoclet/struts-actions.xml =================================================================== diff -u -rad752d4543e8954afc126a6ca72c5a3a692f1172 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/conf/xdoclet/struts-actions.xml (.../struts-actions.xml) (revision ad752d4543e8954afc126a6ca72c5a3a692f1172) +++ lams_central/conf/xdoclet/struts-actions.xml (.../struts-actions.xml) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -10,4 +10,101 @@ > - --> \ No newline at end of file + --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/comments/CommentConstants.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/comments/CommentConstants.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/comments/CommentConstants.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,60 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments; + +public class CommentConstants { + + public static final String ATTR_EXTERNAL_ID = "externalID"; + public static final String ATTR_EXTERNAL_SIG = "externalSig"; + public static final String ATTR_EXTERNAL_TYPE = "externalType"; + public static final String ATTR_SESSION_MAP_ID = "sessionMapID"; + public static final String ATTR_COMMENT_THREAD = "commentThread"; + public static final String ATTR_COMMENT = "comment"; + public static final String ATTR_PARENT_COMMENT_ID = "parentUid"; + public static final String ATTR_ROOT_COMMENT_UID = "rootUid"; + public static final String ATTR_BODY = "body"; + public static final String ATTR_COMMENT_ID = "commentUid"; + public static final String ATTR_LIKE_COUNT = "likeCount"; + public static final String ATTR_THREAD_ID = "threadUid"; + public static final String ATTR_ERR_MESSAGE = "errMessage"; + public static final String ATTR_HIDE_FLAG = "hideFlag"; + public static final String ATTR_STATUS = "status"; + public static final String ATTR_LIKE_AND_DISLIKE = "likeAndDislike"; + public static final String ATTR_READ_ONLY = "readOnly"; + public static final String ATTR_SORT_BY = "sortBy"; // 0 date, 1 likes + + // for paging long topics & inlining reply + public static final String PAGE_LAST_ID = "pageLastId"; + public static final String PAGE_SIZE = "pageSize"; + public static final int DEFAULT_PAGE_SIZE = 20; + public static final String ATTR_NO_MORE_PAGES = "noMorePages"; + + public static final int MAX_BODY_LENGTH = 5000; + + // message keys + public static final String KEY_BODY_VALIDATION = "label.comment.body.validation"; + + +} + Index: lams_central/src/java/org/lamsfoundation/lams/comments/web/CommentAction.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/comments/web/CommentAction.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/comments/web/CommentAction.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,646 @@ +/**************************************************************** + * Copyright (C) 2008 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments.web; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.log4j.Logger; +import org.apache.struts.action.Action; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.tomcat.util.json.JSONException; +import org.apache.tomcat.util.json.JSONObject; +import org.lamsfoundation.lams.comments.Comment; +import org.lamsfoundation.lams.comments.CommentConstants; +import org.lamsfoundation.lams.comments.CommentLike; +import org.lamsfoundation.lams.comments.dto.CommentDTO; +import org.lamsfoundation.lams.comments.service.ICommentService; +import org.lamsfoundation.lams.security.ISecurityService; +import org.lamsfoundation.lams.tool.GroupedToolSession; +import org.lamsfoundation.lams.tool.ToolAccessMode; +import org.lamsfoundation.lams.tool.service.ILamsCoreToolService; +import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.util.MessageService; +import org.lamsfoundation.lams.util.WebUtil; +import org.lamsfoundation.lams.web.session.SessionManager; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.lamsfoundation.lams.web.util.SessionMap; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * @author Fiona Malikoff + */ +public class CommentAction extends Action { + + private static Logger log = Logger.getLogger(CommentAction.class); + + private static IUserManagementService userService; + private static ICommentService commentService; + private static ILamsCoreToolService coreToolService; + private static ISecurityService securityService; + + @Override + public final ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws Exception { + String param = mapping.getParameter(); + if (param.equals("init")) { + return init(mapping, form, request, response); + } + if (param.equals("viewTopic")) { + return viewTopic(mapping, form, request, response); + } + if (param.equals("viewTopicThread")) { + return viewTopicThread(mapping, form, request, response); + } + + if ( param.equals("newComment")) { + return newComment(mapping, form, request, response); + } + + if ( param.equals("newReplyTopic")) { + return newReplyTopic(mapping, form, request, response); + } + if (param.equals("replyTopicInline")) { + return replyTopicInline(mapping, form, request, response); + } + + if ( param.equals("editTopic")) { + return editTopic(mapping, form, request, response); + } + if (param.equals("updateTopicInline")) { + return updateTopicInline(mapping, form, request, response); + } + + if (param.equals("like")) { + return updateLikeCount(mapping, form, request, response, true); + } + if (param.equals("dislike")) { + return updateLikeCount(mapping, form, request, response, false); + } + if (param.equals("hide")) { + return hideComment(mapping, form, request, response, false); + } + + return mapping.findForward("error"); + } + + /** + * Display the comments for a given external id (usually tool session id). The session comments will be + * arranged by Tree structure and loaded thread by thread (with paging). + * + * @param mapping + * @param form + * @param request + * @param response + * @return + * @throws ServletException + */ + @SuppressWarnings("unchecked") + private ActionForward init(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws ServletException { + + // initial Session Map + String sessionMapID = request.getParameter(CommentConstants.ATTR_SESSION_MAP_ID); + SessionMap sessionMap; + + Long externalId; + int externalType; + String externalSignature; + String mode; + boolean likeAndDislike; + boolean readOnly; + Integer pageSize; + Integer sortBy; + + // refresh forum page, not initial enter + if (sessionMapID != null) { + sessionMap = (SessionMap) request.getSession().getAttribute(sessionMapID); + externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + externalType = (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE); + externalSignature = (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG); + mode = (String) sessionMap.get(AttributeNames.ATTR_MODE); + pageSize = (Integer) sessionMap.get(CommentConstants.PAGE_SIZE); + sortBy = (Integer) sessionMap.get(CommentConstants.ATTR_SORT_BY); + + } else { + sessionMap = new SessionMap(); + request.getSession().setAttribute(sessionMap.getSessionID(), sessionMap); + + externalId = WebUtil.readLongParam(request, CommentConstants.ATTR_EXTERNAL_ID); + externalType = WebUtil.readIntParam(request, CommentConstants.ATTR_EXTERNAL_TYPE); + externalSignature = WebUtil.readStrParam(request, CommentConstants.ATTR_EXTERNAL_SIG); + likeAndDislike = WebUtil.readBooleanParam(request, CommentConstants.ATTR_LIKE_AND_DISLIKE); + readOnly = WebUtil.readBooleanParam(request, CommentConstants.ATTR_READ_ONLY); + pageSize = WebUtil.readIntParam(request, CommentConstants.PAGE_SIZE, true); + if ( pageSize == null ) + pageSize = CommentConstants.DEFAULT_PAGE_SIZE; + sortBy = (Integer) WebUtil.readIntParam(request, CommentConstants.ATTR_SORT_BY, true); + if ( sortBy == null ) + sortBy = ICommentService.SORT_BY_DATE; + + sessionMap.put(CommentConstants.ATTR_EXTERNAL_ID, externalId); + sessionMap.put(CommentConstants.ATTR_EXTERNAL_TYPE, externalType); + sessionMap.put(CommentConstants.ATTR_EXTERNAL_SIG, externalSignature); + sessionMap.put(CommentConstants.ATTR_LIKE_AND_DISLIKE, likeAndDislike); + sessionMap.put(CommentConstants.ATTR_READ_ONLY, readOnly); + sessionMap.put(CommentConstants.PAGE_SIZE, pageSize); + sessionMap.put(CommentConstants.ATTR_SORT_BY, sortBy); + + mode = request.getParameter(AttributeNames.ATTR_MODE); + sessionMap.put(AttributeNames.ATTR_MODE, mode != null ? mode : ToolAccessMode.LEARNER.toString()); + } + + User user = getCurrentUser(request); + if ( externalType != Comment.EXTERNAL_TYPE_TOOL ) + throwException("Unknown comment type ", user.getLogin(), externalId, externalType, externalSignature ); + + Comment rootComment = getCommentService().createOrGetRoot(externalId, externalType, externalSignature, user); + sessionMap.put(CommentConstants.ATTR_ROOT_COMMENT_UID, rootComment.getUid()); + return viewTopicImpl(mapping, form, request, response, sessionMap, pageSize, sortBy); + } + + private void throwException(String msg, String loginName, Long externalId, Integer externalType, String externalSignature) throws ServletException { + String error = msg+" User "+loginName+" "+externalId+":"+externalType+":"+externalSignature; + log.error(error); + throw new ServletException(error); + } + + private boolean learnerInToolSession(Long toolSessionId, User user) { + GroupedToolSession toolSession = (GroupedToolSession) getCoreToolService().getToolSessionById(toolSessionId); + return toolSession.getSessionGroup().getUsers().contains(user); + } + + private boolean monitorInToolSession(Long toolSessionId, User user) { + GroupedToolSession toolSession = (GroupedToolSession) getCoreToolService().getToolSessionById(toolSessionId); + return getSecurityService().isLessonMonitor(toolSession.getLesson().getLessonId(), user.getUserId(), "Comment Monitoring Tasks", false); + } + + /** + * Display the comments for a given external id (usually tool session id). The session comments will be + * arranged by Tree structure and loaded thread by thread (with paging). This may set a new value of sort by, so + * make sure the session is updated. + * + * @param mapping + * @param form + * @param request + * @param response + * @return + */ + @SuppressWarnings("unchecked") + private ActionForward viewTopic(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) { + + String sessionMapID = WebUtil.readStrParam(request, CommentConstants.ATTR_SESSION_MAP_ID); + SessionMap sessionMap = (SessionMap) request.getSession().getAttribute(sessionMapID); + Integer pageSize = WebUtil.readIntParam(request, CommentConstants.PAGE_SIZE, true); + Integer sortBy = WebUtil.readIntParam(request, CommentConstants.ATTR_SORT_BY, true); + if ( sortBy != null ) + sessionMap.put( CommentConstants.ATTR_SORT_BY, sortBy); + return viewTopicImpl(mapping, form, request, response, sessionMap, pageSize, sortBy); + + } + + private ActionForward viewTopicImpl(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response, SessionMap sessionMap, Integer pageSize, Integer sortBy) { + + Long externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + Integer externalType = (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE); + String externalSignature = (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG); + + commentService = getCommentService(); + + // get user and check they are in the tool session.... + HttpSession ss = SessionManager.getSession(); + UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); + + Long lastMsgSeqId = WebUtil.readLongParam(request, CommentConstants.PAGE_LAST_ID, true); + String currentLikeCount = WebUtil.readStrParam(request, CommentConstants.ATTR_LIKE_COUNT, true); + + if ( pageSize == null ) + pageSize = (Integer) sessionMap.get(CommentConstants.PAGE_SIZE); + if ( sortBy == null ) + sortBy = (Integer) sessionMap.get(CommentConstants.ATTR_SORT_BY); + + List msgDtoList = commentService.getTopicThread(externalId, externalType, externalSignature, lastMsgSeqId, pageSize, sortBy, currentLikeCount, user.getUserID()); + updateMessageFlag(msgDtoList, user.getUserID()); + request.setAttribute(CommentConstants.ATTR_COMMENT_THREAD, msgDtoList); + + // transfer SessionMapID as well + request.setAttribute(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + + return mapping.findForward("success"); + } + + @SuppressWarnings("unchecked") + private SessionMap getSessionMap(HttpServletRequest request) { + String sessionMapId = WebUtil.readStrParam(request, CommentConstants.ATTR_SESSION_MAP_ID); + return (SessionMap) request.getSession().getAttribute(sessionMapId); + } + + /** + * Display the messages for a particular thread in a particular topic. Returns all messages for this thread - does not need paging. + * + * @param mapping + * @param form + * @param request + * @param response + * @return + */ + private ActionForward viewTopicThread(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) { + + commentService = getCommentService(); + + Long highlightMessageUid = WebUtil.readLongParam(request, CommentConstants.ATTR_COMMENT_ID, true); + SessionMap sessionMap = getSessionMap(request); + + // get forum user and forum + UserDTO user = (UserDTO) SessionManager.getSession().getAttribute(AttributeNames.USER); + Long threadId = WebUtil.readLongParam(request, CommentConstants.ATTR_THREAD_ID, true); + Integer sortBy = WebUtil.readIntParam(request, CommentConstants.ATTR_SORT_BY, true); + if ( sortBy != null ) + sessionMap.put( CommentConstants.ATTR_SORT_BY, sortBy); + + List msgDtoList = commentService.getThread(threadId, sortBy, user.getUserID()); + updateMessageFlag(msgDtoList, user.getUserID()); + request.setAttribute(CommentConstants.ATTR_COMMENT_THREAD, msgDtoList); + + if ( highlightMessageUid != null ) { + request.setAttribute(CommentConstants.ATTR_COMMENT_ID, highlightMessageUid); + } + // transfer SessionMapID as well + request.setAttribute(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + + // don't want to try to scroll as this is a single thread, completely displayed. + request.setAttribute(CommentConstants.ATTR_NO_MORE_PAGES, true); + + return mapping.findForward("success"); + } + + /** + * This method will set the author and voted (has done like/dislike) flag in message DTO + * + * @param msgDtoList + */ + private void updateMessageFlag(List msgDtoList, Integer currUserId) { + Iterator iter = msgDtoList.iterator(); + while (iter.hasNext()) { + CommentDTO dto = iter.next(); + Comment comment = dto.getComment(); + if (comment.getCreatedBy() != null + && currUserId.equals(comment.getCreatedBy().getUserId())) { + dto.setIsAuthor(true); + } else { + dto.setIsAuthor(false); + } + } + } + + /** + * Create a new comment (not a reply) + * + * @param mapping + * @param form + * @param request + * @param response + * @return + * @throws InterruptedException + * @throws JSONException + * @throws IOException + * @throws ServletException + */ + private synchronized ActionForward newComment(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws InterruptedException, JSONException, IOException, ServletException { + + SessionMap sessionMap = getSessionMap(request); + Long externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + Integer externalType = (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE); + String externalSignature = (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG); + + String commentText = request.getParameter(CommentConstants.ATTR_BODY); + if ( commentText != null ) + commentText = commentText.trim(); + + JSONObject JSONObject; + + if ( ! validateText(commentText) ) { + JSONObject = getFailedValidationJSON(); + + } else { + + commentService = getCommentService(); + + User user = getCurrentUser(request); + if ( ! learnerInToolSession(externalId, user) ) + throwException("New comment: User does not have the rights to access the comments. ", user.getLogin(), externalId, externalType, externalSignature ); + + Comment rootSeq = commentService.getRoot(externalId, externalType, externalSignature); + + // save message into database + Comment newComment = commentService.createReply(rootSeq, commentText, user); + + JSONObject = new JSONObject(); + JSONObject.put(CommentConstants.ATTR_COMMENT_ID, newComment.getUid()); + JSONObject.put(CommentConstants.ATTR_THREAD_ID, newComment.getThreadComment().getUid()); + JSONObject.put(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + JSONObject.put(CommentConstants.ATTR_PARENT_COMMENT_ID, newComment.getParent().getUid()); + + } + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONObject); + return null; + } + + /** + * Display replay topic page. Message form subject will include parent topics same subject. + * + * @param mapping + * @param form + * @param request + * @param response + * @return + */ + private ActionForward newReplyTopic(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) { + + request.setAttribute( CommentConstants.ATTR_SESSION_MAP_ID, request.getParameter(CommentConstants.ATTR_SESSION_MAP_ID) ); + request.setAttribute( CommentConstants.ATTR_PARENT_COMMENT_ID, request.getParameter(CommentConstants.ATTR_PARENT_COMMENT_ID) ); + return mapping.findForward("success"); + } + /** + * Create a reply to a parent topic. + * + * @param mapping + * @param form + * @param request + * @param response + * @return + * @throws InterruptedException + * @throws JSONException + * @throws IOException + * @throws ServletException + */ + private synchronized ActionForward replyTopicInline(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws InterruptedException, JSONException, IOException, ServletException { + + SessionMap sessionMap = getSessionMap(request); + Long externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + Integer externalType = (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE); + String externalSignature = (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG); + + Long parentId = WebUtil.readLongParam(request, CommentConstants.ATTR_PARENT_COMMENT_ID); + String commentText = WebUtil.readStrParam(request, CommentConstants.ATTR_BODY, true); + + if ( commentText != null ) + commentText = commentText.trim(); + + JSONObject JSONObject; + + if ( ! validateText(commentText) ) { + JSONObject = getFailedValidationJSON(); + + } else { + + commentService = getCommentService(); + User user = getCurrentUser(request); + if ( ! learnerInToolSession(externalId, user) ) + throwException("New comment: User does not have the rights to access the comments. ", user.getLogin(), externalId, externalType, externalSignature ); + + // save message into database + Comment newComment = commentService.createReply(parentId, commentText.trim(), user); + + JSONObject = new JSONObject(); + JSONObject.put(CommentConstants.ATTR_COMMENT_ID, newComment.getUid()); + JSONObject.put(CommentConstants.ATTR_THREAD_ID, newComment.getThreadComment().getUid()); + JSONObject.put(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + JSONObject.put(CommentConstants.ATTR_PARENT_COMMENT_ID, newComment.getParent().getUid()); + + } + + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONObject); + return null; + } + + private boolean validateText(String commentText) { + return commentText != null && commentText.length() > 0 && commentText.length() < CommentConstants.MAX_BODY_LENGTH; + } + + private JSONObject getFailedValidationJSON() throws JSONException { + MessageService msgService = getCommentService().getMessageService(); + JSONObject JSONObject = new JSONObject(); + JSONObject.put(CommentConstants.ATTR_ERR_MESSAGE, msgService.getMessage(CommentConstants.KEY_BODY_VALIDATION)); + return JSONObject; + } + + /** + * Display a editable form for a topic in order to update it. + * + * @param mapping + * @param form + * @param request + * @param response + * @return + * @throws PersistenceException + */ + public ActionForward editTopic(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) { + + Long commentId = WebUtil.readLongParam(request, CommentConstants.ATTR_COMMENT_ID); + CommentDTO comment = getCommentService().getComment(commentId); + + SessionMap sessionMap = getSessionMap(request); + sessionMap.put(CommentConstants.ATTR_COMMENT_ID, commentId); + request.setAttribute(CommentConstants.ATTR_COMMENT_ID, commentId); + request.setAttribute(CommentConstants.ATTR_COMMENT, comment); + request.setAttribute(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + return mapping.findForward("success"); + } + + /** + * Update a topic. + * @throws ServletException + */ + public ActionForward updateTopicInline(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws JSONException, IOException, ServletException { + + commentService = getCommentService(); + SessionMap sessionMap = getSessionMap(request); + Long commentId = WebUtil.readLongParam(request, CommentConstants.ATTR_COMMENT_ID); + + Long externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + Integer externalType = (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE); + String externalSignature = (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG); + + String commentText = request.getParameter(CommentConstants.ATTR_BODY); + if ( commentText != null ) + commentText = commentText.trim(); + + JSONObject JSONObject; + + if ( ! validateText(commentText) ) { + JSONObject = getFailedValidationJSON(); + + } else { + + CommentDTO originalComment = commentService.getComment(commentId); + + boolean monitoringMode = ToolAccessMode.TEACHER.equals(WebUtil.getToolAccessMode((String)sessionMap.get(AttributeNames.ATTR_MODE))); + User user = getCurrentUser(request); + if ( ! originalComment.getComment().getCreatedBy().equals(user) && ! ( monitoringMode && monitorInToolSession(externalId, user) ) ) + throwException("Update comment: User does not have the rights to update the comment "+commentId+". ", user.getLogin(), externalId, externalType, externalSignature ); + + Comment updatedComment = commentService.updateComment(commentId, commentText, user, monitoringMode); + + JSONObject = new JSONObject(); + JSONObject.put(CommentConstants.ATTR_COMMENT_ID, commentId); + JSONObject.put(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + JSONObject.put(CommentConstants.ATTR_THREAD_ID, updatedComment.getThreadComment().getUid()); + JSONObject.put(CommentConstants.ATTR_PARENT_COMMENT_ID, updatedComment.getParent().getUid()); + + } + + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONObject); + return null; + } + + /** + * Update the likes/dislikes + * @throws ServletException + */ + private synchronized ActionForward updateLikeCount(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response, boolean isLike) throws InterruptedException, JSONException, IOException, ServletException { + + SessionMap sessionMap = getSessionMap(request); + Long messageUid = WebUtil.readLongParam(request, CommentConstants.ATTR_COMMENT_ID); + Long externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + + commentService = getCommentService(); + + User user = getCurrentUser(request); + if ( ! learnerInToolSession(externalId, user) ) + throwException("Update comment: User does not have the rights to like/dislike the comment "+messageUid+". ", user.getLogin(), externalId, + (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE), (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG) ); + + boolean added = commentService.addLike(messageUid, user, isLike ? CommentLike.LIKE : CommentLike.DISLIKE); + + JSONObject JSONObject = new JSONObject(); + JSONObject.put(CommentConstants.ATTR_COMMENT_ID, messageUid); + JSONObject.put(CommentConstants.ATTR_STATUS, added); + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONObject); + return null; + } + + /** + * Update hide flag + * @throws ServletException + */ + private synchronized ActionForward hideComment(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response, boolean isLike) throws InterruptedException, JSONException, IOException, ServletException { + + SessionMap sessionMap = getSessionMap(request); + Long commentId = WebUtil.readLongParam(request, CommentConstants.ATTR_COMMENT_ID); + Long externalId = (Long) sessionMap.get(CommentConstants.ATTR_EXTERNAL_ID); + boolean status = WebUtil.readBooleanParam(request, CommentConstants.ATTR_HIDE_FLAG); + + commentService = getCommentService(); + + boolean monitoringMode = ToolAccessMode.TEACHER.equals(WebUtil.getToolAccessMode((String)sessionMap.get(AttributeNames.ATTR_MODE))); + User user = getCurrentUser(request); + if ( ! monitoringMode || ! monitorInToolSession(externalId, user) ) + throwException("Update comment: User does not have the rights to hide the comment "+commentId+". ", user.getLogin(), externalId, + (Integer) sessionMap.get(CommentConstants.ATTR_EXTERNAL_TYPE), (String) sessionMap.get(CommentConstants.ATTR_EXTERNAL_SIG) ); + + Comment updatedComment = commentService.hideComment(commentId, user, status); + + JSONObject JSONObject = new JSONObject(); + JSONObject.put(CommentConstants.ATTR_COMMENT_ID, updatedComment.getUid()); + JSONObject.put(CommentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); + JSONObject.put(CommentConstants.ATTR_THREAD_ID, updatedComment.getThreadComment().getUid()); + JSONObject.put(CommentConstants.ATTR_PARENT_COMMENT_ID, updatedComment.getParent().getUid()); + + response.setContentType("application/json;charset=utf-8"); + response.getWriter().print(JSONObject); + return null; + } + + /** + * Get login user information from system level session. + */ + private User getCurrentUser(HttpServletRequest request) { + UserDTO user = (UserDTO) SessionManager.getSession().getAttribute(AttributeNames.USER); + return getUserService().getUserByLogin(user.getLogin()); + } + + private IUserManagementService getUserService() { + if (CommentAction.userService == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + CommentAction.userService = (IUserManagementService) ctx.getBean("userManagementService"); + } + return CommentAction.userService; + } + private ICommentService getCommentService() { + if (CommentAction.commentService == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + CommentAction.commentService = (ICommentService) ctx.getBean("commentService"); + } + return CommentAction.commentService; + } + + private ILamsCoreToolService getCoreToolService() { + if (CommentAction.coreToolService == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + CommentAction.coreToolService = (ILamsCoreToolService) ctx.getBean("lamsCoreToolService"); + } + return CommentAction.coreToolService; + } + + private ISecurityService getSecurityService() { + if (CommentAction.securityService == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() + .getServletContext()); + CommentAction.securityService = (ISecurityService) ctx.getBean("securityService"); + } + return CommentAction.securityService; + } + + + +} \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/comments/web/CommentForm.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/comments/web/CommentForm.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/comments/web/CommentForm.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,102 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments.web; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.apache.struts.action.ActionErrors; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.validator.ValidatorForm; +import org.lamsfoundation.lams.comments.Comment; + +/** + * Comment Form. + */ +public class CommentForm extends ValidatorForm { + private static final long serialVersionUID = -9054365604649146734L; + private static Logger logger = Logger.getLogger(CommentForm.class.getName()); + + protected Comment comment; + protected String sessionMapID; + protected Long parentId; + protected Long topicId; + + public CommentForm() { + comment = new Comment(); + } + + /** + * MessageForm validation method from STRUCT interface. + * + */ + @Override + public ActionErrors validate(ActionMapping mapping, javax.servlet.http.HttpServletRequest request) { + ActionErrors errors = new ActionErrors(); + try { + if (StringUtils.isBlank(comment.getBody())) { + ActionMessage error = new ActionMessage("error.body.required"); + errors.add("message.body", error); + } + } catch (Exception e) { + CommentForm.logger.error("", e); + } + return errors; + } + + // -------------------------get/set methods---------------- + public void setComment(Comment comment) { + this.comment = comment; + } + + public Comment getComment() { + return comment; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public Long getParentId() { + return this.parentId; + } + + public void setTopicId(Long topicId) { + this.topicId = topicId; + } + + public Long getTopicId() { + return this.topicId; + } + + public String getSessionMapID() { + return sessionMapID; + } + + public void setSessionMapID(String sessionMapID) { + this.sessionMapID = sessionMapID; + } + +} Index: lams_central/web/WEB-INF/tags/Comments.tag =================================================================== diff -u --- lams_central/web/WEB-INF/tags/Comments.tag (revision 0) +++ lams_central/web/WEB-INF/tags/Comments.tag (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,52 @@ +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-function" prefix="fn" %> + +<%@ attribute name="toolSessionId" required="true" rtexprvalue="true"%> +<%@ attribute name="toolSignature" required="true" rtexprvalue="true"%> +<%@ attribute name="height" required="false" rtexprvalue="true"%> +<%@ attribute name="width" required="false" rtexprvalue="true"%> +<%@ attribute name="mode" required="false" rtexprvalue="true"%> +<%@ attribute name="likeAndDislike" required="false" rtexprvalue="true"%> +<%@ attribute name="readOnly" required="false" rtexprvalue="true"%> +<%@ attribute name="pageSize" required="false" rtexprvalue="true"%> +<%@ attribute name="sortBy" required="false" rtexprvalue="true"%> + + + + + + + + + + + + + &mode=${mode} + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: lams_central/web/WEB-INF/tags/CommentsAuthor.tag =================================================================== diff -u --- lams_central/web/WEB-INF/tags/CommentsAuthor.tag (revision 0) +++ lams_central/web/WEB-INF/tags/CommentsAuthor.tag (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,51 @@ +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + +<%@ attribute name="allowCommentsVariableName" required="false" rtexprvalue="true"%> +<%@ attribute name="allowCommentLabelKey" required="false" rtexprvalue="true"%> +<%@ attribute name="likeDislikeVariableName" required="false" rtexprvalue="true"%> +<%@ attribute name="likeOnlyCommentLabelKey" required="false" rtexprvalue="true"%> +<%@ attribute name="likeDislikeLabelKey" required="false" rtexprvalue="true"%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: lams_central/web/WEB-INF/tlds/lams/lams.tld =================================================================== diff -u -r86a2855c49422f236ac31c35df38fa3c66220dcc -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/web/WEB-INF/tlds/lams/lams.tld (.../lams.tld) (revision 86a2855c49422f236ac31c35df38fa3c66220dcc) +++ lams_central/web/WEB-INF/tlds/lams/lams.tld (.../lams.tld) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -481,5 +481,13 @@ head /WEB-INF/tags/Head.tag + + Comments + /WEB-INF/tags/Comments.tag + + + CommentsAuthor + /WEB-INF/tags/CommentsAuthor.tag + Index: lams_central/web/comments/comments.jsp =================================================================== diff -u --- lams_central/web/comments/comments.jsp (revision 0) +++ lams_central/web/comments/comments.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,101 @@ + + +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%@ include file="new.jsp"%> + + + + + selected> + selected> + + + + + + + + + + + + <%@ include file="topicview.jsp"%> + + + + + + + Index: lams_central/web/comments/edit.jsp =================================================================== diff -u --- lams_central/web/comments/edit.jsp (revision 0) +++ lams_central/web/comments/edit.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,80 @@ +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + +<%@ page import="org.lamsfoundation.lams.comments.CommentConstants"%> + + + + +"> + ${comment.comment.body} + + + + + + + + + + + + + + + + + + Index: lams_central/web/comments/msgview.jsp =================================================================== diff -u --- lams_central/web/comments/msgview.jsp (revision 0) +++ lams_central/web/comments/msgview.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,122 @@ +<%-- commentDto, commentUid, msgLevel needs to be in the session elsewhere --%> + + + + + + + + + + + + + + + highlight + + + + + + + px;"> + + + + + + + + + + + + + () + + + + + + + + + + + + + + + + + + + + + + + Reply + · + + + + + + + Edit + · + + + + + + + + + + + + + + + + + + + + + · + + + ${commentDto.comment.likeCount} + + + + + " + onclick="javascript:likeEntry(${commentDto.comment.uid});" id="msglikebutton${commentDto.comment.uid}"> + + " + onclick="javascript:dislikeEntry(${commentDto.comment.uid});" id="msgdislikebutton${commentDto.comment.uid}"> + + + + " + id="msglikebutton${commentDto.comment.uid}"/> + + + + " + id="msgdislikebutton${commentDto.comment.uid}"> + + + + + + + + + + Index: lams_central/web/comments/msgviewwrapper.jsp =================================================================== diff -u --- lams_central/web/comments/msgviewwrapper.jsp (revision 0) +++ lams_central/web/comments/msgviewwrapper.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,25 @@ + +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + +<%-- Wraps up msgview.jsp for returning a single message - called when an edit is performed. It needs to do all the setup that topicview.jsp normally does. --%> + + + + + + + + <%@ include file="msgview.jsp"%> + + Index: lams_central/web/comments/new.jsp =================================================================== diff -u --- lams_central/web/comments/new.jsp (revision 0) +++ lams_central/web/comments/new.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,86 @@ +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + +<%@ page import="org.lamsfoundation.lams.comments.CommentConstants"%> + + + + +"> + + + + + + + + + + + + + + + + + + Index: lams_central/web/comments/reply.jsp =================================================================== diff -u --- lams_central/web/comments/reply.jsp (revision 0) +++ lams_central/web/comments/reply.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,76 @@ +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + +<%@ page import="org.lamsfoundation.lams.comments.CommentConstants"%> + + + + +"> + + + + + + + + + + + + + + + + Index: lams_central/web/comments/topicview.jsp =================================================================== diff -u --- lams_central/web/comments/topicview.jsp (revision 0) +++ lams_central/web/comments/topicview.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,175 @@ +<%@ page import="org.lamsfoundation.lams.comments.CommentConstants"%> + + + + + + + + + +expandable:true,initialState:'expanded', + expanderTemplate:' ${prompt}', + stringCollapse:'${hide}',stringExpand:'${show}', + clickableNodeNames:true,indent:${indent}, + onNodeInitialized:function() { + if (this.level() >= 2) { + this.collapse(); + } + } + + + + + + + + + + + + + + + + + + <%-- same test & command appears at bottom of script --%> + + + + + + + + + + + + + + + + <%@ include file="msgview.jsp"%> + + + + + + + + + + + + + + + + + " class="button"> + + Index: lams_central/web/comments/topicviewwrapper.jsp =================================================================== diff -u --- lams_central/web/comments/topicviewwrapper.jsp (revision 0) +++ lams_central/web/comments/topicviewwrapper.jsp (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,19 @@ + + + + +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + + + + + + + + +<%@ include file="topicview.jsp"%> + + Index: lams_central/web/css/defaultHTML_learner.css =================================================================== diff -u -rb7c5082756a8b0cdc42797b1176452893f4d4e19 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/web/css/defaultHTML_learner.css (.../defaultHTML_learner.css) (revision b7c5082756a8b0cdc42797b1176452893f4d4e19) +++ lams_central/web/css/defaultHTML_learner.css (.../defaultHTML_learner.css) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -616,4 +616,56 @@ /* jQuery tablesorter/pager/filter defaults */ .tablesorter .disabled { display: none +} + +/* Comments Widget */ +iframe.commentFrame{ + border: 0 none; + margin: 0; + padding: 0; + overflow: hidden; +} + + +.comment-author { + color:#0087e5; + font-size:11px; + margin-bottom:2px; +} + +.comment-date { + font-size:11px; + margin-bottom:2px; +} + +table.comment { + width:100%; + margin-left:0px; + padding-top:0px; + margin-bottom:5px; + text-align:left; +} + +table.comment td { + padding:0px; + padding-left:0px; + font-size:11px; + vertical-align:top; +} +a.comment { + color: #47bc23; + text-decoration: none; + border-bottom: none; +} +textarea.comment { + width: 99%; + height: 60px; + margin-bottom:10px; +} +div.comment-entry { + width: 98%; +} + +.fa-faded { + color: silver } \ No newline at end of file Index: lams_central/web/css/defaultHTML_learner_mobile.css =================================================================== diff -u -rb7c5082756a8b0cdc42797b1176452893f4d4e19 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/web/css/defaultHTML_learner_mobile.css (.../defaultHTML_learner_mobile.css) (revision b7c5082756a8b0cdc42797b1176452893f4d4e19) +++ lams_central/web/css/defaultHTML_learner_mobile.css (.../defaultHTML_learner_mobile.css) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -222,4 +222,56 @@ /* jQuery tablesorter/pager/filter defaults */ .tablesorter .disabled { display: none +} + +/* Comments Widget */ +iframe.commentFrame{ + border: 0 none; + margin: 0; + padding: 0; + overflow: hidden; +} + + +.comment-author { + color:#0087e5; + font-size:11px; + margin-bottom:2px; +} + +.comment-date { + font-size:11px; + margin-bottom:2px; +} + +table.comment { + width:100%; + margin-left:0px; + padding-top:0px; + margin-bottom:5px; + text-align:left; +} + +table.comment td { + padding:0px; + padding-left:0px; + font-size:11px; + vertical-align:top; +} +a.comment { + color: #47bc23; + text-decoration: none; + border-bottom: none; +} +textarea.comment { + width: 99%; + height: 60px; + margin-bottom:10px; +} +div.comment-entry { + width: 98%; +} + +.fa-faded { + color: silver } \ No newline at end of file Index: lams_central/web/css/defaultHTML_rtl_learner.css =================================================================== diff -u -rb7c5082756a8b0cdc42797b1176452893f4d4e19 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_central/web/css/defaultHTML_rtl_learner.css (.../defaultHTML_rtl_learner.css) (revision b7c5082756a8b0cdc42797b1176452893f4d4e19) +++ lams_central/web/css/defaultHTML_rtl_learner.css (.../defaultHTML_rtl_learner.css) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -544,4 +544,56 @@ /* jQuery tablesorter/pager/filter defaults */ .tablesorter .disabled { display: none +} + +/* Comments Widget */ +iframe.commentFrame{ + border: 0 none; + margin: 0; + padding: 0; + overflow: hidden; +} + + +.comment-author { + color:#0087e5; + font-size:11px; + margin-bottom:2px; +} + +.comment-date { + font-size:11px; + margin-bottom:2px; +} + +table.comment { + width:100%; + margin-left:0px; + padding-top:0px; + margin-bottom:5px; + text-align:left; +} + +table.comment td { + padding:0px; + padding-left:0px; + font-size:11px; + vertical-align:top; +} +a.comment { + color: #47bc23; + text-decoration: none; + border-bottom: none; +} +textarea.comment { + width: 99%; + height: 60px; + margin-bottom:10px; +} +div.comment-entry { + width: 98%; +} + +.fa-faded { + color: silver } \ No newline at end of file Index: lams_central/web/css/jquery.treetable.css =================================================================== diff -u --- lams_central/web/css/jquery.treetable.css (revision 0) +++ lams_central/web/css/jquery.treetable.css (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,28 @@ +table.treetable span.indenter { + display: inline-block; + margin: 0; + padding: 0; + text-align: right; + + /* Disable text selection of nodes (for better D&D UX) */ + user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + -webkit-user-select: none; + + /* Force content-box box model for indenter (Bootstrap compatibility) */ + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + + width: 150px; +} + +table.treetable span.indenter a { + background-position: left center; + background-repeat: no-repeat; + display: inline-block; + text-decoration: none; + width: 150px; +} Index: lams_central/web/css/jquery.treetable.lams.css =================================================================== diff -u --- lams_central/web/css/jquery.treetable.lams.css (revision 0) +++ lams_central/web/css/jquery.treetable.lams.css (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,44 @@ +table.treetable span.indenter { + text-align: left; + width: 250px; +} + +table.treetable tr.collapsed span.indenter a { + background-image: url(data:image/png;base64,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); +} + +table.treetable tr.expanded span.indenter a { + background-image: url(data:image/png;base64,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); +} + +table.treetable tr span.indenter a { + outline: none; /* Expander shows outline after upgrading to 3.0 (#141) */ + border-bottom: none; /* Hide the dotted line underneath */ + width: 250px; +} + +table.treetable tr.collapsed.selected span.indenter a { + background-image: url(data:image/png;base64,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); +} + +table.treetable tr.expanded.selected span.indenter a { + background-image: url(data:image/png;base64,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); +} + +table.treetable tr.accept { + background-color: #a3bce4; + color: #fff +} + +table.treetable tr.collapsed.accept td span.indenter a { + background-image: url(data:image/x-png;base64,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); +} + +table.treetable tr.expanded.accept td span.indenter a { + background-image: url(data:image/x-png;base64,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); +} + +div.highlight { + border:2px solid #cacdd1; +} + Index: lams_central/web/includes/font-awesome/css/font-awesome.css =================================================================== diff -u --- lams_central/web/includes/font-awesome/css/font-awesome.css (revision 0) +++ lams_central/web/includes/font-awesome/css/font-awesome.css (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,2086 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.5.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} Index: lams_central/web/includes/font-awesome/css/font-awesome.min.css =================================================================== diff -u --- lams_central/web/includes/font-awesome/css/font-awesome.min.css (revision 0) +++ lams_central/web/includes/font-awesome/css/font-awesome.min.css (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} Index: lams_central/web/includes/font-awesome/fonts/FontAwesome.otf =================================================================== diff -u Binary files differ Index: lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.eot =================================================================== diff -u Binary files differ Index: lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.svg =================================================================== diff -u --- lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.svg (revision 0) +++ lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.svg (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.ttf =================================================================== diff -u Binary files differ Index: lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.woff =================================================================== diff -u Binary files differ Index: lams_central/web/includes/font-awesome/fonts/fontawesome-webfont.woff2 =================================================================== diff -u Binary files differ Index: lams_central/web/includes/javascript/comments.js =================================================================== diff -u --- lams_central/web/includes/javascript/comments.js (revision 0) +++ lams_central/web/includes/javascript/comments.js (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,48 @@ + function resizeIframe() { + parent.resizeCommentFrame(document.body.scrollHeight); + } + + function highlightMessage() { + $('.highlight').filter($('table')).css('background','none'); + $('.highlight').filter($('div')).effect('highlight', {color: "#fcf0ad"}, 6000); + $('.highlight').removeClass('highlight'); + } + + function validateBodyText(bodyText, maxLength, validationMessage) { + bodyText = bodyText.trim(); + if ( bodyText.length==0 || bodyText.length>maxLength ) { + alert(validationMessage); + return false; + } else { + return true; + } + } + + function reloadThread(response) { + var threadDiv = document.getElementById('thread'+response.threadUid); + var threadUid = response.threadUid; + var commentUid = response.commentUid; + + if ( commentUid ) { + if ( ! threadDiv) { + alert(''); + } else { + var loadString = "viewTopicThread.do?&sessionMapID=" + response.sessionMapID + "&threadUid=" + threadUid+"&commentUid="+commentUid; + $(threadDiv).load(loadString, function() { + // expand up to the reply - in case it is buried down in a lot of replies + // don't need to do this if we have started a new thread. + if ( threadUid != commentUid ) { + $('#tree' + threadUid).treetable("reveal",commentUid); + $('#msg'+commentUid).focus(); + } + highlightMessage(); + resizeIframe(); + }); + } + } else if ( response.errMessage ) { + // No valid id? Something failed. Assume it is a response message coming back. + alert(response.errMessage); + } else { + alert(''); + } + } Index: lams_central/web/includes/javascript/jquery.jscroll.js =================================================================== diff -u --- lams_central/web/includes/javascript/jquery.jscroll.js (revision 0) +++ lams_central/web/includes/javascript/jquery.jscroll.js (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,209 @@ +/*! + * jScroll - jQuery Plugin for Infinite Scrolling / Auto-Paging - v2.2.4 + * http://jscroll.com/ + * + * Copyright 2011-2013, Philip Klauzinski + * http://klauzinski.com/ + * Dual licensed under the MIT and GPL Version 2 licenses. + * http://jscroll.com/#license + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl-2.0.html + * + * @author Philip Klauzinski + * @requires jQuery v1.4.3+ + */ +(function($) { + + // Define the jscroll namespace and default settings + $.jscroll = { + defaults: { + debug: false, + autoTrigger: true, + autoTriggerUntil: false, + loadingHtml: 'Loading...', + padding: 0, + nextSelector: 'a:last', + contentSelector: '', + pagingSelector: '', + callback: false + } + }; + + // Constructor + var jScroll = function($e, options) { + + // Private vars + var _data = $e.data('jscroll'), + _userOptions = (typeof options === 'function') ? { callback: options } : options, + _options = $.extend({}, $.jscroll.defaults, _userOptions, _data || {}), + _isWindow = ($e.css('overflow-y') === 'visible'), + _$next = $e.find(_options.nextSelector).first(), + _$window = $(window), + _$body = $('body'), + _$scroll = _isWindow ? _$window : $e, + _nextHref = $.trim(_$next.attr('href') + ' ' + _options.contentSelector); + + // Initialization + $e.data('jscroll', $.extend({}, _data, {initialized: true, waiting: false, nextHref: _nextHref})); + _wrapInnerContent(); + _preloadImage(); + _setBindings(); + + // Private methods + + // Check if a loading image is defined and preload + function _preloadImage() { + var src = $(_options.loadingHtml).filter('img').attr('src'); + if (src) { + var image = new Image(); + image.src = src; + } + } + + // Wrapper inner content, if it isn't already + function _wrapInnerContent() { + if (!$e.find('.jscroll-inner').length) { + $e.contents().wrapAll(''); + } + } + + // Find the next link's parent, or add one, and hide it + function _nextWrap($next) { + if (_options.pagingSelector) { + var $parent = $next.closest(_options.pagingSelector).hide(); + } else { + var $parent = $next.parent().not('.jscroll-inner,.jscroll-added').addClass('jscroll-next-parent').hide(); + if (!$parent.length) { + $next.wrap('').parent().hide(); + } + } + } + + // Remove the jscroll behavior and data from an element + function _destroy() { + return _$scroll.unbind('.jscroll') + .removeData('jscroll') + .find('.jscroll-inner').children().unwrap() + .filter('.jscroll-added').children().unwrap(); + } + + // Observe the scroll event for when to trigger the next load + function _observe() { + _wrapInnerContent(); + var $inner = $e.find('div.jscroll-inner').first(), + data = $e.data('jscroll'), + borderTopWidth = parseInt($e.css('borderTopWidth')), + borderTopWidthInt = isNaN(borderTopWidth) ? 0 : borderTopWidth, + iContainerTop = parseInt($e.css('paddingTop')) + borderTopWidthInt, + iTopHeight = _isWindow ? _$scroll.scrollTop() : $e.offset().top, + innerTop = $inner.length ? $inner.offset().top : 0, + iTotalHeight = Math.ceil(iTopHeight - innerTop + _$scroll.height() + iContainerTop); + + if (!data.waiting && iTotalHeight + _options.padding >= $inner.outerHeight()) { + //data.nextHref = $.trim(data.nextHref + ' ' + _options.contentSelector); + _debug('info', 'jScroll:', $inner.outerHeight() - iTotalHeight, 'from bottom. Loading next request...'); + return _load(); + } + } + + // Check if the href for the next set of content has been set + function _checkNextHref(data) { + data = data || $e.data('jscroll'); + if (!data || !data.nextHref) { + _debug('warn', 'jScroll: nextSelector not found - destroying'); + _destroy(); + return false; + } else { + _setBindings(); + return true; + } + } + + function _setBindings() { + var $next = $e.find(_options.nextSelector).first(); + if (_options.autoTrigger && (_options.autoTriggerUntil === false || _options.autoTriggerUntil > 0)) { + _nextWrap($next); + if (_$body.height() <= _$window.height()) { + _observe(); + } + _$scroll.unbind('.jscroll').bind('scroll.jscroll', function() { + return _observe(); + }); + if (_options.autoTriggerUntil > 0) { + _options.autoTriggerUntil--; + } + } else { + _$scroll.unbind('.jscroll'); + $next.bind('click.jscroll', function() { + _nextWrap($next); + _load(); + return false; + }); + } + } + + // Load the next set of content, if available + function _load() { + var $inner = $e.find('div.jscroll-inner').first(), + data = $e.data('jscroll'); + + data.waiting = true; + $inner.append('') + .children('.jscroll-added').last() + .html('' + _options.loadingHtml + ''); + + return $e.animate({scrollTop: $inner.outerHeight()}, 0, function() { + $inner.find('div.jscroll-added').last().load(data.nextHref, function(r, status, xhr) { + if (status === 'error') { + return _destroy(); + } + var $next = $(this).find(_options.nextSelector).first(); + data.waiting = false; + data.nextHref = $next.attr('href') ? $.trim($next.attr('href') + ' ' + _options.contentSelector) : false; + $('.jscroll-next-parent', $e).remove(); // Remove the previous next link now that we have a new one + _checkNextHref(); + if (_options.callback) { + _options.callback.call(this); + } + _debug('dir', data); + }); + }); + } + + // Safe console debug - http://klauzinski.com/javascript/safe-firebug-console-in-javascript + function _debug(m) { + if (_options.debug && typeof console === 'object' && (typeof m === 'object' || typeof console[m] === 'function')) { + if (typeof m === 'object') { + var args = []; + for (var sMethod in m) { + if (typeof console[sMethod] === 'function') { + args = (m[sMethod].length) ? m[sMethod] : [m[sMethod]]; + console[sMethod].apply(console, args); + } else { + console.log.apply(console, args); + } + } + } else { + console[m].apply(console, Array.prototype.slice.call(arguments, 1)); + } + } + } + + // Expose API methods via the jQuery.jscroll namespace, e.g. $('sel').jscroll.method() + $.extend($e.jscroll, { + destroy: _destroy + }); + return $e; + }; + + // Define the jscroll plugin method and loop + $.fn.jscroll = function(m) { + return this.each(function() { + var $this = $(this), + data = $this.data('jscroll'); + // Instantiate jScroll on this element if it hasn't been already + if (data && data.initialized) return; + var jscroll = new jScroll($this, m); + }); + }; +})(jQuery); \ No newline at end of file Index: lams_central/web/includes/javascript/jquery.treetable.js =================================================================== diff -u --- lams_central/web/includes/javascript/jquery.treetable.js (revision 0) +++ lams_central/web/includes/javascript/jquery.treetable.js (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,634 @@ +/* + * jQuery treetable Plugin 3.2.0 + * http://ludo.cubicphuse.nl/jquery-treetable + * + * Copyright 2013, Ludo van den Boom + * Dual licensed under the MIT or GPL Version 2 licenses. + */ +(function($) { + var Node, Tree, methods; + + Node = (function() { + function Node(row, tree, settings) { + var parentId; + + this.row = row; + this.tree = tree; + this.settings = settings; + + // TODO Ensure id/parentId is always a string (not int) + this.id = this.row.data(this.settings.nodeIdAttr); + + // TODO Move this to a setParentId function? + parentId = this.row.data(this.settings.parentIdAttr); + if (parentId != null && parentId !== "") { + this.parentId = parentId; + } + + this.treeCell = $(this.row.children(this.settings.columnElType)[this.settings.column]); + this.expander = $(this.settings.expanderTemplate); + this.indenter = $(this.settings.indenterTemplate); + this.children = []; + this.initialized = false; + + // Modified Fiona Malikoff 6 Mar 2015 - we want to put the twister triangle after the message not before. + // Changed prepend to append. + // this.treeCell.prepend(this.indenter); + this.treeCell.append(this.indenter); + + } + + Node.prototype.addChild = function(child) { + return this.children.push(child); + }; + + Node.prototype.ancestors = function() { + var ancestors, node; + node = this; + ancestors = []; + while (node = node.parentNode()) { + ancestors.push(node); + } + return ancestors; + }; + + Node.prototype.collapse = function() { + if (this.collapsed()) { + return this; + } + + this.row.removeClass("expanded").addClass("collapsed"); + + this._hideChildren(); + this.expander.attr("title", this.settings.stringExpand); + + if (this.initialized && this.settings.onNodeCollapse != null) { + this.settings.onNodeCollapse.apply(this); + } + + return this; + }; + + Node.prototype.collapsed = function() { + return this.row.hasClass("collapsed"); + }; + + // TODO destroy: remove event handlers, expander, indenter, etc. + + Node.prototype.expand = function() { + if (this.expanded()) { + return this; + } + + this.row.removeClass("collapsed").addClass("expanded"); + + if (this.initialized && this.settings.onNodeExpand != null) { + this.settings.onNodeExpand.apply(this); + } + + if ($(this.row).is(":visible")) { + this._showChildren(); + } + + this.expander.attr("title", this.settings.stringCollapse); + + return this; + }; + + Node.prototype.expanded = function() { + return this.row.hasClass("expanded"); + }; + + Node.prototype.hide = function() { + this._hideChildren(); + this.row.hide(); + return this; + }; + + Node.prototype.isBranchNode = function() { + if(this.children.length > 0 || this.row.data(this.settings.branchAttr) === true) { + return true; + } else { + return false; + } + }; + + Node.prototype.updateBranchLeafClass = function(){ + this.row.removeClass('branch'); + this.row.removeClass('leaf'); + this.row.addClass(this.isBranchNode() ? 'branch' : 'leaf'); + }; + + Node.prototype.level = function() { + return this.ancestors().length; + }; + + Node.prototype.parentNode = function() { + if (this.parentId != null) { + return this.tree[this.parentId]; + } else { + return null; + } + }; + + Node.prototype.removeChild = function(child) { + var i = $.inArray(child, this.children); + return this.children.splice(i, 1) + }; + + Node.prototype.render = function() { + var handler, + settings = this.settings, + target; + + if (settings.expandable === true && this.isBranchNode()) { + handler = function(e) { + $(this).parents("table").treetable("node", $(this).parents("tr").data(settings.nodeIdAttr)).toggle(); + return e.preventDefault(); + }; + + this.indenter.html(this.expander); + target = settings.clickableNodeNames === true ? this.treeCell : this.expander; + + target.off("click.treetable").on("click.treetable", handler); + target.off("keydown.treetable").on("keydown.treetable", function(e) { + if (e.keyCode == 13) { + handler.apply(this, [e]); + } + }); + } + + this.indenter[0].style.paddingLeft = "" + (this.level() * settings.indent) + "px"; + + return this; + }; + + Node.prototype.reveal = function() { + if (this.parentId != null) { + this.parentNode().reveal(); + } + return this.expand(); + }; + + Node.prototype.setParent = function(node) { + if (this.parentId != null) { + this.tree[this.parentId].removeChild(this); + } + this.parentId = node.id; + this.row.data(this.settings.parentIdAttr, node.id); + return node.addChild(this); + }; + + Node.prototype.show = function() { + if (!this.initialized) { + this._initialize(); + } + this.row.show(); + if (this.expanded()) { + this._showChildren(); + } + return this; + }; + + Node.prototype.toggle = function() { + if (this.expanded()) { + this.collapse(); + } else { + this.expand(); + } + return this; + }; + + Node.prototype._hideChildren = function() { + var child, _i, _len, _ref, _results; + _ref = this.children; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + _results.push(child.hide()); + } + return _results; + }; + + Node.prototype._initialize = function() { + var settings = this.settings; + + this.render(); + + if (settings.expandable === true && settings.initialState === "collapsed") { + this.collapse(); + } else { + this.expand(); + } + + if (settings.onNodeInitialized != null) { + settings.onNodeInitialized.apply(this); + } + + return this.initialized = true; + }; + + Node.prototype._showChildren = function() { + var child, _i, _len, _ref, _results; + _ref = this.children; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + _results.push(child.show()); + } + return _results; + }; + + return Node; + })(); + + Tree = (function() { + function Tree(table, settings) { + this.table = table; + this.settings = settings; + this.tree = {}; + + // Cache the nodes and roots in simple arrays for quick access/iteration + this.nodes = []; + this.roots = []; + } + + Tree.prototype.collapseAll = function() { + var node, _i, _len, _ref, _results; + _ref = this.nodes; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + _results.push(node.collapse()); + } + return _results; + }; + + Tree.prototype.expandAll = function() { + var node, _i, _len, _ref, _results; + _ref = this.nodes; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + _results.push(node.expand()); + } + return _results; + }; + + Tree.prototype.findLastNode = function (node) { + if (node.children.length > 0) { + return this.findLastNode(node.children[node.children.length - 1]); + } else { + return node; + } + }; + + Tree.prototype.loadRows = function(rows) { + var node, row, i; + + if (rows != null) { + for (i = 0; i < rows.length; i++) { + row = $(rows[i]); + + if (row.data(this.settings.nodeIdAttr) != null) { + node = new Node(row, this.tree, this.settings); + this.nodes.push(node); + this.tree[node.id] = node; + + if (node.parentId != null && this.tree[node.parentId]) { + this.tree[node.parentId].addChild(node); + } else { + this.roots.push(node); + } + } + } + } + + for (i = 0; i < this.nodes.length; i++) { + node = this.nodes[i].updateBranchLeafClass(); + } + + return this; + }; + + Tree.prototype.move = function(node, destination) { + // Conditions: + // 1: +node+ should not be inserted as a child of +node+ itself. + // 2: +destination+ should not be the same as +node+'s current parent (this + // prevents +node+ from being moved to the same location where it already + // is). + // 3: +node+ should not be inserted in a location in a branch if this would + // result in +node+ being an ancestor of itself. + var nodeParent = node.parentNode(); + if (node !== destination && destination.id !== node.parentId && $.inArray(node, destination.ancestors()) === -1) { + node.setParent(destination); + this._moveRows(node, destination); + + // Re-render parentNode if this is its first child node, and therefore + // doesn't have the expander yet. + if (node.parentNode().children.length === 1) { + node.parentNode().render(); + } + } + + if(nodeParent){ + nodeParent.updateBranchLeafClass(); + } + if(node.parentNode()){ + node.parentNode().updateBranchLeafClass(); + } + node.updateBranchLeafClass(); + return this; + }; + + Tree.prototype.removeNode = function(node) { + // Recursively remove all descendants of +node+ + this.unloadBranch(node); + + // Remove node from DOM () + node.row.remove(); + + // Remove node from parent children list + if (node.parentId != null) { + node.parentNode().removeChild(node); + } + + // Clean up Tree object (so Node objects are GC-ed) + delete this.tree[node.id]; + this.nodes.splice($.inArray(node, this.nodes), 1); + + return this; + } + + Tree.prototype.render = function() { + var root, _i, _len, _ref; + _ref = this.roots; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + root = _ref[_i]; + + // Naming is confusing (show/render). I do not call render on node from + // here. + root.show(); + } + return this; + }; + + Tree.prototype.sortBranch = function(node, sortFun) { + // First sort internal array of children + node.children.sort(sortFun); + + // Next render rows in correct order on page + this._sortChildRows(node); + + return this; + }; + + Tree.prototype.unloadBranch = function(node) { + // Use a copy of the children array to not have other functions interfere + // with this function if they manipulate the children array + // (eg removeNode). + var children = node.children.slice(0), + i; + + for (i = 0; i < children.length; i++) { + this.removeNode(children[i]); + } + + // Reset node's collection of children + node.children = []; + + node.updateBranchLeafClass(); + + return this; + }; + + Tree.prototype._moveRows = function(node, destination) { + var children = node.children, i; + + node.row.insertAfter(destination.row); + node.render(); + + // Loop backwards through children to have them end up on UI in correct + // order (see #112) + for (i = children.length - 1; i >= 0; i--) { + this._moveRows(children[i], node); + } + }; + + // Special _moveRows case, move children to itself to force sorting + Tree.prototype._sortChildRows = function(parentNode) { + return this._moveRows(parentNode, parentNode); + }; + + return Tree; + })(); + + // jQuery Plugin + methods = { + init: function(options, force) { + var settings; + + settings = $.extend({ + branchAttr: "ttBranch", + clickableNodeNames: false, + column: 0, + columnElType: "td", // i.e. 'td', 'th' or 'td,th' + expandable: false, + expanderTemplate: " ", + indent: 19, + indenterTemplate: "", + initialState: "collapsed", + nodeIdAttr: "ttId", // maps to data-tt-id + parentIdAttr: "ttParentId", // maps to data-tt-parent-id + stringExpand: "Expand", + stringCollapse: "Collapse", + + // Events + onInitialized: null, + onNodeCollapse: null, + onNodeExpand: null, + onNodeInitialized: null + }, options); + + return this.each(function() { + var el = $(this), tree; + + if (force || el.data("treetable") === undefined) { + tree = new Tree(this, settings); + tree.loadRows(this.rows).render(); + + el.addClass("treetable").data("treetable", tree); + + if (settings.onInitialized != null) { + settings.onInitialized.apply(tree); + } + } + + return el; + }); + }, + + destroy: function() { + return this.each(function() { + return $(this).removeData("treetable").removeClass("treetable"); + }); + }, + + collapseAll: function() { + this.data("treetable").collapseAll(); + return this; + }, + + collapseNode: function(id) { + var node = this.data("treetable").tree[id]; + + if (node) { + node.collapse(); + } else { + throw new Error("Unknown node '" + id + "'"); + } + + return this; + }, + + expandAll: function() { + this.data("treetable").expandAll(); + return this; + }, + + expandNode: function(id) { + var node = this.data("treetable").tree[id]; + + if (node) { + if (!node.initialized) { + node._initialize(); + } + + node.expand(); + } else { + throw new Error("Unknown node '" + id + "'"); + } + + return this; + }, + + loadBranch: function(node, rows) { + var settings = this.data("treetable").settings, + tree = this.data("treetable").tree; + + // TODO Switch to $.parseHTML + rows = $(rows); + + if (node == null) { // Inserting new root nodes + this.append(rows); + } else { + var lastNode = this.data("treetable").findLastNode(node); + rows.insertAfter(lastNode.row); + } + + this.data("treetable").loadRows(rows); + + // Make sure nodes are properly initialized + rows.filter("tr").each(function() { + tree[$(this).data(settings.nodeIdAttr)].show(); + }); + + if (node != null) { + // Re-render parent to ensure expander icon is shown (#79) + node.render().expand(); + } + + return this; + }, + + move: function(nodeId, destinationId) { + var destination, node; + + node = this.data("treetable").tree[nodeId]; + destination = this.data("treetable").tree[destinationId]; + this.data("treetable").move(node, destination); + + return this; + }, + + node: function(id) { + return this.data("treetable").tree[id]; + }, + + removeNode: function(id) { + var node = this.data("treetable").tree[id]; + + if (node) { + this.data("treetable").removeNode(node); + } else { + throw new Error("Unknown node '" + id + "'"); + } + + return this; + }, + + reveal: function(id) { + var node = this.data("treetable").tree[id]; + + if (node) { + node.reveal(); + } else { + throw new Error("Unknown node '" + id + "'"); + } + + return this; + }, + + sortBranch: function(node, columnOrFunction) { + var settings = this.data("treetable").settings, + prepValue, + sortFun; + + columnOrFunction = columnOrFunction || settings.column; + sortFun = columnOrFunction; + + if ($.isNumeric(columnOrFunction)) { + sortFun = function(a, b) { + var extractValue, valA, valB; + + extractValue = function(node) { + var val = node.row.find("td:eq(" + columnOrFunction + ")").text(); + // Ignore trailing/leading whitespace and use uppercase values for + // case insensitive ordering + return $.trim(val).toUpperCase(); + } + + valA = extractValue(a); + valB = extractValue(b); + + if (valA < valB) return -1; + if (valA > valB) return 1; + return 0; + }; + } + + this.data("treetable").sortBranch(node, sortFun); + return this; + }, + + unloadBranch: function(node) { + this.data("treetable").unloadBranch(node); + return this; + } + }; + + $.fn.treetable = function(method) { + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return methods.init.apply(this, arguments); + } else { + return $.error("Method " + method + " does not exist on jQuery.treetable"); + } + }; + + // Expose classes to world + this.TreeTable || (this.TreeTable = {}); + this.TreeTable.Node = Node; + this.TreeTable.Tree = Tree; +})(jQuery); Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/Comment.hbm.xml =================================================================== diff -u --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/Comment.hbm.xml (revision 0) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/Comment.hbm.xml (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/CommentLike.hbm.xml =================================================================== diff -u --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/CommentLike.hbm.xml (revision 0) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/CommentLike.hbm.xml (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/CommentSession.hbm.xml =================================================================== diff -u --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/CommentSession.hbm.xml (revision 0) +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/comments/CommentSession.hbm.xml (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + Index: lams_common/src/java/org/lamsfoundation/lams/beanRefContext.xml =================================================================== diff -u -rf3fbbb8ec1732de7e5ce51567287b42b55ab33e1 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_common/src/java/org/lamsfoundation/lams/beanRefContext.xml (.../beanRefContext.xml) (revision f3fbbb8ec1732de7e5ce51567287b42b55ab33e1) +++ lams_common/src/java/org/lamsfoundation/lams/beanRefContext.xml (.../beanRefContext.xml) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -41,6 +41,7 @@ /org/lamsfoundation/lams/applicationContext.xml /org/lamsfoundation/lams/toolApplicationContext.xml + /org/lamsfoundation/lams/comments/commentsContext.xml /org/lamsfoundation/lams/contentrepository/applicationContext.xml /org/lamsfoundation/lams/lesson/lessonApplicationContext.xml /org/lamsfoundation/lams/learning/learningApplicationContext.xml Index: lams_common/src/java/org/lamsfoundation/lams/comments/Comment.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/Comment.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/Comment.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,240 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments; + +import java.util.Date; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.lamsfoundation.lams.usermanagement.User; + +/** + * @author Fiona Malikoff + * + */ +public class Comment implements Cloneable { + + // message types - initially just one, the tool type. + public static final int EXTERNAL_TYPE_TOOL = 1; // externalId will be a toolSessionId + + private Long uid; + + private String body; + + private Date created; + private User createdBy; + private Date updated; + private User updatedBy; + + private Date lastReplyDate; + private int replyNumber; + private boolean hideFlag; + + private Comment rootComment; + private Comment threadComment; + private short commentLevel; + + private Comment parent; + private CommentSession session; + + /** Read only fields - calculated when loaded from the database */ + private Integer likeCount; + private Integer vote; + + public Comment() { + } + + /** + * Updates the modification data for this entity. + */ + public void updateModificationData(User user) { + long now = System.currentTimeMillis(); + if (created == null) { + this.setCreated(new Date(now)); + this.setCreatedBy(user); + } + this.setUpdated(new Date(now)); + this.setUpdatedBy(user); + } + + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Comment)) + return false; + + Comment genericEntity = (Comment) o; + + // uses same attributes to determine equality as + // ConditionTopicComparator.compare() + return new EqualsBuilder().append(this.body, genericEntity.getBody()) + .append(this.replyNumber, genericEntity.getReplyNumber()) + .append(this.createdBy, genericEntity.getCreatedBy()) + .append(this.updatedBy, genericEntity.getUpdatedBy()).isEquals(); + } + + public int hashCode() { + return new HashCodeBuilder().append(uid).append(body).append(created).append(updated).append(createdBy) + .append(updatedBy).toHashCode(); + } + + // ********************************************************** + // get/set methods + // ********************************************************** + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public User getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(User createdBy) { + this.createdBy = createdBy; + } + + public Date getUpdated() { + return updated; + } + + public void setUpdated(Date updated) { + this.updated = updated; + } + + public User getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(User updatedBy) { + this.updatedBy = updatedBy; + } + + public Date getLastReplyDate() { + return lastReplyDate; + } + + public void setLastReplyDate(Date lastReplyDate) { + this.lastReplyDate = lastReplyDate; + } + + public int getReplyNumber() { + return replyNumber; + } + + public void setReplyNumber(int replyNumber) { + this.replyNumber = replyNumber; + } + + public boolean isHideFlag() { + return hideFlag; + } + + public void setHideFlag(boolean hideFlag) { + this.hideFlag = hideFlag; + } + + public Comment getParent() { + return parent; + } + + public void setParent(Comment parent) { + this.parent = parent; + } + + public CommentSession getSession() { + return session; + } + + public void setSession(CommentSession session) { + this.session = session; + } + + public Comment getRootComment() { + return rootComment; + } + + public void setRootComment(Comment rootComment) { + this.rootComment = rootComment; + } + + public Comment getThreadComment() { + return threadComment; + } + + public void setThreadComment(Comment threadComment) { + this.threadComment = threadComment; + } + + public short getCommentLevel() { + return commentLevel; + } + + public void setCommentLevel(short commentLevel) { + this.commentLevel = commentLevel; + } + + public Integer getLikeCount() { + return likeCount; + } + + public void setLikeCount(Integer likeCount) { + this.likeCount = likeCount; + } + + public Integer getVote() { + return vote; + } + + public void setVote(Integer vote) { + this.vote = vote; + } + + public String toString() { + return new ToStringBuilder(this).append("uid", uid).append("body", body).toString(); + } + +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/CommentLike.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/CommentLike.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/CommentLike.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,74 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ + +package org.lamsfoundation.lams.comments; + +/** + * @author Fiona.Malikoff + * + */ +public class CommentLike { + + public static int LIKE = 1; + public static int DISLIKE = -1; + + private Long uid; + private Comment comment; + private Integer userId; + private int vote; + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public Comment getComment() { + return comment; + } + + public void setComment(Comment comment) { + this.comment = comment; + } + + public Integer getUserId() { + return userId; + } + + public void setUserId(Integer userId) { + this.userId = userId; + } + + public int getVote() { + return vote; + } + + public void setVote(int vote) { + this.vote = vote; + } + +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/CommentSession.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/CommentSession.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/CommentSession.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,73 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments; + +/** + * @author Fiona Malikoff + * + */ +public class CommentSession implements Cloneable { + + private Long uid; + private Long externalId; + private Integer externalIdType; + private String externalSignature; + + public CommentSession() { + } + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public Long getExternalId() { + return externalId; + } + + public void setExternalId(Long externalId) { + this.externalId = externalId; + } + + public Integer getExternalIdType() { + return externalIdType; + } + + public void setExternalIdType(Integer externalIdType) { + this.externalIdType = externalIdType; + } + + public String getExternalSignature() { + return externalSignature; + } + + public void setExternalSignature(String externalSignature) { + this.externalSignature = externalSignature; + } + + +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/commentsContext.xml =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/commentsContext.xml (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/commentsContext.xml (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED,readOnly + + + + Index: lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentDAO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentDAO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentDAO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,27 @@ +package org.lamsfoundation.lams.comments.dao; + +import java.util.List; +import java.util.SortedSet; + +import org.lamsfoundation.lams.comments.Comment; + +public interface ICommentDAO { + + public abstract void saveOrUpdate(Comment comment); + + public abstract Comment getById(Long commentId); + + /** + * Get the root (dummy / top level) topic in a Session. + * + * @param sessionId + * @return + */ + public abstract Comment getRootTopic(Long externalId, Integer externalIdType, String externalSignature); + + public abstract SortedSet getThreadByThreadId(Long threadCommentId, Integer sortBy, Integer userId); + + public abstract SortedSet getNextThreadByThreadId(final Long rootTopicId, final Long previousThreadMessageId, + Integer numberOfThreads, Integer sortBy, String extraSortParam, Integer userId); + +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentLikeDAO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentLikeDAO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentLikeDAO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,7 @@ +package org.lamsfoundation.lams.comments.dao; + + +public interface ICommentLikeDAO { + + boolean addLike( Long commentUid, Integer userId, Integer vote); +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentSessionDAO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentSessionDAO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dao/ICommentSessionDAO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,8 @@ +package org.lamsfoundation.lams.comments.dao; + +import org.lamsfoundation.lams.comments.CommentSession; + +public interface ICommentSessionDAO { + + void save( CommentSession session); +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentDAO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentDAO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentDAO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,248 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ + +package org.lamsfoundation.lams.comments.dao.hibernate; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.hibernate.SQLQuery; +import org.hibernate.type.IntegerType; +import org.lamsfoundation.lams.comments.Comment; +import org.lamsfoundation.lams.comments.dao.ICommentDAO; +import org.lamsfoundation.lams.comments.service.ICommentService; +import org.lamsfoundation.lams.comments.util.TopicComparator; +import org.lamsfoundation.lams.comments.util.TopicComparatorLike; +import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO; + +/** + * @author Fiona Malikoff + */ +public class CommentDAO extends LAMSBaseDAO implements ICommentDAO { + + /* Standard DAO call */ + + @Override + public void saveOrUpdate(Comment comment) { + saveObject(comment); + } + + @Override + public Comment getById(Long commentId) { + return (Comment) getSession().get(Comment.class, commentId); + } + + /* Session Level Lookups */ + private static final String SQL_QUERY_FIND_ROOT_TOPICS = "from " + Comment.class.getName() + " cs " + + " where cs.parent is null and cs.session.externalId=:externalId and " + + " cs.session.externalIdType=:externalIdType and cs.session.externalSignature=:externalSignature"; + + @Override + public Comment getRootTopic(Long externalId, Integer externalIdType, String externalSignature) { + @SuppressWarnings("rawtypes") + List list = getSession().createQuery(SQL_QUERY_FIND_ROOT_TOPICS).setLong("externalId", externalId) + .setInteger("externalIdType", externalIdType).setString("externalSignature", externalSignature).list(); + if (list != null && list.size() > 0) + return (Comment) list.get(0); + return null; + } + + /* Thread based lookups - Returns a complex structure so that the likes information can be passed + * back with it. */ + private static final String SQL_QUERY_GET_COMPLETE_THREAD = + "SELECT c.*, SUM(l.vote) likes_total, l2.vote user_vote FROM lams_comment c " + + " LEFT JOIN lams_comment_likes l ON c.uid = l.comment_uid " + + " LEFT JOIN lams_comment_likes l2 ON c.uid = l2.comment_uid AND l2.user_id=:userId " + + " WHERE c.thread_comment_uid = :threadId " + + " GROUP BY c.uid"; + + + @Override + @SuppressWarnings("unchecked") + public SortedSet getThreadByThreadId(Long threadCommentId, Integer sortBy, Integer userId) { + SQLQuery query = getSession().createSQLQuery(SQL_QUERY_GET_COMPLETE_THREAD); + query.addEntity("comment", Comment.class) + .addScalar("likes_total", IntegerType.INSTANCE) + .addScalar("user_vote", IntegerType.INSTANCE) + .setLong("userId", userId != null ? userId : 0) + .setLong("threadId", threadCommentId); + List results = query.list(); + return upgradeComments(results, sortBy); + } + + private SortedSet upgradeComments(List rawObjects, Integer sortBy) { + Comparator comparator = ICommentService.SORT_BY_LIKE.equals(sortBy) ? new TopicComparatorLike() : new TopicComparator(); + SortedSet results = new TreeSet(comparator); + for ( Object[] rawObject : rawObjects ) { + Comment comment = (Comment) rawObject[0]; + Integer likeCount = (Integer) rawObject[1]; + comment.setLikeCount(likeCount != null ? likeCount : 0); + Integer userVote = (Integer) rawObject[2]; + comment.setVote(userVote); + results.add(comment); + } + return results; + } + + @Override + public SortedSet getNextThreadByThreadId(final Long rootTopicId, final Long previousThreadMessageId, + Integer numberOfThreads, Integer sortBy, String extraSortParam, Integer userId) { + + if (ICommentService.SORT_BY_LIKE.equals(sortBy)) { + return getNextThreadByThreadIdLikes(rootTopicId, previousThreadMessageId, numberOfThreads, sortBy, extraSortParam, + userId); + } else { + return getNextThreadByThreadIdNewestFirst(rootTopicId, previousThreadMessageId, numberOfThreads, sortBy, userId); + } + + } + + private static final String SQL_QUERY_FIND_FIRST_THREAD_TOP_BY_UID = "SELECT uid FROM lams_comment" + + " WHERE root_comment_uid = :rootUid AND comment_level = 1 ORDER BY uid DESC"; + + private static final String SQL_QUERY_FIND_NEXT_THREAD_TOP = "SELECT uid FROM lams_comment" + + " WHERE root_comment_uid = :rootUid AND uid < :lastUid AND comment_level = 1 ORDER BY uid DESC"; + + private static final String SQL_QUERY_FIND_NEXT_THREAD_MESSAGES = + "SELECT c.*, SUM(l.vote) likes_total, l2.vote user_vote FROM lams_comment c " + + " LEFT JOIN lams_comment_likes l ON c.uid = l.comment_uid " + + " LEFT JOIN lams_comment_likes l2 ON c.uid = l2.comment_uid AND l2.user_id=:userId " + + " WHERE c.thread_comment_uid IN (:threadIds) " + + " GROUP BY c.uid"; + + + @SuppressWarnings({"unchecked" }) + private SortedSet getNextThreadByThreadIdNewestFirst(final Long rootTopicId, + final Long previousThreadMessageId, Integer numberOfThreads, Integer sortBy, Integer userId) { + + // the search to get to the top level is quite light, so get just the uids + // then build a complete set. + List threadUidList = null; + if (previousThreadMessageId == null || previousThreadMessageId == 0L) { + threadUidList = (List) getSession(). + createSQLQuery(SQL_QUERY_FIND_FIRST_THREAD_TOP_BY_UID) + .setLong("rootUid", rootTopicId) + .setMaxResults(numberOfThreads) + .list(); + } else { + threadUidList = (List) getSession() + .createSQLQuery(SQL_QUERY_FIND_NEXT_THREAD_TOP) + .setLong("rootUid", rootTopicId) + .setLong("lastUid", previousThreadMessageId) + .setMaxResults(numberOfThreads) + .list(); + } + + if (threadUidList != null && threadUidList.size() > 0) { + SQLQuery query = getSession().createSQLQuery(SQL_QUERY_FIND_NEXT_THREAD_MESSAGES); + query.addEntity("comment", Comment.class) + .addScalar("likes_total", IntegerType.INSTANCE) + .addScalar("user_vote", IntegerType.INSTANCE) + .setLong("userId", userId != null ? userId : 0) + .setParameterList("threadIds", threadUidList); + List results = query.list(); + return upgradeComments(results, sortBy); + } + return new TreeSet(); + } + + private static final String SQL_QUERY_FIND_FIRST_THREAD_TOP_BY_LIKES = "SELECT c.*, COALESCE(SUM(l.vote),0) likes_total, l2.vote user_vote" + + " FROM lams_comment c " + + " LEFT JOIN lams_comment_likes l ON c.uid = l.comment_uid " + + " LEFT JOIN lams_comment_likes l2 ON c.uid = l2.comment_uid AND l2.user_id=:userId " + + " WHERE root_comment_uid = :rootUid AND comment_level = 1 " + + " GROUP BY c.uid " + + " ORDER BY likes_total DESC, c.uid DESC"; + + private static final String SQL_QUERY_FIND_NEXT_THREAD_TOP_BY_LIKE = "SELECT * FROM ( " + + " SELECT c.*, COALESCE(SUM(l.vote),0) likes_total, l2.vote user_vote " + + " FROM lams_comment c " + + " LEFT JOIN lams_comment_likes l ON c.uid = l.comment_uid " + + " LEFT JOIN lams_comment_likes l2 ON c.uid = l2.comment_uid AND l2.user_id=:userId " + + " WHERE root_comment_uid = :rootUid AND comment_level = 1 " + + " GROUP BY c.uid " + + " ORDER BY likes_total DESC, c.uid DESC) cl " + + " WHERE (cl.likes_total = :like AND cl.uid < :lastUid ) " + + " OR cl.likes_total < :like"; + + private static final String SQL_QUERY_FIND_NEXT_THREAD_MESSAGES_REPLIES_ONLY = + "SELECT c.*, COALESCE(SUM(l.vote),0) likes_total, l2.vote user_vote FROM lams_comment c " + + " LEFT JOIN lams_comment_likes l ON c.uid = l.comment_uid " + + " LEFT JOIN lams_comment_likes l2 ON c.uid = l2.comment_uid AND l2.user_id=:userId " + + " WHERE c.thread_comment_uid IN (:threadIds) and comment_level > 1 " + + " GROUP BY c.uid"; + + @SuppressWarnings({ "unchecked" }) + private SortedSet getNextThreadByThreadIdLikes(final Long rootTopicId, final Long previousThreadMessageId, + Integer numberOfThreads, Integer sortBy, String extraSortParam, Integer userId) { + + // the search to get to the top level is quite heavy and involves grouping the likes, so get all the data + // for the top level then get the child replies. + List topThreadObjects = null; + if (previousThreadMessageId == null || previousThreadMessageId == 0L) { + topThreadObjects = (List) getSession().createSQLQuery(SQL_QUERY_FIND_FIRST_THREAD_TOP_BY_LIKES) + .addEntity("comment", Comment.class) + .addScalar("likes_total", IntegerType.INSTANCE) + .addScalar("user_vote", IntegerType.INSTANCE) + .setLong("rootUid", rootTopicId) + .setLong("userId", userId != null ? userId : 0) + .setMaxResults(numberOfThreads) + .list(); + } else { + // get more entries with the same number of likes or less likes + topThreadObjects = (List) getSession() + .createSQLQuery(SQL_QUERY_FIND_NEXT_THREAD_TOP_BY_LIKE) + .addEntity("comment", Comment.class) + .addScalar("likes_total", IntegerType.INSTANCE) + .addScalar("user_vote", IntegerType.INSTANCE) + .setLong("rootUid", rootTopicId) + .setLong("lastUid", previousThreadMessageId) + .setString("like", extraSortParam) + .setLong("userId", userId != null ? userId : 0) + .setMaxResults(numberOfThreads).list(); + } + if (topThreadObjects != null && topThreadObjects.size() > 0) { + // build the list of uids + List threadUidList = new ArrayList(); + for ( Object[] rawObject : topThreadObjects ) { + Comment comment = (Comment) rawObject[0]; + threadUidList.add(comment.getUid()); + } + SQLQuery query = getSession().createSQLQuery(SQL_QUERY_FIND_NEXT_THREAD_MESSAGES_REPLIES_ONLY); + query.addEntity("comment", Comment.class) + .addScalar("likes_total", IntegerType.INSTANCE) + .addScalar("user_vote", IntegerType.INSTANCE) + .setLong("userId", userId != null ? userId : 0) + .setParameterList("threadIds", threadUidList); + List results = query.list(); + topThreadObjects.addAll(results); + return upgradeComments(topThreadObjects, sortBy); + } + return new TreeSet(); + } +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentLikeDAO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentLikeDAO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentLikeDAO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,44 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ + +package org.lamsfoundation.lams.comments.dao.hibernate; + +import org.lamsfoundation.lams.comments.dao.ICommentLikeDAO; +import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO; + +public class CommentLikeDAO extends LAMSBaseDAO implements ICommentLikeDAO { + + private static String INSERT_LIKE = "INSERT IGNORE INTO lams_comment_likes(comment_uid, user_id, vote) VALUES (:comment,:user,:vote);"; + + public boolean addLike( Long commentUid, Integer userId, Integer vote) { + int status = getSession().createSQLQuery(INSERT_LIKE) + .setParameter("comment", commentUid) + .setParameter("user", userId) + .setParameter("vote", vote) + .executeUpdate(); + return status == 1; + } + +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentSessionDAO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentSessionDAO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dao/hibernate/CommentSessionDAO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,38 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ + +package org.lamsfoundation.lams.comments.dao.hibernate; + +import org.lamsfoundation.lams.comments.CommentSession; +import org.lamsfoundation.lams.comments.dao.ICommentSessionDAO; +import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO; + +public class CommentSessionDAO extends LAMSBaseDAO implements ICommentSessionDAO { + + public void save( CommentSession session) { + saveObject( session); + } + +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/dto/CommentDTO.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/dto/CommentDTO.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/dto/CommentDTO.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,139 @@ +package org.lamsfoundation.lams.comments.dto; +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.lamsfoundation.lams.comments.Comment; + + +public class CommentDTO { + + private Comment comment; + private String authorname; + private boolean isAuthor; + private boolean hasAttachment; + private short level; + private int threadNum; + private boolean liked; + private boolean disliked; + + /** + * Get a CommentDTO instance from a given Comment. + * + * @param msg + * @return + */ + public static CommentDTO getCommentDTO(Comment comment){ + if(comment == null) + return null; + + CommentDTO dto = new CommentDTO(); + dto.setComment(comment); + if(comment.getCreatedBy() != null) + dto.setAuthorname(comment.getCreatedBy().getFirstName()+" "+comment.getCreatedBy().getLastName()); + + dto.liked = false; + dto.disliked = false; + + return dto; + } + + /** + * Get a list of CommentDTO according to given list of Comment. + * @param msgList + * @return + */ + public static List getCommentDTO(List commentList){ + List retSet = new ArrayList(); + if(commentList == null || commentList.isEmpty()) + return retSet; + + Iterator iter = commentList.iterator(); + while(iter.hasNext()){ + Comment msg = iter.next(); + retSet.add(getCommentDTO(msg)); + } + return retSet; + } + //-------------------------------DTO get/set method------------------------------ + public String getAuthorname() { + return authorname; + } + public void setAuthorname(String authorname) { + this.authorname = authorname; + } + public boolean getHasAttachment() { + return hasAttachment; + } + public void setHasAttachment(boolean isAttachment) { + this.hasAttachment = isAttachment; + } + public Comment getComment() { + return comment; + } + public void setComment(Comment comment) { + this.comment = comment; + } + public short getLevel() { + return level; + } + public void setLevel(short level) { + this.level = level; + } + public int getThreadNum() { + return threadNum; + } + public void setThreadNum(int threadNum) { + this.threadNum = threadNum; + } + + public boolean isAuthor() { + return isAuthor; + } + + public void setIsAuthor(boolean isAuthor) { + this.isAuthor = isAuthor; + } + + public boolean isLiked() { + return liked; + } + + public void setLiked(boolean liked) { + this.liked = liked; + } + + public boolean isDisliked() { + return disliked; + } + + public void setDisliked(boolean disliked) { + this.disliked = disliked; + } + + +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/service/CommentService.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/service/CommentService.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/service/CommentService.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,276 @@ +/**************************************************************** + * Copyright (C) 2008 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.SortedSet; + +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.comments.Comment; +import org.lamsfoundation.lams.comments.CommentSession; +import org.lamsfoundation.lams.comments.dao.ICommentDAO; +import org.lamsfoundation.lams.comments.dao.ICommentLikeDAO; +import org.lamsfoundation.lams.comments.dao.ICommentSessionDAO; +import org.lamsfoundation.lams.comments.dto.CommentDTO; +import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.util.MessageService; +import org.lamsfoundation.lams.util.audit.IAuditService; + +/** + * + * Comments Widget Service + * + * @author Fiona Malikoff + * + */ +public class CommentService implements ICommentService { + + private static Logger log = Logger.getLogger(CommentService.class); + + private static final String MODULE_NAME = "Comments"; + + // Services + private IUserManagementService userService; + private MessageService messageService; + private IAuditService auditService; + private ICommentSessionDAO commentSessionDAO; + private ICommentDAO commentDAO; + private ICommentLikeDAO commentLikeDAO; + + @Override + public List getTopicThread(Long externalId, Integer externalType, String externalSignature, Long lastCommentSeqId, + Integer pageSize, Integer sortBy, String extraSortParam, Integer userId) { + + long lastThreadMessageUid = lastCommentSeqId != null ? lastCommentSeqId.longValue() : 0L; + + // hidden root of all the threads! + Comment rootTopic = commentDAO.getRootTopic(externalId, externalType, externalSignature); + + // first time through - no root topic. + if ( rootTopic == null ) { + return new ArrayList(); + } + + SortedSet comments = commentDAO.getNextThreadByThreadId(rootTopic.getUid(), lastThreadMessageUid, pageSize, sortBy, extraSortParam, userId); + return getSortedCommentDTO(comments); + } + + private List getSortedCommentDTO(SortedSet comments) { + + List msgDtoList = new ArrayList(); + for ( Comment comment : comments ) { + CommentDTO dto = CommentDTO.getCommentDTO(comment); + dto.setLevel(comment.getCommentLevel()); + dto.setThreadNum(comment.getThreadComment().getUid().intValue()); + msgDtoList.add(dto); + } + return msgDtoList; + } + + public List getThread( Long threadId, Integer sortBy, Integer userId ) { + SortedSet comments = commentDAO.getThreadByThreadId(threadId, sortBy, userId); + return getSortedCommentDTO(comments); + } + + // Do we need to synchronize this method? Would be nice but it is the equivalent of tool session creation + // and we don't synchonize them! + public Comment createOrGetRoot(Long externalId, Integer externalIdType, String externalSignature, User user) { + Comment rootComment = commentDAO.getRootTopic(externalId, externalIdType, externalSignature); + return ( rootComment != null ? rootComment : + createRoot(externalId, externalIdType, externalSignature, user) ); + } + + public Comment getRoot(Long externalId, Integer externalIdType, String externalSignature) { + return commentDAO.getRootTopic(externalId, externalIdType, externalSignature); + } + + private Comment createRoot(Long externalId, Integer externalIdType, String externalSignature, User user ) { + + CommentSession session = new CommentSession(); + session.setExternalId(externalId); + session.setExternalIdType(externalIdType); + session.setExternalSignature(externalSignature); + + Comment comment = new Comment(); + comment.setBody("Hidden Root Message"); + comment.setHideFlag(true); + comment.setReplyNumber(0); // no replies yet + comment.setSession(session); + comment.updateModificationData(user); + comment.setCommentLevel((short) 0); + comment.setRootComment(comment); + comment.setThreadComment(null); // this one is not part of a thread! + + commentSessionDAO.save(session); + commentDAO.saveOrUpdate(comment); + return comment; + } + + public Comment createReply(Comment parent, String replyText, User user) { + + Comment replyMessage = new Comment(); + replyMessage.setBody(replyText); + replyMessage.setHideFlag(false); + replyMessage.updateModificationData(user); + + replyMessage.setParent(parent); + replyMessage.setSession(parent.getSession()); + + Comment root = parent.getRootComment(); + replyMessage.setCommentLevel((short) (parent.getCommentLevel() + 1)); + replyMessage.setRootComment(root); + + // look back up through the parents to find the thread top - will be level 1 + if ( replyMessage.getCommentLevel() == 1 ) { + replyMessage.setThreadComment(replyMessage); + } else { + Comment threadComment = parent; + while ( threadComment.getCommentLevel() > 1 ) { + threadComment = threadComment.getParent(); + } + replyMessage.setThreadComment(threadComment); + } + + commentDAO.saveOrUpdate(replyMessage); + + // update last reply date for root message + root.setLastReplyDate(new Date()); + // update reply message number for root + root.setReplyNumber(root.getReplyNumber() + 1); + commentDAO.saveOrUpdate(root); + + return replyMessage; + } + + public boolean addLike(Long commentUid, User user, Integer likeVote) { + + return commentLikeDAO.addLike(commentUid, user.getUserId(), likeVote); + } + + public Comment hideComment(Long commentUid, User user, boolean status) { + + Comment comment = commentDAO.getById(commentUid); + comment.setHideFlag(status); + commentDAO.saveOrUpdate(comment); + return comment; + } + + + public Comment createReply(Long parentId, String replyText, User user) { + + Comment parent = commentDAO.getById(parentId); + return(createReply(parent, replyText, user)); + + } + + public Comment updateComment(Long commentUid, String newBody, User user, boolean makeAuditEntry) { + Comment comment = commentDAO.getById(commentUid); + + if ( comment != null && user != null ) { + if ( makeAuditEntry ) { + Long userId = 0L; + String loginName = "Default"; + if (comment.getCreatedBy() != null) { + userId = comment.getCreatedBy().getUserId().longValue(); + loginName = comment.getCreatedBy().getLogin(); + } + getAuditService().logChange(MODULE_NAME, userId, loginName, + comment.getBody(), newBody); + } + + comment.setBody(newBody); + comment.updateModificationData(user); + commentDAO.saveOrUpdate(comment); + + return comment; + } else { + log.error("Unable to update comment as comment not found or user missing. Comment uid "+commentUid + +" new body "+newBody + +" user "+(user!=null?user.getLogin():" missing")); + return null; + } + } + + public CommentDTO getComment(Long commentUid){ + Comment comment = commentDAO.getById(commentUid); + return comment != null ? CommentDTO.getCommentDTO(comment) : null; + } + + public IUserManagementService getUserService() { + return userService; + } + + public void setUserService(IUserManagementService userService) { + this.userService = userService; + } + + + public MessageService getMessageService() { + return messageService; + } + + public void setMessageService(MessageService messageService) { + this.messageService = messageService; + } + + public IAuditService getAuditService() { + return auditService; + } + + public void setAuditService(IAuditService auditService) { + this.auditService = auditService; + } + + public ICommentSessionDAO getCommentSessionDAO() { + return commentSessionDAO; + } + + public void setCommentSessionDAO(ICommentSessionDAO commentSessionDAO) { + this.commentSessionDAO = commentSessionDAO; + } + + public ICommentDAO getCommentDAO() { + return commentDAO; + } + + public void setCommentDAO(ICommentDAO commentDAO) { + this.commentDAO = commentDAO; + } + + public ICommentLikeDAO getCommentLikeDAO() { + return commentLikeDAO; + } + + public void setCommentLikeDAO(ICommentLikeDAO commentLikeDAO) { + this.commentLikeDAO = commentLikeDAO; + } + + + + + // ------------------------------------------------------------------------- +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/service/ICommentService.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/service/ICommentService.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/service/ICommentService.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,73 @@ +/**************************************************************** + * Copyright (C) 2008 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.comments.service; + +import java.util.List; + +import org.lamsfoundation.lams.comments.Comment; +import org.lamsfoundation.lams.comments.dto.CommentDTO; +import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.util.MessageService; + + +public interface ICommentService { + + public static final Integer SORT_BY_DATE = 0; + public static final Integer SORT_BY_LIKE = 1; + + + /** Gets the comments for a tool, based on the tool session and the tool's id. Allows for paging */ + List getTopicThread(Long externalId, Integer externalType, String externalSignature, Long lastMsgSeqId, + Integer pageSize, Integer sortBy, String extraSortParam, Integer userId); + + /** Saves a comment - either creating a whole tree one if there is no parent or saving under the given parent. */ + Comment createReply(Long parentId, String replyText, User user); + Comment createReply(Comment parent, String replyText, User user); + + /** Gets the dummy root for the comment system and if one doesn't exist for this session then set it up! */ + Comment createOrGetRoot(Long externalId, Integer externalIdType, String externalSignature, User user ); + + /** Gets the dummy root. If it doesn't exist, returns null. */ + Comment getRoot(Long externalId, Integer externalIdType, String externalSignature ); + + /** + * Get one complete thread within a topic Note that the return type is DTO. + */ + List getThread( Long threadId, Integer sortBy, Integer userId ); + + /** Get a single comment. Note that the return type is DTO */ + CommentDTO getComment(Long commentUid); + + /** Update the body in a comment, and update the modified time, who by, etc, etc */ + Comment updateComment(Long commentUid, String newBody, User user, boolean makeAuditEntry); + + /** Update the like list for Comment commentUid by likeVote. Returns true if like/dislike added. */ + boolean addLike(Long commentUid, User user, Integer likeVote); + + /** Update the hidden / not hidden flag */ + Comment hideComment(Long commentUid, User user, boolean status); + + /* Utility calls for the Action */ + MessageService getMessageService(); +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/comments/util/TopicComparator.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/util/TopicComparator.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/util/TopicComparator.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,97 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ + +package org.lamsfoundation.lams.comments.util; + +import java.util.Comparator; + +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.comments.Comment; + +/** + * This class implements the java.util.Comparator interface. Based on the Topic Comparator from Forum. + */ +public class TopicComparator implements Comparator { + private static final Logger log = Logger.getLogger(TopicComparator.class); + + public int compare(Comment msg1, Comment msg2) { + + short level1,level2; + level1 = msg1.getCommentLevel(); + level2 = msg2.getCommentLevel(); + Comment parent1,parent2; + //choose the smaller level value + short lessLevel = level1>level2? level2:level1; + for(int compareLevel=0;compareLevel <= lessLevel;compareLevel++){ + //init value, loop from current message + parent1 = msg1; + parent2 = msg2; + level1 = msg1.getCommentLevel(); + level2 = msg2.getCommentLevel(); + while(level1 > compareLevel){ + //get parent until assigned level + if(parent1 == null){ + log.error("Comment "+ parent1 +" level "+ level1 +" has null parent"); + return 0; + } + parent1 = parent1.getParent(); + level1--; + } + while(level2 > compareLevel){ + //get parent until assigned level + if(parent2 == null){ + log.error("Comment "+ parent2 +" level "+ level2 +" has null parent"); + return 0; + } + parent2 = parent2.getParent(); + level2--; + } + //this comparison will handle different branch node + if(parent1 != parent2){ + // show the newest first - this will have the higher uid + return compareUids(compareLevel, parent1, parent2); + } + //this comparison will handle same branch node + //the direct parent level, their parent(or themselves) are still equal + if(compareLevel==lessLevel){ + if(msg1.getCommentLevel() != msg2.getCommentLevel()) + return msg1.getCommentLevel() - msg2.getCommentLevel(); + else{ + return compareUids(compareLevel, msg1, parent2); + } + } + + } + return msg1.getUpdated().before(msg2.getUpdated())?-1:1; + } + + // If Level = 1 then "top" so sort newest first. Otherwise sort oldest first. + protected int compareUids(int compareLevel, Comment c1, Comment c2) { + if ( compareLevel <= 2 ) + return c1.getUid() < c2.getUid() ? 1 : -1; + else + return c1.getUid() > c2.getUid() ? 1 : -1; + } +} Index: lams_common/src/java/org/lamsfoundation/lams/comments/util/TopicComparatorLike.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/comments/util/TopicComparatorLike.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/comments/util/TopicComparatorLike.java (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,45 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ + +package org.lamsfoundation.lams.comments.util; + +import org.lamsfoundation.lams.comments.Comment; + +/** + * This class implements the java.util.Comparator interface. Sorts Level 1 and Level 2 on # likes, Level 3 onwards on creation date/time. + */ +public class TopicComparatorLike extends TopicComparator { + + protected int compareUids(int compareLevel, Comment c1, Comment c2) { + if ( compareLevel <= 2 ) { + if ( c1.getLikeCount() == c2.getLikeCount() ) + return c1.getUid() < c2.getUid() ? 1 : -1; + else + return c1.getLikeCount() < c2.getLikeCount() ? 1 : -1; + } else { + return c1.getUid() > c2.getUid() ? 1 : -1; + } + } +} Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040053.sql =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040053.sql (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040053.sql (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,62 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; + +-- LDEV-3631 Simple Commenting Widget +CREATE TABLE IF NOT EXISTS lams_comment_session ( + `uid` bigint(20) NOT NULL AUTO_INCREMENT, + `external_id` bigint(20) DEFAULT NULL, + `external_id_type` int(1) DEFAULT NULL, + `external_signature` varchar(40) DEFAULT NULL, + PRIMARY KEY (`uid`), + UNIQUE INDEX `comment_ext_sig_user` (`external_id`,`external_id_type`,`external_signature`) +); + +CREATE TABLE IF NOT EXISTS lams_comment ( + `uid` bigint(20) NOT NULL AUTO_INCREMENT, + `session_id` bigint(20) NOT NULL, + `body` text DEFAULT NULL, + `create_by` bigint(20) DEFAULT NULL, + `create_date` datetime DEFAULT NULL, + `update_date` datetime DEFAULT NULL, + `update_by` bigint(20) DEFAULT NULL, + `last_modified` datetime DEFAULT NULL, + `last_reply_date` datetime DEFAULT NULL, + `reply_number` int(11) DEFAULT NULL, + `hide_flag` smallint(6) DEFAULT NULL, + `parent_uid` bigint(20) DEFAULT NULL, + `root_comment_uid` bigint(20) DEFAULT NULL, + `comment_level` smallint(6) DEFAULT NULL, + `thread_comment_uid` bigint(20) DEFAULT NULL, + PRIMARY KEY (`uid`), + KEY `FK_comment_session` (`session_id`), + KEY `FK_comment_create` (`create_by`), + KEY `FK_comment_modify` (`update_by`), + KEY `FK_comment_parent` (`parent_uid`), + KEY `FK_comment_root` (`root_comment_uid`), + KEY `FK_comment_thread` (`thread_comment_uid`), + CONSTRAINT `FK_comment_session` FOREIGN KEY (`session_id`) REFERENCES `lams_comment_session` (`uid`), + CONSTRAINT `FK_comment_create` FOREIGN KEY (`create_by`) REFERENCES `lams_user` (`user_id`), + CONSTRAINT `FK_comment_modify` FOREIGN KEY (`update_by`) REFERENCES `lams_user` (`user_id`), + CONSTRAINT `FK_comment_parent` FOREIGN KEY (`parent_uid`) REFERENCES `lams_comment` (`uid`), + CONSTRAINT `FK_comment_root` FOREIGN KEY (`root_comment_uid`) REFERENCES `lams_comment` (`uid`), + CONSTRAINT `FK_comment_thread` FOREIGN KEY (`thread_comment_uid`) REFERENCES `lams_comment` (`uid`) +); + +CREATE TABLE IF NOT EXISTS lams_comment_likes ( + `uid` bigint(20) NOT NULL AUTO_INCREMENT, + `comment_uid` bigint(20) NOT NULL, + `user_id` bigint(20) NOT NULL, + `vote` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`uid`), + UNIQUE INDEX `comment_like_unique` (`comment_uid`, `user_id`), + KEY `FK_commentlike_comment` (`comment_uid`), + KEY `FK_commentlike_user` (`user_id`), + CONSTRAINT `FK_commentlike_comment` FOREIGN KEY (`comment_uid`) REFERENCES `lams_comment` (`uid`), + CONSTRAINT `FK_commentlike_user` FOREIGN KEY (`user_id`) REFERENCES `lams_user` (`user_id`) +); + +COMMIT; +SET AUTOCOMMIT = 1; + + + Index: lams_tool_nb/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rf7140a37e1b5b7a2d2e3fc6c80ecdb2fc6f76ccd -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision f7140a37e1b5b7a2d2e3fc6c80ecdb2fc6f76ccd) +++ lams_tool_nb/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -57,6 +57,10 @@ monitor.summary.td.notebookInstructions =Notebook instructions label.authoring.heading =Noticeboard Authoring button.submit =Finish +advanced.allow.comments=Allow learner comments +label.view.comments=View Comments +advanced.comments.like.only=Like Button Only +advanced.comments.like.and.dislike=Like and Dislike Buttons #======= End labels: Exported 50 labels for en AU ===== Index: lams_tool_nb/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -rf7140a37e1b5b7a2d2e3fc6c80ecdb2fc6f76ccd -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision f7140a37e1b5b7a2d2e3fc6c80ecdb2fc6f76ccd) +++ lams_tool_nb/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -57,6 +57,10 @@ monitor.summary.td.notebookInstructions =Notebook instructions label.authoring.heading =Noticeboard Authoring button.submit =Finish +advanced.allow.comments=Allow learner comments +label.view.comments=View Comments +advanced.comments.like.only=Like Button Only +advanced.comments.like.and.dislike=Like and Dislike Buttons #======= End labels: Exported 50 labels for en AU ===== Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/NoticeboardConstants.java =================================================================== diff -u -rbe07c35c372d904a65581d98660e73f3b13b69db -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/NoticeboardConstants.java (.../NoticeboardConstants.java) (revision be07c35c372d904a65581d98660e73f3b13b69db) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/NoticeboardConstants.java (.../NoticeboardConstants.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -62,7 +62,8 @@ public static final String MONITOR_PAGE = "monitorPage"; public static final String MONITOR_REFLECTION_PAGE ="monitorReflectionPage"; - + public static final String MONITOR_COMMENTS_PAGE ="monitorCommentsPage"; + // ------------------------------ // Action Names // ------------------------------ Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/NoticeboardContent.java =================================================================== diff -u -rf76780e7823d8ac845669773cc86f5a30012159a -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/NoticeboardContent.java (.../NoticeboardContent.java) (revision f76780e7823d8ac845669773cc86f5a30012159a) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/NoticeboardContent.java (.../NoticeboardContent.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -73,7 +73,13 @@ /** nullable persistent field */ private Date dateUpdated; - + + /** nullable persistent field */ + private boolean allowComments; + + /** nullable persistent field */ + private boolean commentsLikeAndDislike; + /** persistent field */ private Set nbSessions = new HashSet(); @@ -83,8 +89,8 @@ /** full constructor */ public NoticeboardContent(Long nbContentId, String title, String content, boolean defineLater, - boolean reflectOnActivity, String reflectInstructions, boolean contentInUse, Long creatorUserId, - Date dateCreated, Date dateUpdated) { + boolean reflectOnActivity, String reflectInstructions, boolean contentInUse, Long creatorUserId, + Date dateCreated, Date dateUpdated, boolean allowComments, boolean commentsLikeAndDislike) { this.nbContentId = nbContentId; this.title = title; this.content = content; @@ -95,6 +101,8 @@ this.creatorUserId = creatorUserId; this.dateCreated = dateCreated; this.dateUpdated = dateUpdated; + this.allowComments = allowComments; + this.commentsLikeAndDislike = commentsLikeAndDislike; } /** @@ -113,6 +121,8 @@ this.creatorUserId = null; this.dateCreated = dateCreated; this.dateUpdated = null; + this.allowComments = false; + this.commentsLikeAndDislike = false; } /** @@ -213,6 +223,38 @@ } /** + * @hibernate.property column="allow_comments" length="1" + */ + + public boolean isAllowComments() { + return allowComments; + } + + /** + * @param allowComments + * The allowComments to set. + */ + public void setAllowComments(boolean allowComments) { + this.allowComments = allowComments; + } + + /** + * @hibernate.property column="comments_like_dislike" length="1" + */ + + public boolean isCommentsLikeAndDislike() { + return commentsLikeAndDislike; + } + + /** + * @param commentsLikeAndDislike + * The commentsLikeAndDislike to set. + */ + public void setCommentsLikeAndDislike(boolean commentsLikeAndDislike) { + this.commentsLikeAndDislike = commentsLikeAndDislike; + } + + /** * @hibernate.property column="nb_content_id" length="20" not-null="true" */ @@ -277,7 +319,7 @@ RepositoryCheckedException { NoticeboardContent newContent = new NoticeboardContent(toContentId, nb.getTitle(), nb.getContent(), nb.isDefineLater(), nb.getReflectOnActivity(), nb.getReflectInstructions(), nb.isContentInUse(), - nb.getCreatorUserId(), nb.getDateCreated(), nb.getDateUpdated()); + nb.getCreatorUserId(), nb.getDateCreated(), nb.getDateUpdated(), nb.isAllowComments(), nb.isCommentsLikeAndDislike()); return newContent; } Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/dbupdates/patch20151210.sql =================================================================== diff -u --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/dbupdates/patch20151210.sql (revision 0) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/dbupdates/patch20151210.sql (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,16 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS=0; +----------------------Put all sql statements below here------------------------- + +-- LDEV-3631 Use simple commenting widget +ALTER TABLE tl_lanb11_content ADD COLUMN allow_comments TINYINT(1) DEFAULT 0; + +UPDATE lams_tool SET tool_version='20151210' WHERE tool_signature='lanb11'; + +----------------------Put all sql statements above here------------------------- + +-- If there were no errors, commit and restore autocommit to on +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/dbupdates/patch20160105.sql =================================================================== diff -u --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/dbupdates/patch20160105.sql (revision 0) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/dbupdates/patch20160105.sql (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,16 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS=0; +----------------------Put all sql statements below here------------------------- + +-- LDEV-3631 Use simple commenting widget +ALTER TABLE tl_lanb11_content ADD COLUMN comments_like_dislike TINYINT(1) DEFAULT 0; + +UPDATE lams_tool SET tool_version='20160105' WHERE tool_signature='lanb11'; + +----------------------Put all sql statements above here------------------------- + +-- If there were no errors, commit and restore autocommit to on +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbAuthoringForm.java =================================================================== diff -u -rbe07c35c372d904a65581d98660e73f3b13b69db -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbAuthoringForm.java (.../NbAuthoringForm.java) (revision be07c35c372d904a65581d98660e73f3b13b69db) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbAuthoringForm.java (.../NbAuthoringForm.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -77,6 +77,8 @@ private String contentFolderID; private String defineLater; + private boolean allowComments; + private boolean commentsLikeAndDislike; private boolean reflectOnActivity; private String reflectInstructions; @@ -95,6 +97,18 @@ this.defineLater = defineLater; } + public boolean isAllowComments() { + return allowComments; + } + public void setAllowComments(boolean allowComments) { + this.allowComments = allowComments; + } + public boolean isCommentsLikeAndDislike() { + return commentsLikeAndDislike; + } + public void setCommentsLikeAndDislike(boolean commentsLikeAndDislike) { + this.commentsLikeAndDislike = commentsLikeAndDislike; + } public boolean getReflectOnActivity() { return reflectOnActivity; } @@ -194,6 +208,8 @@ { setTitle(nbContent.getTitle()); setBasicContent(nbContent.getContent()); + setAllowComments(nbContent.isAllowComments()); + setCommentsLikeAndDislike(nbContent.isCommentsLikeAndDislike()); setReflectOnActivity(nbContent.getReflectOnActivity()); setReflectInstructions(nbContent.getReflectInstructions()); } @@ -202,6 +218,8 @@ { nbContent.setTitle(getTitle()); nbContent.setContent(getBasicContent()); + nbContent.setAllowComments(isAllowComments()); + nbContent.setCommentsLikeAndDislike(isCommentsLikeAndDislike()); if (defineLater == null || defineLater.length() == 0) { // ie. If defineLater is null or empty, this means we are in authoring nbContent.setReflectOnActivity(getReflectOnActivity()); Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbLearnerAction.java =================================================================== diff -u -r8aded0b18a5a7712622ae56f38923fa297449977 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbLearnerAction.java (.../NbLearnerAction.java) (revision 8aded0b18a5a7712622ae56f38923fa297449977) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbLearnerAction.java (.../NbLearnerAction.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -204,6 +204,8 @@ NoticeboardContent nbContent = nbService.retrieveNoticeboardBySessionID(toolSessionID); request.setAttribute("reflectInstructions", nbContent.getReflectInstructions()); request.setAttribute("title", nbContent.getTitle()); + request.setAttribute("allowComments", nbContent.isAllowComments()); + request.setAttribute("likeAndDislike", nbContent.isCommentsLikeAndDislike()); // get the existing reflection entry NotebookEntry entry = nbService.getEntry(toolSessionID, CoreNotebookConstants.NOTEBOOK_TOOL, NoticeboardConstants.TOOL_SIGNATURE, getUserID(request).intValue()); Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbLearnerStarterAction.java =================================================================== diff -u -rbe07c35c372d904a65581d98660e73f3b13b69db -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbLearnerStarterAction.java (.../NbLearnerStarterAction.java) (revision be07c35c372d904a65581d98660e73f3b13b69db) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbLearnerStarterAction.java (.../NbLearnerStarterAction.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -185,9 +185,11 @@ request.setAttribute("reflectEntry", notebookEntry.getEntry()); } request.setAttribute("reflectInstructions", nbContent.getReflectInstructions()); - request.setAttribute("reflectOnActivity", nbContent.getReflectOnActivity()); + request.setAttribute("reflectOnActivity", nbContent.getReflectOnActivity()); + request.setAttribute("allowComments", nbContent.isAllowComments()); + request.setAttribute("likeAndDislike", nbContent.isCommentsLikeAndDislike()); - Boolean userFinished = (nbUser!=null && NoticeboardUser.COMPLETED.equals(nbUser.getUserStatus())); + Boolean userFinished = (nbUser!=null && NoticeboardUser.COMPLETED.equals(nbUser.getUserStatus())); request.setAttribute("userFinished", userFinished); LearningWebUtil.putActivityPositionInRequestByToolSessionId(toolSessionID, request, getServlet() Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbMonitoringAction.java =================================================================== diff -u -r4cdf3239119d4b0bb10b6bb3db6c6051bb0762da -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbMonitoringAction.java (.../NbMonitoringAction.java) (revision 4cdf3239119d4b0bb10b6bb3db6c6051bb0762da) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbMonitoringAction.java (.../NbMonitoringAction.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -71,6 +71,7 @@ * input=".monitoringContent" validate="false" parameter="method" * @struts:action-forward name="monitorPage" path="/monitoring/monitoring.jsp" * @struts:action-forward name="monitorReflectionPage" path="/monitoring/reflection.jsp" + * @struts:action-forward name="monitorCommentsPage" path="/monitoring/comments.jsp" * ----------------XDoclet Tags-------------------- */ public class NbMonitoringAction extends LamsDispatchAction { @@ -119,13 +120,15 @@ Set sessions = content.getNbSessions(); Iterator i = sessions.iterator(); - Map map = new HashMap(); + Map numUsersMap = new HashMap(); + Map sessionIdMap = new HashMap(); List reflections = new ArrayList(); while (i.hasNext()) { NoticeboardSession session = (NoticeboardSession) i.next(); int numUsersInSession = nbService.getNumberOfUsersInSession(session); - map.put(session.getNbSessionName(), new Integer(numUsersInSession)); + numUsersMap.put(session.getNbSessionName(), new Integer(numUsersInSession)); + sessionIdMap.put(session.getNbSessionName(), session.getNbSessionId()); // Get list of users that have made a reflection entry if (content.getReflectOnActivity()) { List sessionUsers = nbService.getUsersBySession(session.getNbSessionId()); @@ -143,16 +146,19 @@ } } } - monitorForm.setGroupStatsMap(map); + monitorForm.setGroupStatsMap(numUsersMap); + monitorForm.setSessionIdMap(sessionIdMap); boolean isGroupedActivity = nbService.isGroupedActivity(toolContentId); request.setAttribute("isGroupedActivity", isGroupedActivity); - + // Set reflection statistics, if reflection is set request.setAttribute("reflectOnActivity", content.getReflectOnActivity()); request.setAttribute("reflectInstructions", content.getReflectInstructions()); request.setAttribute("reflections", reflections); - + + request.setAttribute("allowComments", content.isAllowComments()); + String currentTab = WebUtil.readStrParam(request, AttributeNames.PARAM_CURRENT_TAB,true); monitorForm.setCurrentTab(currentTab != null? currentTab : SUMMARY_TABID); request.setAttribute(FORM, monitorForm); @@ -178,5 +184,15 @@ return mapping.findForward(NoticeboardConstants.MONITOR_REFLECTION_PAGE); } - + + public ActionForward viewComments ( + ActionMapping mapping, + ActionForm form, + HttpServletRequest request, + HttpServletResponse response) throws NbApplicationException + { + request.setAttribute(NoticeboardConstants.TOOL_SESSION_ID, request.getParameter(NoticeboardConstants.TOOL_SESSION_ID)); + return mapping.findForward(NoticeboardConstants.MONITOR_COMMENTS_PAGE); + } + } Index: lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbMonitoringForm.java =================================================================== diff -u -rbe07c35c372d904a65581d98660e73f3b13b69db -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbMonitoringForm.java (.../NbMonitoringForm.java) (revision be07c35c372d904a65581d98660e73f3b13b69db) +++ lams_tool_nb/src/java/org/lamsfoundation/lams/tool/noticeboard/web/NbMonitoringForm.java (.../NbMonitoringForm.java) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -72,6 +72,7 @@ private String contentEditable; private Integer totalLearners; private Map groupStatsMap; + private Map sessionIdMap; /** * @return Returns the parametersToAppend. @@ -154,4 +155,10 @@ public void setTotalLearners(Integer totalLearners) { this.totalLearners = totalLearners; } + public Map getSessionIdMap() { + return sessionIdMap; + } + public void setSessionIdMap(Map sessionIdMap) { + this.sessionIdMap = sessionIdMap; + } } Index: lams_tool_nb/web/WEB-INF/tags/Comments.tag =================================================================== diff -u --- lams_tool_nb/web/WEB-INF/tags/Comments.tag (revision 0) +++ lams_tool_nb/web/WEB-INF/tags/Comments.tag (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,48 @@ +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-lams" prefix="lams"%> +<%@ taglib uri="tags-function" prefix="fn" %> + +<%@ attribute name="toolSessionId" required="true" rtexprvalue="true"%> +<%@ attribute name="toolSignature" required="true" rtexprvalue="true"%> +<%@ attribute name="height" required="false" rtexprvalue="true"%> +<%@ attribute name="width" required="false" rtexprvalue="true"%> +<%@ attribute name="mode" required="false" rtexprvalue="true"%> +<%@ attribute name="likeAndDislike" required="false" rtexprvalue="true"%> +<%@ attribute name="readOnly" required="false" rtexprvalue="true"%> +<%@ attribute name="pageSize" required="false" rtexprvalue="true"%> +<%@ attribute name="sortBy" required="false" rtexprvalue="true"%> + + + + + + + + + + + + + &mode=${mode} + + + + + + + + + + + + + + + + + \ No newline at end of file Index: lams_tool_nb/web/WEB-INF/tags/CommentsAuthor.tag =================================================================== diff -u --- lams_tool_nb/web/WEB-INF/tags/CommentsAuthor.tag (revision 0) +++ lams_tool_nb/web/WEB-INF/tags/CommentsAuthor.tag (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -0,0 +1,51 @@ +<%@ taglib uri="tags-core" prefix="c"%> +<%@ taglib uri="tags-html" prefix="html"%> +<%@ taglib uri="tags-fmt" prefix="fmt"%> + +<%@ attribute name="allowCommentsVariableName" required="false" rtexprvalue="true"%> +<%@ attribute name="allowCommentLabelKey" required="false" rtexprvalue="true"%> +<%@ attribute name="likeDislikeVariableName" required="false" rtexprvalue="true"%> +<%@ attribute name="likeOnlyCommentLabelKey" required="false" rtexprvalue="true"%> +<%@ attribute name="likeDislikeLabelKey" required="false" rtexprvalue="true"%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: lams_tool_nb/web/WEB-INF/tlds/lams/lams.tld =================================================================== diff -u -r86a2855c49422f236ac31c35df38fa3c66220dcc -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/web/WEB-INF/tlds/lams/lams.tld (.../lams.tld) (revision 86a2855c49422f236ac31c35df38fa3c66220dcc) +++ lams_tool_nb/web/WEB-INF/tlds/lams/lams.tld (.../lams.tld) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -481,5 +481,13 @@ head /WEB-INF/tags/Head.tag + + Comments + /WEB-INF/tags/Comments.tag + + + CommentsAuthor + /WEB-INF/tags/CommentsAuthor.tag + Index: lams_tool_nb/web/authoring/advance.jsp =================================================================== diff -u -r1977f2d6d4038839d28238041d7e5cd4dfd35036 -rd3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746 --- lams_tool_nb/web/authoring/advance.jsp (.../advance.jsp) (revision 1977f2d6d4038839d28238041d7e5cd4dfd35036) +++ lams_tool_nb/web/authoring/advance.jsp (.../advance.jsp) (revision d3c9d3f03c16e8f7025d7b2bfe84e68af4fc0746) @@ -2,6 +2,7 @@ + -<%@ page - import="org.lamsfoundation.lams.tool.noticeboard.NoticeboardConstants"%> +<%@ page import="org.lamsfoundation.lams.tool.noticeboard.NoticeboardConstants"%> + + + + + +
+ + + +
+ + +
CommentDTO
Comment
java.util.Comparator
-<%@ page - import="org.lamsfoundation.lams.tool.noticeboard.NoticeboardConstants"%> +<%@ page import="org.lamsfoundation.lams.tool.noticeboard.NoticeboardConstants"%> + + + + + +