/*
* Group.java
*
* Version: $Revision: 4903 $
*
* Date: $Date: 2010-05-10 08:29:50 +0000 (Mon, 10 May 2010) $
*
* Copyright (c) 2002-2009, The DSpace Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the DSpace Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.eperson;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.io.IOException;
import org.apache.log4j.Logger;
import org.dspace.app.util.AuthorizeUtil;
import org.dspace.authorize.AuthorizeConfiguration;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.event.Event;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator;
/**
* Class representing a group of e-people.
*
* @author David Stuve
* @version $Revision: 4903 $
*/
public class Group extends DSpaceObject
{
// findAll sortby types
public static final int ID = 0; // sort by ID
public static final int NAME = 1; // sort by NAME (default)
/** log4j logger */
private static Logger log = Logger.getLogger(Group.class);
/** Our context */
private Context myContext;
/** The row in the table representing this object */
private TableRow myRow;
/** lists of epeople and groups in the group */
private List<EPerson> epeople = new ArrayList<EPerson>();
private List<Group> groups = new ArrayList<Group>();
/** lists that need to be written out again */
private boolean epeopleChanged = false;
private boolean groupsChanged = false;
/** is this just a stub, or is all data loaded? */
private boolean isDataLoaded = false;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/**
* Construct a Group from a given context and tablerow
*
* @param context
* @param row
*/
Group(Context context, TableRow row) throws SQLException
{
myContext = context;
myRow = row;
// Cache ourselves
context.cache(this, row.getIntColumn("eperson_group_id"));
modifiedMetadata = false;
clearDetails();
}
/**
* Populate Group with eperson and group objects
*
* @throws SQLException
*/
public void loadData()
{
// only populate if not already populated
if (!isDataLoaded)
{
// naughty thing to do - swallowing SQL exception and throwing it as
// a RuntimeException - a hack to avoid changing the API all over
// the place
try
{
// get epeople objects
TableRowIterator tri = DatabaseManager.queryTable(myContext,"eperson",
"SELECT eperson.* FROM eperson, epersongroup2eperson WHERE " +
"epersongroup2eperson.eperson_id=eperson.eperson_id AND " +
"epersongroup2eperson.eperson_group_id= ?",
myRow.getIntColumn("eperson_group_id"));
try
{
while (tri.hasNext())
{
TableRow r = (TableRow) tri.next();
// First check the cache
EPerson fromCache = (EPerson) myContext.fromCache(
EPerson.class, r.getIntColumn("eperson_id"));
if (fromCache != null)
{
epeople.add(fromCache);
}
else
{
epeople.add(new EPerson(myContext, r));
}
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
// now get Group objects
tri = DatabaseManager.queryTable(myContext,"epersongroup",
"SELECT epersongroup.* FROM epersongroup, group2group WHERE " +
"group2group.child_id=epersongroup.eperson_group_id AND "+
"group2group.parent_id= ? ",
myRow.getIntColumn("eperson_group_id"));
try
{
while (tri.hasNext())
{
TableRow r = (TableRow) tri.next();
// First check the cache
Group fromCache = (Group) myContext.fromCache(Group.class,
r.getIntColumn("eperson_group_id"));
if (fromCache != null)
{
groups.add(fromCache);
}
else
{
groups.add(new Group(myContext, r));
}
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
isDataLoaded = true;
}
}
/**
* Create a new group
*
* @param context
* DSpace context object
*/
public static Group create(Context context) throws SQLException,
AuthorizeException
{
// FIXME - authorization?
if (!AuthorizeManager.isAdmin(context))
{
throw new AuthorizeException(
"You must be an admin to create an EPerson Group");
}
// Create a table row
TableRow row = DatabaseManager.create(context, "epersongroup");
Group g = new Group(context, row);
log.info(LogManager.getHeader(context, "create_group", "group_id="
+ g.getID()));
context.addEvent(new Event(Event.CREATE, Constants.GROUP, g.getID(), null));
return g;
}
/**
* get the ID of the group object
*
* @return id
*/
public int getID()
{
return myRow.getIntColumn("eperson_group_id");
}
/**
* get name of group
*
* @return name
*/
public String getName()
{
return myRow.getStringColumn("name");
}
/**
* set name of group
*
* @param name
* new group name
*/
public void setName(String name)
{
myRow.setColumn("name", name);
modifiedMetadata = true;
addDetails("name");
}
/**
* add an eperson member
*
* @param e
* eperson
*/
public void addMember(EPerson e)
{
loadData(); // make sure Group has data loaded
if (isMember(e))
{
return;
}
epeople.add(e);
epeopleChanged = true;
myContext.addEvent(new Event(Event.ADD, Constants.GROUP, getID(), Constants.EPERSON, e.getID(), e.getEmail()));
}
/**
* add group to this group
*
* @param g
*/
public void addMember(Group g)
{
loadData(); // make sure Group has data loaded
// don't add if it's already a member
// and don't add itself
if (isMember(g) || getID()==g.getID())
{
return;
}
groups.add(g);
groupsChanged = true;
myContext.addEvent(new Event(Event.ADD, Constants.GROUP, getID(), Constants.GROUP, g.getID(), g.getName()));
}
/**
* remove an eperson from a group
*
* @param e
* eperson
*/
public void removeMember(EPerson e)
{
loadData(); // make sure Group has data loaded
if (epeople.remove(e))
{
epeopleChanged = true;
myContext.addEvent(new Event(Event.REMOVE, Constants.GROUP, getID(), Constants.EPERSON, e.getID(), e.getEmail()));
}
}
/**
* remove group from this group
*
* @param g
*/
public void removeMember(Group g)
{
loadData(); // make sure Group has data loaded
if (groups.remove(g))
{
groupsChanged = true;
myContext.addEvent(new Event(Event.REMOVE, Constants.GROUP, getID(), Constants.GROUP, g.getID(), g.getName()));
}
}
/**
* check to see if an eperson is a direct member.
* If the eperson is a member via a subgroup will be returned <code>false</code>
*
* @param e
* eperson to check membership
*/
public boolean isMember(EPerson e)
{
// special, group 0 is anonymous
if (getID() == 0)
{
return true;
}
loadData(); // make sure Group has data loaded
return epeople.contains(e);
}
/**
* check to see if g is a direct group member.
* If g is a subgroup via another group will be returned <code>false</code>
*
* @param g
* group to check
* @return
*/
public boolean isMember(Group g)
{
loadData(); // make sure Group has data loaded
return groups.contains(g);
}
/**
* fast check to see if an eperson is a member called with eperson id, does
* database lookup without instantiating all of the epeople objects and is
* thus a static method
*
* @param c
* context
* @param groupid
* group ID to check
*/
public static boolean isMember(Context c, int groupid) throws SQLException
{
// special, everyone is member of group 0 (anonymous)
if (groupid == 0)
{
return true;
}
EPerson currentuser = c.getCurrentUser();
return epersonInGroup(c, groupid, currentuser);
}
/**
* Get all of the groups that an eperson is a member of
*
* @param c
* @param e
* @return
* @throws SQLException
*/
public static Group[] allMemberGroups(Context c, EPerson e)
throws SQLException
{
List<Group> groupList = new ArrayList<Group>();
Set<Integer> myGroups = allMemberGroupIDs(c, e);
// now convert those Integers to Groups
Iterator i = myGroups.iterator();
while (i.hasNext())
{
groupList.add(Group.find(c, ((Integer) i.next()).intValue()));
}
return (Group[]) groupList.toArray(new Group[0]);
}
/**
* get Set of Integers all of the group memberships for an eperson
*
* @param c
* @param e
* @return Set of Integer groupIDs
* @throws SQLException
*/
public static Set<Integer> allMemberGroupIDs(Context c, EPerson e)
throws SQLException
{
Set<Integer> groupIDs = new HashSet<Integer>();
if (e != null)
{
// two queries - first to get groups eperson is a member of
// second query gets parent groups for groups eperson is a member of
TableRowIterator tri = DatabaseManager.queryTable(c,
"epersongroup2eperson",
"SELECT * FROM epersongroup2eperson WHERE eperson_id= ?", e
.getID());
try
{
while (tri.hasNext())
{
TableRow row = tri.next();
int childID = row.getIntColumn("eperson_group_id");
groupIDs.add(new Integer(childID));
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
}
// Also need to get all "Special Groups" user is a member of!
// Otherwise, you're ignoring the user's membership to these groups!
// However, we only do this is we are looking up the special groups
// of the current user, as we cannot look up the special groups
// of a user who is not logged in.
if ((c.getCurrentUser() == null) || (((c.getCurrentUser() != null) && (c.getCurrentUser().getID() == e.getID()))))
{
Group[] specialGroups = c.getSpecialGroups();
for(Group special : specialGroups)
{
groupIDs.add(new Integer(special.getID()));
}
}
// all the users are members of the anonymous group
groupIDs.add(new Integer(0));
// now we have all owning groups, also grab all parents of owning groups
// yes, I know this could have been done as one big query and a union,
// but doing the Oracle port taught me to keep to simple SQL!
String groupQuery = "";
Iterator i = groupIDs.iterator();
// Build a list of query parameters
Object[] parameters = new Object[groupIDs.size()];
int idx = 0;
while (i.hasNext())
{
int groupID = ((Integer) i.next()).intValue();
parameters[idx++] = new Integer(groupID);
groupQuery += "child_id= ? ";
if (i.hasNext())
groupQuery += " OR ";
}
// was member of at least one group
// NOTE: even through the query is built dynamicaly all data is seperated into the
// the parameters array.
TableRowIterator tri = DatabaseManager.queryTable(c, "group2groupcache",
"SELECT * FROM group2groupcache WHERE " + groupQuery,
parameters);
try
{
while (tri.hasNext())
{
TableRow row = tri.next();
int parentID = row.getIntColumn("parent_id");
groupIDs.add(new Integer(parentID));
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
return groupIDs;
}
/**
* Get all of the epeople who are a member of the
* specified group, or a member of a sub-group of the
* specified group, etc.
*
* @param c
* DSpace context
* @param g
* Group object
* @return Array of EPerson objects
* @throws SQLException
*/
public static EPerson[] allMembers(Context c, Group g)
throws SQLException
{
List<EPerson> epersonList = new ArrayList<EPerson>();
Set<Integer> myEpeople = allMemberIDs(c, g);
// now convert those Integers to EPerson objects
Iterator i = myEpeople.iterator();
while (i.hasNext())
{
epersonList.add(EPerson.find(c, ((Integer) i.next()).intValue()));
}
return (EPerson[]) epersonList.toArray(new EPerson[0]);
}
/**
* Get Set of all Integers all of the epeople
* members for a group
*
* @param c
* DSpace context
* @param g
* Group object
* @return Set of Integer epersonIDs
* @throws SQLException
*/
public static Set<Integer> allMemberIDs(Context c, Group g)
throws SQLException
{
// two queries - first to get all groups which are a member of this group
// second query gets all members of each group in the first query
Set<Integer> epeopleIDs = new HashSet<Integer>();
// Get all groups which are a member of this group
TableRowIterator tri = DatabaseManager.queryTable(c, "group2groupcache",
"SELECT * FROM group2groupcache WHERE parent_id= ? ",
g.getID());
Set<Integer> groupIDs = new HashSet<Integer>();
try
{
while (tri.hasNext())
{
TableRow row = tri.next();
int childID = row.getIntColumn("child_id");
groupIDs.add(new Integer(childID));
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
// now we have all the groups (including this one)
// it is time to find all the EPeople who belong to those groups
// and filter out all duplicates
Object[] parameters = new Object[groupIDs.size()+1];
int idx = 0;
Iterator i = groupIDs.iterator();
// don't forget to add the current group to this query!
parameters[idx++] = new Integer(g.getID());
String epersonQuery = "eperson_group_id= ? ";
if (i.hasNext())
epersonQuery += " OR ";
while (i.hasNext())
{
int groupID = ((Integer) i.next()).intValue();
parameters[idx++] = new Integer(groupID);
epersonQuery += "eperson_group_id= ? ";
if (i.hasNext())
epersonQuery += " OR ";
}
//get all the EPerson IDs
// Note: even through the query is dynamicaly built all data is seperated
// into the parameters array.
tri = DatabaseManager.queryTable(c, "epersongroup2eperson",
"SELECT * FROM epersongroup2eperson WHERE " + epersonQuery,
parameters);
try
{
while (tri.hasNext())
{
TableRow row = tri.next();
int epersonID = row.getIntColumn("eperson_id");
epeopleIDs.add(new Integer(epersonID));
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
return epeopleIDs;
}
private static boolean epersonInGroup(Context c, int groupID, EPerson e)
throws SQLException
{
Set<Integer> groupIDs = Group.allMemberGroupIDs(c, e);
return groupIDs.contains(new Integer(groupID));
}
/**
* find the group by its ID
*
* @param context
* @param id
*/
public static Group find(Context context, int id) throws SQLException
{
// First check the cache
Group fromCache = (Group) context.fromCache(Group.class, id);
if (fromCache != null)
{
return fromCache;
}
TableRow row = DatabaseManager.find(context, "epersongroup", id);
if (row == null)
{
return null;
}
else
{
return new Group(context, row);
}
}
/**
* Find the group by its name - assumes name is unique
*
* @param context
* @param name
*
* @return Group
*/
public static Group findByName(Context context, String name)
throws SQLException
{
TableRow row = DatabaseManager.findByUnique(context, "epersongroup",
"name", name);
if (row == null)
{
return null;
}
else
{
// First check the cache
Group fromCache = (Group) context.fromCache(Group.class, row
.getIntColumn("eperson_group_id"));
if (fromCache != null)
{
return fromCache;
}
else
{
return new Group(context, row);
}
}
}
/**
* Finds all groups in the site
*
* @param context
* DSpace context
* @param sortField
* field to sort by -- Group.ID or Group.NAME
*
* @return array of all groups in the site
*/
public static Group[] findAll(Context context, int sortField)
throws SQLException
{
String s;
switch (sortField)
{
case ID:
s = "eperson_group_id";
break;
case NAME:
s = "name";
break;
default:
s = "name";
}
// NOTE: The use of 's' in the order by clause can not cause an sql
// injection because the string is derived from constant values above.
TableRowIterator rows = DatabaseManager.queryTable(
context, "epersongroup",
"SELECT * FROM epersongroup ORDER BY "+s);
try
{
List gRows = rows.toList();
Group[] groups = new Group[gRows.size()];
for (int i = 0; i < gRows.size(); i++)
{
TableRow row = (TableRow) gRows.get(i);
// First check the cache
Group fromCache = (Group) context.fromCache(Group.class, row
.getIntColumn("eperson_group_id"));
if (fromCache != null)
{
groups[i] = fromCache;
}
else
{
groups[i] = new Group(context, row);
}
}
return groups;
}
finally
{
if (rows != null)
rows.close();
}
}
/**
* Find the groups that match the search query across eperson_group_id or name
*
* @param context
* DSpace context
* @param query
* The search string
*
* @return array of Group objects
*/
public static Group[] search(Context context, String query)
throws SQLException
{
return search(context, query, -1, -1);
}
/**
* Find the groups that match the search query across eperson_group_id or name
*
* @param context
* DSpace context
* @param query
* The search string
* @param offset
* Inclusive offset
* @param limit
* Maximum number of matches returned
*
* @return array of Group objects
*/
public static Group[] search(Context context, String query, int offset, int limit)
throws SQLException
{
String params = "%"+query.toLowerCase()+"%";
StringBuffer queryBuf = new StringBuffer();
queryBuf.append("SELECT * FROM epersongroup WHERE LOWER(name) LIKE LOWER(?) OR eperson_group_id = ? ORDER BY name ASC ");
// Add offset and limit restrictions - Oracle requires special code
if ("oracle".equals(ConfigurationManager.getProperty("db.name")))
{
// First prepare the query to generate row numbers
if (limit > 0 || offset > 0)
{
queryBuf.insert(0, "SELECT /*+ FIRST_ROWS(n) */ rec.*, ROWNUM rnum FROM (");
queryBuf.append(") ");
}
// Restrict the number of rows returned based on the limit
if (limit > 0)
{
queryBuf.append("rec WHERE rownum<=? ");
// If we also have an offset, then convert the limit into the maximum row number
if (offset > 0)
limit += offset;
}
// Return only the records after the specified offset (row number)
if (offset > 0)
{
queryBuf.insert(0, "SELECT * FROM (");
queryBuf.append(") WHERE rnum>?");
}
}
else
{
if (limit > 0)
queryBuf.append(" LIMIT ? ");
if (offset > 0)
queryBuf.append(" OFFSET ? ");
}
String dbquery = queryBuf.toString();
// When checking against the eperson-id, make sure the query can be made into a number
Integer int_param;
try {
int_param = Integer.valueOf(query);
}
catch (NumberFormatException e) {
int_param = new Integer(-1);
}
// Create the parameter array, including limit and offset if part of the query
Object[] paramArr = new Object[]{params, int_param};
if (limit > 0 && offset > 0)
paramArr = new Object[] {params, int_param,limit,offset};
else if (limit > 0)
paramArr = new Object[] {params, int_param,limit};
else if (offset > 0)
paramArr = new Object[] {params, int_param,offset};
TableRowIterator rows =
DatabaseManager.query(context, dbquery, paramArr);
try
{
List groupRows = rows.toList();
Group[] groups = new Group[groupRows.size()];
for (int i = 0; i < groupRows.size(); i++)
{
TableRow row = (TableRow) groupRows.get(i);
// First check the cache
Group fromCache = (Group) context.fromCache(Group.class, row
.getIntColumn("eperson_group_id"));
if (fromCache != null)
{
groups[i] = fromCache;
}
else
{
groups[i] = new Group(context, row);
}
}
return groups;
}
finally
{
if (rows != null)
rows.close();
}
}
/**
* Returns the total number of groups returned by a specific query, without the overhead
* of creating the Group objects to store the results.
*
* @param context
* DSpace context
* @param query
* The search string
*
* @return the number of groups mathching the query
*/
public static int searchResultCount(Context context, String query)
throws SQLException
{
String params = "%"+query.toLowerCase()+"%";
String dbquery = "SELECT count(*) as gcount FROM epersongroup WHERE LOWER(name) LIKE LOWER(?) OR eperson_group_id = ? ";
// When checking against the eperson-id, make sure the query can be made into a number
Integer int_param;
try {
int_param = Integer.valueOf(query);
}
catch (NumberFormatException e) {
int_param = new Integer(-1);
}
// Get all the epeople that match the query
TableRow row = DatabaseManager.querySingle(context, dbquery, new Object[] {params, int_param});
// use getIntColumn for Oracle count data
Long count;
if ("oracle".equals(ConfigurationManager.getProperty("db.name")))
{
count = new Long(row.getIntColumn("gcount"));
}
else //getLongColumn works for postgres
{
count = new Long(row.getLongColumn("gcount"));
}
return count.intValue();
}
/**
* Delete a group
*
*/
public void delete() throws SQLException
{
// FIXME: authorizations
myContext.addEvent(new Event(Event.DELETE, Constants.GROUP, getID(), getName()));
// Remove from cache
myContext.removeCached(this, getID());
// Remove any ResourcePolicies that reference this group
AuthorizeManager.removeGroupPolicies(myContext, getID());
// Remove any group memberships first
DatabaseManager.updateQuery(myContext,
"DELETE FROM EPersonGroup2EPerson WHERE eperson_group_id= ? ",
getID());
// remove any group2groupcache entries
DatabaseManager.updateQuery(myContext,
"DELETE FROM group2groupcache WHERE parent_id= ? OR child_id= ? ",
getID(),getID());
// Now remove any group2group assignments
DatabaseManager.updateQuery(myContext,
"DELETE FROM group2group WHERE parent_id= ? OR child_id= ? ",
getID(),getID());
// don't forget the new table
deleteEpersonGroup2WorkspaceItem();
// Remove ourself
DatabaseManager.delete(myContext, myRow);
epeople.clear();
log.info(LogManager.getHeader(myContext, "delete_group", "group_id="
+ getID()));
}
/**
* @throws SQLException
*/
private void deleteEpersonGroup2WorkspaceItem() throws SQLException
{
DatabaseManager.updateQuery(myContext,
"DELETE FROM EPersonGroup2WorkspaceItem WHERE eperson_group_id= ? ",
getID());
}
/**
* Return EPerson members of a Group
*/
public EPerson[] getMembers()
{
loadData(); // make sure all data is loaded
EPerson[] myArray = new EPerson[epeople.size()];
myArray = (EPerson[]) epeople.toArray(myArray);
return myArray;
}
/**
* Return Group members of a Group
*
* @return
*/
public Group[] getMemberGroups()
{
loadData(); // make sure all data is loaded
Group[] myArray = new Group[groups.size()];
myArray = (Group[]) groups.toArray(myArray);
return myArray;
}
/**
* Return true if group has no direct or indirect members
*/
public boolean isEmpty()
{
loadData(); // make sure all data is loaded
// the only fast check available is on epeople...
boolean hasMembers = (epeople.size() != 0);
if (hasMembers)
{
return false;
}
else
{
// well, groups is never null...
for (Group subGroup : groups){
hasMembers = !subGroup.isEmpty();
if (hasMembers){
return false;
}
}
return !hasMembers;
}
}
/**
* Update the group - writing out group object and EPerson list if necessary
*/
public void update() throws SQLException, AuthorizeException
{
// FIXME: Check authorisation
DatabaseManager.update(myContext, myRow);
if (modifiedMetadata)
{
myContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.GROUP, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
// Redo eperson mappings if they've changed
if (epeopleChanged)
{
// Remove any existing mappings
DatabaseManager.updateQuery(myContext,
"delete from epersongroup2eperson where eperson_group_id= ? ",
getID());
// Add new mappings
Iterator i = epeople.iterator();
while (i.hasNext())
{
EPerson e = (EPerson) i.next();
TableRow mappingRow = DatabaseManager.create(myContext,
"epersongroup2eperson");
mappingRow.setColumn("eperson_id", e.getID());
mappingRow.setColumn("eperson_group_id", getID());
DatabaseManager.update(myContext, mappingRow);
}
epeopleChanged = false;
}
// Redo Group mappings if they've changed
if (groupsChanged)
{
// Remove any existing mappings
DatabaseManager.updateQuery(myContext,
"delete from group2group where parent_id= ? ",
getID());
// Add new mappings
Iterator i = groups.iterator();
while (i.hasNext())
{
Group g = (Group) i.next();
TableRow mappingRow = DatabaseManager.create(myContext,
"group2group");
mappingRow.setColumn("parent_id", getID());
mappingRow.setColumn("child_id", g.getID());
DatabaseManager.update(myContext, mappingRow);
}
// groups changed, now change group cache
rethinkGroupCache();
groupsChanged = false;
}
log.info(LogManager.getHeader(myContext, "update_group", "group_id="
+ getID()));
}
/**
* Return <code>true</code> if <code>other</code> is the same Group as
* this object, <code>false</code> otherwise
*
* @param other
* object to compare to
*
* @return <code>true</code> if object passed in represents the same group
* as this object
*/
public boolean equals(Object other)
{
if (!(other instanceof Group))
{
return false;
}
return (getID() == ((Group) other).getID());
}
public int getType()
{
return Constants.GROUP;
}
public String getHandle()
{
return null;
}
/**
* Regenerate the group cache AKA the group2groupcache table in the database -
* meant to be called when a group is added or removed from another group
*
*/
private void rethinkGroupCache() throws SQLException
{
// read in the group2group table
TableRowIterator tri = DatabaseManager.queryTable(myContext, "group2group",
"SELECT * FROM group2group");
Map<Integer,Set<Integer>> parents = new HashMap<Integer,Set<Integer>>();
try
{
while (tri.hasNext())
{
TableRow row = (TableRow) tri.next();
Integer parentID = new Integer(row.getIntColumn("parent_id"));
Integer childID = new Integer(row.getIntColumn("child_id"));
// if parent doesn't have an entry, create one
if (!parents.containsKey(parentID))
{
Set<Integer> children = new HashSet<Integer>();
// add child id to the list
children.add(childID);
parents.put(parentID, children);
}
else
{
// parent has an entry, now add the child to the parent's record
// of children
Set<Integer> children = parents.get(parentID);
children.add(childID);
}
}
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
tri.close();
}
// now parents is a hash of all of the IDs of groups that are parents
// and each hash entry is a hash of all of the IDs of children of those
// parent groups
// so now to establish all parent,child relationships we can iterate
// through the parents hash
Iterator i = parents.keySet().iterator();
while (i.hasNext())
{
Integer parentID = (Integer) i.next();
Set<Integer> myChildren = getChildren(parents, parentID);
Iterator j = myChildren.iterator();
while (j.hasNext())
{
// child of a parent
Integer childID = (Integer) j.next();
((Set<Integer>) parents.get(parentID)).add(childID);
}
}
// empty out group2groupcache table
DatabaseManager.updateQuery(myContext,
"DELETE FROM group2groupcache WHERE id >= 0");
// write out new one
Iterator pi = parents.keySet().iterator(); // parent iterator
while (pi.hasNext())
{
Integer parent = (Integer) pi.next();
Set<Integer> children = parents.get(parent);
Iterator ci = children.iterator(); // child iterator
while (ci.hasNext())
{
Integer child = (Integer) ci.next();
TableRow row = DatabaseManager.create(myContext,
"group2groupcache");
int parentID = parent.intValue();
int childID = child.intValue();
row.setColumn("parent_id", parentID);
row.setColumn("child_id", childID);
DatabaseManager.update(myContext, row);
}
}
}
/**
* Used recursively to generate a map of ALL of the children of the given
* parent
*
* @param parents
* Map of parent,child relationships
* @param parent
* the parent you're interested in
* @return Map whose keys are all of the children of a parent
*/
private Set<Integer> getChildren(Map<Integer,Set<Integer>> parents, Integer parent)
{
Set<Integer> myChildren = new HashSet<Integer>();
// degenerate case, this parent has no children
if (!parents.containsKey(parent))
return myChildren;
// got this far, so we must have children
Set<Integer> children = parents.get(parent);
// now iterate over all of the children
Iterator i = children.iterator();
while (i.hasNext())
{
Integer childID = (Integer) i.next();
// add this child's ID to our return set
myChildren.add(childID);
// and now its children
myChildren.addAll(getChildren(parents, childID));
}
return myChildren;
}
public DSpaceObject getParentObject() throws SQLException
{
// could a collection/community admin manage related groups?
// check before the configuration options could give a performance gain
// if all group management are disallowed
if (AuthorizeConfiguration.canCollectionAdminManageAdminGroup()
|| AuthorizeConfiguration.canCollectionAdminManageSubmitters()
|| AuthorizeConfiguration.canCollectionAdminManageWorkflows()
|| AuthorizeConfiguration.canCommunityAdminManageAdminGroup()
|| AuthorizeConfiguration
.canCommunityAdminManageCollectionAdminGroup()
|| AuthorizeConfiguration
.canCommunityAdminManageCollectionSubmitters()
|| AuthorizeConfiguration
.canCommunityAdminManageCollectionWorkflows())
{
// is this a collection related group?
TableRow qResult = DatabaseManager
.querySingle(
myContext,
"SELECT collection_id, workflow_step_1, workflow_step_2, " +
" workflow_step_3, submitter, admin FROM collection "
+ " WHERE workflow_step_1 = ? OR "
+ " workflow_step_2 = ? OR "
+ " workflow_step_3 = ? OR "
+ " submitter = ? OR " + " admin = ?",
getID(), getID(), getID(), getID(), getID());
if (qResult != null)
{
Collection collection = Collection.find(myContext, qResult
.getIntColumn("collection_id"));
if ((qResult.getIntColumn("workflow_step_1") == getID() ||
qResult.getIntColumn("workflow_step_2") == getID() ||
qResult.getIntColumn("workflow_step_3") == getID()))
{
if (AuthorizeConfiguration.canCollectionAdminManageWorkflows())
{
return collection;
}
else if (AuthorizeConfiguration.canCommunityAdminManageCollectionWorkflows())
{
return collection.getParentObject();
}
}
if (qResult.getIntColumn("submitter") == getID())
{
if (AuthorizeConfiguration.canCollectionAdminManageSubmitters())
{
return collection;
}
else if (AuthorizeConfiguration.canCommunityAdminManageCollectionSubmitters())
{
return collection.getParentObject();
}
}
if (qResult.getIntColumn("admin") == getID())
{
if (AuthorizeConfiguration.canCollectionAdminManageAdminGroup())
{
return collection;
}
else if (AuthorizeConfiguration.canCommunityAdminManageCollectionAdminGroup())
{
return collection.getParentObject();
}
}
}
// is the group releated to a community and community admin allowed
// to manage it?
else if (AuthorizeConfiguration.canCommunityAdminManageAdminGroup())
{
qResult = DatabaseManager.querySingle(myContext,
"SELECT community_id FROM community "
+ "WHERE admin = ?", getID());
if (qResult != null)
{
Community community = Community.find(myContext, qResult
.getIntColumn("community_id"));
return community;
}
}
}
return null;
}
}