From 320040c6726e5cc597c3349a2314aae306725175 Mon Sep 17 00:00:00 2001 From: stetsche <53173679+stetsche@users.noreply.github.com> Date: Fri, 19 May 2023 10:57:58 +0200 Subject: [PATCH 1/3] S2U-27 5.1.3.1 Messages: Convert a private message into a forum message in the FAQ forum (#11575) Co-Authored-By: stetsche <53173679+stetsche@users.noreply.github.com> --- .../java/org/sakaiproject/site/api/Site.java | 5 + .../sakaiproject/site/api/SiteService.java | 14 ++ .../site/impl/BaseSiteService.java | 27 +++ library/src/skins/default/src/sass/tool.scss | 13 +- .../messagecenter/bundle/Messages.properties | 17 ++ .../bundle/Messages_ca.properties | 16 ++ .../bundle/Messages_es.properties | 16 ++ .../bundle/Messages_eu.properties | 18 ++ .../app/messageforums/DiscussionForum.java | 5 + .../app/messageforums/DiscussionTopic.java | 4 + .../MessageForumsForumManager.java | 38 ++++ msgcntr/messageforums-app/pom.xml | 4 + .../messageforums/DiscussionForumTool.java | 36 +++- .../MessageForumPublishToFaqBean.java | 186 ++++++++++++++++++ .../messageforums/PrivateMessagesTool.java | 87 +++++++- .../src/webapp/WEB-INF/faces-config.xml | 5 + .../webapp/jsp/privateMsg/pvtMsgDetail.jsp | 5 + .../jsp/privateMsg/pvtMsgPublishToFaqEdit.jsp | 80 ++++++++ .../MessageForumsForumManagerImpl.java | 99 ++++++++++ .../dao/hibernate/DiscussionForumImpl.java | 4 + .../dao/hibernate/DiscussionTopicImpl.java | 4 + .../dao/hibernate/OpenForum.hbm.xml | 12 ++ .../messageforums/dao/hibernate/Topic.hbm.xml | 3 + 23 files changed, 685 insertions(+), 13 deletions(-) create mode 100644 msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/MessageForumPublishToFaqBean.java create mode 100644 msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgPublishToFaqEdit.jsp diff --git a/kernel/api/src/main/java/org/sakaiproject/site/api/Site.java b/kernel/api/src/main/java/org/sakaiproject/site/api/Site.java index 4b3511458100..f806bad40697 100644 --- a/kernel/api/src/main/java/org/sakaiproject/site/api/Site.java +++ b/kernel/api/src/main/java/org/sakaiproject/site/api/Site.java @@ -74,6 +74,11 @@ public interface Site extends Edit, Comparable, Serializable, AuthzGroup */ public final static String PROP_SITE_MATHJAX_ALLOWED = "mathJaxAllowed"; + /** + * property name for site locale + */ + public final static String PROP_SITE_LOCALE = "locale_string"; + /** * property name for custom overview */ diff --git a/kernel/api/src/main/java/org/sakaiproject/site/api/SiteService.java b/kernel/api/src/main/java/org/sakaiproject/site/api/SiteService.java index d1dd2d2e9cfc..d82766244933 100644 --- a/kernel/api/src/main/java/org/sakaiproject/site/api/SiteService.java +++ b/kernel/api/src/main/java/org/sakaiproject/site/api/SiteService.java @@ -23,7 +23,9 @@ import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.sakaiproject.entity.api.Entity; @@ -1441,4 +1443,16 @@ public enum SiteTitleValidationStatus { * @return true if the stealthed tool is present in the given site; false otherwise */ public boolean isStealthedToolPresent(Site site, String toolID); + + /** + * Gets site locale for site id + * @return Optional of locale defined as site property + */ + public Optional getSiteLocale(String siteId); + + /** + * Gets site locale for site + * @return Optional of locale defined as site property + */ + public Optional getSiteLocale(Site site); } diff --git a/kernel/kernel-impl/src/main/java/org/sakaiproject/site/impl/BaseSiteService.java b/kernel/kernel-impl/src/main/java/org/sakaiproject/site/impl/BaseSiteService.java index 7b52fb6baf77..530745ba5b0e 100644 --- a/kernel/kernel-impl/src/main/java/org/sakaiproject/site/impl/BaseSiteService.java +++ b/kernel/kernel-impl/src/main/java/org/sakaiproject/site/impl/BaseSiteService.java @@ -31,9 +31,11 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Observable; import java.util.Observer; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.Stack; @@ -3926,4 +3928,29 @@ public boolean isStealthedToolPresent(Site site, String toolID) { return false; } + + public Optional getSiteLocale(String siteId) { + try { + return getSiteLocale(getSite(siteId)); + } catch (IdUnusedException e) { + log.error("Could not find site with id [{}], returning empty optional: {}", + siteId, e.toString()); + return Optional.empty(); + } + } + + public Optional getSiteLocale(Site site) { + if (site != null) { + String localeString = site.getProperties().getProperty(Site.PROP_SITE_LOCALE); + if (localeString != null) { + Locale locale = serverConfigurationService().getLocaleFromString(localeString); + return Optional.of(locale); + } + // No locale specified in site properties, that's okay + } else { + log.error("Site is null, returning empty optional: {}"); + } + + return Optional.empty(); + } } diff --git a/library/src/skins/default/src/sass/tool.scss b/library/src/skins/default/src/sass/tool.scss index c4b92b18ef18..cd46ccf6321c 100644 --- a/library/src/skins/default/src/sass/tool.scss +++ b/library/src/skins/default/src/sass/tool.scss @@ -228,11 +228,14 @@ label { color: var(--sakai-text-color-2); } -.form-control-required .form-control-label::after { - content: ' *'; - color: var(--sakai-highlight-color); -} - +.form-control-required { + .form-control-label, .form-label { + &::after { + content: ' *'; + color: var(--sakai-highlight-color); + } + } + } // to make sakai task accordion visible in account menu .global-overlays { diff --git a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages.properties b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages.properties index bdddbe6f76ed..a1f1a99a6a56 100644 --- a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages.properties +++ b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages.properties @@ -971,3 +971,20 @@ widget_title=Discussions forums_revise_title_validation=Please enter a valid forum title. topics_revise_title_validation=Please enter a valid topic title. + +#S2U-27 +pvt_publish_to_faq=Publish to FAQ +pvt_publish_to_faq_success=The message was successfully published to FAQ +pvt_title=Title +pvt_question=Question +pvt_question_title_prefix=Q: +pvt_answer=Answer +pvt_answer_title_prefix=A: +pvt_answer_info=You can leave the answer field empty if you don\u0027t want to post it yet. +pvt_missing_title=You must enter a title before you can post to FAQ. +pvt_missing_question=You must enter a question before you can post to FAQ. + +cdfm_faq_forum_title=FAQ Forum +cdfm_faq_forum_description=Forum for frequently asked questions +cdfm_faq_topic_title=FAQ Topic +cdfm_faq_topic_description=Topic for frequently asked questions diff --git a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_ca.properties b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_ca.properties index 1a964c9c0481..6baf30bfa70a 100644 --- a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_ca.properties +++ b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_ca.properties @@ -966,3 +966,19 @@ widget_title=Debat forums_revise_title_validation=Cal que introdu\u00efu un nom de f\u00f2rum v\u00e0lid. topics_revise_title_validation=Cal que introdu\u00efu un nom de tema v\u00e0lid. + +#S2U-27 +pvt_publish_to_faq=Publicar en Preguntes Freq\u00fcents +pvt_publish_to_faq_success=El missatge s\u2019ha publicat correctament en Preguntes Mes Frequents +pvt_title=T\u00edtol +pvt_question=Pregunta +pvt_question_title_prefix=P: +pvt_answer=Resposta +pvt_answer_title_prefix=R: +pvt_answer_info=Podeu deixar el camp de resposta buit si encara no el voleu publicar +pvt_missing_title=Heu d\u0027introduir un titol abans de poder publicar a les Preguntes Freq\u00fcents +pvt_missing_question=Heu d\u0027introduir una pregunta abans de poder publicar a les Preguntes Freq\u00fcents +cdfm_faq_forum_title=F\u00f2rum Preguntes Freq\u00fcents +cdfm_faq_forum_description=F\u00f2rum per Preguntes Freq\u00fcents +cdfm_faq_topic_title=Tema Preguntes Freq\u00fcents +cdfm_faq_topic_description=Tema per a Preguntes Freq\u00fcents diff --git a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_es.properties b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_es.properties index b4a3ff10f2b6..0a3ccab8e889 100644 --- a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_es.properties +++ b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_es.properties @@ -951,3 +951,19 @@ widget_title=Foros forums_revise_title_validation=Introduzca un t\u00edtulo v\u00e1lido para el foro topics_revise_title_validation=Introduzca un t\u00edtulo v\u00e1lido para el tema + +#S2U-27 +pvt_publish_to_faq=Publicar en preguntas frecuentes +pvt_publish_to_faq_success=El mensaje se ha publicado correctamente en preguntas m\u00e1s frecuentes +pvt_title=T\u00edtulo +pvt_question=Pregunta +pvt_question_title_prefix=P: +pvt_answer=Respuesta +pvt_answer_title_prefix=R: +pvt_answer_info=Puedes dejar la respuesta vac\u00eda si no quieres publicarla a\u00fan. +pvt_missing_title=Debe introducir un t\u00edtulo antes de poder publicar en preguntas frecuentes. +pvt_missing_question=Debe introducir una pregunta antes de poder publicar en preguntas frecuentes. +cdfm_faq_forum_title=Foro preguntas frecuentes +cdfm_faq_forum_description=Foro para preguntas frecuentes +cdfm_faq_topic_title=Preguntas frecuentes +cdfm_faq_topic_description=Tema para las preguntas m\u00e1s frecuentes diff --git a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_eu.properties b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_eu.properties index 4da2986e3440..637fdeaef778 100644 --- a/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_eu.properties +++ b/msgcntr/messageforums-api/src/bundle/org/sakaiproject/api/app/messagecenter/bundle/Messages_eu.properties @@ -966,3 +966,21 @@ widget_title=Foroak forums_revise_title_validation=Idatzi baliozko titulu bat fororako. topics_revise_title_validation=Idatzi baliozko titulu bat gairako. + +#S2U-27 +pvt_publish_to_faq=Publish to FAQ +pvt_publish_to_faq=Publikatu FAQ gisa +pvt_publish_to_faq_success=Mezua ongi publikatu da FAQ gisa +pvt_title=Titulua +pvt_question=Galdera +pvt_question_title_prefix=Q: +pvt_answer=Erantzuna +pvt_answer_title_prefix=A: +pvt_answer_info=Erantzunaren eremua hutsik utz dezakezu publikatu nahi ez baduzu oraindik. +pvt_missing_title=Ezinbestekoa da izenburu bat sartzea "Ohiko galderak" atalean publikatzeko. +pvt_missing_question=Ezinbestekoa da galdera bat sartzea "Ohiko galderak" atalean publikatzeko. + +cdfm_faq_forum_title=FAQ foroa +cdfm_faq_forum_description=Galdera ohikoentzako foroa +cdfm_faq_topic_title=FAQ gaia +cdfm_faq_topic_description=Galdera ohikoentzako gaia diff --git a/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionForum.java b/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionForum.java index 7aa76cb714b0..3e1c1f5cfdd3 100644 --- a/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionForum.java +++ b/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionForum.java @@ -47,4 +47,9 @@ public interface DiscussionForum extends OpenForum { public Boolean getRestrictPermissionsForGroups(); public void setRestrictPermissionsForGroups(Boolean restrictPermissionsForGroups); + + public Boolean getFaqForum(); + + public void setFaqForum(Boolean isFaqForum); + } \ No newline at end of file diff --git a/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionTopic.java b/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionTopic.java index 0645408adf1b..4b177acba2dd 100644 --- a/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionTopic.java +++ b/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/DiscussionTopic.java @@ -64,4 +64,8 @@ public interface DiscussionTopic extends OpenTopic { public Boolean getRestrictPermissionsForGroups(); public void setRestrictPermissionsForGroups(Boolean restrictPermissionsForGroups); + + public Boolean getFaqTopic(); + + public void setFaqTopic(Boolean isFaqTopic); } \ No newline at end of file diff --git a/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java b/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java index 903f6cfbaaf7..a6dd34e023dd 100644 --- a/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java +++ b/msgcntr/messageforums-api/src/java/org/sakaiproject/api/app/messageforums/MessageForumsForumManager.java @@ -75,6 +75,44 @@ public interface MessageForumsForumManager { public BaseForum getForumById(boolean open, Long forumId); public BaseForum getForumByUuid(String forumId); + /** + * Retrieve FAQ Forum from a given area + * @return existing faq forum or null + */ + public DiscussionForum getFaqForumForArea(Area area); + + /** + * Retrieve FAQ Topic from a given forum + * @return existing faq forum or null + */ + public DiscussionTopic getFaqTopicForForum(DiscussionForum discussionForum); + + /** + * Create and save FAQ Forum for a given area + * @return newly created faq forum + */ + public DiscussionForum createFaqForum(Area discussionArea); + + /** + * Create and save FAQ Topic for a given forum + * @return newly created faq topic + */ + public DiscussionTopic createFaqTopic(DiscussionForum discussionForum); + + /** + * Retrieve or create and save FAQ Forum from a given area + * Create and save FAQ Forum for a given area + * @return existing or newly created faq forum + */ + public DiscussionForum getOrCreateFaqForumForArea(Area area); + + /** + * Retrieve or create and save FAQ Topic from a given forum + * Create and save FAQ Forum for a given area + * @return existing or newly created faq topic + */ + public DiscussionTopic getOrCreateFaqTopicForForum(DiscussionForum discussionForum); + /** * Create and save an empty discussion forum * @return discussion forum diff --git a/msgcntr/messageforums-app/pom.xml b/msgcntr/messageforums-app/pom.xml index 53c7b92730a9..de9553fffa88 100644 --- a/msgcntr/messageforums-app/pom.xml +++ b/msgcntr/messageforums-app/pom.xml @@ -94,6 +94,10 @@ org.springframework spring-core + + org.springframework + spring-web + org.springframework spring-orm diff --git a/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java b/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java index 5f984f017460..9a6aade1f36c 100644 --- a/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java +++ b/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/DiscussionForumTool.java @@ -324,6 +324,8 @@ public class DiscussionForumTool { private static final String AUTOCREATE_TOPICS_GROUPS_DESCRIPTION = "cdfm_autocreate_topics_desc_groups"; private static final String DUPLICATE_COPY_TITLE = "cdfm_duplicate_copy_title"; private static final String TASK_NOT_CREATED = "cdfm_cant_create_task"; + private static final String MSG_PVT_ANSWER_PREFIX = "pvt_answer_title_prefix"; + private static final String MSG_PVT_QUESTION_PREFIX = "pvt_question_title_prefix"; private static final String FROM_PAGE = "msgForum:mainOrForumOrTopic"; /** @@ -4316,12 +4318,34 @@ public String processDfMsgReplyMsgFromEntire() public String processDfMsgReplyMsg() { - selectedMessageCount = 0; - if(selectedMessage.getMessage().getTitle() != null && !selectedMessage.getMessage().getTitle().startsWith(getResourceBundleString(MSG_REPLY_PREFIX))) - this.composeTitle = getResourceBundleString(MSG_REPLY_PREFIX) + " " + selectedMessage.getMessage().getTitle() + " "; - else - this.composeTitle = selectedMessage.getMessage().getTitle(); - + selectedMessageCount = 0; + + boolean isFaqForum = Boolean.TRUE.equals(selectedTopic.getTopic().getFaqTopic()); + + String replyPrefix = getResourceBundleString(MSG_REPLY_PREFIX); + String answerPrefix = getResourceBundleString(MSG_PVT_ANSWER_PREFIX); + String questionPrefix = getResourceBundleString(MSG_PVT_QUESTION_PREFIX); + String title = StringUtils.trim(selectedMessage.getMessage().getTitle()); + + if (StringUtils.startsWith(title, replyPrefix)) { + // Re: title --> Re: title + this.composeTitle = title; + } else if (isFaqForum) { + if (StringUtils.startsWith(title, questionPrefix)) { + // Q: title -> A: title + this.composeTitle = StringUtils.replace(title, questionPrefix, answerPrefix); + } else { + // title --> Re: title + // A: title --> Re: A: title + this.composeTitle = replyPrefix + " " + title; + } + } else { + // title --> Re: title + // A: title --> Re: A: title + // Q: title --> Re: Q: title + this.composeTitle = replyPrefix + " " + title; + } + return "dfMessageReply"; } diff --git a/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/MessageForumPublishToFaqBean.java b/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/MessageForumPublishToFaqBean.java new file mode 100644 index 000000000000..55eeb09002a0 --- /dev/null +++ b/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/MessageForumPublishToFaqBean.java @@ -0,0 +1,186 @@ +package org.sakaiproject.tool.messageforums; + +import java.io.Serializable; +import java.util.Locale; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.Set; + +import javax.annotation.PostConstruct; +import javax.faces.bean.ManagedBean; +import javax.faces.bean.SessionScoped; + +import org.apache.commons.lang3.StringUtils; +import org.sakaiproject.api.app.messageforums.Area; +import org.sakaiproject.api.app.messageforums.AreaManager; +import org.sakaiproject.api.app.messageforums.DiscussionForum; +import org.sakaiproject.api.app.messageforums.DiscussionForumService; +import org.sakaiproject.api.app.messageforums.DiscussionTopic; +import org.sakaiproject.api.app.messageforums.Message; +import org.sakaiproject.api.app.messageforums.MessageForumsForumManager; +import org.sakaiproject.api.app.messageforums.MessageForumsMessageManager; +import org.sakaiproject.api.app.messageforums.ui.DiscussionForumManager; +import org.sakaiproject.api.app.messageforums.ui.UIPermissionsManager; +import org.sakaiproject.exception.IdUnusedException; +import org.sakaiproject.site.api.SiteService; +import org.sakaiproject.tool.api.ToolManager; +import org.sakaiproject.user.api.UserDirectoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.context.support.SpringBeanAutowiringSupport; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Data +@NoArgsConstructor +@ManagedBean(name = MessageForumPublishToFaqBean.NAME) +@SessionScoped +@EqualsAndHashCode(callSuper = false) +public class MessageForumPublishToFaqBean extends SpringBeanAutowiringSupport implements Serializable { + + + private static final String MESSAGECENTER_BUNDLE = "org.sakaiproject.api.app.messagecenter.bundle.Messages"; + private static final Integer TITLE_MAX_LENGTH = 255; + + private String title; + private String question; + private String answer; + private String siteId; + private String userId; + private Area discussionArea; + private ResourceBundle resourceBundle; + + @Setter(AccessLevel.NONE) + private Boolean canPost; + @Setter(AccessLevel.NONE) + private Boolean canReply; + + @Autowired + private AreaManager areaManager; + + @Autowired + private MessageForumsForumManager forumManager; + + @Autowired + private MessageForumsMessageManager messageManager; + + @Autowired + private DiscussionForumManager discussionForumManager; + + @Autowired + private UIPermissionsManager permissionsManager; + + @Autowired + private ToolManager toolManager; + + @Autowired + private SiteService siteService; + + @Autowired + private UserDirectoryService userDirectoryService; + + public static final String NAME = "mfPublishToFaqBean"; + + + @PostConstruct + public void init() { + log.debug("Initializing MessageForumPublishToFaqBean" + "(" + NAME + ")"); + + Objects.requireNonNull(areaManager); + Objects.requireNonNull(forumManager); + Objects.requireNonNull(messageManager); + Objects.requireNonNull(discussionForumManager); + Objects.requireNonNull(permissionsManager); + Objects.requireNonNull(toolManager); + Objects.requireNonNull(siteService); + Objects.requireNonNull(userDirectoryService); + + siteId = toolManager.getCurrentPlacement().getContext(); + userId = userDirectoryService.getCurrentUser().getId(); + discussionArea = areaManager.getDiscussionArea(siteId); + resourceBundle = ResourceBundle.getBundle(MESSAGECENTER_BUNDLE, + siteService.getSiteLocale(siteId).orElse(Locale.getDefault())); + + DiscussionForum faqForum = forumManager.getFaqForumForArea(discussionArea); + DiscussionTopic faqTopic = forumManager.getFaqTopicForForum(faqForum); + if (faqForum != null) { + if (faqTopic != null) { + canPost = permissionsManager.isNewResponse(faqTopic, faqForum); + canReply = permissionsManager.isNewResponseToResponse(faqTopic, faqForum); + log.debug("FAQ Forum and FAQ Topic present; canPost = {}; canReply = {};"); + } else { + canPost = canReply = permissionsManager.isNewTopic(faqForum); + log.debug("FAQ Forum, but no FAQ Topic present; Can create new Topic? canPost = canReply = {};", canPost); + } + } else { + canPost = canReply = permissionsManager.isNewForum(); + log.debug("No FAQ Forum present; Can create new Forum? canPost = canReply = {};", canPost); + } + } + + public void setMessage(Message message) { + if (message != null) { + title = message.getTitle(); + question = message.getBody(); + } else { + title = null; + question = null; + } + answer = null; + } + + public void publishToFaq() { + DiscussionForum faqForum = forumManager.getOrCreateFaqForumForArea(discussionArea); + DiscussionTopic faqTopic = forumManager.getOrCreateFaqTopicForForum(faqForum); + + log.debug("Creating question message for Forum [{}] and Topic [{}]", faqForum, faqTopic); + + if (!Boolean.TRUE.equals(canPost)) { + log.warn("User with id [{}] does not have permissions to create a new forum post", userId); + return; + } + + String questionTitlePrefix = resourceBundle.getString("pvt_question_title_prefix"); + String answerTitlePrefix = resourceBundle.getString("pvt_answer_title_prefix"); + // Use the longer prefix for the length, + 1 for the space character + int prefixLength = Math.max(questionTitlePrefix.length(), answerTitlePrefix.length()) + 1; + String title = StringUtils.substring(StringUtils.trim(this.title), 0, TITLE_MAX_LENGTH - prefixLength - 1); + String question = StringUtils.trim(this.question); + + Message createdQuestionMessage = messageManager.createDiscussionMessage(); + createdQuestionMessage.setAuthor(userId); + createdQuestionMessage.setTitle(questionTitlePrefix + " " + title); + createdQuestionMessage.setBody(question); + createdQuestionMessage.setTopic(faqTopic); + createdQuestionMessage.setDeleted(Boolean.FALSE); + createdQuestionMessage.setApproved(Boolean.TRUE); + + Message savedQuestionMessage = discussionForumManager.saveMessage(createdQuestionMessage); + discussionForumManager.markMessageReadStatusForUser(savedQuestionMessage, true, userId); + + String answer = StringUtils.trim(this.answer); + if (Boolean.TRUE.equals(canReply) && StringUtils.isNotEmpty(answer)) { + log.debug("Creating answer message"); + + Message createdAnswerMessage = messageManager.createDiscussionMessage(); + createdAnswerMessage.setAuthor(userId); + createdAnswerMessage.setTitle(answerTitlePrefix + " " + title); + createdAnswerMessage.setBody(answer); + createdAnswerMessage.setTopic(faqTopic); + createdAnswerMessage.setDeleted(Boolean.FALSE); + createdAnswerMessage.setApproved(Boolean.TRUE); + createdAnswerMessage.setInReplyTo(savedQuestionMessage); + + Message savedAnswerMessage = discussionForumManager.saveMessage(createdAnswerMessage); + discussionForumManager.markMessageReadStatusForUser(savedAnswerMessage, true, userId); + } else { + log.debug("Not creating answer message"); + } + } + +} diff --git a/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/PrivateMessagesTool.java b/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/PrivateMessagesTool.java index efcf36fdf1b2..143810eb8255 100644 --- a/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/PrivateMessagesTool.java +++ b/msgcntr/messageforums-app/src/java/org/sakaiproject/tool/messageforums/PrivateMessagesTool.java @@ -90,6 +90,8 @@ import org.sakaiproject.util.comparator.GroupTitleComparator; import org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException; +import javax.faces.FactoryFinder; +import javax.faces.application.ApplicationFactory; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; @@ -101,6 +103,7 @@ import javax.servlet.http.HttpServletRequest; import java.text.ParseException; +import java.io.Serializable; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -155,6 +158,8 @@ public class PrivateMessagesTool { private static final String MISSING_SUBJECT_DRAFT = "pvt_missing_subject_draft"; private static final String MISSING_BODY = "pvt_missing_body"; private static final String MISSING_BODY_DRAFT = "pvt_missing_body_draft"; + private static final String MISSING_TITLE = "pvt_missing_title"; + private static final String MISSING_QUESTION = "pvt_missing_question"; private static final String SELECT_MSG_RECIPIENT = "pvt_select_msg_recipient"; private static final String MULTIPLE_WINDOWS = "pvt_multiple_windows"; @@ -166,6 +171,7 @@ public class PrivateMessagesTool { private static final String NO_MARKED_MOVE_MESSAGE = "pvt_no_message_mark_move"; private static final String MULTIDELETE_SUCCESS_MSG = "pvt_deleted_success"; private static final String PERM_DELETE_SUCCESS_MSG = "pvt_perm_deleted_success"; + private static final String SUCCESS_PUBLISH_TO_FAQ = "pvt_publish_to_faq_success"; public static final String RECIPIENTS_UNDISCLOSED = "pvt_bccUndisclosed"; @@ -262,6 +268,7 @@ public class PrivateMessagesTool { public static final String DELETE_MESSAGE_PG="pvtMsgDelete"; public static final String REVISE_FOLDER_PG="pvtMsgFolderRevise"; public static final String MOVE_MESSAGE_PG="pvtMsgMove"; + public static final String PUBLISH_TO_FAQ_EDIT = "pvtMsgPublishToFaqEdit"; public static final String ADD_FOLDER_IN_FOLDER_PG="pvtMsgFolderInFolderAdd"; public static final String ADD_MESSAGE_FOLDER_PG="pvtMsgFolderAdd"; public static final String PVTMSG_COMPOSE = "pvtMsgCompose"; @@ -288,7 +295,9 @@ public class PrivateMessagesTool { //huxt public static final String EXTERNAL_TOPIC_ID = "pvtMsgTopicId"; public static final String EXTERNAL_WHICH_TOPIC = "selectedTopic"; - + + @Getter private final String BREADCRUMB_SEPARATOR = " / "; + PrivateForumDecoratedBean decoratedForum; private List aggregateList = new ArrayList(); @@ -1036,6 +1045,7 @@ public String processActionPrivateMessages() public String processDisplayForum() { log.debug("processDisplayForum()"); + multiDeleteSuccess = false; if (searchPvtMsgs != null) searchPvtMsgs.clear(); return DISPLAY_MESSAGES_PG; @@ -1299,6 +1309,7 @@ public String processPvtMsgDetail() { public String processPvtMsgReply() { log.debug("processPvtMsgReply()"); + multiDeleteSuccess = false; setDetailMsgCount = 0; if (getDetailMsg() == null) @@ -1373,6 +1384,7 @@ public String processPvtMsgReply() { public String processPvtMsgForward() { log.debug("processPvtMsgForward()"); + multiDeleteSuccess = false; setDetailMsgCount = 0; if (getDetailMsg() == null) @@ -1455,6 +1467,7 @@ else if (fileType.equalsIgnoreCase("application/msword")) public String processPvtMsgReplyAll() { log.debug("processPvtMsgReplyAll()"); + multiDeleteSuccess = false; setDetailMsgCount = 0; if (getDetailMsg() == null) @@ -1613,6 +1626,7 @@ else if(currentUserasAuther.replace(" ", ", ").equals(msgautherString)){ public String processPvtMsgDeleteConfirm() { log.debug("processPvtMsgDeleteConfirm()"); + this.setMultiDeleteSuccess(false); this.setDeleteConfirm(true); setErrorMessage(getResourceBundleString(CONFIRM_MSG_DELETE)); /* @@ -2059,6 +2073,7 @@ public PrivateMessage constructMessage(boolean clearAttachments, PrivateMessage */ public String processDisplayPreviousMsg() { + multiDeleteSuccess = false; List tempMsgs = getDecoratedPvtMsgs(); // all messages int currentMsgPosition = -1; @@ -2123,6 +2138,7 @@ public String processDisplayPreviousMsg() */ public String processDisplayNextMsg() { + multiDeleteSuccess = false; List tempMsgs = getDecoratedPvtMsgs(); int currentMsgPosition = -1; @@ -3822,6 +3838,7 @@ else if(createFolder.length() > 100) public String getMoveToTopic() { + multiDeleteSuccess = false; if(StringUtils.isNotEmpty(moveToNewTopic)) { moveToTopic=moveToNewTopic; @@ -3842,6 +3859,45 @@ public String processPvtMsgMove() { return MOVE_MESSAGE_PG; } + public String processPvtMsgPublishToFaq() { + log.debug("processPvtMsgPublishToFaq()"); + MessageForumPublishToFaqBean publishToFaqBean = + (MessageForumPublishToFaqBean) lookupBean(MessageForumPublishToFaqBean.NAME); + + if (StringUtils.isBlank(publishToFaqBean.getTitle())) { + setErrorMessage(getResourceBundleString(MISSING_TITLE)); + return null; + } else if (StringUtils.isBlank(publishToFaqBean.getQuestion())) { + setErrorMessage(getResourceBundleString(MISSING_QUESTION)); + return null; + } else { + publishToFaqBean.publishToFaq(); + multiDeleteSuccessMsg = getResourceBundleString(SUCCESS_PUBLISH_TO_FAQ); + multiDeleteSuccess = true; + return SELECTED_MESSAGE_PG; + } + + } + + public String processPvtMsgPublishToFaqEdit() { + log.debug("processPvtMsgPublishToFaqEdit()"); + multiDeleteSuccess = false; + + MessageForumPublishToFaqBean publishToFaqBean = + (MessageForumPublishToFaqBean) lookupBean(MessageForumPublishToFaqBean.NAME); + + publishToFaqBean.setMessage(detailMsg.getMsg()); + + return PUBLISH_TO_FAQ_EDIT; + } + + public Object lookupBean(String beanName) { + ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY); + + return (Serializable) applicationFactory.getApplication().getVariableResolver() + .resolveVariable(FacesContext.getCurrentInstance(), beanName); + } + public void processPvtMsgParentFolderMove(ValueChangeEvent event) { log.debug("processPvtMsgSettingsRevise()"); @@ -4278,7 +4334,15 @@ private void setErrorMessage(String errorMsg) { log.debug("setErrorMessage(String " + errorMsg + ")"); FacesContext.getCurrentInstance().addMessage(null, - new FacesMessage(getResourceBundleString(ALERT) + ' ' + errorMsg)); + new FacesMessage(FacesMessage.SEVERITY_ERROR, + getResourceBundleString(ALERT) + " " + errorMsg, null)); + } + + private void setSuccessMessage(String successMsg) + { + log.debug("setSuccessMessage(String " + successMsg + ")"); + FacesContext.getCurrentInstance().addMessage(null, + new FacesMessage(FacesMessage.SEVERITY_INFO, successMsg, null)); } private void setInformationMessage(String infoMsg) @@ -4381,6 +4445,25 @@ public boolean isSearchPvtMsgsEmpty() return searchPvtMsgs == null || searchPvtMsgs.isEmpty(); } + public boolean isDetailMessagePublishableToFaq() { + String siteId = getSiteId(); + + if (!StringUtils.equalsAny(getMsgNavMode(), + PrivateMessagesTool.PVTMSG_MODE_SENT, PrivateMessagesTool.PVTMSG_MODE_RECEIVED)) { + return false; + } + + boolean forumsToolPresent = false; + try { + forumsToolPresent = siteService.getSite(siteId) + .getToolForCommonId(DiscussionForumService.FORUMS_TOOL_ID) != null; + } catch (IdUnusedException e) { + log.error("Could not find site with id [{}]: {}", siteId, e.toString()); + } + + return forumsToolPresent; + } + public void setMsgNavMode(String msgNavMode) { this.msgNavMode = msgNavMode; } diff --git a/msgcntr/messageforums-app/src/webapp/WEB-INF/faces-config.xml b/msgcntr/messageforums-app/src/webapp/WEB-INF/faces-config.xml index 1b9bb6eb05e3..2298ca1bbf6c 100644 --- a/msgcntr/messageforums-app/src/webapp/WEB-INF/faces-config.xml +++ b/msgcntr/messageforums-app/src/webapp/WEB-INF/faces-config.xml @@ -189,6 +189,11 @@ /jsp/privateMsg/pvtMsgMove.jsp + + pvtMsgPublishToFaqEdit + /jsp/privateMsg/pvtMsgPublishToFaqEdit.jsp + + pvtMsgFolderInFolderAdd /jsp/privateMsg/pvtMsgFolderInFolderAdd.jsp diff --git a/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgDetail.jsp b/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgDetail.jsp index b07b7c9639eb..eb0d4466f591 100644 --- a/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgDetail.jsp +++ b/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgDetail.jsp @@ -49,6 +49,7 @@ <%-- --%> + @@ -100,6 +101,8 @@ <%--SAK-10505 add forward --%> + @@ -240,6 +243,8 @@ <%--SAKAI-10505 add forward--%> + diff --git a/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgPublishToFaqEdit.jsp b/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgPublishToFaqEdit.jsp new file mode 100644 index 000000000000..98e31c136272 --- /dev/null +++ b/msgcntr/messageforums-app/src/webapp/jsp/privateMsg/pvtMsgPublishToFaqEdit.jsp @@ -0,0 +1,80 @@ +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://sakaiproject.org/jsf2/sakai" prefix="sakai" %> + + + + + + + + + + + + <%@ include file="/jsp/privateMsg/pvtMenu.jsp" %> + + <%-- BREADCRUMBS START --%> + + <%-- BREADCRUMBS START --%> + + <%-- ERROR MESSAGE --%> + + + <%-- CONTENT START --%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%-- CONTENT END --%> + + + diff --git a/msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java b/msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java index a61aed7f25ac..4c13c24d8fb1 100644 --- a/msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java +++ b/msgcntr/messageforums-component-impl/src/java/org/sakaiproject/component/app/messageforums/MessageForumsForumManagerImpl.java @@ -28,6 +28,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.ResourceBundle; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -76,6 +79,9 @@ import org.sakaiproject.tool.api.Placement; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.tool.api.ToolManager; +import org.sakaiproject.user.api.UserDirectoryService; +import org.sakaiproject.util.ResourceLoader; +import org.springframework.cglib.core.Local; import org.springframework.orm.hibernate5.HibernateCallback; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; @@ -137,6 +143,7 @@ public class MessageForumsForumManagerImpl extends HibernateDaoSupport implement private static final String QUERY_TOPICS_WITH_MSGS_AND_ATTACHMENTS_AND_MEMBERSHIPS_FOR_FORUM = "findTopicsWithMessagesMembershipAndAttachmentsForForum"; private static final String QUERY_FORUMS_FOR_MAIN_PAGE = "findForumsForMainPage"; + private static final String QUERY_FAQ_FORUMS = "findFaqForums"; private static final String QUERY_BY_TOPIC_ID = "findTopicById"; private static final String QUERY_OPEN_BY_TOPIC_AND_PARENT = "findOpenTopicAndParentById"; @@ -155,6 +162,8 @@ public class MessageForumsForumManagerImpl extends HibernateDaoSupport implement private static final String QUERY_GET_FORUM_BY_ID_WITH_TOPICS_AND_ATT_AND_MSGS = "findForumByIdWithTopicsAndAttachmentsAndMessages"; + private static final String MESSAGECENTER_BUNDLE = "org.sakaiproject.api.app.messagecenter.bundle.Messages"; + /** Sorts the forums by the sort index and if the same index then order by the creation date */ public static final Comparator FORUM_SORT_INDEX_CREATED_DATE_COMPARATOR_DESC = new ForumBySortIndexAscAndCreatedDateDesc(); @@ -692,10 +701,53 @@ public DiscussionForum createDiscussionForum() { forum.setPostFirst(Boolean.FALSE); forum.setAutoMarkThreadsRead(DEFAULT_AUTO_MARK_READ); forum.setRestrictPermissionsForGroups(Boolean.FALSE); + forum.setFaqForum(Boolean.FALSE); log.debug("createDiscussionForum executed"); return forum; } + public DiscussionForum getFaqForumForArea(Area area) { + HibernateCallback hibernateCallback = (session) -> { + Query query = session.getNamedQuery(QUERY_FAQ_FORUMS); + query.setParameter("typeUuid", area.getTypeUuid()); + query.setParameter("contextId", area.getContextId()); + + return query.list().stream().findAny().orElse(null); + }; + + DiscussionForum existingFaqForum = getHibernateTemplate().execute(hibernateCallback); + return existingFaqForum != null ? existingFaqForum : createFaqForum(area); + } + + public DiscussionTopic getFaqTopicForForum(DiscussionForum faqForum) { + if (faqForum == null) { + return null; + } + + Set faqForumTopics = faqForum.getTopicsSet(); + if (faqForumTopics == null) { + return null; + } + + return faqForumTopics.stream() + .filter(topic -> Boolean.TRUE.equals(topic.getFaqTopic())) + .findAny().orElse(null); + } + + public DiscussionForum getOrCreateFaqForumForArea(Area area) { + DiscussionForum existingFaqForum = getFaqForumForArea(area); + return existingFaqForum != null + ? existingFaqForum + : createFaqForum(area); + } + + public DiscussionTopic getOrCreateFaqTopicForForum(DiscussionForum discussionForum) { + DiscussionTopic existingFaqTopic = getFaqTopicForForum(discussionForum); + return existingFaqTopic != null + ? existingFaqTopic + : createFaqTopic(discussionForum); + } + public ActorPermissions createDefaultActorPermissions() { ActorPermissions actorPermissions = new ActorPermissionsImpl(); @@ -710,6 +762,52 @@ public ActorPermissions createDefaultActorPermissions() return actorPermissions; } + // Create a new FAQ forum based on defaults + public DiscussionForum createFaqForum(Area discussionArea) { + log.debug("Creating a new FAQ Forum"); + + // Get site locale first, use server locale as fallback + Locale locale = siteService.getSiteLocale(discussionArea.getContextId()).orElse(Locale.getDefault()); + ResourceBundle resourceBundle = ResourceBundle.getBundle(MESSAGECENTER_BUNDLE, locale); + + DiscussionForum createdForum = createDiscussionForum(); + createdForum.setArea(discussionArea); + createdForum.setCreatedBy(UserDirectoryService.ADMIN_ID); + createdForum.setTitle(resourceBundle.getString("cdfm_faq_forum_title")); + createdForum.setShortDescription(resourceBundle.getString("cdfm_faq_forum_description")); + createdForum.setModerated(discussionArea.getModerated()); + createdForum.setPostFirst(discussionArea.getPostFirst()); + createdForum.setFaqForum(Boolean.TRUE); + + DiscussionForum savedForum = saveDiscussionForum(createdForum); + + DiscussionTopic discussionTopic = createFaqTopic(savedForum); + + savedForum.addTopic(discussionTopic); + + return savedForum; + } + + // Create a new FAQ topic based on defaults + public DiscussionTopic createFaqTopic(DiscussionForum discussionForum) { + log.debug("Creating a new FAQ Topic"); + + // Get site locale first, use server locale as fallback + Locale locale = siteService.getSiteLocale(discussionForum.getArea().getContextId()).orElse(Locale.getDefault()); + ResourceBundle resourceBundle = ResourceBundle.getBundle(MESSAGECENTER_BUNDLE, locale); + + DiscussionTopic createdTopic = createDiscussionForumTopic(discussionForum); + createdTopic.setTitle(resourceBundle.getString("cdfm_faq_topic_title")); + createdTopic.setShortDescription(resourceBundle.getString("cdfm_faq_topic_description")); + createdTopic.setCreatedBy(UserDirectoryService.ADMIN_ID); + createdTopic.setFaqTopic(Boolean.TRUE); + createdTopic.setBaseForum(discussionForum); + + DiscussionTopic savedTopic = saveDiscussionForumTopic(createdTopic, false); + + return savedTopic; + } + /** * @see org.sakaiproject.api.app.messageforums.MessageForumsForumManager#createPrivateForum(java.lang.String) */ @@ -869,6 +967,7 @@ public DiscussionTopic createDiscussionForumTopic(DiscussionForum forum) { topic.setRevealIDsToRoles(Boolean.FALSE); topic.setAutoMarkThreadsRead(forum.getAutoMarkThreadsRead()); topic.setRestrictPermissionsForGroups(Boolean.FALSE); + topic.setFaqTopic(Boolean.FALSE); log.debug("createDiscussionForumTopic executed"); return topic; } diff --git a/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionForumImpl.java b/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionForumImpl.java index 06f3b5712734..48adb70f74cf 100644 --- a/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionForumImpl.java +++ b/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionForumImpl.java @@ -22,6 +22,8 @@ import java.util.List; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.sakaiproject.api.app.messageforums.ActorPermissions; @@ -39,6 +41,8 @@ public class DiscussionForumImpl extends OpenForumImpl implements DiscussionForu private int areaindex; private Boolean autoMarkThreadsRead; private Boolean restrictPermissionsForGroups; + @Getter @Setter + private Boolean faqForum; public int getAreaindex() { try { diff --git a/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionTopicImpl.java b/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionTopicImpl.java index 98ef8ee68db9..4e1b4655a67b 100644 --- a/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionTopicImpl.java +++ b/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/DiscussionTopicImpl.java @@ -22,6 +22,8 @@ import java.util.List; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.sakaiproject.api.app.messageforums.ActorPermissions; @@ -42,6 +44,8 @@ public class DiscussionTopicImpl extends OpenTopicImpl implements DiscussionTopi private String gradebook; private String gradebookAssignment; private Boolean restrictPermissionsForGroups; + @Getter @Setter + private Boolean faqTopic; public ActorPermissions getActorPermissions() { return actorPermissions; diff --git a/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml b/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml index e78c7bbf5430..ddcc990a6525 100644 --- a/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml +++ b/msgcntr/messageforums-hbm/src/java/org/sakaiproject/component/app/messageforums/dao/hibernate/OpenForum.hbm.xml @@ -146,6 +146,9 @@ + + + @@ -211,6 +214,15 @@ where forum.typeUuid = :typeUuid and forum.area.contextId = :contextId ]]> + + + + +