Package org.dspace.content

Source Code of org.dspace.content.Collection

/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content;

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.authorize.ResourcePolicy;
import org.dspace.browse.BrowseException;
import org.dspace.browse.IndexBrowse;
import org.dspace.browse.ItemCountException;
import org.dspace.browse.ItemCounter;
import org.dspace.core.*;
import org.dspace.eperson.Group;
import org.dspace.event.Event;
import org.dspace.handle.HandleManager;
import org.dspace.storage.rdbms.DatabaseManager;
import org.dspace.storage.rdbms.TableRow;
import org.dspace.storage.rdbms.TableRowIterator;
import org.dspace.workflow.WorkflowItem;
import org.dspace.xmlworkflow.storedcomponents.CollectionRole;
import org.dspace.xmlworkflow.storedcomponents.XmlWorkflowItem;

import java.io.IOException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
* Class representing a collection.
* <P>
* The collection's metadata (name, introductory text etc), workflow groups, and
* default group of submitters are loaded into memory. Changes to metadata are
* not written to the database until <code>update</code> is called. If you
* create or remove a workflow group, the change is only reflected in the
* database after calling <code>update</code>. The default group of
* submitters is slightly different - creating or removing this has instant
* effect.
*
* @author Robert Tansley
* @version $Revision$
*/
public class Collection extends DSpaceObject
{
    /** log4j category */
    private static Logger log = Logger.getLogger(Collection.class);

    /** The table row corresponding to this item */
    private TableRow collectionRow;

    /** The logo bitstream */
    private Bitstream logo;

    /** The item template */
    private Item template;

    /** Our Handle */
    private String handle;

    /** Flag set when data is modified, for events */
    private boolean modified;

    /**
     * Groups corresponding to workflow steps - NOTE these start from one, so
     * workflowGroups[0] corresponds to workflow_step_1.
     */
    private Group[] workflowGroup;

    /** The default group of submitters */
    private Group submitters;

    /** The default group of administrators */
    private Group admins;

    // Keys for accessing Collection metadata
    public static final String COPYRIGHT_TEXT = "copyright_text";
    public static final String INTRODUCTORY_TEXT = "introductory_text";
    public static final String SHORT_DESCRIPTION = "short_description";
    public static final String SIDEBAR_TEXT = "side_bar_text";
    public static final String PROVENANCE_TEXT = "provenance_description";
   
    /**
     * Construct a collection with the given table row
     *
     * @param context
     *            the context this object exists in
     * @param row
     *            the corresponding row in the table
     * @throws SQLException
     */
    Collection(Context context, TableRow row) throws SQLException
    {
        super(context);
        collectionRow = row;

        // Get the logo bitstream
        if (collectionRow.isColumnNull("logo_bitstream_id"))
        {
            logo = null;
        }
        else
        {
            logo = Bitstream.find(ourContext, collectionRow
                    .getIntColumn("logo_bitstream_id"));
        }

        // Get the template item
        if (collectionRow.isColumnNull("template_item_id"))
        {
            template = null;
        }
        else
        {
            template = Item.find(ourContext, collectionRow
                    .getIntColumn("template_item_id"));
        }

        // Get the relevant groups
        workflowGroup = new Group[3];

        workflowGroup[0] = groupFromColumn("workflow_step_1");
        workflowGroup[1] = groupFromColumn("workflow_step_2");
        workflowGroup[2] = groupFromColumn("workflow_step_3");

        submitters = groupFromColumn("submitter");
        admins = groupFromColumn("admin");

        // Get our Handle if any
        handle = HandleManager.findHandle(context, this);

        // Cache ourselves
        context.cache(this, row.getIntColumn("collection_id"));

        modified = false;
        clearDetails();
    }

    /**
     * Get a collection from the database. Loads in the metadata
     *
     * @param context
     *            DSpace context object
     * @param id
     *            ID of the collection
     *
     * @return the collection, or null if the ID is invalid.
     * @throws SQLException
     */
    public static Collection find(Context context, int id) throws SQLException
    {
        // First check the cache
        Collection fromCache = (Collection) context.fromCache(Collection.class,
                id);

        if (fromCache != null)
        {
            return fromCache;
        }

        TableRow row = DatabaseManager.find(context, "collection", id);

        if (row == null)
        {
            if (log.isDebugEnabled())
            {
                log.debug(LogManager.getHeader(context, "find_collection",
                        "not_found,collection_id=" + id));
            }

            return null;
        }

        // not null, return Collection
        if (log.isDebugEnabled())
        {
            log.debug(LogManager.getHeader(context, "find_collection",
                    "collection_id=" + id));
        }

        return new Collection(context, row);
    }

    /**
     * Create a new collection, with a new ID. This method is not public, and
     * does not check authorisation.
     *
     * @param context
     *            DSpace context object
     *
     * @return the newly created collection
     * @throws SQLException
     * @throws AuthorizeException
     */
    static Collection create(Context context) throws SQLException,
            AuthorizeException
    {
        return create(context, null);
    }

    /**
     * Create a new collection, with a new ID. This method is not public, and
     * does not check authorisation.
     *
     * @param context
     *            DSpace context object
     *
     * @param handle the pre-determined Handle to assign to the new community
     * @return the newly created collection
     * @throws SQLException
     * @throws AuthorizeException
     */
    static Collection create(Context context, String handle) throws SQLException,
            AuthorizeException
    {
        TableRow row = DatabaseManager.create(context, "collection");
        Collection c = new Collection(context, row);

        try
        {
            c.handle = (handle == null) ?
                       HandleManager.createHandle(context, c) :
                       HandleManager.createHandle(context, c, handle);
        }
        catch(IllegalStateException ie)
        {
            //If an IllegalStateException is thrown, then an existing object is already using this handle
            //Remove the collection we just created -- as it is incomplete
            try
            {
                if(c!=null)
                {
                    c.delete();
                }
            } catch(Exception e) { }

            //pass exception on up the chain
            throw ie;
        }

        // create the default authorization policy for collections
        // of 'anonymous' READ
        Group anonymousGroup = Group.find(context, 0);

        ResourcePolicy myPolicy = ResourcePolicy.create(context);
        myPolicy.setResource(c);
        myPolicy.setAction(Constants.READ);
        myPolicy.setGroup(anonymousGroup);
        myPolicy.update();

        // now create the default policies for submitted items
        myPolicy = ResourcePolicy.create(context);
        myPolicy.setResource(c);
        myPolicy.setAction(Constants.DEFAULT_ITEM_READ);
        myPolicy.setGroup(anonymousGroup);
        myPolicy.update();

        myPolicy = ResourcePolicy.create(context);
        myPolicy.setResource(c);
        myPolicy.setAction(Constants.DEFAULT_BITSTREAM_READ);
        myPolicy.setGroup(anonymousGroup);
        myPolicy.update();

        context.addEvent(new Event(Event.CREATE, Constants.COLLECTION,
                c.getID(), c.handle, c.getIdentifiers(context)));

        log.info(LogManager.getHeader(context, "create_collection",
                "collection_id=" + row.getIntColumn("collection_id"))
                + ",handle=" + c.handle);

        return c;
    }

    /**
     * Get all collections in the system. These are alphabetically sorted by
     * collection name.
     *
     * @param context
     *            DSpace context object
     *
     * @return the collections in the system
     * @throws SQLException
     */
    public static Collection[] findAll(Context context) throws SQLException {
        TableRowIterator tri = null;
        try {
            String query = "SELECT c.* FROM collection c " +
                    "LEFT JOIN metadatavalue m on (m.resource_id = c.collection_id and m.resource_type_id = ? and m.metadata_field_id = ?) ";
            if(DatabaseManager.isOracle()){
                query += " ORDER BY cast(m.text_value as varchar2(128))";
            }else{
                query += " ORDER BY m.text_value";
            }

            tri = DatabaseManager.query(context,
                    query,
                    Constants.COLLECTION,
                    MetadataField.findByElement(context, MetadataSchema.find(context, MetadataSchema.DC_SCHEMA).getSchemaID(), "title", null).getFieldID()
            );
        } catch (SQLException e) {
            log.error("Find all Collections - ",e);
            throw e;
        }

        List<Collection> collections = new ArrayList<Collection>();

        try
        {
            while (tri.hasNext())
            {
                TableRow row = tri.next();

                // First check the cache
                Collection fromCache = (Collection) context.fromCache(
                        Collection.class, row.getIntColumn("collection_id"));

                if (fromCache != null)
                {
                    collections.add(fromCache);
                }
                else
                {
                    collections.add(new Collection(context, row));
                }
            }
        }
        finally
        {
            // close the TableRowIterator to free up resources
            if (tri != null)
            {
                tri.close();
            }
        }

        Collection[] collectionArray = new Collection[collections.size()];
        collectionArray = (Collection[]) collections.toArray(collectionArray);

        return collectionArray;
    }

    /**
     * Get all collections in the system. Adds support for limit and offset.
     * @param context
     * @param limit
     * @param offset
     * @return
     * @throws SQLException
     */
    public static Collection[] findAll(Context context, Integer limit, Integer offset) throws SQLException
    {
        TableRowIterator tri = null;
        try{
            String query = "SELECT c.* FROM collection c " +
                    "LEFT JOIN metadatavalue m on (m.resource_id = c.collection_id and m.resource_type_id = ? and m.metadata_field_id = ?) ";

            if(DatabaseManager.isOracle()){
                query += " ORDER BY cast(m.text_value as varchar2(128))";
            }else{
                query += " ORDER BY m.text_value";
            }
            query += " limit ? offset ?";
            tri = DatabaseManager.query(context,
                    query,
                    Constants.COLLECTION,
                    MetadataField.findByElement(context, MetadataSchema.find(context, MetadataSchema.DC_SCHEMA).getSchemaID(), "title", null).getFieldID(),
                    limit,
                    offset
            );
        } catch (SQLException e) {
            log.error("Find all Collections offset/limit - ",e);
            throw e;
        }
        List<Collection> collections = new ArrayList<Collection>();

        try
        {
            while (tri.hasNext())
            {
                TableRow row = tri.next();

                // First check the cache
                Collection fromCache = (Collection) context.fromCache(
                        Collection.class, row.getIntColumn("collection_id"));

                if (fromCache != null)
                {
                    collections.add(fromCache);
                }
                else
                {
                    collections.add(new Collection(context, row));
                }
            }
        }
        finally
        {
            // close the TableRowIterator to free up resources
            if (tri != null)
            {
                tri.close();
            }
        }

        Collection[] collectionArray = new Collection[collections.size()];
        collectionArray = (Collection[]) collections.toArray(collectionArray);

        return collectionArray;
    }

    /**
     * Get the in_archive items in this collection. The order is indeterminate.
     *
     * @return an iterator over the items in the collection.
     * @throws SQLException
     */
    public ItemIterator getItems() throws SQLException
    {
        String myQuery = "SELECT item.* FROM item, collection2item WHERE "
                + "item.item_id=collection2item.item_id AND "
                + "collection2item.collection_id= ? "
                + "AND item.in_archive='1'";

        TableRowIterator rows = DatabaseManager.queryTable(ourContext, "item",
                myQuery,getID());

        return new ItemIterator(ourContext, rows);
    }

    /**
     * Get the in_archive items in this collection. The order is indeterminate.
     * Provides the ability to use limit and offset, for efficient paging.
     * @param limit Max number of results in set
     * @param offset Number of results to jump ahead by. 100 = 100th result is first, not 100th page.
     * @return an iterator over the items in the collection.
     * @throws SQLException
     */
    public ItemIterator getItems(Integer limit, Integer offset) throws SQLException
    {
        String myQuery = "SELECT item.* FROM item, collection2item WHERE "
                + "item.item_id=collection2item.item_id AND "
                + "collection2item.collection_id= ? "
                + "AND item.in_archive='1' limit ? offset ?";

        TableRowIterator rows = DatabaseManager.queryTable(ourContext, "item",
                myQuery,getID(), limit, offset);

        return new ItemIterator(ourContext, rows);
    }

    /**
     * Get all the items in this collection. The order is indeterminate.
     *
     * @return an iterator over the items in the collection.
     * @throws SQLException
     */
    public ItemIterator getAllItems() throws SQLException
    {
        String myQuery = "SELECT item.* FROM item, collection2item WHERE "
                + "item.item_id=collection2item.item_id AND "
                + "collection2item.collection_id= ? ";

        TableRowIterator rows = DatabaseManager.queryTable(ourContext, "item",
                myQuery,getID());

        return new ItemIterator(ourContext, rows);
    }

     /**
     * Get the internal ID of this collection
     *
     * @return the internal identifier
     */
    public int getID()
    {
        return collectionRow.getIntColumn("collection_id");
    }

    /**
     * @see org.dspace.content.DSpaceObject#getHandle()
     */
    public String getHandle()
    {
        if(handle == null) {
          try {
        handle = HandleManager.findHandle(this.ourContext, this);
      } catch (SQLException e) {
        // TODO Auto-generated catch block
        //e.printStackTrace();
      }
        }
      return handle;
    }

    /**
     * Get the value of a metadata field
     *
     * @param field
     *            the name of the metadata field to get
     *
     * @return the value of the metadata field
     *
     * @exception IllegalArgumentException
     *                if the requested metadata field doesn't exist
     */
    @Deprecated
    public String getMetadata(String field)
    {
        String[] MDValue = getMDValueByLegacyField(field);
        String value = getMetadataFirstValue(MDValue[0], MDValue[1], MDValue[2], Item.ANY);
        return value == null ? "" : value;
    }

    /**
     * Set a metadata value
     *
     * @param field
     *            the name of the metadata field to get
     * @param value
     *            value to set the field to
     *
     * @exception IllegalArgumentException
     *                if the requested metadata field doesn't exist
     * @exception MissingResourceException
     */
    @Deprecated
    public void setMetadata(String field, String value) throws MissingResourceException {
        if ((field.trim()).equals("name") && (value == null || value.trim().equals("")))
        {
            try
            {
                value = I18nUtil.getMessage("org.dspace.workflow.WorkflowManager.untitled");
            }
            catch (MissingResourceException e)
            {
                value = "Untitled";
            }
        }

        String[] MDValue = getMDValueByLegacyField(field);

        /*
         * Set metadata field to null if null
         * and trim strings to eliminate excess
         * whitespace.
         */
    if(value == null)
        {
            clearMetadata(MDValue[0], MDValue[1], MDValue[2], Item.ANY);
            modifiedMetadata = true;
        }
        else
        {
            setMetadataSingleValue(MDValue[0], MDValue[1], MDValue[2], null, value);
        }

        addDetails(field);
    }

    public String getName()
    {
        String value = getMetadataFirstValue(MetadataSchema.DC_SCHEMA, "title", null, Item.ANY);
        return value == null ? "" : value;
    }

    /**
     * Get the logo for the collection. <code>null</code> is returned if the
     * collection does not have a logo.
     *
     * @return the logo of the collection, or <code>null</code>
     */
    public Bitstream getLogo()
    {
        return logo;
    }

    /**
     * Give the collection a logo. Passing in <code>null</code> removes any
     * existing logo. You will need to set the format of the new logo bitstream
     * before it will work, for example to "JPEG". Note that
     * <code>update</code> will need to be called for the change to take
     * effect.  Setting a logo and not calling <code>update</code> later may
     * result in a previous logo lying around as an "orphaned" bitstream.
     *
     * @param  is the stream to use as the new logo
     *
     * @return   the new logo bitstream, or <code>null</code> if there is no
     *           logo (<code>null</code> was passed in)
     * @throws AuthorizeException
     * @throws IOException
     * @throws SQLException
     */
    public Bitstream setLogo(InputStream is) throws AuthorizeException,
            IOException, SQLException
    {
        // Check authorisation
        // authorized to remove the logo when DELETE rights
        // authorized when canEdit
        if (!((is == null) && AuthorizeManager.authorizeActionBoolean(
                ourContext, this, Constants.DELETE)))
        {
            canEdit(true);
        }

        // First, delete any existing logo
        if (!collectionRow.isColumnNull("logo_bitstream_id"))
        {
            logo.delete();
        }

        if (is == null)
        {
            collectionRow.setColumnNull("logo_bitstream_id");
            logo = null;

            log.info(LogManager.getHeader(ourContext, "remove_logo",
                    "collection_id=" + getID()));
        }
        else
        {
            Bitstream newLogo = Bitstream.create(ourContext, is);
            collectionRow.setColumn("logo_bitstream_id", newLogo.getID());
            logo = newLogo;

            // now create policy for logo bitstream
            // to match our READ policy
            List<ResourcePolicy> policies = AuthorizeManager.getPoliciesActionFilter(ourContext, this, Constants.READ);
            AuthorizeManager.addPolicies(ourContext, policies, newLogo);

            log.info(LogManager.getHeader(ourContext, "set_logo",
                    "collection_id=" + getID() + "logo_bitstream_id="
                            + newLogo.getID()));
        }

        modified = true;
        return logo;
    }

    /**
     * Create a workflow group for the given step if one does not already exist.
     * Returns either the newly created group or the previously existing one.
     * Note that while the new group is created in the database, the association
     * between the group and the collection is not written until
     * <code>update</code> is called.
     *
     * @param step
     *            the step (1-3) of the workflow to create or get the group for
     *
     * @return the workflow group associated with this collection
     * @throws SQLException
     * @throws AuthorizeException
     */
    public Group createWorkflowGroup(int step) throws SQLException,
            AuthorizeException
    {
        // Check authorisation - Must be an Admin to create Workflow Group
        AuthorizeUtil.authorizeManageWorkflowsGroup(ourContext, this);

        if (workflowGroup[step - 1] == null)
        {
            //turn off authorization so that Collection Admins can create Collection Workflow Groups
            ourContext.turnOffAuthorisationSystem();
            Group g = Group.create(ourContext);
            ourContext.restoreAuthSystemState();

            g.setName("COLLECTION_" + getID() + "_WORKFLOW_STEP_" + step);
            g.update();
            setWorkflowGroup(step, g);

            AuthorizeManager.addPolicy(ourContext, this, Constants.ADD, g);
        }

        return workflowGroup[step - 1];
    }

    /**
     * Set the workflow group corresponding to a particular workflow step.
     * <code>null</code> can be passed in if there should be no associated
     * group for that workflow step; any existing group is NOT deleted.
     *
     * @param step
     *            the workflow step (1-3)
     * @param g
     *            the new workflow group, or <code>null</code>
     */
    public void setWorkflowGroup(int step, Group g)
    {
        workflowGroup[step - 1] = g;

        if (g == null)
        {
            collectionRow.setColumnNull("workflow_step_" + step);
        }
        else
        {
            collectionRow.setColumn("workflow_step_" + step, g.getID());
        }
        modified = true;
    }

    /**
     * Get the the workflow group corresponding to a particular workflow step.
     * This returns <code>null</code> if there is no group associated with
     * this collection for the given step.
     *
     * @param step
     *            the workflow step (1-3)
     *
     * @return the group of reviewers or <code>null</code>
     */
    public Group getWorkflowGroup(int step)
    {
        return workflowGroup[step - 1];
    }

    /**
     * Create a default submitters group if one does not already exist. Returns
     * either the newly created group or the previously existing one. Note that
     * other groups may also be allowed to submit to this collection by the
     * authorization system.
     *
     * @return the default group of submitters associated with this collection
     * @throws SQLException
     * @throws AuthorizeException
     */
    public Group createSubmitters() throws SQLException, AuthorizeException
    {
        // Check authorisation - Must be an Admin to create Submitters Group
        AuthorizeUtil.authorizeManageSubmittersGroup(ourContext, this);

        if (submitters == null)
        {
            //turn off authorization so that Collection Admins can create Collection Submitters
            ourContext.turnOffAuthorisationSystem();
            submitters = Group.create(ourContext);
            ourContext.restoreAuthSystemState();

            submitters.setName("COLLECTION_" + getID() + "_SUBMIT");
            submitters.update();
        }

        // register this as the submitter group
        collectionRow.setColumn("submitter", submitters.getID());

        AuthorizeManager.addPolicy(ourContext, this, Constants.ADD, submitters);

        modified = true;
        return submitters;
    }

    /**
     * Remove the submitters group, if no group has already been created
     * then return without error. This will merely dereference the current
     * submitters group from the collection so that it may be deleted
     * without violating database constraints.
     */
    public void removeSubmitters() throws SQLException, AuthorizeException
    {
      // Check authorisation - Must be an Admin to delete Submitters Group
        AuthorizeUtil.authorizeManageSubmittersGroup(ourContext, this);

        // just return if there is no administrative group.
        if (submitters == null)
        {
            return;
        }

        // Remove the link to the collection table.
        collectionRow.setColumnNull("submitter");
        submitters = null;

        modified = true;
    }


    /**
     * Get the default group of submitters, if there is one. Note that the
     * authorization system may allow others to submit to the collection, so
     * this is not necessarily a definitive list of potential submitters.
     * <P>
     * The default group of submitters for collection 100 is the one called
     * <code>collection_100_submit</code>.
     *
     * @return the default group of submitters, or <code>null</code> if there
     *         is no default group.
     */
    public Group getSubmitters()
    {
        return submitters;
    }

    /**
     * Create a default administrators group if one does not already exist.
     * Returns either the newly created group or the previously existing one.
     * Note that other groups may also be administrators.
     *
     * @return the default group of editors associated with this collection
     * @throws SQLException
     * @throws AuthorizeException
     */
    public Group createAdministrators() throws SQLException, AuthorizeException
    {
        // Check authorisation - Must be an Admin to create more Admins
        AuthorizeUtil.authorizeManageAdminGroup(ourContext, this);

        if (admins == null)
        {
            //turn off authorization so that Community Admins can create Collection Admins
            ourContext.turnOffAuthorisationSystem();
            admins = Group.create(ourContext);
            ourContext.restoreAuthSystemState();

            admins.setName("COLLECTION_" + getID() + "_ADMIN");
            admins.update();
        }

        AuthorizeManager.addPolicy(ourContext, this,
                Constants.ADMIN, admins);

        // register this as the admin group
        collectionRow.setColumn("admin", admins.getID());

        modified = true;
        return admins;
    }

    /**
     * Remove the administrators group, if no group has already been created
     * then return without error. This will merely dereference the current
     * administrators group from the collection so that it may be deleted
     * without violating database constraints.
     */
    public void removeAdministrators() throws SQLException, AuthorizeException
    {
        // Check authorisation - Must be an Admin of the parent community to delete Admin Group
        AuthorizeUtil.authorizeRemoveAdminGroup(ourContext, this);

        // just return if there is no administrative group.
        if (admins == null)
        {
            return;
        }

        // Remove the link to the collection table.
        collectionRow.setColumnNull("admin");
        admins = null;

        modified = true;
    }

    /**
     * Get the default group of administrators, if there is one. Note that the
     * authorization system may allow others to be administrators for the
     * collection.
     * <P>
     * The default group of administrators for collection 100 is the one called
     * <code>collection_100_admin</code>.
     *
     * @return group of administrators, or <code>null</code> if there is no
     *         default group.
     */
    public Group getAdministrators()
    {
        return admins;
    }

    /**
     * Get the license that users must grant before submitting to this
     * collection. If the collection does not have a specific license, the
     * site-wide default is returned.
     *
     * @return the license for this collection
     */
    public String getLicense()
    {
        String license = getMetadata("license");

        if (license == null || license.trim().equals(""))
        {
            // Fallback to site-wide default
            license = LicenseManager.getDefaultSubmissionLicense();
        }

        return license;
    }

    /**
     * Get the license that users must grant before submitting to this
     * collection.
     *
     * @return the license for this collection
     */
    public String getLicenseCollection()
    {
        return getMetadata("license");
    }

    /**
     * Find out if the collection has a custom license
     *
     * @return <code>true</code> if the collection has a custom license
     */
    public boolean hasCustomLicense()
    {
        String license = getMetadata("license");

        return !( license == null || license.trim().equals("") );
    }

    /**
     * Set the license for this collection. Passing in <code>null</code> means
     * that the site-wide default will be used.
     *
     * @param license
     *            the license, or <code>null</code>
     */
    public void setLicense(String license) {
        setMetadata("license",license);
    }

    /**
     * Get the template item for this collection. <code>null</code> is
     * returned if the collection does not have a template. Submission
     * mechanisms may copy this template to provide a convenient starting point
     * for a submission.
     *
     * @return the item template, or <code>null</code>
     */
    public Item getTemplateItem() throws SQLException
    {
        return template;
    }

    /**
     * Create an empty template item for this collection. If one already exists,
     * no action is taken. Caution: Make sure you call <code>update</code> on
     * the collection after doing this, or the item will have been created but
     * the collection record will not refer to it.
     *
     * @throws SQLException
     * @throws AuthorizeException
     */
    public void createTemplateItem() throws SQLException, AuthorizeException
    {
        // Check authorisation
        AuthorizeUtil.authorizeManageTemplateItem(ourContext, this);

        if (template == null)
        {
            template = Item.create(ourContext);
            collectionRow.setColumn("template_item_id", template.getID());

            log.info(LogManager.getHeader(ourContext, "create_template_item",
                    "collection_id=" + getID() + ",template_item_id="
                            + template.getID()));
        }
        modified = true;
    }

    /**
     * Remove the template item for this collection, if there is one. Note that
     * since this has to remove the old template item ID from the collection
     * record in the database, the collection record will be changed, including
     * any other changes made; in other words, this method does an
     * <code>update</code>.
     *
     * @throws SQLException
     * @throws AuthorizeException
     * @throws IOException
     */
    public void removeTemplateItem() throws SQLException, AuthorizeException,
            IOException
    {
        // Check authorisation
        AuthorizeUtil.authorizeManageTemplateItem(ourContext, this);

        collectionRow.setColumnNull("template_item_id");
        DatabaseManager.update(ourContext, collectionRow);

        if (template != null)
        {
            log.info(LogManager.getHeader(ourContext, "remove_template_item",
                    "collection_id=" + getID() + ",template_item_id="
                            + template.getID()));
            // temporarily turn off auth system, we have already checked the permission on the top of the method
            // check it again will fail because we have already broken the relation between the collection and the item
            ourContext.turnOffAuthorisationSystem();
            template.delete();
            ourContext.restoreAuthSystemState();
            template = null;
        }

        ourContext.addEvent(new Event(Event.MODIFY, Constants.COLLECTION,
                getID(), "remove_template_item", getIdentifiers(ourContext)));
    }

    /**
     * Add an item to the collection. This simply adds a relationship between
     * the item and the collection - it does nothing like set an issue date,
     * remove a personal workspace item etc. This has instant effect;
     * <code>update</code> need not be called.
     *
     * @param item
     *            item to add
     * @throws SQLException
     * @throws AuthorizeException
     */
    public void addItem(Item item) throws SQLException, AuthorizeException
    {
        // Check authorisation
        AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD);

        log.info(LogManager.getHeader(ourContext, "add_item", "collection_id="
                + getID() + ",item_id=" + item.getID()));

        // Create mapping
        TableRow row = DatabaseManager.row("collection2item");

        row.setColumn("collection_id", getID());
        row.setColumn("item_id", item.getID());

        DatabaseManager.insert(ourContext, row);

        ourContext.addEvent(new Event(Event.ADD, Constants.COLLECTION, getID(),
                Constants.ITEM, item.getID(), item.getHandle(),
                getIdentifiers(ourContext)));
    }

    /**
     * Remove an item. If the item is then orphaned, it is deleted.
     *
     * @param item
     *            item to remove
     * @throws SQLException
     * @throws AuthorizeException
     * @throws IOException
     */
    public void removeItem(Item item) throws SQLException, AuthorizeException,
            IOException
    {
        // Check authorisation
        AuthorizeManager.authorizeAction(ourContext, this, Constants.REMOVE);

        // will the item be an orphan? is it in other collections?
        TableRow row = DatabaseManager.querySingle(ourContext,
                "SELECT COUNT(DISTINCT collection_id) AS num FROM collection2item WHERE item_id= ? ",
                item.getID());
        boolean orphan = (row.getLongColumn("num") == 1);

        log.info(LogManager.getHeader(ourContext, "remove_item",
                "collection_id=" + getID() + ",item_id=" + item.getID()));

        // First, remove its association with this collection
        DatabaseManager.updateQuery(ourContext,
                "DELETE FROM collection2item WHERE collection_id= ? "+
                "AND item_id= ? ",
                getID(), item.getID());

        // Then, if it is an orphaned Item, delete it
        if (orphan)
        {
            item.delete();
        }

        ourContext.addEvent(new Event(Event.REMOVE, Constants.COLLECTION,
                getID(), Constants.ITEM, item.getID(), item.getHandle(),
                getIdentifiers(ourContext)));
    }

    /**
     * Update the collection metadata (including logo and workflow groups) to
     * the database. Inserts if this is a new collection.
     *
     * @throws SQLException
     * @throws IOException
     * @throws AuthorizeException
     */
    public void update() throws SQLException, AuthorizeException
    {
        // Check authorisation
        canEdit(true);

        log.info(LogManager.getHeader(ourContext, "update_collection",
                "collection_id=" + getID()));

        DatabaseManager.update(ourContext, collectionRow);

        if (modified)
        {
            ourContext.addEvent(new Event(Event.MODIFY, Constants.COLLECTION,
                    getID(), null, getIdentifiers(ourContext)));
            modified = false;
        }
        if (modifiedMetadata)
        {
            updateMetadata();
            clearDetails();
        }
    }

    public boolean canEditBoolean() throws java.sql.SQLException
    {
        return canEditBoolean(true);
    }

    public boolean canEditBoolean(boolean useInheritance) throws java.sql.SQLException
    {
        try
        {
            canEdit(useInheritance);

            return true;
        }
        catch (AuthorizeException e)
        {
            return false;
        }
    }

    public void canEdit()  throws AuthorizeException, SQLException
    {
        canEdit(true);
    }

    public void canEdit(boolean useInheritance) throws AuthorizeException, SQLException
    {
        Community[] parents = getCommunities();

        for (int i = 0; i < parents.length; i++)
        {
            if (AuthorizeManager.authorizeActionBoolean(ourContext, parents[i],
                    Constants.WRITE, useInheritance))
            {
                return;
            }

            if (AuthorizeManager.authorizeActionBoolean(ourContext, parents[i],
                    Constants.ADD, useInheritance))
            {
                return;
            }
        }

        AuthorizeManager.authorizeAction(ourContext, this, Constants.WRITE, useInheritance);
    }

    /**
     * Delete the collection, including the metadata and logo. Items that are
     * then orphans are deleted. Groups associated with this collection
     * (workflow participants and submitters) are NOT deleted.
     *
     * @throws SQLException
     * @throws AuthorizeException
     * @throws IOException
     */
    void delete() throws SQLException, AuthorizeException, IOException
    {
        log.info(LogManager.getHeader(ourContext, "delete_collection",
                "collection_id=" + getID()));

        ourContext.addEvent(new Event(Event.DELETE, Constants.COLLECTION,
                getID(), getHandle(), getIdentifiers(ourContext)));

        // Remove from cache
        ourContext.removeCached(this, getID());

        // remove subscriptions - hmm, should this be in Subscription.java?
        DatabaseManager.updateQuery(ourContext,
                "DELETE FROM subscription WHERE collection_id= ? ",
                getID());

        // Remove Template Item
        removeTemplateItem();

        // Remove items
        ItemIterator items = getAllItems();

        try
        {
          while (items.hasNext())
          {
            Item item = items.next();
            IndexBrowse ib = new IndexBrowse(ourContext);

            if (item.isOwningCollection(this))
            {
              // the collection to be deleted is the owning collection, thus remove
              // the item from all collections it belongs to
              Collection[] collections = item.getCollections();
              for (int i=0; i< collections.length; i++)
              {
                //notify Browse of removing item.
                ib.itemRemoved(item);
                // Browse.itemRemoved(ourContext, itemId);
                collections[i].removeItem(item);
              }

            }
            // the item was only mapped to this collection, so just remove it
            else
            {
              //notify Browse of removing item mapping.
              ib.indexItem(item);
              // Browse.itemChanged(ourContext, item);
              removeItem(item);
            }
          }
        }
        catch (BrowseException e)
        {
          log.error("caught exception: ", e);
          throw new IOException(e.getMessage(), e);
        }
        finally
        {
            if (items != null)
            {
                items.close();
            }
        }

        // Delete bitstream logo
        setLogo(null);

        // Remove all authorization policies
        AuthorizeManager.removeAllPolicies(ourContext, this);

        if(ConfigurationManager.getProperty("workflow","workflow.framework").equals("xmlworkflow")){
            // Remove any xml_WorkflowItems
            XmlWorkflowItem[] xmlWfarray = XmlWorkflowItem
                    .findByCollection(ourContext, this);

            for (XmlWorkflowItem aXmlWfarray : xmlWfarray) {
                // remove the workflowitem first, then the item
                Item myItem = aXmlWfarray.getItem();
                aXmlWfarray.deleteWrapper();
                myItem.delete();
            }
        }else{
            // Remove any WorkflowItems
            WorkflowItem[] wfarray = WorkflowItem
                    .findByCollection(ourContext, this);

            for (WorkflowItem aWfarray : wfarray) {
                // remove the workflowitem first, then the item
                Item myItem = aWfarray.getItem();
                aWfarray.deleteWrapper();
                myItem.delete();
            }
        }



        // Remove any WorkspaceItems
        WorkspaceItem[] wsarray = WorkspaceItem.findByCollection(ourContext,
                this);

        for (WorkspaceItem aWsarray : wsarray) {
            aWsarray.deleteAll();
        }

        //  get rid of the content count cache if it exists
        try
        {
          ItemCounter ic = new ItemCounter(ourContext);
          ic.remove(this);
        }
        catch (ItemCountException e)
        {
          // FIXME: upside down exception handling due to lack of good
          // exception framework
          throw new IllegalStateException(e.getMessage(), e);
        }

        // Remove any Handle
        HandleManager.unbindHandle(ourContext, this);

        if(ConfigurationManager.getProperty("workflow","workflow.framework").equals("xmlworkflow")){
            // delete all CollectionRoles for this Collection
            for (CollectionRole collectionRole : CollectionRole.findByCollection(ourContext, this.getID())) {
                collectionRole.delete();
            }
        }

        // Delete collection row
        DatabaseManager.delete(ourContext, collectionRow);

        // Remove any workflow groups - must happen after deleting collection
        Group g = null;

        g = getWorkflowGroup(1);

        if (g != null)
        {
            g.delete();
        }

        g = getWorkflowGroup(2);

        if (g != null)
        {
            g.delete();
        }

        g = getWorkflowGroup(3);

        if (g != null)
        {
            g.delete();
        }

        // Remove default administrators group
        g = getAdministrators();

        if (g != null)
        {
            g.delete();
        }

        // Remove default submitters group
        g = getSubmitters();

        if (g != null)
        {
            g.delete();
        }

        removeMetadataFromDatabase();
    }

    /**
     * Get the communities this collection appears in
     *
     * @return array of <code>Community</code> objects
     * @throws SQLException
     */
    public Community[] getCommunities() throws SQLException
    {
        // Get the bundle table rows
        TableRowIterator tri = DatabaseManager.queryTable(ourContext,"community",
                        "SELECT community.* FROM community, community2collection WHERE " +
                        "community.community_id=community2collection.community_id " +
                        "AND community2collection.collection_id= ? ",
                        getID());

        // Build a list of Community objects
        List<Community> communities = new ArrayList<Community>();

        try
        {
            while (tri.hasNext())
            {
                TableRow row = tri.next();

                // First check the cache
                Community owner = (Community) ourContext.fromCache(Community.class,
                        row.getIntColumn("community_id"));

                if (owner == null)
                {
                    owner = new Community(ourContext, row);
                }

                communities.add(owner);

                // now add any parent communities
                Community[] parents = owner.getAllParents();
                communities.addAll(Arrays.asList(parents));
            }
        }
        finally
        {
            // close the TableRowIterator to free up resources
            if (tri != null)
            {
                tri.close();
            }
        }

        Community[] communityArray = new Community[communities.size()];
        communityArray = (Community[]) communities.toArray(communityArray);

        return communityArray;
    }

    /**
     * Return <code>true</code> if <code>other</code> is the same Collection
     * as this object, <code>false</code> otherwise
     *
     * @param other
     *            object to compare to
     *
     * @return <code>true</code> if object passed in represents the same
     *         collection as this object
     */
     @Override
     public boolean equals(Object other)
     {
         if (other == null)
         {
             return false;
         }
         if (getClass() != other.getClass())
         {
             return false;
         }
         final Collection otherCollection = (Collection) other;
         if (this.getID() != otherCollection.getID())
         {
             return false;
         }

         return true;
     }

     @Override
     public int hashCode()
     {
         int hash = 7;
         hash = 89 * hash + (this.collectionRow != null ? this.collectionRow.hashCode() : 0);
         return hash;
     }


    /**
     * Utility method for reading in a group from a group ID in a column. If the
     * column is null, null is returned.
     *
     * @param col
     *            the column name to read
     * @return the group referred to by that column, or null
     * @throws SQLException
     */
    private Group groupFromColumn(String col) throws SQLException
    {
        if (collectionRow.isColumnNull(col))
        {
            return null;
        }

        return Group.find(ourContext, collectionRow.getIntColumn(col));
    }

    /**
     * return type found in Constants
     *
     * @return int Constants.COLLECTION
     */
    public int getType()
    {
        return Constants.COLLECTION;
    }

    /**
     * return an array of collections that user has a given permission on
     * (useful for trimming 'select to collection' list) or figuring out which
     * collections a person is an editor for.
     *
     * @param context
     * @param comm
     *            (optional) restrict search to a community, else null
     * @param actionID
     *            of the action
     *
     * @return Collection [] of collections with matching permissions
     * @throws SQLException
     */
    public static Collection[] findAuthorized(Context context, Community comm,
            int actionID) throws java.sql.SQLException
    {
        List<Collection> myResults = new ArrayList<Collection>();

        Collection[] myCollections = null;

        if (comm != null)
        {
            myCollections = comm.getCollections();
        }
        else
        {
            myCollections = Collection.findAll(context);
        }

        // now build a list of collections you have authorization for
        for (int i = 0; i < myCollections.length; i++)
        {
            if (AuthorizeManager.authorizeActionBoolean(context,
                    myCollections[i], actionID))
            {
                myResults.add(myCollections[i]);
            }
        }

        myCollections = new Collection[myResults.size()];
        myCollections = (Collection[]) myResults.toArray(myCollections);

        return myCollections;
    }

    public static Collection[] findAuthorizedOptimized(Context context, int actionID) throws java.sql.SQLException
    {
        if(! ConfigurationManager.getBooleanProperty("org.dspace.content.Collection.findAuthorizedPerformanceOptimize", true)) {
            // Fallback to legacy query if config says so. The rationale could be that a site found a bug.
            return findAuthorized(context, null, actionID);
        }

        List<Collection> myResults = new ArrayList<Collection>();

        if(AuthorizeManager.isAdmin(context))
        {
            return findAll(context);
        }

        //Check eperson->policy
        Collection[] directToCollection = findDirectMapped(context, actionID);
        for (int i = 0; i< directToCollection.length; i++)
        {
            if(!myResults.contains(directToCollection[i]))
            {
                myResults.add(directToCollection[i]);
            }
        }

        //Check eperson->groups->policy
        Collection[] groupToCollection = findGroupMapped(context, actionID);

        for (int i = 0; i< groupToCollection.length; i++)
        {
            if(!myResults.contains(groupToCollection[i]))
            {
                myResults.add(groupToCollection[i]);
            }
        }

        //Check eperson->groups->groups->policy->collection
        //i.e. Malcolm Litchfield is a member of OSU_Press_Embargo,
        // which is a member of: COLLECTION_24_ADMIN, COLLECTION_24_SUBMIT
        Collection[] group2GroupToCollection = findGroup2GroupMapped(context, actionID);

        for (int i = 0; i< group2GroupToCollection.length; i++)
        {
            if(!myResults.contains(group2GroupToCollection[i]))
            {
                myResults.add(group2GroupToCollection[i]);
            }
        }

        //TODO Check eperson->groups->groups->policy->community


        //TODO Check eperson->groups->policy->community
        // i.e. Typical Community Admin -- name.# > COMMUNITY_10_ADMIN > Ohio State University Press

        //Check eperson->comm-admin
        Collection[] group2commCollections = findGroup2CommunityMapped(context);
        for (int i = 0; i< group2commCollections.length; i++)
        {
            if(!myResults.contains(group2commCollections[i]))
            {
                myResults.add(group2commCollections[i]);
            }
        }


        // Return the collections, sorted alphabetically
        Collections.sort(myResults, new CollectionComparator());

        Collection[] myCollections = new Collection[myResults.size()];
        myCollections = (Collection[]) myResults.toArray(myCollections);

        return myCollections;


    }

  /**
     * counts items in this collection
     *
     * @return  total items
     */
     public int countItems()
        throws SQLException
     {
         int itemcount = 0;
         PreparedStatement statement = null;
         ResultSet rs = null;

         try
         {
             String query = "SELECT count(*) FROM collection2item, item WHERE "
                    + "collection2item.collection_id =  ? "
                    + "AND collection2item.item_id = item.item_id "
                    + "AND in_archive ='1' AND item.withdrawn='0' ";

            statement = ourContext.getDBConnection().prepareStatement(query);
            statement.setInt(1,getID());

            rs = statement.executeQuery();
            if (rs != null)
            {
                rs.next();
                itemcount = rs.getInt(1);
            }
         }
         finally
         {
             if (rs != null)
             {
                 try { rs.close(); } catch (SQLException sqle) { }
             }

             if (statement != null)
             {
                 try { statement.close(); } catch (SQLException sqle) { }
             }
         }

        return itemcount;
     }

    public DSpaceObject getAdminObject(int action) throws SQLException
    {
        DSpaceObject adminObject = null;
        Community community = null;
        Community[] communities = getCommunities();
        if (communities != null && communities.length > 0)
        {
            community = communities[0];
        }

        switch (action)
        {
        case Constants.REMOVE:
            if (AuthorizeConfiguration.canCollectionAdminPerformItemDeletion())
            {
                adminObject = this;
            }
            else if (AuthorizeConfiguration.canCommunityAdminPerformItemDeletion())
            {
                adminObject = community;
            }
            break;

        case Constants.DELETE:
            if (AuthorizeConfiguration.canCommunityAdminPerformSubelementDeletion())
            {
                adminObject = community;
            }
            break;
        default:
            adminObject = this;
            break;
        }
        return adminObject;
    }

    @Override
    public DSpaceObject getParentObject() throws SQLException
    {
        Community[] communities = this.getCommunities();
        if (communities != null && (communities.length > 0 && communities[0] != null))
        {
            return communities[0];
        }
        else
        {
            return null;
        }
    }

    @Override
    public void updateLastModified()
    {
        //Also fire a modified event since the collection HAS been modified
        ourContext.addEvent(new Event(Event.MODIFY, Constants.COLLECTION,
                getID(), null, getIdentifiers(ourContext)));
    }

    //TODO replace hard-coded action_id's with constants...
    public static Collection[] findDirectMapped(Context context, int actionID) throws java.sql.SQLException
    {
        //eperson_id -> resourcepolicy.eperson_id
        TableRowIterator tri = DatabaseManager.query(context,
                "SELECT * FROM collection, resourcepolicy, eperson " +
                        "WHERE resourcepolicy.resource_id = collection.collection_id AND " +
                        "eperson.eperson_id = resourcepolicy.eperson_id AND "+
                        "resourcepolicy.resource_type_id = 3 AND "+
                        "( resourcepolicy.action_id = 3 OR resourcepolicy.action_id = 11 ) AND "+
                        "eperson.eperson_id = ?", context.getCurrentUser().getID());
        return produceCollectionsFromQuery(context, tri);
    }

    public static Collection[] findGroupMapped(Context context, int actionID) throws java.sql.SQLException
    {
        //eperson_id -> resourcepolicy.eperson_id
        TableRowIterator tri = DatabaseManager.query(context,
                "SELECT * FROM collection, resourcepolicy, eperson, epersongroup2eperson " +
                        "WHERE resourcepolicy.resource_id = collection.collection_id AND "+
                        "eperson.eperson_id = epersongroup2eperson.eperson_id AND "+
                        "epersongroup2eperson.eperson_group_id = resourcepolicy.epersongroup_id AND "+
                        "resourcepolicy.resource_type_id = 3 AND "+
                        "( resourcepolicy.action_id = 3 OR resourcepolicy.action_id = 11 ) AND "+
                        "eperson.eperson_id = ?", context.getCurrentUser().getID());
        return produceCollectionsFromQuery(context, tri);
    }

    public static Collection[] findGroup2GroupMapped(Context context, int actionID) throws SQLException {
        TableRowIterator tri = DatabaseManager.query(context,
                "SELECT \n" +
                        "  * \n" +
                        "FROM \n" +
                        "  public.eperson, \n" +
                        "  public.epersongroup2eperson, \n" +
                        "  public.epersongroup, \n" +
                        "  public.group2group, \n" +
                        "  public.resourcepolicy rp_parent, \n" +
                        "  public.collection\n" +
                        "WHERE \n" +
                        "  epersongroup2eperson.eperson_id = eperson.eperson_id AND\n" +
                        "  epersongroup.eperson_group_id = epersongroup2eperson.eperson_group_id AND\n" +
                        "  group2group.child_id = epersongroup.eperson_group_id AND\n" +
                        "  rp_parent.epersongroup_id = group2group.parent_id AND\n" +
                        "  collection.collection_id = rp_parent.resource_id AND\n" +
                        "  eperson.eperson_id = ? AND \n" +
                        "  (rp_parent.action_id = 3 OR \n" +
                        "  rp_parent.action_id = 11  \n" +
                        "  )  AND rp_parent.resource_type_id = 3;", context.getCurrentUser().getID());
        return produceCollectionsFromQuery(context, tri);
    }

    public static Collection[] findGroup2CommunityMapped(Context context) throws SQLException {
        TableRowIterator tri = DatabaseManager.query(context,
                "SELECT \n" +
                        "  * \n" +
                        "FROM \n" +
                        "  public.eperson, \n" +
                        "  public.epersongroup2eperson, \n" +
                        "  public.epersongroup, \n" +
                        "  public.community, \n" +
                        "  public.resourcepolicy\n" +
                        "WHERE \n" +
                        "  epersongroup2eperson.eperson_id = eperson.eperson_id AND\n" +
                        "  epersongroup.eperson_group_id = epersongroup2eperson.eperson_group_id AND\n" +
                        "  resourcepolicy.epersongroup_id = epersongroup.eperson_group_id AND\n" +
                        "  resourcepolicy.resource_id = community.community_id AND\n" +
                        " ( resourcepolicy.action_id = 3 OR \n" +
                        "  resourcepolicy.action_id = 11) AND \n" +
                        "  resourcepolicy.resource_type_id = 4 AND eperson.eperson_id = ?", context.getCurrentUser().getID());

        return produceCollectionsFromCommunityQuery(context, tri);
    }

    public static class CollectionComparator implements Comparator<Collection> {
        @Override
        public int compare(Collection collection1, Collection collection2) {
            return collection1.getName().compareTo(collection2.getName());
        }
    }

    public static Collection[] produceCollectionsFromQuery(Context context, TableRowIterator tri) throws SQLException {
        List<Collection> collections = new ArrayList<Collection>();

        while(tri.hasNext()) {
            TableRow row = tri.next();
            Collection collection = Collection.find(context, row.getIntColumn("collection_id"));
            collections.add(collection);
        }

        return collections.toArray(new Collection[0]);
    }

    public static Collection[] produceCollectionsFromCommunityQuery(Context context, TableRowIterator tri) throws SQLException {
        List<Collection> collections = new ArrayList<Collection>();

        while(tri.hasNext()) {
            TableRow commRow = tri.next();
            Community community = Community.find(context, commRow.getIntColumn("community_id"));

            Collection[] comCollections = community.getCollections();
            for(Collection collection : comCollections) {
                collections.add(collection);
            }

            //ugh, handle that communities has subcommunities...
            //TODO  community.getAllCollections();

        }
        return collections.toArray(new Collection[0]);
    }
}
TOP

Related Classes of org.dspace.content.Collection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.