0001: /*
0002: * Collection.java
0003: *
0004: * Version: $Revision: 2339 $
0005: *
0006: * Date: $Date: 2007-11-12 17:39:13 -0600 (Mon, 12 Nov 2007) $
0007: *
0008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
0009: * Institute of Technology. All rights reserved.
0010: *
0011: * Redistribution and use in source and binary forms, with or without
0012: * modification, are permitted provided that the following conditions are
0013: * met:
0014: *
0015: * - Redistributions of source code must retain the above copyright
0016: * notice, this list of conditions and the following disclaimer.
0017: *
0018: * - Redistributions in binary form must reproduce the above copyright
0019: * notice, this list of conditions and the following disclaimer in the
0020: * documentation and/or other materials provided with the distribution.
0021: *
0022: * - Neither the name of the Hewlett-Packard Company nor the name of the
0023: * Massachusetts Institute of Technology nor the names of their
0024: * contributors may be used to endorse or promote products derived from
0025: * this software without specific prior written permission.
0026: *
0027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
0032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
0033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
0034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
0036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
0037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
0038: * DAMAGE.
0039: */
0040: package org.dspace.content;
0041:
0042: import java.io.IOException;
0043: import java.io.InputStream;
0044: import java.sql.PreparedStatement;
0045: import java.sql.ResultSet;
0046: import java.sql.SQLException;
0047: import java.util.ArrayList;
0048: import java.util.List;
0049: import java.util.MissingResourceException;
0050:
0051: import org.apache.log4j.Logger;
0052: import org.dspace.authorize.AuthorizeException;
0053: import org.dspace.authorize.AuthorizeManager;
0054: import org.dspace.authorize.ResourcePolicy;
0055: import org.dspace.browse.BrowseException;
0056: import org.dspace.browse.IndexBrowse;
0057: import org.dspace.browse.ItemCounter;
0058: import org.dspace.browse.ItemCountException;
0059: import org.dspace.core.ConfigurationManager;
0060: import org.dspace.core.Constants;
0061: import org.dspace.core.Context;
0062: import org.dspace.core.I18nUtil;
0063: import org.dspace.core.LogManager;
0064: import org.dspace.eperson.Group;
0065: import org.dspace.event.Event;
0066: import org.dspace.handle.HandleManager;
0067: import org.dspace.storage.rdbms.DatabaseManager;
0068: import org.dspace.storage.rdbms.TableRow;
0069: import org.dspace.storage.rdbms.TableRowIterator;
0070: import org.dspace.workflow.WorkflowItem;
0071:
0072: /**
0073: * Class representing a collection.
0074: * <P>
0075: * The collection's metadata (name, introductory text etc), workflow groups, and
0076: * default group of submitters are loaded into memory. Changes to metadata are
0077: * not written to the database until <code>update</code> is called. If you
0078: * create or remove a workflow group, the change is only reflected in the
0079: * database after calling <code>update</code>. The default group of
0080: * submitters is slightly different - creating or removing this has instant
0081: * effect.
0082: *
0083: * @author Robert Tansley
0084: * @version $Revision: 2339 $
0085: */
0086: public class Collection extends DSpaceObject {
0087: /** log4j category */
0088: private static Logger log = Logger.getLogger(Collection.class);
0089:
0090: /** Our context */
0091: private Context ourContext;
0092:
0093: /** The table row corresponding to this item */
0094: private TableRow collectionRow;
0095:
0096: /** The logo bitstream */
0097: private Bitstream logo;
0098:
0099: /** The item template */
0100: private Item template;
0101:
0102: /** Our Handle */
0103: private String handle;
0104:
0105: /** Flag set when data is modified, for events */
0106: private boolean modified;
0107:
0108: /** Flag set when metadata is modified, for events */
0109: private boolean modifiedMetadata;
0110:
0111: /**
0112: * Groups corresponding to workflow steps - NOTE these start from one, so
0113: * workflowGroups[0] corresponds to workflow_step_1.
0114: */
0115: private Group[] workflowGroup;
0116:
0117: /** The default group of submitters */
0118: private Group submitters;
0119:
0120: /** The default group of administrators */
0121: private Group admins;
0122:
0123: /**
0124: * Construct a collection with the given table row
0125: *
0126: * @param context
0127: * the context this object exists in
0128: * @param row
0129: * the corresponding row in the table
0130: * @throws SQLException
0131: */
0132: Collection(Context context, TableRow row) throws SQLException {
0133: ourContext = context;
0134: collectionRow = row;
0135:
0136: // Get the logo bitstream
0137: if (collectionRow.isColumnNull("logo_bitstream_id")) {
0138: logo = null;
0139: } else {
0140: logo = Bitstream.find(ourContext, collectionRow
0141: .getIntColumn("logo_bitstream_id"));
0142: }
0143:
0144: // Get the template item
0145: if (collectionRow.isColumnNull("template_item_id")) {
0146: template = null;
0147: } else {
0148: template = Item.find(ourContext, collectionRow
0149: .getIntColumn("template_item_id"));
0150: }
0151:
0152: // Get the relevant groups
0153: workflowGroup = new Group[3];
0154:
0155: workflowGroup[0] = groupFromColumn("workflow_step_1");
0156: workflowGroup[1] = groupFromColumn("workflow_step_2");
0157: workflowGroup[2] = groupFromColumn("workflow_step_3");
0158:
0159: submitters = groupFromColumn("submitter");
0160: admins = groupFromColumn("admin");
0161:
0162: // Get our Handle if any
0163: handle = HandleManager.findHandle(context, this );
0164:
0165: // Cache ourselves
0166: context.cache(this , row.getIntColumn("collection_id"));
0167:
0168: modified = modifiedMetadata = false;
0169: clearDetails();
0170: }
0171:
0172: /**
0173: * Get a collection from the database. Loads in the metadata
0174: *
0175: * @param context
0176: * DSpace context object
0177: * @param id
0178: * ID of the collection
0179: *
0180: * @return the collection, or null if the ID is invalid.
0181: * @throws SQLException
0182: */
0183: public static Collection find(Context context, int id)
0184: throws SQLException {
0185: // First check the cache
0186: Collection fromCache = (Collection) context.fromCache(
0187: Collection.class, id);
0188:
0189: if (fromCache != null) {
0190: return fromCache;
0191: }
0192:
0193: TableRow row = DatabaseManager.find(context, "collection", id);
0194:
0195: if (row == null) {
0196: if (log.isDebugEnabled()) {
0197: log.debug(LogManager.getHeader(context,
0198: "find_collection", "not_found,collection_id="
0199: + id));
0200: }
0201:
0202: return null;
0203: }
0204:
0205: // not null, return Collection
0206: if (log.isDebugEnabled()) {
0207: log.debug(LogManager.getHeader(context, "find_collection",
0208: "collection_id=" + id));
0209: }
0210:
0211: return new Collection(context, row);
0212: }
0213:
0214: /**
0215: * Create a new collection, with a new ID. This method is not public, and
0216: * does not check authorisation.
0217: *
0218: * @param context
0219: * DSpace context object
0220: *
0221: * @return the newly created collection
0222: * @throws SQLException
0223: * @throws AuthorizeException
0224: */
0225: static Collection create(Context context) throws SQLException,
0226: AuthorizeException {
0227: TableRow row = DatabaseManager.create(context, "collection");
0228: Collection c = new Collection(context, row);
0229: c.handle = HandleManager.createHandle(context, c);
0230:
0231: // create the default authorization policy for collections
0232: // of 'anonymous' READ
0233: Group anonymousGroup = Group.find(context, 0);
0234:
0235: ResourcePolicy myPolicy = ResourcePolicy.create(context);
0236: myPolicy.setResource(c);
0237: myPolicy.setAction(Constants.READ);
0238: myPolicy.setGroup(anonymousGroup);
0239: myPolicy.update();
0240:
0241: // now create the default policies for submitted items
0242: myPolicy = ResourcePolicy.create(context);
0243: myPolicy.setResource(c);
0244: myPolicy.setAction(Constants.DEFAULT_ITEM_READ);
0245: myPolicy.setGroup(anonymousGroup);
0246: myPolicy.update();
0247:
0248: myPolicy = ResourcePolicy.create(context);
0249: myPolicy.setResource(c);
0250: myPolicy.setAction(Constants.DEFAULT_BITSTREAM_READ);
0251: myPolicy.setGroup(anonymousGroup);
0252: myPolicy.update();
0253:
0254: context.addEvent(new Event(Event.CREATE, Constants.COLLECTION,
0255: c.getID(), c.handle));
0256:
0257: log.info(LogManager.getHeader(context, "create_collection",
0258: "collection_id=" + row.getIntColumn("collection_id"))
0259: + ",handle=" + c.handle);
0260:
0261: return c;
0262: }
0263:
0264: /**
0265: * Get all collections in the system. These are alphabetically sorted by
0266: * collection name.
0267: *
0268: * @param context
0269: * DSpace context object
0270: *
0271: * @return the collections in the system
0272: * @throws SQLException
0273: */
0274: public static Collection[] findAll(Context context)
0275: throws SQLException {
0276: TableRowIterator tri = DatabaseManager.queryTable(context,
0277: "collection", "SELECT * FROM collection ORDER BY name");
0278:
0279: List<Collection> collections = new ArrayList<Collection>();
0280:
0281: while (tri.hasNext()) {
0282: TableRow row = tri.next();
0283:
0284: // First check the cache
0285: Collection fromCache = (Collection) context
0286: .fromCache(Collection.class, row
0287: .getIntColumn("collection_id"));
0288:
0289: if (fromCache != null) {
0290: collections.add(fromCache);
0291: } else {
0292: collections.add(new Collection(context, row));
0293: }
0294: }
0295: // close the TableRowIterator to free up resources
0296: tri.close();
0297:
0298: Collection[] collectionArray = new Collection[collections
0299: .size()];
0300: collectionArray = (Collection[]) collections
0301: .toArray(collectionArray);
0302:
0303: return collectionArray;
0304: }
0305:
0306: /**
0307: * Get the in_archive items in this collection. The order is indeterminate.
0308: *
0309: * @return an iterator over the items in the collection.
0310: * @throws SQLException
0311: */
0312: public ItemIterator getItems() throws SQLException {
0313: String myQuery = "SELECT item.* FROM item, collection2item WHERE "
0314: + "item.item_id=collection2item.item_id AND "
0315: + "collection2item.collection_id= ? "
0316: + "AND item.in_archive='1'";
0317:
0318: TableRowIterator rows = DatabaseManager.queryTable(ourContext,
0319: "item", myQuery, getID());
0320:
0321: return new ItemIterator(ourContext, rows);
0322: }
0323:
0324: /**
0325: * Get all the items in this collection. The order is indeterminate.
0326: *
0327: * @return an iterator over the items in the collection.
0328: * @throws SQLException
0329: */
0330: public ItemIterator getAllItems() throws SQLException {
0331: String myQuery = "SELECT item.* FROM item, collection2item WHERE "
0332: + "item.item_id=collection2item.item_id AND "
0333: + "collection2item.collection_id= ? ";
0334:
0335: TableRowIterator rows = DatabaseManager.queryTable(ourContext,
0336: "item", myQuery, getID());
0337:
0338: return new ItemIterator(ourContext, rows);
0339: }
0340:
0341: /**
0342: * Get the internal ID of this collection
0343: *
0344: * @return the internal identifier
0345: */
0346: public int getID() {
0347: return collectionRow.getIntColumn("collection_id");
0348: }
0349:
0350: /**
0351: * @see org.dspace.content.DSpaceObject#getHandle()
0352: */
0353: public String getHandle() {
0354: if (handle == null) {
0355: try {
0356: handle = HandleManager
0357: .findHandle(this .ourContext, this );
0358: } catch (SQLException e) {
0359: // TODO Auto-generated catch block
0360: //e.printStackTrace();
0361: }
0362: }
0363: return handle;
0364: }
0365:
0366: /**
0367: * Get the value of a metadata field
0368: *
0369: * @param field
0370: * the name of the metadata field to get
0371: *
0372: * @return the value of the metadata field
0373: *
0374: * @exception IllegalArgumentException
0375: * if the requested metadata field doesn't exist
0376: */
0377: public String getMetadata(String field) {
0378: String metadata = collectionRow.getStringColumn(field);
0379: return (metadata == null) ? "" : metadata;
0380: }
0381:
0382: /**
0383: * Set a metadata value
0384: *
0385: * @param field
0386: * the name of the metadata field to get
0387: * @param value
0388: * value to set the field to
0389: *
0390: * @exception IllegalArgumentException
0391: * if the requested metadata field doesn't exist
0392: * @exception MissingResourceException
0393: */
0394: public void setMetadata(String field, String value)
0395: throws MissingResourceException {
0396: if ((field.trim()).equals("name") && (value.trim()).equals("")) {
0397: try {
0398: value = I18nUtil
0399: .getMessage("org.dspace.workflow.WorkflowManager.untitled");
0400: } catch (MissingResourceException e) {
0401: value = "Untitled";
0402: }
0403: }
0404: collectionRow.setColumn(field, value);
0405: modifiedMetadata = true;
0406: addDetails(field);
0407: }
0408:
0409: public String getName() {
0410: return getMetadata("name");
0411: }
0412:
0413: /**
0414: * Get the logo for the collection. <code>null</code> is return if the
0415: * collection does not have a logo.
0416: *
0417: * @return the logo of the collection, or <code>null</code>
0418: */
0419: public Bitstream getLogo() {
0420: return logo;
0421: }
0422:
0423: /**
0424: * Give the collection a logo. Passing in <code>null</code> removes any
0425: * existing logo. You will need to set the format of the new logo bitstream
0426: * before it will work, for example to "JPEG". Note that
0427: * <code>update(/code> will need to be called for the change to take
0428: * effect. Setting a logo and not calling <code>update</code> later may
0429: * result in a previous logo lying around as an "orphaned" bitstream.
0430: *
0431: * @param is the stream to use as the new logo
0432: *
0433: * @return the new logo bitstream, or <code>null</code> if there is no
0434: * logo (<code>null</code> was passed in)
0435: * @throws AuthorizeException
0436: * @throws IOException
0437: * @throws SQLException
0438: */
0439: public Bitstream setLogo(InputStream is) throws AuthorizeException,
0440: IOException, SQLException {
0441: // Check authorisation
0442: // authorized to remove the logo when DELETE rights
0443: // authorized when canEdit
0444: if (!((is == null) && AuthorizeManager.authorizeActionBoolean(
0445: ourContext, this , Constants.DELETE))) {
0446: canEdit();
0447: }
0448:
0449: // First, delete any existing logo
0450: if (!collectionRow.isColumnNull("logo_bitstream_id")) {
0451: logo.delete();
0452: }
0453:
0454: if (is == null) {
0455: collectionRow.setColumnNull("logo_bitstream_id");
0456: logo = null;
0457:
0458: log.info(LogManager.getHeader(ourContext, "remove_logo",
0459: "collection_id=" + getID()));
0460: } else {
0461: Bitstream newLogo = Bitstream.create(ourContext, is);
0462: collectionRow.setColumn("logo_bitstream_id", newLogo
0463: .getID());
0464: logo = newLogo;
0465:
0466: // now create policy for logo bitstream
0467: // to match our READ policy
0468: List policies = AuthorizeManager.getPoliciesActionFilter(
0469: ourContext, this , Constants.READ);
0470: AuthorizeManager.addPolicies(ourContext, policies, newLogo);
0471:
0472: log.info(LogManager.getHeader(ourContext, "set_logo",
0473: "collection_id=" + getID() + "logo_bitstream_id="
0474: + newLogo.getID()));
0475: }
0476:
0477: modified = true;
0478: return logo;
0479: }
0480:
0481: /**
0482: * Create a workflow group for the given step if one does not already exist.
0483: * Returns either the newly created group or the previously existing one.
0484: * Note that while the new group is created in the database, the association
0485: * between the group and the collection is not written until
0486: * <code>update</code> is called.
0487: *
0488: * @param step
0489: * the step (1-3) of the workflow to create or get the group for
0490: *
0491: * @return the workflow group associated with this collection
0492: * @throws SQLException
0493: * @throws AuthorizeException
0494: */
0495: public Group createWorkflowGroup(int step) throws SQLException,
0496: AuthorizeException {
0497: // Check authorisation
0498: AuthorizeManager.authorizeAction(ourContext, this ,
0499: Constants.WRITE);
0500:
0501: if (workflowGroup[step - 1] == null) {
0502: Group g = Group.create(ourContext);
0503: g.setName("COLLECTION_" + getID() + "_WORKFLOW_STEP_"
0504: + step);
0505: g.update();
0506: setWorkflowGroup(step, g);
0507:
0508: AuthorizeManager.addPolicy(ourContext, this , Constants.ADD,
0509: g);
0510: }
0511:
0512: return workflowGroup[step - 1];
0513: }
0514:
0515: /**
0516: * Set the workflow group corresponding to a particular workflow step.
0517: * <code>null</code> can be passed in if there should be no associated
0518: * group for that workflow step; any existing group is NOT deleted.
0519: *
0520: * @param step
0521: * the workflow step (1-3)
0522: * @param g
0523: * the new workflow group, or <code>null</code>
0524: */
0525: public void setWorkflowGroup(int step, Group g) {
0526: workflowGroup[step - 1] = g;
0527:
0528: if (g == null) {
0529: collectionRow.setColumnNull("workflow_step_" + step);
0530: } else {
0531: collectionRow.setColumn("workflow_step_" + step, g.getID());
0532: }
0533: modified = true;
0534: }
0535:
0536: /**
0537: * Get the the workflow group corresponding to a particular workflow step.
0538: * This returns <code>null</code> if there is no group associated with
0539: * this collection for the given step.
0540: *
0541: * @param step
0542: * the workflow step (1-3)
0543: *
0544: * @return the group of reviewers or <code>null</code>
0545: */
0546: public Group getWorkflowGroup(int step) {
0547: return workflowGroup[step - 1];
0548: }
0549:
0550: /**
0551: * Create a default submitters group if one does not already exist. Returns
0552: * either the newly created group or the previously existing one. Note that
0553: * other groups may also be allowed to submit to this collection by the
0554: * authorization system.
0555: *
0556: * @return the default group of submitters associated with this collection
0557: * @throws SQLException
0558: * @throws AuthorizeException
0559: */
0560: public Group createSubmitters() throws SQLException,
0561: AuthorizeException {
0562: // Check authorisation
0563: AuthorizeManager.authorizeAction(ourContext, this ,
0564: Constants.WRITE);
0565:
0566: if (submitters == null) {
0567: submitters = Group.create(ourContext);
0568: submitters.setName("COLLECTION_" + getID() + "_SUBMIT");
0569: submitters.update();
0570: }
0571:
0572: // register this as the submitter group
0573: collectionRow.setColumn("submitter", submitters.getID());
0574:
0575: AuthorizeManager.addPolicy(ourContext, this , Constants.ADD,
0576: submitters);
0577:
0578: modified = true;
0579: return submitters;
0580: }
0581:
0582: /**
0583: * Get the default group of submitters, if there is one. Note that the
0584: * authorization system may allow others to submit to the collection, so
0585: * this is not necessarily a definitive list of potential submitters.
0586: * <P>
0587: * The default group of submitters for collection 100 is the one called
0588: * <code>collection_100_submit</code>.
0589: *
0590: * @return the default group of submitters, or <code>null</code> if there
0591: * is no default group.
0592: */
0593: public Group getSubmitters() {
0594: return submitters;
0595: }
0596:
0597: /**
0598: * Create a default administrators group if one does not already exist.
0599: * Returns either the newly created group or the previously existing one.
0600: * Note that other groups may also be administrators.
0601: *
0602: * @return the default group of editors associated with this collection
0603: * @throws SQLException
0604: * @throws AuthorizeException
0605: */
0606: public Group createAdministrators() throws SQLException,
0607: AuthorizeException {
0608: // Check authorisation
0609: AuthorizeManager.authorizeAction(ourContext, this ,
0610: Constants.WRITE);
0611:
0612: if (admins == null) {
0613: admins = Group.create(ourContext);
0614: admins.setName("COLLECTION_" + getID() + "_ADMIN");
0615: admins.update();
0616: }
0617:
0618: AuthorizeManager.addPolicy(ourContext, this ,
0619: Constants.COLLECTION_ADMIN, admins);
0620:
0621: // register this as the admin group
0622: collectionRow.setColumn("admin", admins.getID());
0623:
0624: // administrators also get ADD on the submitter group
0625: if (submitters != null) {
0626: AuthorizeManager.addPolicy(ourContext, submitters,
0627: Constants.ADD, admins);
0628: }
0629:
0630: modified = true;
0631: return admins;
0632: }
0633:
0634: /**
0635: * Get the default group of administrators, if there is one. Note that the
0636: * authorization system may allow others to be administrators for the
0637: * collection.
0638: * <P>
0639: * The default group of administrators for collection 100 is the one called
0640: * <code>collection_100_admin</code>.
0641: *
0642: * @return group of administrators, or <code>null</code> if there is no
0643: * default group.
0644: */
0645: public Group getAdministrators() {
0646: return admins;
0647: }
0648:
0649: /**
0650: * Get the license that users must grant before submitting to this
0651: * collection. If the collection does not have a specific license, the
0652: * site-wide default is returned.
0653: *
0654: * @return the license for this collection
0655: */
0656: public String getLicense() {
0657: String license = collectionRow.getStringColumn("license");
0658:
0659: if ((license == null) || license.equals("")) {
0660: // Fallback to site-wide default
0661: license = ConfigurationManager
0662: .getDefaultSubmissionLicense();
0663: }
0664:
0665: return license;
0666: }
0667:
0668: /**
0669: * Get the license that users must grant before submitting to this
0670: * collection.
0671: *
0672: * @return the license for this collection
0673: */
0674: public String getLicenseCollection() {
0675: String license = collectionRow.getStringColumn("license");
0676: return license;
0677: }
0678:
0679: /**
0680: * Find out if the collection has a custom license
0681: *
0682: * @return <code>true</code> if the collection has a custom license
0683: */
0684: public boolean hasCustomLicense() {
0685: String license = collectionRow.getStringColumn("license");
0686:
0687: return ((license != null) && !license.equals(""));
0688: }
0689:
0690: /**
0691: * Set the license for this collection. Passing in <code>null</code> means
0692: * that the site-wide default will be used.
0693: *
0694: * @param license
0695: * the license, or <code>null</code>
0696: */
0697: public void setLicense(String license) {
0698: if (license == null) {
0699: collectionRow.setColumnNull("license");
0700: } else {
0701: collectionRow.setColumn("license", license);
0702: }
0703: modified = true;
0704: }
0705:
0706: /**
0707: * Get the template item for this collection. <code>null</code> is
0708: * returned if the collection does not have a template. Submission
0709: * mechanisms may copy this template to provide a convenient starting point
0710: * for a submission.
0711: *
0712: * @return the item template, or <code>null</code>
0713: */
0714: public Item getTemplateItem() throws SQLException {
0715: return template;
0716: }
0717:
0718: /**
0719: * Create an empty template item for this collection. If one already exists,
0720: * no action is taken. Caution: Make sure you call <code>update</code> on
0721: * the collection after doing this, or the item will have been created but
0722: * the collection record will not refer to it.
0723: *
0724: * @throws SQLException
0725: * @throws AuthorizeException
0726: */
0727: public void createTemplateItem() throws SQLException,
0728: AuthorizeException {
0729: // Check authorisation
0730: canEdit();
0731:
0732: if (template == null) {
0733: template = Item.create(ourContext);
0734: collectionRow.setColumn("template_item_id", template
0735: .getID());
0736:
0737: log.info(LogManager.getHeader(ourContext,
0738: "create_template_item", "collection_id=" + getID()
0739: + ",template_item_id=" + template.getID()));
0740: }
0741: modified = true;
0742: }
0743:
0744: /**
0745: * Remove the template item for this collection, if there is one. Note that
0746: * since this has to remove the old template item ID from the collection
0747: * record in the database, the colletion record will be changed, including
0748: * any other changes made; in other words, this method does an
0749: * <code>update</code>.
0750: *
0751: * @throws SQLException
0752: * @throws AuthorizeException
0753: * @throws IOException
0754: */
0755: public void removeTemplateItem() throws SQLException,
0756: AuthorizeException, IOException {
0757: // Check authorisation
0758: canEdit();
0759:
0760: collectionRow.setColumnNull("template_item_id");
0761: DatabaseManager.update(ourContext, collectionRow);
0762:
0763: if (template != null) {
0764: log.info(LogManager.getHeader(ourContext,
0765: "remove_template_item", "collection_id=" + getID()
0766: + ",template_item_id=" + template.getID()));
0767:
0768: template.delete();
0769: template = null;
0770: }
0771: ourContext.addEvent(new Event(Event.MODIFY,
0772: Constants.COLLECTION, getID(), "remove_template_item"));
0773: }
0774:
0775: /**
0776: * Add an item to the collection. This simply adds a relationship between
0777: * the item and the collection - it does nothing like set an issue date,
0778: * remove a personal workspace item etc. This has instant effect;
0779: * <code>update</code> need not be called.
0780: *
0781: * @param item
0782: * item to add
0783: * @throws SQLException
0784: * @throws AuthorizeException
0785: */
0786: public void addItem(Item item) throws SQLException,
0787: AuthorizeException {
0788: // Check authorisation
0789: AuthorizeManager.authorizeAction(ourContext, this ,
0790: Constants.ADD);
0791:
0792: log.info(LogManager
0793: .getHeader(ourContext, "add_item", "collection_id="
0794: + getID() + ",item_id=" + item.getID()));
0795:
0796: // Create mapping
0797: TableRow row = DatabaseManager.create(ourContext,
0798: "collection2item");
0799:
0800: row.setColumn("collection_id", getID());
0801: row.setColumn("item_id", item.getID());
0802:
0803: DatabaseManager.update(ourContext, row);
0804:
0805: ourContext
0806: .addEvent(new Event(Event.ADD, Constants.COLLECTION,
0807: getID(), Constants.ITEM, item.getID(), item
0808: .getHandle()));
0809: }
0810:
0811: /**
0812: * Remove an item. If the item is then orphaned, it is deleted.
0813: *
0814: * @param item
0815: * item to remove
0816: * @throws SQLException
0817: * @throws AuthorizeException
0818: * @throws IOException
0819: */
0820: public void removeItem(Item item) throws SQLException,
0821: AuthorizeException, IOException {
0822: // Check authorisation
0823: AuthorizeManager.authorizeAction(ourContext, this ,
0824: Constants.REMOVE);
0825:
0826: log.info(LogManager
0827: .getHeader(ourContext, "remove_item", "collection_id="
0828: + getID() + ",item_id=" + item.getID()));
0829:
0830: DatabaseManager.updateQuery(ourContext,
0831: "DELETE FROM collection2item WHERE collection_id= ? "
0832: + "AND item_id= ? ", getID(), item.getID());
0833:
0834: ourContext.addEvent(new Event(Event.REMOVE,
0835: Constants.COLLECTION, getID(), Constants.ITEM, item
0836: .getID(), item.getHandle()));
0837:
0838: // Is the item an orphan?
0839: TableRowIterator tri = DatabaseManager.query(ourContext,
0840: "SELECT * FROM collection2item WHERE item_id= ? ", item
0841: .getID());
0842:
0843: if (!tri.hasNext()) {
0844: //make the right to remove the item explicit because the implicit
0845: // relation
0846: //has been removed. This only has to concern the currentUser
0847: // because
0848: //he started the removal process and he will end it too.
0849: //also add right to remove from the item to remove it's bundles.
0850: AuthorizeManager.addPolicy(ourContext, item,
0851: Constants.DELETE, ourContext.getCurrentUser());
0852: AuthorizeManager.addPolicy(ourContext, item,
0853: Constants.REMOVE, ourContext.getCurrentUser());
0854:
0855: // Orphan; delete it
0856: item.delete();
0857: }
0858: // close the TableRowIterator to free up resources
0859: tri.close();
0860: }
0861:
0862: /**
0863: * Update the collection metadata (including logo, and workflow groups) to
0864: * the database. Inserts if this is a new collection.
0865: *
0866: * @throws SQLException
0867: * @throws IOException
0868: * @throws AuthorizeException
0869: */
0870: public void update() throws SQLException, IOException,
0871: AuthorizeException {
0872: // Check authorisation
0873: canEdit();
0874:
0875: log.info(LogManager.getHeader(ourContext, "update_collection",
0876: "collection_id=" + getID()));
0877:
0878: DatabaseManager.update(ourContext, collectionRow);
0879:
0880: if (modified) {
0881: ourContext.addEvent(new Event(Event.MODIFY,
0882: Constants.COLLECTION, getID(), null));
0883: modified = false;
0884: }
0885: if (modifiedMetadata) {
0886: ourContext.addEvent(new Event(Event.MODIFY_METADATA,
0887: Constants.COLLECTION, getID(), getDetails()));
0888: modifiedMetadata = false;
0889: clearDetails();
0890: }
0891: }
0892:
0893: public boolean canEditBoolean() throws java.sql.SQLException {
0894: try {
0895: canEdit();
0896:
0897: return true;
0898: } catch (AuthorizeException e) {
0899: return false;
0900: }
0901: }
0902:
0903: public void canEdit() throws AuthorizeException, SQLException {
0904: Community[] parents = getCommunities();
0905:
0906: for (int i = 0; i < parents.length; i++) {
0907: if (AuthorizeManager.authorizeActionBoolean(ourContext,
0908: parents[i], Constants.WRITE)) {
0909: return;
0910: }
0911:
0912: if (AuthorizeManager.authorizeActionBoolean(ourContext,
0913: parents[i], Constants.ADD)) {
0914: return;
0915: }
0916: }
0917:
0918: AuthorizeManager.authorizeAnyOf(ourContext, this , new int[] {
0919: Constants.WRITE, Constants.COLLECTION_ADMIN });
0920: }
0921:
0922: /**
0923: * Delete the collection, including the metadata and logo. Items that are
0924: * then orphans are deleted. Groups associated with this collection
0925: * (workflow participants and submitters) are NOT deleted.
0926: *
0927: * @throws SQLException
0928: * @throws AuthorizeException
0929: * @throws IOException
0930: */
0931: void delete() throws SQLException, AuthorizeException, IOException {
0932: log.info(LogManager.getHeader(ourContext, "delete_collection",
0933: "collection_id=" + getID()));
0934:
0935: ourContext.addEvent(new Event(Event.DELETE,
0936: Constants.COLLECTION, getID(), getHandle()));
0937:
0938: // Remove from cache
0939: ourContext.removeCached(this , getID());
0940:
0941: // remove subscriptions - hmm, should this be in Subscription.java?
0942: DatabaseManager.updateQuery(ourContext,
0943: "DELETE FROM subscription WHERE collection_id= ? ",
0944: getID());
0945:
0946: // Remove Template Item
0947: removeTemplateItem();
0948:
0949: // Remove items
0950: ItemIterator items = getAllItems();
0951:
0952: try {
0953: while (items.hasNext()) {
0954: Item item = items.next();
0955: IndexBrowse ib = new IndexBrowse(ourContext);
0956:
0957: if (item.isOwningCollection(this )) {
0958: // the collection to be deletd is the owning collection, thus remove
0959: // the item from all collections it belongs to
0960: Collection[] collections = item.getCollections();
0961: for (int i = 0; i < collections.length; i++) {
0962: //notify Browse of removing item.
0963: ib.itemRemoved(item);
0964: // Browse.itemRemoved(ourContext, itemId);
0965: collections[i].removeItem(item);
0966: }
0967:
0968: }
0969: // the item was only mapped to this collection, so just remove it
0970: else {
0971: //notify Browse of removing item mapping.
0972: ib.indexItem(item);
0973: // Browse.itemChanged(ourContext, item);
0974: removeItem(item);
0975: }
0976: }
0977: } catch (BrowseException e) {
0978: log.error("caught exception: ", e);
0979: throw new IOException(e.getMessage());
0980: }
0981:
0982: // Delete bitstream logo
0983: setLogo(null);
0984:
0985: // Remove all authorization policies
0986: AuthorizeManager.removeAllPolicies(ourContext, this );
0987:
0988: // Remove any WorkflowItems
0989: WorkflowItem[] wfarray = WorkflowItem.findByCollection(
0990: ourContext, this );
0991:
0992: for (int x = 0; x < wfarray.length; x++) {
0993: // remove the workflowitem first, then the item
0994: Item myItem = wfarray[x].getItem();
0995: wfarray[x].deleteWrapper();
0996: myItem.delete();
0997: }
0998:
0999: // Remove any WorkspaceItems
1000: WorkspaceItem[] wsarray = WorkspaceItem.findByCollection(
1001: ourContext, this );
1002:
1003: for (int x = 0; x < wsarray.length; x++) {
1004: wsarray[x].deleteAll();
1005: }
1006:
1007: // get rid of the content count cache if it exists
1008: try {
1009: ItemCounter ic = new ItemCounter(ourContext);
1010: ic.remove(this );
1011: } catch (ItemCountException e) {
1012: // FIXME: upside down exception handling due to lack of good
1013: // exception framework
1014: throw new RuntimeException(e.getMessage(), e);
1015: }
1016:
1017: // Delete collection row
1018: DatabaseManager.delete(ourContext, collectionRow);
1019:
1020: // Remove any workflow groups - must happen after deleting collection
1021: Group g = null;
1022:
1023: g = getWorkflowGroup(1);
1024:
1025: if (g != null) {
1026: g.delete();
1027: }
1028:
1029: g = getWorkflowGroup(2);
1030:
1031: if (g != null) {
1032: g.delete();
1033: }
1034:
1035: g = getWorkflowGroup(3);
1036:
1037: if (g != null) {
1038: g.delete();
1039: }
1040:
1041: // Remove default administrators group
1042: g = getAdministrators();
1043:
1044: if (g != null) {
1045: g.delete();
1046: }
1047:
1048: // Remove default submitters group
1049: g = getSubmitters();
1050:
1051: if (g != null) {
1052: g.delete();
1053: }
1054: }
1055:
1056: /**
1057: * Get the communities this collection appears in
1058: *
1059: * @return array of <code>Community</code> objects
1060: * @throws SQLException
1061: */
1062: public Community[] getCommunities() throws SQLException {
1063: // Get the bundle table rows
1064: TableRowIterator tri = DatabaseManager
1065: .queryTable(
1066: ourContext,
1067: "community",
1068: "SELECT community.* FROM community, community2collection WHERE "
1069: + "community.community_id=community2collection.community_id "
1070: + "AND community2collection.collection_id= ? ",
1071: getID());
1072:
1073: // Build a list of Community objects
1074: List<Community> communities = new ArrayList<Community>();
1075:
1076: while (tri.hasNext()) {
1077: TableRow row = tri.next();
1078:
1079: // First check the cache
1080: Community owner = (Community) ourContext.fromCache(
1081: Community.class, row.getIntColumn("community_id"));
1082:
1083: if (owner == null) {
1084: owner = new Community(ourContext, row);
1085: }
1086:
1087: communities.add(owner);
1088:
1089: // now add any parent communities
1090: Community[] parents = owner.getAllParents();
1091:
1092: for (int i = 0; i < parents.length; i++) {
1093: communities.add(parents[i]);
1094: }
1095: }
1096: // close the TableRowIterator to free up resources
1097: tri.close();
1098:
1099: Community[] communityArray = new Community[communities.size()];
1100: communityArray = (Community[]) communities
1101: .toArray(communityArray);
1102:
1103: return communityArray;
1104: }
1105:
1106: /**
1107: * Return <code>true</code> if <code>other</code> is the same Collection
1108: * as this object, <code>false</code> otherwise
1109: *
1110: * @param other
1111: * object to compare to
1112: *
1113: * @return <code>true</code> if object passed in represents the same
1114: * collection as this object
1115: */
1116: public boolean equals(Object other) {
1117: if (!(other instanceof Collection)) {
1118: return false;
1119: }
1120:
1121: return (getID() == ((Collection) other).getID());
1122: }
1123:
1124: /**
1125: * Utility method for reading in a group from a group ID in a column. If the
1126: * column is null, null is returned.
1127: *
1128: * @param col
1129: * the column name to read
1130: * @return the group referred to by that column, or null
1131: * @throws SQLException
1132: */
1133: private Group groupFromColumn(String col) throws SQLException {
1134: if (collectionRow.isColumnNull(col)) {
1135: return null;
1136: }
1137:
1138: return Group.find(ourContext, collectionRow.getIntColumn(col));
1139: }
1140:
1141: /**
1142: * return type found in Constants
1143: *
1144: * @return int Constants.COLLECTION
1145: */
1146: public int getType() {
1147: return Constants.COLLECTION;
1148: }
1149:
1150: /**
1151: * return an array of collections that user has a given permission on
1152: * (useful for trimming 'select to collection' list) or figuring out which
1153: * collections a person is an editor for.
1154: *
1155: * @param context
1156: * @param comm
1157: * (optional) restrict search to a community, else null
1158: * @param actionID
1159: * fo the action
1160: *
1161: * @return Collection [] of collections with matching permissions
1162: * @throws SQLException
1163: */
1164: public static Collection[] findAuthorized(Context context,
1165: Community comm, int actionID) throws java.sql.SQLException {
1166: List<Collection> myResults = new ArrayList<Collection>();
1167:
1168: Collection[] myCollections = null;
1169:
1170: if (comm != null) {
1171: myCollections = comm.getCollections();
1172: } else {
1173: myCollections = Collection.findAll(context);
1174: }
1175:
1176: // now build a list of collections you have authorization for
1177: for (int i = 0; i < myCollections.length; i++) {
1178: if (AuthorizeManager.authorizeActionBoolean(context,
1179: myCollections[i], actionID)) {
1180: myResults.add(myCollections[i]);
1181: }
1182: }
1183:
1184: myCollections = new Collection[myResults.size()];
1185: myCollections = (Collection[]) myResults.toArray(myCollections);
1186:
1187: return myCollections;
1188: }
1189:
1190: /**
1191: * counts items in this collection
1192: *
1193: * @return total items
1194: */
1195: public int countItems() throws SQLException {
1196: String query = "SELECT count(*) FROM collection2item, item WHERE "
1197: + "collection2item.collection_id = ? "
1198: + "AND collection2item.item_id = item.item_id "
1199: + "AND in_archive ='1' AND item.withdrawn='0' ";
1200:
1201: PreparedStatement statement = ourContext.getDBConnection()
1202: .prepareStatement(query);
1203: statement.setInt(1, getID());
1204:
1205: ResultSet rs = statement.executeQuery();
1206:
1207: rs.next();
1208: int itemcount = rs.getInt(1);
1209:
1210: statement.close();
1211:
1212: return itemcount;
1213: }
1214: }
|