/**********************************************************************************
* $URL: $
* $Id: $
***********************************************************************************
*
* Author: Charles Hedrick, hedrick@rutgers.edu
*
* Copyright (c) 2010 Rutgers, the State University of New Jersey
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.lessonbuildertool.service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.lessonbuildertool.service.LessonSubmission;
import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean;
import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean.UrlItem;
import org.sakaiproject.api.app.messageforums.BaseForum;
import org.sakaiproject.api.app.messageforums.DiscussionForum;
import org.sakaiproject.api.app.messageforums.DiscussionTopic;
import org.sakaiproject.api.app.messageforums.Topic;
import org.sakaiproject.api.app.messageforums.MessageForumsForumManager;
import org.sakaiproject.api.app.messageforums.ui.DiscussionForumManager;
import org.sakaiproject.api.app.messageforums.MessageForumsMessageManager;
import org.sakaiproject.api.app.messageforums.PermissionLevelManager;
import org.sakaiproject.api.app.messageforums.MessageForumsTypeManager;
import org.sakaiproject.api.app.messageforums.AreaManager;
import org.sakaiproject.api.app.messageforums.Attachment;
import org.sakaiproject.api.app.messageforums.DBMembershipItem;
import org.sakaiproject.api.app.messageforums.PermissionLevel;
import org.sakaiproject.api.app.messageforums.PermissionsMask;
import org.sakaiproject.api.app.messageforums.ui.UIPermissionsManager;
import org.sakaiproject.component.app.messageforums.MembershipItem;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.authz.api.Role;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.authz.cover.AuthzGroupService;
import org.sakaiproject.id.cover.IdManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.db.cover.SqlService;
import org.sakaiproject.memory.api.Cache;
import org.sakaiproject.memory.api.CacheRefresher;
import org.sakaiproject.memory.api.MemoryService;
import org.sakaiproject.util.FormattedText;
import java.net.URLEncoder;
import uk.org.ponder.messageutil.MessageLocator;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
/**
* Interface to Message Forums, the forum that comes with Sakai
*
* @author Charles Hedrick <hedrick@rutgers.edu>
*
*/
// NOTE: almost no other class should import this. We want to be able
// to support both forums and jforum. So typically there will be a
// forumEntity, but it's injected, and it can be either forum and jforum.
// Hence it has to be declared LessonEntity. That leads to a lot of
// declarations like LessonEntity forumEntity. In this case forumEntity
// means either a ForumEntity or a JForumEntity. We can't just call the
// variables lessonEntity because the same module will probably have an
// injected class to handle tests and quizes as well. That will eventually
// be converted to be a LessonEntity.
// this monstrosity has to do hibernate directly to avoid the dreaded
// multiple objects with the same ID error. I do merge rather than save.
// unfortunately the normal API does saveOrUpdate.
// This is complicated by the fact that we sessionFactory is set only
// when the main bean is set up. But setGroups is only called from
// instances of this class that aren't the bean. Hence in the bean
// we save a copy of the session factory and then set it in the
// instance when we need it.
public class ForumEntity extends HibernateDaoSupport implements LessonEntity, ForumInterface {
private static Log log = LogFactory.getLog(ForumEntity.class);
private static Cache topicCache = null; // topicid => grouplist
protected static final int DEFAULT_EXPIRATION = 10 * 60;
private static SessionFactory sessionFactory = null;
static MessageForumsForumManager forumManager = (MessageForumsForumManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.MessageForumsForumManager");
static MessageForumsMessageManager messageManager = (MessageForumsMessageManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.MessageForumsMessageManager");
static PermissionLevelManager permissionLevelManager = (PermissionLevelManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.PermissionLevelManager");
static UIPermissionsManager uiPermissionsManager = (UIPermissionsManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.ui.UIPermissionsManager");
static DiscussionForumManager discussionForumManager = (DiscussionForumManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.ui.DiscussionForumManager");
static AreaManager areaManager = (AreaManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.AreaManager");
static MessageForumsTypeManager typeManager = (MessageForumsTypeManager)
ComponentManager.get("org.sakaiproject.api.app.messageforums.MessageForumsTypeManager");
private LessonEntity nextEntity = null;
public void setNextEntity(LessonEntity e) {
nextEntity = e;
}
public LessonEntity getNextEntity() {
return nextEntity;
}
static MessageLocator messageLocator = null;
public void setMessageLocator(MessageLocator m) {
messageLocator = m;
}
static MemoryService memoryService = null;
public void setMemoryService(MemoryService m) {
memoryService = m;
}
public void init () {
// topicCache = memoryService
// .newCache("org.sakaiproject.lessonbuildertool.service.ForumEntity.cache");
sessionFactory = getSessionFactory();
}
protected void initDao() throws Exception {
super.initDao();
log.info("initDao template " + getHibernateTemplate());
}
public void destroy()
{
// topicCache.destroy();
// topicCache = null;
log.info("destroy()");
}
// to create bean. the bean is used only to call the pseudo-static
// methods such as getEntitiesInSite. So type, id, etc are left uninitialized
protected ForumEntity() {
}
protected ForumEntity(int type, Long id, int level) {
this.type = type;
this.id = id;
this.level = level;
}
public String getToolId() {
return "sakai.forums";
}
// the underlying object, something Sakaiish
protected Long id;
protected int type;
protected int level;
// not required fields. If we need to look up
// the actual objects, lets us cache them
protected Topic topic = null;
protected BaseForum forum = null;
// type of the underlying object
public int getType() {
return type;
}
public int getLevel() {
return level;
}
public int getTypeOfGrade() {
return 1;
}
public boolean isUsable() {
if (type == TYPE_FORUM_TOPIC)
return true;
else
return false;
}
public String getReference() {
if (type == TYPE_FORUM_TOPIC)
return "/" + FORUM_TOPIC + "/" + id;
else
return "/" + FORUM_FORUM + "/" + id;
}
public class ForumBySortIndexAscAndCreatedDateDesc implements Comparator<BaseForum> {
public int compare(BaseForum forum, BaseForum otherForum) {
if (forum != null && otherForum != null) {
Integer index1 = forum.getSortIndex();
Integer index2 = otherForum.getSortIndex();
if (index1.intValue() != index2.intValue()) return index1.intValue() - index2.intValue();
Date date1 = forum.getCreated();
Date date2 = otherForum.getCreated();
int rval = date2.compareTo(date1);
if (rval == 0) {
return otherForum.getId().compareTo(forum.getId());
} else {
return rval;
}
}
return -1;
}
}
public List<LessonEntity> getEntitiesInSite() {
return getEntitiesInSite(null);
}
// find topics in site, but organized by forum
public List<LessonEntity> getEntitiesInSite(SimplePageBean bean) {
List<LessonEntity> ret = new ArrayList<LessonEntity>();
// LSNBLDR-21. If the tool is not in the current site we shouldn't query
// for topics owned by the tool.
Site site = null;
try {
site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
} catch (Exception impossible) {
return ret;
}
ToolConfiguration tool = site.getToolForCommonId("sakai.forums");
if(tool == null) {
// Forums is not in this site. Move on to the next provider.
if (nextEntity != null)
ret.addAll(nextEntity.getEntitiesInSite());
return ret;
}
//ForumEntity e = new ForumEntity(TYPE_FORUM_TOPIC, 3L, 2);
//e.setGroups(Arrays.asList("1c24287b-b880-43da-8cdd-c6cdc1249c5c", "75184424-853e-4dd4-9e92-980c851f0580"));
//e.setGroups(Arrays.asList("75184424-853e-4dd4-9e92-980c851f0580"));
SortedSet<DiscussionForum> forums = new TreeSet<DiscussionForum>(new ForumBySortIndexAscAndCreatedDateDesc());
for (DiscussionForum forum: forumManager.getForumsForMainPage())
forums.add(forum);
// security. assume this is only used in places where it's OK, so skip security checks
for (DiscussionForum forum: forums) {
if (!forum.getDraft()) {
ForumEntity entity = new ForumEntity(TYPE_FORUM_FORUM, forum.getId(), 1);
entity.forum = forum;
ret.add(entity);
for (Object o: forum.getTopicsSet()) {
DiscussionTopic topic = (DiscussionTopic)o;
if (topic.getDraft().equals(Boolean.FALSE)) {
entity = new ForumEntity(TYPE_FORUM_TOPIC, topic.getId(), 2);
entity.topic = topic;
ret.add(entity);
}
}
}
}
if (nextEntity != null)
ret.addAll(nextEntity.getEntitiesInSite(bean));
return ret;
}
public LessonEntity getEntity(String ref, SimplePageBean o) {
return getEntity(ref);
}
public LessonEntity getEntity(String ref) {
int i = ref.indexOf("/",1);
if (i < 0)
return null;
String typeString = ref.substring(1, i);
String idString = ref.substring(i+1);
Long id = 0L;
if (typeString.equals(FORUM_TOPIC) || typeString.equals(FORUM_FORUM)) {
try {
id = Long.parseLong(idString);
} catch (Exception ignore) {
return null;
}
}
// note: I'm returning the minimal structures, not those with
// topics and postings attached
if (typeString.equals(FORUM_TOPIC)) {
return new ForumEntity(TYPE_FORUM_TOPIC, id, 2);
} else if (typeString.equals(FORUM_FORUM)) {
return new ForumEntity(TYPE_FORUM_FORUM, id, 1);
} else if (nextEntity != null) {
return nextEntity.getEntity(ref);
} else
return null;
}
public Topic getTopicById(boolean flag, Long id) {
try {
Topic topic = forumManager.getTopicById(flag, id);
if (topic == null)
return null;
// the purpose of this is to trigger a null pointer error if the forum doesn't exist
// if you delete the forum, it doesn't delete the topics, but showing a topic without
// its forum causes confusion. The /direct code does the following, so it will give
// an error.
if (topic.getOpenForum().getArea() == null)
return null;
return topic;
} catch (Exception e) {
return null;
}
}
public BaseForum getForumById(boolean flag, Long id) {
try {
return forumManager.getForumById(flag, id);
} catch (Exception e) {
return null;
}
}
// properties of entities
public String getTitle() {
if (type == TYPE_FORUM_TOPIC) {
if (topic == null)
topic = getTopicById(true, id);
if (topic == null)
return null;
return topic.getTitle();
} else {
if (forum == null)
forum = getForumById(true, id);
if (forum == null)
return null;
return forum.getTitle();
}
}
public String getUrl() {
if (topic == null)
topic = getTopicById(true, id);
if (topic == null)
return "javascript:alert('" + messageLocator.getMessage("simplepage.forumdeleted") + "')";
Site site = null;
try {
site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
} catch (Exception impossible) {
return null;
}
ToolConfiguration tool = site.getToolForCommonId("sakai.forums");
// LSNBLDR-21. If the tool is not in the current site we shouldn't return a url
if(tool == null) {
return null;
}
String placement = tool.getId();
if (type == TYPE_FORUM_TOPIC)
// if /direct doesn't work, but that was only in one beta release
// return "/messageforums-tool/jsp/discussionForum/message/dfAllMessagesDirect.jsf?topicId=" + id + "&placementId=" + placement;
return "/direct/forum_topic/" + id;
else
return "/direct/forum/" + id;
}
// I don't think they have this
public Date getDueDate() {
return null;
}
// The msgcntr permissions model is completely undocumented. Here's a cheat sheet:
// DBMembershipItem has
// type, which is ALL, ROLE, GROUP or USER
// name which is the specific rolename, groupname or username
// permissionlevelname which is "Owner", "Contributor", "None", etc.
// In addition there is a bitmask that says which specific permissions
// are present, but we always use the default permissions for each level.
// Here's typical code. First we create a permissionlevel with a given bitmask.
// This permissionlevel controls the detailed permissions. As noted, we always
// default levels. Unfortunately we have to create a new copy of the level
// for every entry. Some fo their internal code uses common permissionlevels, but
// if you don't have a separate level object and database entry for each membershipitem,
// things get very confused.
// PermissionLevel contributorLevel = permissionLevelManager.
// createPermissionLevel("Contributor", typeManager.getContributorLevelType(), contributorMask);
// permissionLevelManager.savePermissionLevel(contributorLevel);
// Now we create the actual entry. Note that this one says members of the specified
// group are contributors. Then it sets the default contributor bitmask. You can call
// a permission contributor but set any bits you want. However we're going to assume
// that people pick a name that represents what they want, and make minimal changes.
// DBMembershipItem membershipItem = permissionLevelManager.
// createDBMembershipItem(groupName, "Contributor", MembershipItem.TYPE_GROUP);
// membershipItem.setPermissionLevel(contributorLevel);
// permissionLevelManager.saveDBMembershipItem(membershipItem);
// How we use it:
// ACCESS CONTROL:
// Our model is fairly simple. When we control access, Owner is the maintain role, and
// contributor is the group we control. Everything else is set to none.
// When you decontrol something, we make Owner the maintain role
// and Contributor all the other roles. Once we support groups, we'll put back
// saved group access, but we won't try to put back anything else.
// The tool code makes sure that items are added for all roles, so we don't have to add entries
// in most cases, just change their permission levels.
// GROUP ACCESS WHEN WE AREN'T CONTROLLING:
// Now, our group management code, which should only be used when we're not controlling:
// Getgroups returns which groups are contributor.
// If you want something more complex, you'll have to do it in the tool, but if there
// are any groups with contributor, we'll only let students in those groups access
// through our tool.
// Setgroups with a non-null list: we set all contributor entries to none, and then set the
// specified groups to contribtor. By only handling groups, we avoid interfering with
// anything you might do in the tool. But the moment you use access control, we take
// over. Sorry. Once we've done that you could go back into the tool and hack, but I
// don't recommend that.
// Setgroups with a null list: we set all contributor entries to none, and then set all roles
// other than maintain to contributor.
// It may be safer to do changes in the tool.
// the following methods all take references. So they're in effect static.
// They ignore the entity from which they're called.
// The reason for not making them a normal method is that many of the
// implementations seem to let you set access control and find submissions
// from a reference, without needing the actual object. So doing it this
// way could save some database activity
PermissionsMask noneMask = null;
PermissionsMask contributorMask = null;
PermissionsMask ownerMask = null;
private void setMasks() {
if (noneMask == null) {
noneMask = new PermissionsMask();
noneMask.put(PermissionLevel.NEW_FORUM, Boolean.valueOf(false));
noneMask.put(PermissionLevel.NEW_TOPIC, Boolean.valueOf(false));
noneMask.put(PermissionLevel.NEW_RESPONSE, Boolean.valueOf(false));
noneMask.put(PermissionLevel.NEW_RESPONSE_TO_RESPONSE, Boolean.valueOf(false));
noneMask.put(PermissionLevel.MOVE_POSTING, Boolean.valueOf(false));
noneMask.put(PermissionLevel.CHANGE_SETTINGS,Boolean.valueOf(false));
noneMask.put(PermissionLevel.POST_TO_GRADEBOOK, Boolean.valueOf(false));
noneMask.put(PermissionLevel.READ, Boolean.valueOf(false));
noneMask.put(PermissionLevel.MARK_AS_READ,Boolean.valueOf(false));
noneMask.put(PermissionLevel.MODERATE_POSTINGS, Boolean.valueOf(false));
noneMask.put(PermissionLevel.DELETE_OWN, Boolean.valueOf(false));
noneMask.put(PermissionLevel.DELETE_ANY, Boolean.valueOf(false));
noneMask.put(PermissionLevel.REVISE_OWN, Boolean.valueOf(false));
noneMask.put(PermissionLevel.REVISE_ANY, Boolean.valueOf(false));
}
if (contributorMask == null) {
contributorMask = new PermissionsMask();
contributorMask.put(PermissionLevel.NEW_FORUM, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.NEW_TOPIC, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.NEW_RESPONSE, Boolean.valueOf(true));
contributorMask.put(PermissionLevel.NEW_RESPONSE_TO_RESPONSE, Boolean.valueOf(true));
contributorMask.put(PermissionLevel.MOVE_POSTING, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.CHANGE_SETTINGS,Boolean.valueOf(false));
contributorMask.put(PermissionLevel.POST_TO_GRADEBOOK, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.READ, Boolean.valueOf(true));
contributorMask.put(PermissionLevel.MARK_AS_READ,Boolean.valueOf(true));
contributorMask.put(PermissionLevel.MODERATE_POSTINGS, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.DELETE_OWN, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.DELETE_ANY, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.REVISE_OWN, Boolean.valueOf(false));
contributorMask.put(PermissionLevel.REVISE_ANY, Boolean.valueOf(false));
}
if (ownerMask == null) {
ownerMask = new PermissionsMask();
ownerMask.put(PermissionLevel.NEW_FORUM, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.NEW_TOPIC, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.NEW_RESPONSE, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.NEW_RESPONSE_TO_RESPONSE, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.MOVE_POSTING, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.CHANGE_SETTINGS,Boolean.valueOf(true));
ownerMask.put(PermissionLevel.POST_TO_GRADEBOOK, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.READ, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.MARK_AS_READ,Boolean.valueOf(true));
ownerMask.put(PermissionLevel.MODERATE_POSTINGS, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.DELETE_OWN, Boolean.valueOf(false));
ownerMask.put(PermissionLevel.DELETE_ANY, Boolean.valueOf(true));
ownerMask.put(PermissionLevel.REVISE_OWN, Boolean.valueOf(false));
ownerMask.put(PermissionLevel.REVISE_ANY, Boolean.valueOf(true));
}
}
// access control
public boolean addEntityControl(String siteId, String groupId) throws IOException {
setMasks();
if (topic == null)
topic = getTopicById(true, id);
if (topic == null)
return false;
Set<DBMembershipItem> oldMembershipItemSet = uiPermissionsManager.getTopicItemsSet((DiscussionTopic)topic);
Set membershipItemSet = new HashSet();
String groupName = null;
String maintainRole = null;
try {
Site site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
groupName = site.getGroup(groupId).getTitle();
maintainRole = AuthzGroupService.getAuthzGroup("/site/" + site.getId()).getMaintainRole();
} catch (Exception e) {
System.out.println("Unable to get site info for AddEntityControl " + e);
}
PermissionLevel ownerLevel = permissionLevelManager.
createPermissionLevel("Owner", typeManager.getOwnerLevelType(), ownerMask);
permissionLevelManager.savePermissionLevel(ownerLevel);
PermissionLevel contributorLevel = permissionLevelManager.
createPermissionLevel("Contributor", typeManager.getContributorLevelType(), contributorMask);
permissionLevelManager.savePermissionLevel(contributorLevel);
DBMembershipItem membershipItem = permissionLevelManager.
createDBMembershipItem(groupName, "Contributor", MembershipItem.TYPE_GROUP);
membershipItem.setPermissionLevel(contributorLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
membershipItem = permissionLevelManager.
createDBMembershipItem(maintainRole, "Owner", MembershipItem.TYPE_ROLE);
membershipItem.setPermissionLevel(ownerLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
// now change any existing ones into null
for (DBMembershipItem item: oldMembershipItemSet) {
if (!(maintainRole.equals(item.getName()) && item.getType().equals(MembershipItem.TYPE_ROLE) ||
groupName.equals(item.getName()) && item.getType().equals(MembershipItem.TYPE_GROUP))) {
PermissionLevel noneLevel = permissionLevelManager.
createPermissionLevel("None", typeManager.getNoneLevelType(), noneMask);
permissionLevelManager.savePermissionLevel(noneLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "None", item.getType());
membershipItem.setPermissionLevel(noneLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
}
}
permissionLevelManager.deleteMembershipItems(oldMembershipItemSet);
topic.setMembershipItemSet(membershipItemSet);
discussionForumManager.saveTopic((DiscussionTopic) topic);
return true;
};
public boolean removeEntityControl(String siteId, String groupId) throws IOException {
setMasks();
if (topic == null)
topic = getTopicById(true, id);
if (topic == null)
return false;
Set<DBMembershipItem> oldMembershipItemSet = uiPermissionsManager.getTopicItemsSet((DiscussionTopic)topic);
Set membershipItemSet = new HashSet();
String groupName = null;
String maintainRole = null;
try {
Site site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
groupName = site.getGroup(groupId).getTitle();
maintainRole = AuthzGroupService.getAuthzGroup("/site/" + site.getId()).getMaintainRole();
} catch (Exception e) {
System.out.println("Unable to get site info for AddEntityControl " + e);
}
PermissionLevel ownerLevel = permissionLevelManager.
createPermissionLevel("Owner", typeManager.getOwnerLevelType(), ownerMask);
permissionLevelManager.savePermissionLevel(ownerLevel);
DBMembershipItem membershipItem = permissionLevelManager.
createDBMembershipItem(maintainRole, "Owner", MembershipItem.TYPE_ROLE);
membershipItem.setPermissionLevel(ownerLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
// now change any existing ones into null
for (DBMembershipItem item: oldMembershipItemSet) {
if (item.getType().equals(MembershipItem.TYPE_ROLE)) {
if (!maintainRole.equals(item.getName())) { // that was done above, other roles contributor
PermissionLevel contributorLevel = permissionLevelManager.
createPermissionLevel("Contributor", typeManager.getContributorLevelType(), contributorMask);
permissionLevelManager.savePermissionLevel(contributorLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "Contributor", item.getType());
membershipItem.setPermissionLevel(contributorLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
}
} else { // everything else off
PermissionLevel noneLevel = permissionLevelManager.
createPermissionLevel("None", typeManager.getNoneLevelType(), noneMask);
permissionLevelManager.savePermissionLevel(noneLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "None", item.getType());
membershipItem.setPermissionLevel(noneLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
}
}
permissionLevelManager.deleteMembershipItems(oldMembershipItemSet);
topic.setMembershipItemSet(membershipItemSet);
discussionForumManager.saveTopic((DiscussionTopic) topic);
return true;
};
// submission
// do we need the data from submission?
// not for the moment. If a posting is required, we just check whether one
// has been done. While you can grade submissions, grading is done manually
// later. It's unlikely that faculty will want to test on those grades
public boolean needSubmission(){
return false;
}
public LessonSubmission getSubmission(String user) {
return null; // not used
}
public int getSubmissionCount(String user) {
return messageManager.findAuhtoredMessageCountByTopicIdByUserId(id, user);
}
// URL to create a new item. Normally called from the generic entity, not a specific one
// can't be null
public List<UrlItem> createNewUrls(SimplePageBean bean) {
ArrayList<UrlItem> list = new ArrayList<UrlItem>();
String tool = bean.getCurrentTool("sakai.forums");
if (tool != null) {
tool = ServerConfigurationService.getToolUrl() + "/" + tool + "/discussionForum/forumsOnly/dfForums";
list.add(new UrlItem(tool, messageLocator.getMessage("simplepage.create_forums")));
}
if (nextEntity != null)
list.addAll(nextEntity.createNewUrls(bean));
return list;
}
// URL to edit an existing entity.
// Can be null if we can't get one or it isn't needed
public String editItemUrl(SimplePageBean bean) {
return getUrl();
}
// for most entities editItem is enough, however tests allow separate editing of
// contents and settings. This will be null except in that situation
public String editItemSettingsUrl(SimplePageBean bean) {
return null;
}
// returns SakaiId of thing just created
public String importObject(String title, String topicTitle, String text, boolean texthtml, String base, String siteId, List<String>attachmentHrefs, boolean hide) {
DiscussionForum ourForum = null;
DiscussionTopic ourTopic = null;
int forumtry = 0;
int topictry = 0;
for (;;) {
ourForum = null;
SortedSet<DiscussionForum> forums = new TreeSet<DiscussionForum>(new ForumBySortIndexAscAndCreatedDateDesc());
for (DiscussionForum forum: discussionForumManager.getForumsForMainPage())
forums.add(forum);
for (DiscussionForum forum: forums) {
if (forum.getTitle().equals(title)) {
ourForum = forum;
break;
}
}
if (ourForum == null) {
if (forumtry > 0) {
System.out.println("oops, forum still not there the second time");
return null;
}
forumtry ++;
// if a new site, may need to create the area or we'll get a backtrace when creating forum
areaManager.getDiscussionArea(siteId);
ourForum = discussionForumManager.createForum();
ourForum.setTitle(title);
discussionForumManager.saveForum(siteId, ourForum);
continue; // reread, better be there this time
}
// forum now exists, and was just reread
ourTopic = null;
for (Object o: ourForum.getTopicsSet()) {
DiscussionTopic topic = (DiscussionTopic)o;
if (topic.getTitle().equals(topicTitle)) {
ourTopic = topic;
break;
}
}
if (ourTopic != null) // ok, forum and topic exist
break;
if (topictry > 0) {
System.out.println("oops, topic still not there the second time");
return null;
}
topictry ++;
// create it
ourTopic = discussionForumManager.createTopic(ourForum);
ourTopic.setTitle(topicTitle);
StringBuilder attachHtml = new StringBuilder("");
if (attachmentHrefs != null && attachmentHrefs.size() > 0) {
for (String href: attachmentHrefs) {
String label = href;
int slash = label.lastIndexOf("/");
if (slash >= 0)
label = label.substring(slash+1);
if (label.equals(""))
label = "Attachment";
attachHtml.append("<p><a target='_blank' href='");
attachHtml.append(base);
attachHtml.append(href);
attachHtml.append("'>");
attachHtml.append(label);
attachHtml.append("</a>");
}
}
String shortText = null;
if (texthtml) {
ourTopic.setExtendedDescription(text.replaceAll("\\$IMS-CC-FILEBASE\\$", base) + attachHtml.toString());
shortText = FormattedText.convertFormattedTextToPlaintext(text);
} else {
ourTopic.setExtendedDescription(FormattedText.convertPlaintextToFormattedText(text) + attachHtml.toString());
shortText = text;
}
shortText = org.apache.commons.lang.StringUtils.abbreviate(shortText,254);
ourTopic.setShortDescription(shortText);
// there's a better way to do attachments, but it's too complex for now
if (hide)
discussionForumManager.saveTopicAsDraft(ourTopic);
else
discussionForumManager.saveTopic(ourTopic);
// now go back and mmake sure everything is there
}
return "/" + FORUM_TOPIC + "/" + ourTopic.getId();
}
public boolean objectExists() {
if (topic == null)
topic = getTopicById(true, id);
return topic != null;
}
public boolean notPublished(String ref) {
return false;
}
// return the list of groups if the item is only accessible to specific groups
// null if it's accessible to the whole site.
public List<String> getGroups(boolean nocache) {
// don't need cache, since simplepagebean is now caching groups
// List<String>ret = (List<String>)topicCache.get(id);
// if (!nocache && ret != null) {
// if (ret.size() == 0)
// return null;
// else
// return ret;
// } else {
// }
List <String>ret = new ArrayList<String>();
if (topic == null)
topic = getTopicById(true, id);
if (topic == null)
return null;
Set<DBMembershipItem> oldMembershipItemSet = uiPermissionsManager.getTopicItemsSet((DiscussionTopic)topic);
Collection<Group> groups = null;
try {
Site site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
groups = site.getGroups();
} catch (Exception e) {
System.out.println("Unable to get site info for getGroups " + e);
}
// now change any existing ones into null
for (DBMembershipItem item: oldMembershipItemSet) {
if (item.getPermissionLevelName().equals("Contributor") &&
item.getType().equals(MembershipItem.TYPE_GROUP)) {
String name = item.getName(); // oddly, this is the actual name, not the ID
for (Group group: groups) {
if (name.equals(group.getTitle()))
ret.add(group.getId());
}
}
}
// topicCache.put(id, ret, DEFAULT_EXPIRATION);
if (ret.size() == 0)
return null;
else
return ret;
}
// set the item to be accessible only to the specific groups.
// null to make it accessible to the whole site
public void setGroups(Collection<String> groups) {
// Setgroups with a non-null list: we set all contributor entries to none, and then set the
// specified groups to contribtor. By only handling groups, we avoid interfering with
// anything you might do in the tool. But the moment you use access control, we take
// over. Sorry. Once we've done that you could go back into the tool and hack, but I
// don't recommend that.
// Setgroups with a null list: we set all contributor entries to none, and then set all roles
// other than maintain to contributor.
setMasks();
//System.out.println("topic 1 " + topic + " " + groups);
if (topic == null)
topic = getTopicById(true, id);
//System.out.println("topic 2 " + topic);
if (topic == null)
return;
// topicCache.remove(id);
// old entries
Set<DBMembershipItem> oldMembershipItemSet = uiPermissionsManager.getTopicItemsSet((DiscussionTopic)topic);
// which old entires to delete
Set<DBMembershipItem> deleteItemSet = new HashSet<DBMembershipItem>();
// all entries we will keep
Set membershipItemSet = new HashSet();
Site site = null;
String maintainRole = null;
// used so we can give an access level to each role. Remove roles from this as we see
// them, so at the we just do the ones remaining
List<String> roles = new ArrayList<String>();
try {
site = SiteService.getSite(ToolManager.getCurrentPlacement().getContext());
maintainRole = AuthzGroupService.getAuthzGroup("/site/" + site.getId()).getMaintainRole();
Set<Role>roleObjs = AuthzGroupService.getAuthzGroup("/site/" + site.getId()).getRoles();
for (Role roleObj: roleObjs)
roles.add(roleObj.getId());
} catch (Exception e) {
System.out.println("Unable to get site info for AddEntityControl " + e);
return;
}
DBMembershipItem membershipItem = null;
boolean haveOwner = false;
if (groups != null && groups.size() > 0) {
// this is the groups we've been asked to use
// remove groups form this as we see them if they already have access
// so at the end we just add the ones remaining
List<String>groupNames = new ArrayList<String>();
for (String groupId: groups)
groupNames.add(site.getGroup(groupId).getTitle());
// delete groups from here as they are done.
// if we've seen an owner. Otherwise set the maintain role as owner
// Setgroups with a non-null list: we set all contributor entries to none, and then set the
// specified groups to contribtor. However we don't touch owner.
// By only handling groups, we avoid interfering with
// anything you might do in the tool. But the moment you use access control, we take
// over. Sorry. Once we've done that you could go back into the tool and hack, but I
// don't recommend that.
for (DBMembershipItem item: oldMembershipItemSet) {
//System.out.println("old item " + item.getPermissionLevelName() + " " + item.getType() + " " + item.getName());
if (item.getPermissionLevelName().equals("Owner"))
haveOwner = true;
if (item.getType().equals(MembershipItem.TYPE_ROLE) && roles.contains(item.getName()))
roles.remove(item.getName()); // we've seen it, don't need to add
if (item.getType().equals(MembershipItem.TYPE_GROUP) && groupNames.contains(item.getName())) {
// if it's one of our groups make it a contributor if it's not already an owner
if (!item.getPermissionLevelName().equals("Contributor") &&
!item.getPermissionLevelName().equals("Owner")) {
//System.out.println("make contributor");
PermissionLevel contributorLevel = permissionLevelManager.
createPermissionLevel("Contributor", IdManager.createUuid(), contributorMask);
permissionLevelManager.savePermissionLevel(contributorLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "Contributor", MembershipItem.TYPE_GROUP);
membershipItem.setPermissionLevel(contributorLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
deleteItemSet.add(item);
} else { // if it was contributor or owner, keep it
//System.out.println("keep");
membershipItemSet.add(item);
}
groupNames.remove(item.getName()); // it's done
} else if (item.getPermissionLevelName().equals("Contributor")) { // only group members are contributors
// remove contributor from anything else, both groups and roles
//System.out.println("set none");
PermissionLevel noneLevel = permissionLevelManager.
createPermissionLevel("None", IdManager.createUuid(), noneMask);
permissionLevelManager.savePermissionLevel(noneLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "None", item.getType());
membershipItem.setPermissionLevel(noneLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
deleteItemSet.add(item);
} else { // for other permission types, leave as is
//System.out.println("leave alone");
membershipItemSet.add(item);
}
}
// do any left
for (String name: groupNames) {
//System.out.println("make contributor: " + name);
PermissionLevel contributorLevel = permissionLevelManager.
createPermissionLevel("Contributor", IdManager.createUuid(), contributorMask);
permissionLevelManager.savePermissionLevel(contributorLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(name, "Contributor", MembershipItem.TYPE_GROUP);
membershipItem.setPermissionLevel(contributorLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
}
if (!haveOwner) {
//System.out.println("add owner");
PermissionLevel ownerLevel = permissionLevelManager.
createPermissionLevel("Owner", IdManager.createUuid(), ownerMask);
permissionLevelManager.savePermissionLevel(ownerLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(maintainRole, "Owner", MembershipItem.TYPE_ROLE);
membershipItem.setPermissionLevel(ownerLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
roles.remove(maintainRole); // we've processed this, so don't make it None
}
for (String name: roles) {
//System.out.println("make none " + name);
PermissionLevel noneLevel = permissionLevelManager.
createPermissionLevel("None", IdManager.createUuid(), noneMask);
permissionLevelManager.savePermissionLevel(noneLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(name, "None", MembershipItem.TYPE_ROLE);
membershipItem.setPermissionLevel(noneLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
}
} else {
// Setgroups with a null list: we set all contributor entries to none, and then set all roles
// to contributor. However we don't touch Owners.
for (DBMembershipItem item: oldMembershipItemSet) {
if (item.getPermissionLevelName().equals("Owner"))
haveOwner = true;
if (item.getType().equals(MembershipItem.TYPE_ROLE) && roles.contains(item.getName()))
roles.remove(item.getName()); // we've seen it, don't need to add
if (item.getType().equals(MembershipItem.TYPE_ROLE) && !item.getPermissionLevelName().equals("Owner")) {
// turn all roles into contributor, unless already owner
PermissionLevel contributorLevel = permissionLevelManager.
createPermissionLevel("Contributor", IdManager.createUuid(), contributorMask);
permissionLevelManager.savePermissionLevel(contributorLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "Contributor", item.getType());
membershipItem.setPermissionLevel(contributorLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
deleteItemSet.add(item);
} else if (item.getPermissionLevelName().equals("Contributor")) {
// kill other contributors
PermissionLevel noneLevel = permissionLevelManager.
createPermissionLevel("None", IdManager.createUuid(), noneMask);
permissionLevelManager.savePermissionLevel(noneLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(item.getName(), "None", item.getType());
membershipItem.setPermissionLevel(noneLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
deleteItemSet.add(item);
} else { // for other permission types, leave as is
membershipItemSet.add(item);
}
}
if (!haveOwner) {
//System.out.println("make owner " +maintainRole);
PermissionLevel ownerLevel = permissionLevelManager.
createPermissionLevel("Owner", IdManager.createUuid(), ownerMask);
permissionLevelManager.savePermissionLevel(ownerLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(maintainRole, "Owner", MembershipItem.TYPE_ROLE);
membershipItem.setPermissionLevel(ownerLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
roles.remove(maintainRole); // we've processed this, so don't make it None
}
for (String name: roles) {
//System.out.println("make contributor: " + name);
PermissionLevel contributorLevel = permissionLevelManager.
createPermissionLevel("Contributor", IdManager.createUuid(), contributorMask);
permissionLevelManager.savePermissionLevel(contributorLevel);
membershipItem = permissionLevelManager.
createDBMembershipItem(name, "Contributor", MembershipItem.TYPE_ROLE);
membershipItem.setPermissionLevel(contributorLevel);
permissionLevelManager.saveDBMembershipItem(membershipItem);
membershipItemSet.add(membershipItem);
}
}
//System.out.println("delete " + deleteItemSet);
permissionLevelManager.deleteMembershipItems(deleteItemSet);
//System.out.println("new membership list ");
//for (DBMembershipItem item: (Set<DBMembershipItem>)membershipItemSet) {
// System.out.println(item.getPermissionLevelName() + " " + item.getType() + " " + item.getName());
//}
topic.setMembershipItemSet(membershipItemSet);
// should do
//discussionForumManager.saveTopic((DiscussionTopic) topic);
// but that uses saveOrUpdate, which gives an error because
// we typically have more than one copy of the topic in the session.
// The only fix that works without modifying code I can't touch
// is to do merge rather than saveOrUpdate. But that means I
// have to do my own hibernate save rather than using the
// API's savetopic. I checked the code for saveTopic, and
// when you're dealing with an existing topic, all you need
// is the save. The real saveTopic code just sets up fields
// that would be null if it's a new topic. Of course the changed
// object won't be visible in other sesssions. So if you try
// getGroups after doing the save, and you're in the same session,
// which is typically the same request, you'll get the old value.
// Sorry about that.
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.merge(topic);
tx.commit();
} catch (Exception e) {
if (tx != null)
tx.rollback();
} finally {
if (session != null)
session.close();
}
}
// only used for topics
public String getObjectId(){
String title = getTitle();
// fetches topic as well
if (title == null)
return null;
BaseForum forum = topic.getBaseForum();
return "forum_topic/" + id + "/" + title + "\n" + forum.getTitle();
}
public String findObject(String objectid, Map<String,String>objectMap, String siteid) {
if (!objectid.startsWith("forum_topic/")) {
if (nextEntity != null) {
return nextEntity.findObject(objectid, objectMap, siteid);
}
return null;
}
// isolate forum_topic/NNN from title
int i = objectid.indexOf("/", "forum_topic/".length());
if (i <= 0)
return null;
String realobjectid = objectid.substring(0, i);
// now see if it's in the map
String newtopic = objectMap.get(realobjectid);
if (newtopic != null)
return "/" + newtopic; // sakaiid is /forum_topic/ID
// this must be 2.8. Can't find the topic in the map
// i is start of title
int j = objectid.indexOf("\n");
String title = objectid.substring(i+1,j);
String forumtitle = objectid.substring(j+1);
// unfortunately we have to search the topic tree to find it.
SortedSet<DiscussionForum> forums = new TreeSet<DiscussionForum>(new ForumBySortIndexAscAndCreatedDateDesc());
for (DiscussionForum forum: forumManager.getForumsForMainPage())
forums.add(forum);
// security. assume this is only used in places where it's OK, so skip security checks
// ignore draft status. We want to show drafts.
for (DiscussionForum forum: forums) {
if (forum.getTitle().equals(forumtitle)) {
for (Object o: forum.getTopicsSet()) {
DiscussionTopic topic = (DiscussionTopic)o;
if (topic.getTitle().equals(title)) {
return "/forum_topic/" + topic.getId();
}
}
}
}
return null;
}
public String getSiteId() {
// should be this:
// return topic.getBaseForum().getArea().getContextId();
// but requires a hibernate session, which doesn't exit.
String sql = "select c.context_id from MFR_TOPIC_T a,MFR_OPEN_FORUM_T b,MFR_AREA_T c where a.id=? and a.of_surrogateKey=b.id and b.surrogatekey=c.id";
Object fields[] = new Object[1];
fields[0] = id;
List<String> siteIds = SqlService.dbRead(sql, fields, null);
if (siteIds != null && siteIds.size() > 0)
return siteIds.get(0);
return null;
}
}