mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-15 22:13:08 +00:00

git-svn-id: http://scm.dspace.org/svn/repo/dspace/trunk@5474 9c30dcfa-912a-0410-8fc2-9e0234be79fd
1421 lines
42 KiB
Java
1421 lines
42 KiB
Java
/*
|
|
* Group.java
|
|
*
|
|
* Version: $Revision$
|
|
*
|
|
* Date: $Date$
|
|
*
|
|
* 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 org.apache.log4j.Logger;
|
|
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$
|
|
*/
|
|
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<Integer> i = myGroups.iterator();
|
|
|
|
while (i.hasNext())
|
|
{
|
|
groupList.add(Group.find(c, (i.next()).intValue()));
|
|
}
|
|
|
|
return groupList.toArray(new Group[groupList.size()]);
|
|
}
|
|
|
|
/**
|
|
* 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<Integer> i = groupIDs.iterator();
|
|
|
|
// Build a list of query parameters
|
|
Object[] parameters = new Object[groupIDs.size()];
|
|
int idx = 0;
|
|
while (i.hasNext())
|
|
{
|
|
int groupID = (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 dynamically, all data is
|
|
// separated into 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<Integer> i = myEpeople.iterator();
|
|
|
|
while (i.hasNext())
|
|
{
|
|
epersonList.add(EPerson.find(c, (i.next()).intValue()));
|
|
}
|
|
|
|
return epersonList.toArray(new EPerson[epersonList.size()]);
|
|
}
|
|
|
|
/**
|
|
* 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<Integer> 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 = (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 dynamically built all data is separated
|
|
// 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<TableRow> gRows = rows.toList();
|
|
|
|
Group[] groups = new Group[gRows.size()];
|
|
|
|
for (int i = 0; i < gRows.size(); i++)
|
|
{
|
|
TableRow row = 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<TableRow> groupRows = rows.toList();
|
|
Group[] groups = new Group[groupRows.size()];
|
|
|
|
for (int i = 0; i < groupRows.size(); i++)
|
|
{
|
|
TableRow row = 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 matching 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<EPerson> i = epeople.iterator();
|
|
|
|
while (i.hasNext())
|
|
{
|
|
EPerson e = 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<Group> i = groups.iterator();
|
|
|
|
while (i.hasNext())
|
|
{
|
|
Group g = 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 obj
|
|
* object to compare to
|
|
*
|
|
* @return <code>true</code> if object passed in represents the same group
|
|
* as this object
|
|
*/
|
|
@Override
|
|
public boolean equals(Object obj)
|
|
{
|
|
if (obj == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (getClass() != obj.getClass())
|
|
{
|
|
return false;
|
|
}
|
|
final Group other = (Group) obj;
|
|
if(this.getID() != other.getID()) return false;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode()
|
|
{
|
|
int hash = 7;
|
|
hash = 59 * hash + (this.myRow != null ? this.myRow.hashCode() : 0);
|
|
return hash;
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
for (Map.Entry<Integer, Set<Integer>> parent : parents.entrySet())
|
|
{
|
|
Set<Integer> myChildren = getChildren(parents, parent.getKey());
|
|
parent.getValue().addAll(myChildren);
|
|
}
|
|
|
|
// empty out group2groupcache table
|
|
DatabaseManager.updateQuery(myContext,
|
|
"DELETE FROM group2groupcache WHERE id >= 0");
|
|
|
|
// write out new one
|
|
Iterator<Integer> pi = parents.keySet().iterator(); // parent iterator
|
|
|
|
while (pi.hasNext())
|
|
{
|
|
Integer parent = pi.next();
|
|
|
|
Set<Integer> children = parents.get(parent);
|
|
Iterator<Integer> ci = children.iterator(); // child iterator
|
|
|
|
while (ci.hasNext())
|
|
{
|
|
Integer child = 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<Integer> i = children.iterator();
|
|
|
|
while (i.hasNext())
|
|
{
|
|
Integer childID = 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 administrator 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 related to a community and community administrator 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;
|
|
}
|
|
}
|