package org.sakaiproject.component.app.messageforums.jobs;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.sakaiproject.api.app.messageforums.MessageForumsMessageManager;
import org.sakaiproject.api.app.messageforums.MessageForumsTypeManager;
import org.sakaiproject.api.app.messageforums.cover.SynopticMsgcntrManagerCover;
import org.sakaiproject.api.app.messageforums.ui.DiscussionForumManager;
import org.sakaiproject.api.app.messageforums.ui.PrivateMessageManager;
import org.sakaiproject.api.app.messageforums.ui.UIPermissionsManager;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.db.cover.SqlService;
import org.sakaiproject.site.api.SiteService;
public class UpdateSynopticMessageCounts implements Job{
private PrivateMessageManager pvtMessageManager;
private MessageForumsTypeManager typeManager;
private DiscussionForumManager forumManager;
private UIPermissionsManager uiPermissionsManager;
private MessageForumsMessageManager messageManager;
private SiteService siteService;
private static final Log LOG = LogFactory.getLog(UpdateSynopticMessageCounts.class);
private static final boolean runOracleSQL = false;
//this SQL is more generic but also slower
private static final String FIND_ALL_SYNOPTIC_SITES_QUERY_GENERIC = "select SITE_ID, TITLE from SAKAI_SITE where IS_USER = 0 and PUBLISHED = 1 and IS_SPECIAL = 0";
private static final String FIND_ALL_SYNOPTIC_SITES_BY_SITE_QUERY_GENERIC = FIND_ALL_SYNOPTIC_SITES_QUERY_GENERIC + " and SITE_ID like ?";
//this SQL works for Oracle and runs faster than the Generic Query
private static final String FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE_PART1 = "select q.SITE_ID, q.TITLE, sum(q.Decoded) as BINARY_FLAGS from (" +
"select ss.SITE_ID, ss.TITLE, " +
"decode (sst.REGISTRATION,'sakai.messagecenter',100,'sakai.messages',10,'sakai.forums',1,0) as Decoded " +
"from SAKAI_SITE ss, SAKAI_SITE_TOOL sst " +
"where ss.IS_USER = 0 " +
"and ss.PUBLISHED = 1 " +
"and ss.IS_SPECIAL = 0 " +
"and ss.SITE_ID = sst.SITE_ID ";
private static final String FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE_PART2 = "and sst.REGISTRATION in ('sakai.messagecenter','sakai.messages','sakai.forums')) q " +
"Group By q.SITE_ID, q.TITLE";
private static final String FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE = FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE_PART1 + FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE_PART2;
private static final String FIND_ALL_SYNOPTIC_SITES_BY_SITE_QUERY_ORACLE = FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE_PART1 +
"and ss.SITE_ID like ? " +
FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE_PART2;
private static final String UNREAD_MESSAGES_QUERY = "SELECT message.USER_ID, message.CONTEXT_ID, count(*) unread_messages " +
"FROM MFR_PVT_MSG_USR_T message " +
"where READ_STATUS = 0 " +
"group by message.USER_ID, message.CONTEXT_ID";
private static final String UNREAD_MESSAGES_BY_SITE_QUERY = "SELECT message.USER_ID, message.CONTEXT_ID, count(*) unread_messages " +
"FROM MFR_PVT_MSG_USR_T message " +
"where READ_STATUS = 0 " +
"and message.CONTEXT_ID like ?" +
"group by message.USER_ID, message.CONTEXT_ID";
private static final String TOPICS_AND_FORUMS_QUERY = "select area.CONTEXT_ID, forum.ID as FORUM_ID, topic.ID as TOPIC_ID, forum.DRAFT as isForumDraft, topic.DRAFT as isTopicDraft, topic.MODERATED as isTopicModerated, forum.LOCKED as isForumLocked, topic.LOCKED as isTopicLocked, forum.CREATED_BY as forumCreatedBy, topic.CREATED_BY as topicCreatedBy " +
"from MFR_AREA_T area, MFR_OPEN_FORUM_T forum, MFR_TOPIC_T topic " +
"Where area.ID = forum.surrogateKey and forum.ID = topic.of_surrogateKey";
private boolean updateNewMembersOnly = ServerConfigurationService.getBoolean("msgcntr.synoptic.updateMessageCounts.updateNewMembersOnly", false);
//by default, this job only updates/adds the counts when the counts for forums or messages isn't 0, this overrides that and forces updates for all items no matter what
private boolean addItemsWhenNoUnreadCounts = ServerConfigurationService.getBoolean("msgcntr.synoptic.updateMessageCounts.addItemsWhenNoUnreadCounts", false);
public void init() {
}
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
Connection clConnection = null;
Statement statement = null;
ResultSet unreadMessageCountRS = null;
ResultSet allTopicsAndForumsRS = null;
ResultSet synotpicSitesRS = null;
PreparedStatement unreadMessagesbySitePS = null;
PreparedStatement findSitesbySitePS = null;
String siteFilter = ServerConfigurationService.getString("msgcntr.synoptic.updateMessageCountsSiteFilter");
boolean filterSites = siteFilter != null && !"".equals(siteFilter);
//loop through all sites and call updateSynopticToolInfoForAllUsers
int count = 0;
LOG.info("UpdateSynopticMessageCounts job launched: " + new Date());
try {
clConnection = SqlService.borrowConnection();
statement = clConnection.createStatement();
//CREATE HASHMAP OF UNREAD MESSAGES COUNT
if(filterSites){
unreadMessagesbySitePS = clConnection.prepareStatement(UNREAD_MESSAGES_BY_SITE_QUERY);
unreadMessagesbySitePS.setString(1, siteFilter);
unreadMessageCountRS = unreadMessagesbySitePS.executeQuery();
}else{
unreadMessageCountRS = statement.executeQuery(UNREAD_MESSAGES_QUERY);
}
HashMap<String, HashMap<String, Integer>> siteAndUserMessageCountHM = getSiteAndUserMessageCountHM(unreadMessageCountRS);
//CREATE HASHMAP OF ALL TOPICS AND FORUMS
allTopicsAndForumsRS = statement.executeQuery(TOPICS_AND_FORUMS_QUERY);
HashMap<String, HashMap<Long, DecoratedForumInfo>> allTopicsAndForumsHM = getAllTopicsAndForumsHM(allTopicsAndForumsRS);
//LOOP TRHOUGH ALL SITES AND UPDATE INFO
String siteId = "";
String siteTitle = "";
int BINARY_FLAGS;
if(runOracleSQL){
if(filterSites){
findSitesbySitePS = clConnection.prepareStatement(FIND_ALL_SYNOPTIC_SITES_BY_SITE_QUERY_ORACLE);
findSitesbySitePS.setString(1, siteFilter);
synotpicSitesRS = findSitesbySitePS.executeQuery();
}else{
synotpicSitesRS = statement.executeQuery(FIND_ALL_SYNOPTIC_SITES_QUERY_ORACLE);
}
}else{
if(filterSites){
findSitesbySitePS = clConnection.prepareStatement(FIND_ALL_SYNOPTIC_SITES_BY_SITE_QUERY_GENERIC);
findSitesbySitePS.setString(1, siteFilter);
synotpicSitesRS = findSitesbySitePS.executeQuery();
}else{
synotpicSitesRS = statement.executeQuery(FIND_ALL_SYNOPTIC_SITES_QUERY_GENERIC);
}
}
while (synotpicSitesRS.next()) {
boolean isMessageForumsPageInSite = false;
boolean isMessagesPageInSite = false;
boolean isForumsPageInSite = false;
siteId = synotpicSitesRS.getString("SITE_ID");
siteTitle = synotpicSitesRS.getString("TITLE");
if(runOracleSQL){
BINARY_FLAGS = synotpicSitesRS.getInt("BINARY_FLAGS");
//BINARY_FLAGS returns a sumation of the values:
//has messagecenter= 100
//has messages = 10
//has forums = 1
//so if a tool has both messages and forums, the value would be 11
//this if it as binary true false where first bit is messagecenter,
//second is messages, and 3rd is forums
if(BINARY_FLAGS % 10 >= 1)
isForumsPageInSite = true;
if(BINARY_FLAGS % 100 >= 10)
isMessagesPageInSite = true;
if(BINARY_FLAGS % 1000 >= 100)
isMessageForumsPageInSite = true;
}
updateSynopticToolInfoForAllUsers(siteId, siteTitle, clConnection, isMessageForumsPageInSite, isMessagesPageInSite, isForumsPageInSite, siteAndUserMessageCountHM, allTopicsAndForumsHM);
count++;
if(count % 1000 == 0){
LOG.info("UpdateSynopticMessageCounts Progress: " + count + " Sites updated");
}
}
} catch (Exception e1) {
LOG.error(e1);
} finally {
try {
if(unreadMessageCountRS != null)
unreadMessageCountRS.close();
} catch (Exception e) {
LOG.warn(e);
}
try {
if(allTopicsAndForumsRS != null)
allTopicsAndForumsRS.close();
} catch (Exception e) {
LOG.warn(e);
}
try {
if(synotpicSitesRS != null)
synotpicSitesRS.close();
} catch (Exception e) {
LOG.warn(e);
}
try {
if(statement != null)
statement.close();
} catch (Exception e) {
LOG.warn(e);
}
try{
if(unreadMessagesbySitePS != null)
unreadMessagesbySitePS.close();
}catch(Exception e){
LOG.warn(e);
}
try{
if(findSitesbySitePS != null)
findSitesbySitePS.close();
}catch(Exception e){
LOG.warn(e);
}
SqlService.returnConnection(clConnection);
}
LOG.info("UpdateSynopticMessageCounts job finished: " + new Date());
}
private void updateSynopticToolInfoForAllUsers(String siteId, String siteTitle, Connection clConnection, boolean isMessageForumsPageInSite, boolean isMessagesPageInSite, boolean isForumsPageInSite, HashMap<String, HashMap<String, Integer>> siteAndUserMessageCountHM, HashMap<String, HashMap<Long, DecoratedForumInfo>> allTopicsAndForumsHM) throws Exception
{
PreparedStatement isMessageForumsPageInSiteQuery = null;
PreparedStatement isMessagesPageInSiteQuery = null;
PreparedStatement isForumsPageInSiteQuery = null;
PreparedStatement getAllUsersInSiteQuery = null;
if(!runOracleSQL){
ResultSet rsMessagesForums = null;
ResultSet rsMessages = null;
ResultSet rsForusm = null;
try{
isMessageForumsPageInSiteQuery = clConnection.prepareStatement("select * from SAKAI_SITE_TOOL where SITE_ID = ? and REGISTRATION = 'sakai.messagecenter'");
isMessageForumsPageInSiteQuery.setString(1, siteId);
isMessagesPageInSiteQuery = clConnection.prepareStatement("select * from SAKAI_SITE_TOOL where SITE_ID = ? and REGISTRATION = 'sakai.messages'");
isMessagesPageInSiteQuery.setString(1, siteId);
isForumsPageInSiteQuery = clConnection.prepareStatement("select * from SAKAI_SITE_TOOL where SITE_ID = ? and REGISTRATION = 'sakai.forums'");
isForumsPageInSiteQuery.setString(1, siteId);
rsMessagesForums = isMessageForumsPageInSiteQuery.executeQuery();
while(rsMessagesForums.next()){
isMessageForumsPageInSite = true;
}
rsMessages = isMessagesPageInSiteQuery.executeQuery();
while(rsMessages.next()){
isMessagesPageInSite = true;
}
rsForusm = isForumsPageInSiteQuery.executeQuery();
while(rsForusm.next()){
isForumsPageInSite = true;
}
}catch (Exception e){
LOG.warn(e);
}finally{
try{
if(rsMessagesForums != null)
rsMessagesForums.close();
}catch (Exception e){
LOG.warn(e);
}
try{
if(rsMessages != null)
rsMessages.close();
}catch (Exception e){
LOG.warn(e);
}
try{
if(rsForusm != null)
rsForusm.close();
}catch (Exception e){
LOG.warn(e);
}
}
}
ResultSet usersMap = null;
try{
String allUsersQuery = "select USER_ID from SAKAI_SITE_USER where SITE_ID = ?";
if(updateNewMembersOnly){
allUsersQuery += " and USER_ID not in (select USER_ID from MFR_SYNOPTIC_ITEM where SITE_ID = ?)";
}
getAllUsersInSiteQuery = clConnection.prepareStatement(allUsersQuery);
getAllUsersInSiteQuery.setString(1, siteId);
if(updateNewMembersOnly){
getAllUsersInSiteQuery.setString(2, siteId);
}
usersMap = getAllUsersInSiteQuery.executeQuery();
//loop through all users in site and update their information:
HashMap<String, Integer> userHM = null;
Integer count = null;
while(usersMap.next()){
int unreadPrivate = 0;
int unreadForum = 0;
String userId = usersMap.getString("USER_ID");
//message count:
if (isMessageForumsPageInSite || isMessagesPageInSite)
{
userHM = siteAndUserMessageCountHM.get(siteId);
if(userHM != null){
count = userHM.get(userId);
if(count != null){
unreadPrivate = count.intValue();
}
}
}
boolean isSuperUser = SecurityService.isSuperUser(userId);
//forums count:
HashMap<Long, DecoratedForumInfo> dfHM = null;
if (isMessageForumsPageInSite || isForumsPageInSite){
dfHM = allTopicsAndForumsHM.get(siteId);
if(dfHM != null){
//site has forums added to the tool
Set<Entry<Long, DecoratedForumInfo>> dfEntrySet = dfHM.entrySet();
for (Iterator<Entry<Long, DecoratedForumInfo>> iterator = dfEntrySet.iterator(); iterator.hasNext();) {
Entry<Long, DecoratedForumInfo> entry = iterator.next();
Long dfId = entry.getKey();
DecoratedForumInfo dForum = entry.getValue();
boolean isInstructor = getForumManager().isInstructor(userId, "/site/" + siteId);
// Only count unread messages for forums the user can view:
if (dForum.getIsDraft().equals(Boolean.FALSE)
||isInstructor
|| isSuperUser
||forumManager.isForumOwner(dfId, dForum.getCreator(), userId, "/site/" + siteId))
{
final Iterator<DecoratedTopicsInfo> topicIter = dForum.getTopics().iterator();
while (topicIter.hasNext())
{
DecoratedTopicsInfo topic = (DecoratedTopicsInfo) topicIter.next();
Long topicId = topic.getTopicId();
Boolean isTopicDraft = topic.getIsDraft();
Boolean isTopicModerated = topic.getIsModerated();
Boolean isTopicLocked = topic.getIsLocked();
String topicOwner = topic.getCreator();
//Only count unread messages for topics the user can view:
if (isTopicDraft.equals(Boolean.FALSE)
|| isInstructor
|| isSuperUser
||forumManager.isTopicOwner(topicId, topicOwner, userId, "/site/" + siteId)){
if (getUiPermissionsManager().isRead(topicId, isTopicDraft, dForum.getIsDraft(), userId, siteId))
{
if (!isTopicModerated.booleanValue() || (isTopicModerated.booleanValue() &&
getUiPermissionsManager().isModeratePostings(topicId, dForum.getIsLocked(), dForum.getIsDraft(), isTopicLocked, isTopicDraft, userId, siteId)))
{
unreadForum += getMessageManager().findUnreadMessageCountByTopicIdByUserId(topicId, userId);
}
else
{
// b/c topic is moderated and user does not have mod perm, user may only
// see approved msgs or pending/denied msgs authored by user
unreadForum += getMessageManager().findUnreadViewableMessageCountByTopicIdByUserId(topicId, userId);
}
}
}
}
}
}
}
}
//update synoptic tool info:
if(unreadPrivate != 0 || unreadForum != 0 || updateNewMembersOnly || addItemsWhenNoUnreadCounts){
SynopticMsgcntrManagerCover.createOrUpdateSynopticToolInfo(userId, siteId, siteTitle, unreadPrivate, unreadForum);
}
}
}catch (Exception e){
LOG.warn(e);
}finally{
try{
if(usersMap != null)
usersMap.close();
}catch(Exception e){
LOG.warn(e);
}
try{
if(getAllUsersInSiteQuery != null)
getAllUsersInSiteQuery.close();
} catch (Exception e) {
LOG.warn(e);
}
try {
if (isForumsPageInSiteQuery != null)
isForumsPageInSiteQuery.close();
} catch (Exception e) {
LOG.warn(e);
}
try {
if (isMessagesPageInSiteQuery != null)
isMessagesPageInSiteQuery.close();
} catch (Exception e) {
LOG.warn(e);
}
try {
if (isMessageForumsPageInSiteQuery != null)
isMessageForumsPageInSiteQuery.close();
} catch (Exception e) {
LOG.warn(e);
}
}
}
public HashMap<String, HashMap<Long, DecoratedForumInfo>> getAllTopicsAndForumsHM(ResultSet rs){
HashMap<String, HashMap<Long, DecoratedForumInfo>> returnHM = new HashMap<String, HashMap<Long, DecoratedForumInfo>>();
if(rs != null){
String CONTEXT_ID, FORUM_CREATED_BY, TOPIC_CREATED_BY;
Long FORUM_ID, TOPIC_ID;
Boolean IS_TOPIC_DRAFT, IS_FORUM_DRAFT, IS_TOPIC_MODERATED, IS_FORUM_LOCKED, IS_TOPIC_LOCKED;
try{
while(rs.next()){
CONTEXT_ID = rs.getString("CONTEXT_ID");
FORUM_ID = rs.getLong("FORUM_ID");
TOPIC_ID = rs.getLong("TOPIC_ID");
IS_TOPIC_DRAFT = rs.getBoolean("isForumDraft");
IS_FORUM_DRAFT = rs.getBoolean("isTopicDraft");
IS_TOPIC_MODERATED = rs.getBoolean("isTopicModerated");
IS_FORUM_LOCKED = rs.getBoolean("isForumLocked");
IS_TOPIC_LOCKED = rs.getBoolean("isTopicLocked");
FORUM_CREATED_BY = rs.getString("forumCreatedBy");
TOPIC_CREATED_BY = rs.getString("topicCreatedBy");
if(returnHM.containsKey(CONTEXT_ID)){
//hashmap already has this site id, now look for forum id:
if(returnHM.get(CONTEXT_ID).containsKey(FORUM_ID)){
DecoratedTopicsInfo dTopic = new DecoratedTopicsInfo(TOPIC_ID, IS_TOPIC_LOCKED, IS_TOPIC_DRAFT, IS_TOPIC_MODERATED, TOPIC_CREATED_BY);
returnHM.get(CONTEXT_ID).get(FORUM_ID).addTopic(dTopic);
}else{
//this is a new forum, so add it to the list
DecoratedTopicsInfo dTopic = new DecoratedTopicsInfo(TOPIC_ID, IS_TOPIC_LOCKED, IS_TOPIC_DRAFT, IS_TOPIC_MODERATED, TOPIC_CREATED_BY);
DecoratedForumInfo dForum = new DecoratedForumInfo(FORUM_ID, IS_FORUM_LOCKED, IS_FORUM_DRAFT, FORUM_CREATED_BY);
dForum.addTopic(dTopic);
returnHM.get(CONTEXT_ID).put(FORUM_ID, dForum);
}
}else{
DecoratedTopicsInfo dTopic = new DecoratedTopicsInfo(TOPIC_ID, IS_TOPIC_LOCKED, IS_TOPIC_DRAFT, IS_TOPIC_MODERATED, TOPIC_CREATED_BY);
DecoratedForumInfo dForum = new DecoratedForumInfo(FORUM_ID, IS_FORUM_LOCKED, IS_FORUM_DRAFT, FORUM_CREATED_BY);
dForum.addTopic(dTopic);
HashMap<Long, DecoratedForumInfo> forumHM = new HashMap<Long, DecoratedForumInfo>();
forumHM.put(FORUM_ID, dForum);
returnHM.put(CONTEXT_ID, forumHM);
}
}
}catch(Exception e){
LOG.error(e);
}
}
return returnHM;
}
public HashMap<String, HashMap<String, Integer>> getSiteAndUserMessageCountHM(ResultSet rs){
HashMap<String, HashMap<String, Integer>> returnHM = new HashMap<String, HashMap<String, Integer>>();
if(rs != null){
try{
String siteId, userId;
Integer messageCount;
while(rs.next()){
siteId = rs.getString("CONTEXT_ID");
userId = rs.getString("USER_ID");
messageCount = Integer.valueOf(rs.getInt("unread_messages"));
if(returnHM.containsKey(siteId)){
returnHM.get(siteId).put(userId, messageCount);
}else{
HashMap<String, Integer> newHashMap = new HashMap<String, Integer>();
newHashMap.put(userId, messageCount);
returnHM.put(siteId, newHashMap);
}
}
}catch(Exception e){
LOG.error(e);
}
}
return returnHM;
}
public PrivateMessageManager getPvtMessageManager() {
return pvtMessageManager;
}
public void setPvtMessageManager(PrivateMessageManager pvtMessageManager) {
this.pvtMessageManager = pvtMessageManager;
}
public MessageForumsTypeManager getTypeManager() {
return typeManager;
}
public void setTypeManager(MessageForumsTypeManager typeManager) {
this.typeManager = typeManager;
}
public DiscussionForumManager getForumManager() {
return forumManager;
}
public void setForumManager(DiscussionForumManager forumManager) {
this.forumManager = forumManager;
}
public UIPermissionsManager getUiPermissionsManager() {
return uiPermissionsManager;
}
public void setUiPermissionsManager(UIPermissionsManager uiPermissionsManager) {
this.uiPermissionsManager = uiPermissionsManager;
}
public MessageForumsMessageManager getMessageManager() {
return messageManager;
}
public void setMessageManager(MessageForumsMessageManager messageManager) {
this.messageManager = messageManager;
}
public SiteService getSiteService() {
return siteService;
}
public void setSiteService(SiteService siteService) {
this.siteService = siteService;
}
public class DecoratedForumInfo{
private Long forumId;
private Boolean isLocked, isDraft;
private String creator;
private ArrayList<DecoratedTopicsInfo> topics = new ArrayList<DecoratedTopicsInfo>();
public DecoratedForumInfo(Long forumId, Boolean isLocked, Boolean isDraft, String creator){
this.forumId = forumId;
this.isLocked = isLocked;
this.isDraft = isDraft;
this.creator = creator;
}
public Long getForumId() {
return forumId;
}
public void setForumId(Long forumId) {
this.forumId = forumId;
}
public Boolean getIsLocked() {
return isLocked;
}
public void setIsLocked(Boolean isLocked) {
this.isLocked = isLocked;
}
public Boolean getIsDraft() {
return isDraft;
}
public void setIsDraft(Boolean isDraft) {
this.isDraft = isDraft;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public void addTopic(DecoratedTopicsInfo dTopics){
topics.add(dTopics);
}
public ArrayList<DecoratedTopicsInfo> getTopics(){
return topics;
}
}
public class DecoratedTopicsInfo{
private Long topicId;
private Boolean isLocked, isDraft, isModerated;
private String creator;
public DecoratedTopicsInfo(Long topicId, Boolean isLocked, Boolean isDraft, Boolean isModerated, String creator){
this.topicId = topicId;
this.isLocked = isLocked;
this.isDraft = isDraft;
this.isModerated = isModerated;
this.creator = creator;
}
public Long getTopicId() {
return topicId;
}
public void setTopicId(Long topicId) {
this.topicId = topicId;
}
public Boolean getIsLocked() {
return isLocked;
}
public void setIsLocked(Boolean isLocked) {
this.isLocked = isLocked;
}
public Boolean getIsDraft() {
return isDraft;
}
public void setIsDraft(Boolean isDraft) {
this.isDraft = isDraft;
}
public Boolean getIsModerated() {
return isModerated;
}
public void setIsModerated(Boolean isModerated) {
this.isModerated = isModerated;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
}
}