DocForumService.java
package sk.iway.iwcm.components.forum.rest;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import sk.iway.Password;
import sk.iway.iwcm.Adminlog;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.DB;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.LabelValueDetails;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.RequestBean;
import sk.iway.iwcm.SetCharacterEncodingFilter;
import sk.iway.iwcm.SpamProtection;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.common.CloudToolsForCore;
import sk.iway.iwcm.common.DocTools;
import sk.iway.iwcm.common.ForumTools;
import sk.iway.iwcm.common.SearchTools;
import sk.iway.iwcm.components.forum.jpa.DocForumEntity;
import sk.iway.iwcm.components.forum.jpa.DocForumIdAndParentId;
import sk.iway.iwcm.components.forum.jpa.DocForumRepository;
import sk.iway.iwcm.components.forum.jpa.ForumGroupEntity;
import sk.iway.iwcm.database.ComplexQuery;
import sk.iway.iwcm.database.Mapper;
import sk.iway.iwcm.database.SimpleQuery;
import sk.iway.iwcm.doc.DocDB;
import sk.iway.iwcm.doc.DocDetails;
import sk.iway.iwcm.doc.GroupDetails;
import sk.iway.iwcm.doc.GroupsDB;
import sk.iway.iwcm.doc.ShowDoc;
import sk.iway.iwcm.forum.ForumDB;
import sk.iway.iwcm.forum.ForumSortBy;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.system.datatable.json.LabelValue;
import sk.iway.iwcm.system.fulltext.indexed.Forums;
import sk.iway.iwcm.users.UsersDB;
import sk.iway.spirit.MediaDB;
import sk.iway.spirit.model.Media;
public class DocForumService {
private static final String SAVE_FORUM_SUCCESS = "/components/forum/saveok";
private DocForumService() {
//utility class
}
/**
* Represent recursive action's upon forum's.
*/
public enum ActionType {
DELETE, //Delete forum recursive (with all child's)
RECOVER, //Recover forum recursive (with all child's)
APPROVE, //Approve forum recursive (with all child's)
REJECT, //Reject forum recursive (with all child's)
LOCK, //Lock forum recursive (with all child's)
UNLOCK //Unlock forum recursive (with all child's)
}
/**
* Return icon options for status icon select.
* @param prop
* @return
*/
public static List<LabelValue> getStatusIconOptions(Prop prop) {
List<LabelValue> icons = new ArrayList<>();
icons.add(new LabelValue("<i class=\"ti ti-circle-check\" style=\"color: #00be9f;\"></i> " + prop.getText("apps.forum.icon.confirmed"), "confirmed:true"));
icons.add(new LabelValue("<i class=\"ti ti-circle-x\" style=\"color: #ff4b58;\"></i> " + prop.getText("apps.forum.icon.non_confirmed"), "confirmed:false"));
icons.add(new LabelValue("<i class=\"ti ti-lock\" style=\"color: #000000;\"></i> " + prop.getText("apps.forum.icon.non_active"), "active:false"));
icons.add(new LabelValue("<i class=\"ti ti-lock-open\" style=\"color: #000000;\"></i> " + prop.getText("apps.forum.icon.active"), "active:true"));
icons.add(new LabelValue("<i class=\"ti ti-trash\" style=\"color: #fabd00;\"></i> " + prop.getText("apps.forum.icon.deleted"), "deleted:true"));
icons.add(new LabelValue("<i class=\"ti ti-trash-off\" style=\"color: #fabd00;\"></i> " + prop.getText("apps.forum.icon.not_deleted"), "deleted:false"));
return icons;
}
/******************************* MAIN LOGIC methods (save / delete / recover / approve / reject) ****************************************** */
/**
* Save given ForumFormBean entity, update and send confirmation notification email
*
* @param request
* @param response
* @param forumForm - entity to be saved (forum)
* @return
* @throws IOException
*/
public static String saveDocForum(HttpServletRequest request, HttpServletResponse response, DocForumEntity forumForm) {
DocForumRepository docForumRepository = getDocForumRepository();
HttpSession session = request.getSession();
Identity sender = (Identity) session.getAttribute(Constants.USER_KEY);
Integer domainId = Tools.getIntValue(DocDB.getDomain(request), 1);
int userId = -1;
String hashCode;
RequestBean rb = SetCharacterEncodingFilter.getCurrentRequestBean();
String language = rb.getLng();
Prop prop = Prop.getInstance(language);
if (sender == null || sender.isAdmin() == false) {
synchronized (DocForumRepository.class) {
//kontrola pred duplicitnym odoslanim prispevku (nejaka ajax haluz) - http://helpdesk.interway.sk/?bugID=4676
//v access logu servera sa zaznamy skutocne nasli viac krat, preco nikto netusi
String lastSessionText = (String)session.getAttribute("DocForumRepository.lastText");
if (lastSessionText!=null && lastSessionText.equals(forumForm.getQuestion())) {
setPermissionDenied(request, "sameText");
return SAVE_FORUM_SUCCESS;
}
session.setAttribute("DocForumRepository.lastText", forumForm.getQuestion());
}
}
//Get forum group aka setting
ForumGroupEntity forumGroup = ForumGroupService.getForum(forumForm.getDocId(), true);
//Check if parent we answering to (if set) is active
//If not permit this comment / forum ...
if(forumForm.getParentId() > 0) {
boolean isParentActive = (new SimpleQuery()).forBoolean("SELECT active FROM document_forum WHERE forum_id=?", forumForm.getParentId());
if(!isParentActive) {
boolean isAdmin = false;
if(forumGroup != null && forumGroup.isAdmin(sender)) isAdmin = true;
//Only admin can be exception for restriction
if(!isAdmin) {
setError(request, prop.getText("components.forum.active.error"));
return SAVE_FORUM_SUCCESS;
}
}
}
if (forumGroup == null) {
//najdi parent adresar a skus podla neho
DocDetails docDetails = DocDB.getInstance().getBasicDocDetails(forumForm.getDocId(), false);
if (docDetails != null) {
GroupDetails group = GroupsDB.getInstance().getGroup(docDetails.getGroupId());
if (group != null) {
GroupDetails parentGroup = GroupsDB.getInstance().getGroup(group.getParentGroupId());
if (parentGroup!=null)
forumGroup = ForumGroupService.getForum(parentGroup.getDefaultDocId(), true);
}
}
}
if (forumGroup == null) {
DocDetails docDetails = DocDB.getInstance().getDoc(forumForm.getDocId());
if (docDetails != null) {
int defaultDocId = docDetails.getGroup().getDefaultDocId();
DocDetails defaultDocDetails = DocDB.getInstance().getDoc(defaultDocId);
forumGroup = ForumGroupService.getForum(defaultDocDetails.getDocId(), true);
}
}
ForumGroupEntity forumGroupPerms = forumGroup;
if (forumGroupPerms == null && Tools.isNotEmpty(Constants.getString("forumDefaultAddmessageGroups"))) {
forumGroupPerms = new ForumGroupEntity();
forumGroupPerms.setAddmessageGroups(Constants.getString("forumDefaultAddmessageGroups"));
}
if (forumGroupPerms != null && forumGroupPerms.canPostMessage(sender)==false) {
//nema dostatocne prava (kontrola user groups)
setError(request, "components.forum.wrong_user_groups_for_post");
return SAVE_FORUM_SUCCESS;
}
if (ForumGroupService.isActive(forumForm.getDocId()) == false && (sender == null || sender.isAdmin() == false)) {
setError(request, "components.forum.forum_closed");
return SAVE_FORUM_SUCCESS;
}
if (isVulgar(forumForm.getAuthorName()) || isVulgar(forumForm.getAuthorEmail()) ||
isVulgar(forumForm.getQuestion()) || isVulgar(forumForm.getSubject()) ||
ShowDoc.getXssRedirectUrl(request)!=null)
{
request.setAttribute("isVulgar", "true");
return SAVE_FORUM_SUCCESS;
}
String plainQuestion = SearchTools.htmlToPlain(forumForm.getQuestion());
plainQuestion = Tools.replace(plainQuestion, "\n", "");
plainQuestion = Tools.replace(plainQuestion, "\r", "");
if ("true".equals(request.getParameter("forumAllowEmptyMessage"))==false &&
(Tools.isEmpty(forumForm.getQuestion()) || Tools.isEmpty(plainQuestion)))
{
setError(request, "components.forum.error.questionEmpty");
return SAVE_FORUM_SUCCESS;
}
//fixni hlasku o nahravani editora (ak tam je)
forumForm.setQuestion(Tools.replace(forumForm.getQuestion(), prop.getText("editor.loading_please_wait"), ""));
//ak je vypnuty wysiwyg editor, tak nedovol HTML
if (Constants.getBoolean("disableWysiwyg")) {
String oldText = forumForm.getQuestion();
String newText = oldText.replace("<", "<");
newText = newText.replace(">", ">");
newText = newText.replace("\n", "<br />");
forumForm.setQuestion(newText);
}
//nastav linkam nofollow
forumForm.setQuestion(Tools.replace(forumForm.getQuestion(), "<a href", "<a rel=\"nofollow\" href"));
forumForm.setQuestion(Tools.replace(forumForm.getQuestion(), "<A HREF", "<a rel=\"nofollow\" href"));
if (sender == null || sender.isAdmin() == false) forumForm.setId((long) -1);
if (sender != null) userId = sender.getUserId();
//Clear MsOffice tags from question
forumForm.setQuestion( clearMsOfficeTags( forumForm.getQuestion() ) );
hashCode = Password.generatePassword(15);
if (sender != null && Tools.isNotEmpty(sender.getSignature()) && "true".equals(request.getParameter("addSignature"))) {
if ("true".equals(request.getParameter("dontReplaceSignatureCodes")))
forumForm.setQuestion(forumForm.getQuestion() + "<div class='forumSignature'>"+sender.getSignature()+"</div>");
else
forumForm.setQuestion(forumForm.getQuestion() + "<div class='forumSignature'>"+ ForumTools.replaceSignatureCodes(sender)+"</div>");
}
if (!SpamProtection.canPost("forum", forumForm.getQuestion(), request)) {
setPermissionDenied(request, "postLimit");
return SAVE_FORUM_SUCCESS;
}
if (sender != null) {
//ako autora povolime len login, alebo meno
if (sender.getLogin().equals(forumForm.getAuthorName())==false) forumForm.setAuthorName(sender.getFullName());
forumForm.setAuthorEmail(sender.getEmail());
}
if (forumForm.getParentId() == -1) {
//
prepareForumForSave(forumForm, forumGroup, sender, request, hashCode, userId, true);
docForumRepository.save(forumForm);
Adminlog.add(Adminlog.TYPE_FORUM_CREATE, "Vytvorena tema diskusie: "+forumForm.getSubject(), -1, forumForm.getDocId());
} else {
//
prepareForumForSave(forumForm, forumGroup, sender, request, hashCode, userId, false);
docForumRepository.save(forumForm);
//Update SUBTOPIC by incrementing statReplies and setting actual lastPost Date
int fId = ForumDB.getForumMessageParent(forumForm.getParentId(), forumForm.getDocId());
if (fId > 0) docForumRepository.updateSubtopic(new Date(), Long.valueOf(fId), domainId);
Adminlog.add(Adminlog.TYPE_FORUM_CREATE, "Vytvoreny prispevok: "+forumForm.getSubject(), -1, forumForm.getDocId());
}
//If user is logged, increment user forum rank
if(userId > 0) (new SimpleQuery()).execute("UPDATE users SET forum_rank=forum_rank+1 WHERE user_id=?", userId);
//Increment WebPage forum_count
(new SimpleQuery()).execute("UPDATE documents SET forum_count=forum_count+1 WHERE doc_id=?", forumForm.getDocId());
//get new forum ID and set it into forum form
if (forumForm.getId() < 1)
forumForm.setId( new SimpleQuery().forLong("SELECT max(forum_id) AS forum_id FROM document_forum WHERE doc_id = ? "+CloudToolsForCore.getDomainIdSqlWhere(true), forumForm.getDocId()) );
//Set default forum group values
if (forumGroup == null) {
String forumDefaultApproveEmail = Constants.getString("forumDefaultApproveEmail");
String forumDefaultNotifyEmail = Constants.getString("forumDefaultNotifyEmail");
if (Tools.isNotEmpty(forumDefaultApproveEmail) || Tools.isNotEmpty(forumDefaultNotifyEmail)) {
forumGroup = new ForumGroupEntity();
forumGroup.setMessageConfirmation(false);
if (Tools.isNotEmpty(forumDefaultApproveEmail)) {
forumGroup.setApproveEmail(forumDefaultApproveEmail);
forumGroup.setMessageConfirmation(true);
}
if (Tools.isNotEmpty(forumDefaultNotifyEmail))
forumGroup.setNotifEmail(forumDefaultNotifyEmail);
}
}
//Send confirmation email
DocForumEmailService emailService = new DocForumEmailService(forumForm, forumGroupPerms, request, prop);
emailService.sendForumEmails();
String fromName = Constants.getString("forumNotifySenderName");
if(Tools.isEmpty(fromName)) fromName = forumForm.getAuthorName();
String fromEmail = Constants.getString("forumNotifySenderEmail");
if (Tools.isEmpty(fromEmail)) fromEmail = forumForm.getAuthorEmail();
if (Tools.isNotEmpty(fromName)) {
//uloz meno a email do cookies
Cookie forumName = new Cookie("forumname", Tools.URLEncode(fromName));
forumName.setPath("/");
forumName.setMaxAge(60 * 24 * 3600);
forumName.setHttpOnly(true);
Cookie forumEmail = new Cookie("forumemail", Tools.URLEncode(fromEmail));
forumEmail.setPath("/");
forumEmail.setMaxAge(60 * 24 * 3600);
forumEmail.setHttpOnly(true);
Tools.addCookie(forumName, response, request);
Tools.addCookie(forumEmail, response, request);
}
Forums.updateSingleQuestion(forumForm.getId().intValue());
//return link to saveok.jsp
return SAVE_FORUM_SUCCESS;
}
/**
* Perform recursive supported action uppon forum.
* More details in this fn code coments.
*
* @param actionType - Supported recursive actions
* @param forumId
* @param docId
* @param user - logged user
* @return
*/
@SuppressWarnings("unchecked")
public static boolean docForumRecursiveAction(ActionType actionType, int forumId, int docId, Identity user) {
//There must be logged user
if(user == null) return true;
String domainSql = CloudToolsForCore.getDomainIdSqlWhere(true);
//Get all forum's under this one (all child's)
List<DocForumEntity> msgList = getForumFieldsForDoc(actionType, docId, forumId, true);
String fIds = getChildForumIdsRecursive(msgList, forumId);
if (Tools.isNotEmpty(fIds)) fIds += forumId;
else fIds = Integer.toString(forumId);
boolean isAdmin = user.isAdmin();
if (isAdmin == false) {
ForumGroupEntity forumGroupBean = ForumGroupService.getForum(docId, false);
isAdmin = forumGroupBean.isAdmin(user);
}
//Customize query
StringBuilder simpleQuery = new StringBuilder("SELECT user_id FROM document_forum WHERE forum_id IN (" + fIds + ")" + domainSql);
if(actionType == ActionType.DELETE) simpleQuery.append(" AND deleted < 1"); //Because change will affect only NON-Deleted forum's
else if(actionType == ActionType.RECOVER) simpleQuery.append(" AND deleted > 0"); //Because change will affect only Deleted forum's
else if(actionType == ActionType.APPROVE) simpleQuery.append(" AND confirmed < 1"); //Because change will affect only NON-Confirmed forum's
else if(actionType == ActionType.REJECT) simpleQuery.append(" AND confirmed > 0"); //Because change will affect only Confirmed forum's
else if(actionType == ActionType.LOCK) simpleQuery.append(" AND active > 0"); //Because change will affect only Active forum's
else if(actionType == ActionType.UNLOCK) simpleQuery.append(" AND active < 1"); //Because change will affect only NON-Active forum's
//Get users from affected forum's (those where I perform action)
List<Number> usersInForums = null;
try {
usersInForums = new SimpleQuery().forList(simpleQuery.toString());
} catch (Exception ex) {
sk.iway.iwcm.Logger.error(ex);
usersInForums = new ArrayList<>();
if (isAdmin == false) usersInForums.add(user.getUserId());
}
final String WHERE_FORUM_ID_IN = " WHERE forum_id IN (";
final String AND_USER_ID = " AND user_id=";
//PREPARE QUERY FOR THE ACTION
simpleQuery = new StringBuilder("");
if(actionType == ActionType.DELETE) {
//By constant decide if use SOFT delete or HARD delete
if (Constants.getBoolean("forumReallyDeleteMessages"))
simpleQuery.append("DELETE FROM document_forum WHERE forum_id IN (").append(fIds).append(")").append(domainSql); //HARD DELETE
else
simpleQuery.append("UPDATE document_forum SET deleted = ").append(DB.getBooleanSql(true)).append(WHERE_FORUM_ID_IN).append(fIds).append(") ").append(domainSql); //SOFT DELETE (update)
} else if(actionType == ActionType.RECOVER) {
simpleQuery.append("UPDATE document_forum SET deleted = ").append(DB.getBooleanSql(false)).append(WHERE_FORUM_ID_IN).append(fIds).append(")").append(domainSql);
} else if(actionType == ActionType.APPROVE) {
simpleQuery.append("UPDATE document_forum SET confirmed = ").append(DB.getBooleanSql(true)).append(WHERE_FORUM_ID_IN).append(fIds).append(")").append(domainSql);
} else if(actionType == ActionType.REJECT) {
simpleQuery.append("UPDATE document_forum SET confirmed = ").append(DB.getBooleanSql(false)).append(WHERE_FORUM_ID_IN).append(fIds).append(")").append(domainSql);
} else if(actionType == ActionType.LOCK) {
simpleQuery.append("UPDATE document_forum SET active = ").append(DB.getBooleanSql(false)).append(WHERE_FORUM_ID_IN).append(fIds).append(")").append(domainSql);
} else if(actionType == ActionType.UNLOCK) {
simpleQuery.append("UPDATE document_forum SET active = ").append(DB.getBooleanSql(true)).append(WHERE_FORUM_ID_IN).append(fIds).append(")").append(domainSql);
}
// Specification for user if is not admin
if(!isAdmin) simpleQuery.append(AND_USER_ID).append(user.getUserId());
//PERFORM action AND save number of changed rows
Integer numberOfUpdated = new SimpleQuery().executeWithUpdateCount(simpleQuery.toString());
if(numberOfUpdated != null && numberOfUpdated > 0 && usersInForums != null && usersInForums.isEmpty() == false) {
String loggerMsg = "";
String sqlSign = "";
if(actionType == ActionType.DELETE || actionType == ActionType.REJECT) {
//During DELETE / REJECT action, user's loosing rank, and forum loosing count
loggerMsg = "Lowering rank for user: ";
sqlSign = "-";
} else {
//During RECOVER / APPROVE action, user's retrieve rank, and forum retrieve count
loggerMsg = "Increasing rank for user: ";
sqlSign = "+";
}
//Update user rank
for (Number userId : usersInForums) {
Logger.debug(DocForumService.class, loggerMsg + userId.intValue());
//Only real user (-1 userId represent NOT logged user)
if(userId.intValue() > 0)
(new SimpleQuery()).execute("UPDATE users SET forum_rank=forum_rank" + sqlSign + "1 WHERE user_id=?", userId.intValue());
}
//Update webpage forum_count
(new SimpleQuery()).execute("UPDATE documents SET forum_count=forum_count" + sqlSign + "? WHERE doc_id=?", numberOfUpdated, docId);
}
DocForumEntity docForumEntity = getForumBean(forumId, true);
Integer parentId = docForumEntity != null ? docForumEntity.getParentId() : -1;
//Update forum info like stat view ...
updateForumStatInfo(docId, parentId);
if(docForumEntity != null) {
if(actionType == ActionType.DELETE)
Adminlog.add(Adminlog.TYPE_FORM_DELETE, "Zmazany diskusny prispevok: " + docForumEntity.getSubject(), forumId, docId);
else if(actionType == ActionType.RECOVER)
Adminlog.add(Adminlog.TYPE_FORUM_UNDELETE, "Obnoveny prispevok: " + (docForumEntity == null ? " ktory neexistuje" : docForumEntity.getSubject()), forumId, parentId);
}
return true;
}
/**
* testuje, ci nie je prispevok vulgarny, alebo obsahuje nepovoleny HTML kod
* @param message
* @return
*/
public static boolean isVulgar(String message) {
//Get vulgar words
String vulgarisms = Constants.getString("vulgarizmy");
if (Tools.isEmpty(vulgarisms)) return false;
//Test message
if(Tools.isNotEmpty(message)) {
message = " " + message.toLowerCase();
//aby to naslo aj take ze ...text<script...
message = Tools.replace(message, "<", " <");
message = Tools.replace(message, ">", "> ");
message = message.replace('\n', ' ');
message = message.replace('\r', ' ');
StringTokenizer st = new StringTokenizer(vulgarisms, ",");
while (st.hasMoreTokens()) {
String word = st.nextToken().trim();
if (Tools.isEmpty(word)) continue;
if (message.indexOf(" " + word) != -1) {
Logger.println(DocForumService.class, "vulgar: " + word + " message: " + message);
Adminlog.add(Adminlog.TYPE_FORUM_SAVE, "vulgar: " + word + " message: " + message, -1, -1);
return true;
}
}
}
return false;
}
/**
* Odstrani Microsot Office tagy z textu. Tieto tagy mozu sposobovat zle
* odsadzovanie textu.
*
* @return String - povodny text ostripovany o tagy
*/
public static String clearMsOfficeTags(String text) {
String oldText = text;
String forumWysiwygIcons = Constants.getString("forumWysiwygIcons");
if (forumWysiwygIcons.indexOf("createlink") == -1) {
//odpazme linky
Pattern linkPattern = Pattern.compile("<\\s*[^>]*\\s*a\\s*href\\s*=\\s*\"[^\"]*\"\\s*[^>]*>|</a>", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); //NOSONAR
Matcher linkMatcher = linkPattern.matcher(text);
text = linkMatcher.replaceAll("");
}
if (forumWysiwygIcons.indexOf("insertimage") == -1) {
//odpazme linky
Pattern linkPattern = Pattern.compile("<\\s*[^>]*\\s*img\\s*src\\s*=\\s*\"[^\"]*\"\\s*[^>]*>|</img>", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); //NOSONAR
Matcher linkMatcher = linkPattern.matcher(text);
text = linkMatcher.replaceAll("");
}
try {
//ak neobsahuje tento text, tak sa nie je coho bat
if (!text.contains("class=MsoNormal") && !text.contains("class=\"MsoNormal\""))
return text;
//v prvom rade zmaz vsetky <FONT> a </FONT> tagy
Pattern fontPattern = Pattern.compile("<FONT .*?>",Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); //NOSONAR
Matcher fontMatcher = fontPattern.matcher(text);
while (fontMatcher.find()) {
text = text.replace(fontMatcher.group(0), "");
fontMatcher = fontPattern.matcher(text);
}
text = text.replace("</FONT>", "");
//potom samotne MsoNormal tagy
Pattern msTagPattern = Pattern.compile("<P.*?class=.?MsoNormal.*?>",Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); //NOSONAR
Matcher msTagMatcher = msTagPattern.matcher(text);
while (msTagMatcher.find()) {
text = text.replace(msTagMatcher.group(0), "<P>");
msTagMatcher = msTagPattern.matcher(text);
}
//toto su dalsie MS specific tagy...
text = text.replace("<o:p>", "");
text = text.replace("</o:p>", "");
} catch (Exception e) {
sk.iway.iwcm.Logger.error(e);
return oldText;
}
return text;
}
/**
* Prepare new created instance of DocForumEntity for save.
*
*/
private static void prepareForumForSave(DocForumEntity forumForm, ForumGroupEntity forumGroup, Identity sender, HttpServletRequest request, String hashCode, Integer userId, Boolean isSubtopic) {
//If user is logged, use his login/email as sender or use data from forumForm
if(sender != null && Tools.isEmpty(forumForm.getAuthorName())) {
forumForm.setAuthorName( sender.getLogin() );
forumForm.setAuthorEmail( sender.getEmail());
}
//If validation is ENABLED, set confirmed as FALSE (because it needs approve)
if (forumGroup != null && forumGroup.getMessageConfirmation()) {
//Need's approve
//BUT if user is logged AND he is approver of forum AND he is author of this new contribution ... DO a AUTO approve
if(sender != null && sender.getEmail().equals(forumGroup.getApproveEmail()) && sender.getEmail().equals(forumForm.getAuthorEmail()))
forumForm.setConfirmed(true);
else
forumForm.setConfirmed(false);
} else //Forum do NOT need approve
forumForm.setConfirmed(true);
forumForm.setQuestionDate( new Date() );
forumForm.setIp( Tools.getRemoteIP(request) );
forumForm.setHashCode(hashCode);
forumForm.setUserId(userId);
forumForm.setActive(true);
forumForm.setDomainId( CloudToolsForCore.getDomainId() );
forumForm.setDeleted(false);
if(Boolean.TRUE.equals(isSubtopic)) {
//If the forumForm represent subtopic
forumForm.setStatViews(0);
forumForm.setStatReplies(0);
forumForm.setStatLastPost(new Date());
}
}
/**
* Get DocForumEntity based on forumId (id), is entity is present, convert it to dDocForumEntity or return null.
*
* @param forumId - entity id
* @param sortAscending - true = ASC, false = DESC
* @return
*/
public static DocForumEntity getForumBean(int forumId, boolean sortAscending) {
DocForumRepository docForumRepository = getDocForumRepository();
Integer domainId = CloudToolsForCore.getDomainId();
//Based on param sortAscending call getData
Optional<DocForumEntity> entityOpt;
if(sortAscending)
entityOpt = docForumRepository.findByIdAndDomainIdOrderByQuestionDateAsc(Long.valueOf(forumId), domainId);
else
entityOpt = docForumRepository.findByIdAndDomainIdOrderByQuestionDateDesc(Long.valueOf(forumId), domainId);
if(entityOpt.isPresent())
return entityOpt.get();
return null;
}
/**
* Recursively sort List (unsorted) and sorted values add into List (sorted).
*
* @param unsorted
* @param sorted
* @param level
* @param parent
*/
public static void recursiveSort(List<DocForumEntity> unsorted, List<DocForumEntity> sorted, int level, int parent) {
for (DocForumEntity fb : unsorted) {
if (fb.getParentId() != null && fb.getParentId().intValue() == parent) {
fb.setPrefix("<div style=\"margin-left:" + (20 * level) + "px\">");
fb.setLevel(level);
sorted.add(fb);
recursiveSort(unsorted, sorted, level + 1, fb.getId().intValue());
}
}
}
public static List<DocForumEntity> getForumFieldsForDoc(HttpServletRequest request, int docId, boolean onlyConfirmed, int parentId, boolean sortAscending, boolean showDeleted, boolean isForumSearch) {
//It's Mental gymnastic, we using SQL where BOOLEAN params are compared as (boolean == val1 OR boolean == val2)
//By setting val1 and val2 we can return all combinations true/false/doesnt matter
if(showDeleted)
return getForumFieldsForDoc(docId, parentId, sortAscending, onlyConfirmed, true, true, false, isForumSearch); //speaking about deleted, true && false -> all forum's, not depending on deleted value
else
return getForumFieldsForDoc(docId, parentId, sortAscending, onlyConfirmed, true, false, false, isForumSearch); //speaking about deleted, flase && false -> only not deleted forum's
}
private static List<DocForumEntity> getForumFieldsForDoc(ActionType actionType, int docId, int parentId, boolean sortAscending) {
boolean deleteA = true;
boolean deleteB = false;
boolean confirmA = true;
boolean confirmB = false;
if(actionType == ActionType.DELETE) //Only NOT deleted
deleteA = false;
else if(actionType == ActionType.RECOVER) //Only deleted
deleteB = true;
else if(actionType == ActionType.APPROVE) //Only NOT approved (not confirmed)
confirmA = false;
else if(actionType == ActionType.REJECT) //Only approved (confirmed)
confirmB = true;
return getForumFieldsForDoc(docId, parentId, sortAscending, confirmA, confirmB, deleteA, deleteB, false);
}
/**
* confirmedA == true && confirmedB == true -> only confirmed.
* confirmedA == false && confirmedB == false -> only NOT confirmed.
* else -> all records, confirmed value doesnt matter.
*
* deletedA == true && deletedB == true -> only deleted.
* deletedA == false && deletedB == false -> only NOT deleted.
* else -> all records, deleted value doesnt matter.
*/
private static List<DocForumEntity> getForumFieldsForDoc(int docId, int parentId, boolean sortAscending, boolean confirmedA, boolean confirmedB, boolean deletedA, boolean deletedB, boolean isForumSearch) {
//Returned List, even empty
List<DocForumEntity> sorted = new ArrayList<>();
if(docId > 0) {
List<DocForumEntity> unsorted = new ArrayList<>();
List<Integer> parentIds = new ArrayList<>(Arrays.asList(parentId));
HashSet<Integer> obtainedParentIds = new HashSet<>(Arrays.asList(parentId));
HashSet<Integer> obtainedChildIds = new HashSet<>();
Integer domainId = CloudToolsForCore.getDomainId();
DocForumRepository docForumRepository = getDocForumRepository();
//Get all relevant forum entities
List<DocForumIdAndParentId> entities;
//Very special FIX -> only in case of calling ForumDB.getForumMessageParent (after forum search), we must include massage-board forum's with parent id < 0
//It's because we cant prepare link for forum in search, when we do not load allso root parent
if(isForumSearch) entities = docForumRepository.getDocForumListForumIdParentIdWithRootParents(docId, deletedA||deletedB, confirmedA||confirmedB, domainId);
else entities = docForumRepository.getDocForumListForumIdParentId(docId, deletedA||deletedB, confirmedA||confirmedB, domainId);
//Loop all entities and make String of all parent ids (even sub levels)
for(DocForumIdAndParentId entity : entities) {
int childForumId = entity.getId().intValue();
int childParentId = entity.getParentId();
//To make answers work in simple forum
if (parentId == 0) parentIds.add(childParentId);
if (obtainedParentIds.contains(childParentId))
obtainedChildIds.add(childForumId);
else if (obtainedChildIds.contains(childParentId)) {
obtainedChildIds.add(childForumId);
obtainedParentIds.add(childParentId);
parentIds.add(childParentId);
}
}
//Based of parent id's, get all their child's entities
List<DocForumEntity> childForums = new ArrayList<>();
if(sortAscending)
childForums.addAll( docForumRepository.getDocForumListAsc(docId, parentIds, deletedA||deletedB, confirmedA||confirmedB, domainId) );
else
childForums.addAll( docForumRepository.getDocForumListDesc(docId, parentIds, deletedA||deletedB, confirmedA||confirmedB, domainId) );
//It must be ForumBean List
unsorted.addAll( childForums );
DocForumEntity parentForum = getForumBean(parentId, true);
//Push from start
if(parentForum != null && sortAscending) sorted.add(parentForum);
//Sort it
recursiveSort(unsorted, sorted, 0, parentId);
//Push at end (aka first question)
if (parentForum != null && !sortAscending) sorted.add(parentForum);
}
//return sorted values
return sorted;
}
/**
* BAsed on give forumId, return string where are joined all children id's.
*
* @param msgList - list of entities to loop
* @param forumId - forumId of root parent
* @return
*/
public static String getChildForumIdsRecursive(List<DocForumEntity> msgList, int forumId) {
StringBuilder idStr = new StringBuilder();
if (msgList != null && forumId > 0) {
for (DocForumEntity fb : msgList) {
if (fb.getParentId() == forumId) {
if (Tools.isNotEmpty(idStr)) idStr.append(String.valueOf(fb.getId())).append(',');
else idStr = new StringBuilder(String.valueOf(fb.getId())).append(',');
idStr.append(getChildForumIdsRecursive(msgList, fb.getId().intValue()));
}
}
}
return idStr.toString();
}
public static void updateForumStatInfo(int docId, int forumId) {
String childForumIdsString;
String[] childForumIds;
int countStatReplies = 0;
List<DocForumEntity> msgList = getForumFieldsForDoc(null, docId, true, forumId, true, false, false);
int parentId = findParentRecursive(msgList, forumId);
String domain = CloudToolsForCore.getDomainIdSqlWhere(true);
childForumIdsString = getChildForumIdsRecursive(msgList, parentId);
if (Tools.isNotEmpty(childForumIdsString) && childForumIdsString.endsWith(",")) childForumIdsString = childForumIdsString.substring(0, childForumIdsString.length() - 1);
childForumIds = Tools.getTokens(childForumIdsString, ",");
if (childForumIds != null)
countStatReplies = childForumIds.length;
if (countStatReplies == 0) {
(new SimpleQuery()).execute("UPDATE document_forum SET stat_replies=?, stat_last_post=question_date WHERE forum_id=? AND parent_id=-1" + domain, countStatReplies, parentId);
}
if (Tools.isNotEmpty(childForumIdsString)) {
StringBuilder sql = new StringBuilder("SELECT ");
if (Constants.DB_TYPE == Constants.DB_MSSQL)
sql.append("TOP 1 ");
sql.append("question_date FROM document_forum WHERE ");
if(Constants.DB_TYPE == Constants.DB_ORACLE)
sql.append(" rownum = 1 AND ");
sql.append("doc_id=?" + domain + " AND forum_id IN (" + childForumIdsString + ")" + " ORDER BY question_date DESC");
if (Constants.DB_TYPE == Constants.DB_MYSQL || Constants.DB_TYPE == Constants.DB_PGSQL)
sql.append(" LIMIT 1");
Date lastPostDate = (new SimpleQuery()).forDate(sql.toString(), docId);
if (lastPostDate != null && lastPostDate.getTime() > 0 && countStatReplies > 0)
(new SimpleQuery()).execute("UPDATE document_forum SET stat_replies=?, stat_last_post=? WHERE forum_id=? AND parent_id=-1" + domain, countStatReplies, lastPostDate, parentId);
}
}
/**
* Rekurzivne vyhlada forumId pod-temy
*
* @param msgList
* @param parentId
* @return
*/
public static int findParentRecursive(List<DocForumEntity> msgList, int parentId) {
int forumId = -1;
for (DocForumEntity fb : msgList) {
if (fb.getId().intValue() == parentId) {
forumId = findParentRecursive(msgList, fb.getParentId());
if (forumId < 0) forumId = fb.getId().intValue();
}
}
return forumId;
}
/**
* Upload file for specific forum. Using MediaDB.
*
* @param uploadFile - file to upload
* @param request
* @param response
* @return - path to file (forward)
*/
public static String uploadForumFile(CommonsMultipartFile uploadFile, HttpServletRequest request) {
int forumId = Tools.getIntValue(request.getParameter("forumId") , -1);
if(forumId == -1 || uploadFile == null || uploadFile.isEmpty()) return null;
DocForumRepository docForumRepository = getDocForumRepository();
Optional<DocForumEntity> forumOpt = docForumRepository.findById((long) forumId);
if(!forumOpt.isPresent()) return null;
try {
return uploadForumFileLogic(uploadFile, forumOpt.get(), request);
} catch(Exception e) {
sk.iway.iwcm.Logger.error(e);
}
return SAVE_FORUM_SUCCESS;
}
private static String uploadForumFileLogic(CommonsMultipartFile uploadedFile, DocForumEntity docForum, HttpServletRequest request) throws IOException {
//Check logged user
Identity user = UsersDB.getCurrentUser(request);
if (user == null) {
setPermissionDenied(request, "true");
return SAVE_FORUM_SUCCESS;
}
//Check if we upload file to MY post
if (docForum.getUserId() < 1 || docForum.getUserId() != user.getUserId()) {
setPermissionDenied(request, "true");
return SAVE_FORUM_SUCCESS;
}
//Get upload limits
LabelValueDetails uploadLimits = ForumDB.getUploadLimits(docForum.getDocId(), request);
if (uploadLimits == null) {
setPermissionDenied(request, "true");
return SAVE_FORUM_SUCCESS;
}
//Check uploaded file size limit
if (uploadLimits.getInt1() > 0 && (uploadLimits.getInt1() * 1024) < uploadedFile.getFileItem().getSize()) {
setPermissionDenied(request, "fileSize");
return SAVE_FORUM_SUCCESS;
}
//Check the file rights
String fileName = DocTools.removeChars(uploadedFile.getFileItem().getName()).toLowerCase();
//Check the suffix
if (fileName.endsWith(".jsp") || fileName.endsWith(".class")) {
setPermissionDenied(request, "true");
return SAVE_FORUM_SUCCESS;
}
boolean canUpload = false;
if ("*".equals(uploadLimits.getValue())) {
canUpload = true;
} else {
//skontroluj priponu suboru
StringTokenizer st = new StringTokenizer(uploadLimits.getValue(), ",");
while (st.hasMoreTokens() && canUpload == false)
if (fileName.endsWith(st.nextToken())) canUpload = true;
}
if (!canUpload) {
setPermissionDenied(request, "fileType");
return SAVE_FORUM_SUCCESS;
}
//ok, let upload image
String baseDir = "/images/apps/forum/";
baseDir = baseDir.replace('\\', '/');
if (baseDir.endsWith("/") == false) baseDir = baseDir + "/";
fileName = user.getUserId() + "-" + docForum.getId() + "-" + fileName;
File f = new File(Tools.getRealPath(baseDir + fileName));
if (f.getParentFile().exists() == false && f.getParentFile().mkdirs() == false) {
setPermissionDenied(request, "true");
return SAVE_FORUM_SUCCESS;
}
boolean fileAllreadyExists = f.exists();
//zapis subor na disk
int bytesRead = 0;
byte[] buffer = new byte[8192];
BufferedInputStream buffReader = new BufferedInputStream(uploadedFile.getFileItem().getInputStream());
FileOutputStream fos = new FileOutputStream(f);
while ((bytesRead = buffReader.read(buffer, 0, 8192)) != -1)
fos.write(buffer, 0, bytesRead);
buffReader.close();
fos.close();
uploadedFile.getFileItem().delete();
//ak prepise existujuci subor, aby sa to neobjavilo v zozname viac krat
if (fileAllreadyExists == false) {
//zapis do medii
Media m = new Media();
m.setMediaFkId( docForum.getId().intValue() );
m.setMediaFkTableName("document_forum");
if (Tools.isNotEmpty(docForum.getSubject()))
m.setMediaTitleSk(docForum.getSubject());
else
m.setMediaTitleSk(uploadedFile.getFileItem().getName());
m.setMediaLink(baseDir + fileName);
m.setMediaSortOrder(10);
new MediaDB().save(m);
}
return SAVE_FORUM_SUCCESS;
}
public static DocForumEntity getForumStat(int docId) {
if(docId < 1) return null;
Date lastPost = null;
int totalMessages = 0;
DocForumRepository docForumRepository = getDocForumRepository();
List<DocForumEntity> forums = docForumRepository.getForumStat(docId, false, CloudToolsForCore.getDomainId());
for(DocForumEntity forum : forums) {
totalMessages += forum.getStatReplies() + 1;
if(forum.getStatLastPost() != null) lastPost = forum.getStatLastPost();
}
DocForumEntity stat = new DocForumEntity();
stat.setStatReplies(totalMessages);
if(lastPost != null) stat.setStatLastPost(lastPost);
return stat;
}
public static List<DocForumEntity> getForumTopics(int docId, boolean onlyConfirmed, boolean showDeleted, String flagSearch, ForumSortBy sortBy) {
List<DocForumEntity> topics = new ArrayList<>();
//docId mus be set !!
if(docId < 1) return topics;
//Params for query
List<Object> params = new ArrayList<>();
params.add(docId);
//Prepare sql query
String sql = "SELECT * FROM document_forum WHERE doc_id=? AND parent_id=-1" + CloudToolsForCore.getDomainIdSqlWhere(true);
if (showDeleted == false) sql += " AND deleted = "+DB.getBooleanSql(false);
if (Tools.isNotEmpty(flagSearch)) {
sql += " AND flag LIKE ?";
params.add(flagSearch + "%");
}
sql += " ORDER BY flag DESC, " + sortBy.getColumnName() + " DESC";
Logger.debug(ForumDB.class, "getForumTopics: docId=" + docId + " flagSearch=" + flagSearch + " sql=" + sql);
new ComplexQuery().setSql(sql).setParams(params.toArray()).list(new Mapper<DocForumEntity>() {
@Override
public DocForumEntity map(ResultSet rs) throws SQLException {
boolean isConfirmed = rs.getBoolean("confirmed");
if(onlyConfirmed && !isConfirmed) {
//If confirmed only but this one is not confirmed
} else {
DocForumEntity tmpEntity = rsToDocForumEntity(rs);
//Insert to our list
if(tmpEntity != null)
topics.add(tmpEntity);
}
return null;
}
});
//Add ForumGroupEntity
for(DocForumEntity topic : topics)
topic.setForumGroupEntity( ForumGroupService.getForum(topic.getDocId(), false) );
return topics;
}
private static DocForumEntity rsToDocForumEntity(ResultSet rs) {
try {
DocForumEntity tmpEntity = new DocForumEntity();
tmpEntity.setId(rs.getLong("forum_id"));
tmpEntity.setDocId(rs.getInt("doc_id"));
tmpEntity.setParentId(rs.getInt("parent_id"));
tmpEntity.setSubject(DB.getDbString(rs, "subject"));
tmpEntity.setQuestion(DB.getDbString(rs, "question"));
tmpEntity.setQuestionDate( new Date( DB.getDbTimestamp(rs, "question_date") ) );
tmpEntity.setAuthorName(DB.getDbString(rs, "author_name"));
tmpEntity.setAuthorEmail(DB.getDbString(rs, "author_email"));
tmpEntity.setConfirmed(rs.getBoolean("confirmed"));
tmpEntity.setHashCode(DB.getDbString(rs, "hash_code"));
tmpEntity.setSendAnswerNotif(rs.getBoolean("send_answer_notif"));
tmpEntity.setStatReplies(rs.getInt("stat_replies"));
tmpEntity.setStatViews(rs.getInt("stat_views"));
tmpEntity.setStatLastPost( new Date( DB.getDbTimestamp(rs, "stat_last_post") ) );
tmpEntity.setFlag(DB.getDbString(rs, "flag"));
tmpEntity.setUserId(rs.getInt("user_id"));
tmpEntity.setActive(rs.getBoolean("active"));
tmpEntity.setDeleted(rs.getBoolean("deleted"));
return tmpEntity;
} catch (SQLException ex) {
sk.iway.iwcm.Logger.error(ex);
}
return null;
}
public static DocForumEntity getLastMessage(int docId) {
DocForumRepository docForumRepository = getDocForumRepository();
Optional<DocForumEntity> optMsp = docForumRepository.findFirstByDocIdAndDomainIdOrderByQuestionDateDesc(docId, CloudToolsForCore.getDomainId());
if(optMsp.isPresent()) return optMsp.get();
return null;
}
private static DocForumRepository getDocForumRepository() {
return Tools.getSpringBean("docForumRepository", DocForumRepository.class);
}
private static void setError(HttpServletRequest request, String error) {
request.setAttribute("errorKey", error);
}
private static void setPermissionDenied(HttpServletRequest request, String error) {
Logger.debug(ForumDB.class, "Permission denied, error=" + error + " subject="+request.getParameter("subject"));
request.setAttribute("permissionDenied", error);
}
}