Index: config/dspace.cfg =================================================================== --- config/dspace.cfg 2006-12-29 20:31:55.000000000 -0500 +++ ../dspace/config/dspace.cfg 2007-01-04 03:03:56.000000000 -0500 @@ -642,22 +642,50 @@ http://purl.org/dc/elements/1.1/ http://dublincore.org/schemas/xmls/qdc/2006/01/06/dc.xsd crosswalk.qdc.properties.qdc = crosswalks/QDC.properties -# METS ingester configuration: -# map of metadata type declared in mdWrap to a crosswalk plugin name: -mets.submission.crosswalk.DC = QDC +#### METS ingester configuration: # Option to save METS manifest in the item: (default is false) -mets.submission.preserveManifest = false +mets.dspaceSIP.ingest.preserveManifest = false + +# This will be required if preserveManifest is true: +#mets.dspaceSIP.ingest.manifestBitstreamFormat = DSpace SIP Manifest + +## Configurable METS ingesters, defaults: +mets.default.ingest.crosswalk.DC = QDC +mets.default.ingest.crosswalk.DSpaceDepositLicense = DSPACE_DEPLICENSE +mets.default.ingest.crosswalk.Creative\ Commons = DSPACE_CCRDF +mets.default.ingest.crosswalk.CreativeCommonsRDF = DSPACE_CCRDF +mets.default.ingest.crosswalk.CreativeCommonsText = NULLSTREAM + +# local copies of XML schema documents to save time on ingest: +mets.xsd.mets = http://www.loc.gov/METS/ mets.xsd +mets.xsd.xlink = http://www.w3.org/1999/xlink xlink.xsd +mets.xsd.mods = http://www.loc.gov/mods/v3 mods-3-1.xsd +mets.xsd.xml = http://www.w3.org/XML/1998/namespace xml.xsd +mets.xsd.premis = http://www.loc.gov/standards/premis PREMIS-v1-0.xsd +mets.xsd.premisObj = http://www.loc.gov/standards/premis Object-v1-0.xsd +mets.xsd.premisEvn = http://www.loc.gov/standards/premis Event-v1-0.xsd +mets.xsd.premisAgt = http://www.loc.gov/standards/premis Agent-v1-0.xsd +mets.xsd.premisRgh = http://www.loc.gov/standards/premis Rights-v1-0.xsd # Crosswalk Plugins: plugin.named.org.dspace.content.crosswalk.IngestionCrosswalk = \ + org.dspace.content.crosswalk.AIPDIMCrosswalk = DIM \ + org.dspace.content.crosswalk.AIPTechMDCrosswalk = AIP-TECHMD \ org.dspace.content.crosswalk.PREMISCrosswalk = PREMIS \ org.dspace.content.crosswalk.NullIngestionCrosswalk = NIL plugin.selfnamed.org.dspace.content.crosswalk.IngestionCrosswalk = \ org.dspace.content.crosswalk.XSLTIngestionCrosswalk +plugin.named.org.dspace.content.crosswalk.StreamIngestionCrosswalk = \ + org.dspace.content.crosswalk.NullStreamIngestionCrosswalk = NULLSTREAM, \ + org.dspace.content.crosswalk.CreativeCommonsRDFStreamIngestionCrosswalk = DSPACE_CCRDF, \ + org.dspace.content.crosswalk.LicenseStreamIngestionCrosswalk = DSPACE_DEPLICENSE + plugin.named.org.dspace.content.crosswalk.DisseminationCrosswalk = \ + org.dspace.content.crosswalk.AIPDIMCrosswalk = DIM \ + org.dspace.content.crosswalk.AIPTechMDCrosswalk = AIP-TECHMD \ org.dspace.content.crosswalk.SimpleDCDisseminationCrosswalk = DC \ org.dspace.content.crosswalk.SimpleDCDisseminationCrosswalk = dc \ org.dspace.content.crosswalk.PREMISCrosswalk = PREMIS \ @@ -669,12 +697,19 @@ org.dspace.content.crosswalk.XSLTDisseminationCrosswalk, \ org.dspace.content.crosswalk.QDCCrosswalk +plugin.named.org.dspace.content.crosswalk.StreamDisseminationCrosswalk = \ + org.dspace.content.crosswalk.CreativeCommonsRDFStreamDisseminationCrosswalk = DSPACE_CCRDF, \ + org.dspace.content.crosswalk.CreativeCommonsTextStreamDisseminationCrosswalk = DSPACE_CCTEXT, \ + org.dspace.content.crosswalk.LicenseStreamDisseminationCrosswalk = DSPACE_DEPLICENSE + # Packager Plugins: plugin.named.org.dspace.content.packager.PackageDisseminator = \ + org.dspace.content.packager.DSpaceAIPDisseminator = AIP, \ org.dspace.content.packager.DSpaceMETSDisseminator = METS plugin.named.org.dspace.content.packager.PackageIngester = \ + org.dspace.content.packager.DSpaceAIPIngester = AIP, \ org.dspace.content.packager.PDFPackager = Adobe PDF, PDF, \ org.dspace.content.packager.DSpaceMETSIngester = METS @@ -733,3 +768,37 @@ ## Named "cron" since it could be run by cron on Unix platforms. #event.dispatcher.cron.class = org.dspace.event.AsynchDispatcher #event.dispatcher.cron.consumers = search:async, browse:async, mail:async + +#### AIP and METS configuration + +# AIPManager's event dispatcher configuration +# (example: choose "restore", a special dispatcher config for AIP restoration +# that skips History.) +#aipManager.dispatcher = restore + +# Choose package type for Internal AIPs +#aip.packager = AIP + +# AIP ingester plugin +plugin.named.org.dspace.content.packager.AIPIngester = \ + org.dspace.content.packager.DSpaceAIPIngester = AIP + +## Configurable METS elements for AIP - ingesters for MD types: +mets.dspaceAIP.ingest.crosswalk.DSpaceDepositLicense = NULLSTREAM +mets.dspaceAIP.ingest.crosswalk.CreativeCommonsRDF = NULLSTREAM +mets.dspaceAIP.ingest.crosswalk.CreativeCommonsText = NULLSTREAM +mets.dspaceAIP.ingest.crosswalk.DSpaceHistory = NULLSTREAM +mets.dspaceAIP.ingest.crosswalk.AllPolicies = NULLSTREAM +mets.dspaceAIP.ingest.crosswalk.ObjectPolicies = NULLSTREAM + +# Technical MD types, format is MDNAME:xwalkPlugin [, ...] +#aip.disseminate.techMD = PREMIS + +# Source MD types, format is MDNAME:xwalkPlugin [, ...] +#aip.disseminate.sourceMD = AIP-TECHMD + +# Descriptive MD types, format is MDNAME:xwalkPlugin [, ...] +#aip.disseminate.dmd = MODS, DIM + +# Create EPerson if necessary for Submitter when ingesting AIP (default=false) +#aip.ingest.createEperson = false Index: src/org/dspace/app/packager/Packager.java =================================================================== --- src/org/dspace/app/packager/Packager.java 2006-12-28 03:56:04.000000000 -0500 +++ ../dspace/src/org/dspace/app/packager/Packager.java 2007-01-03 20:54:07.000000000 -0500 @@ -50,9 +50,9 @@ import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.PosixParser; -import org.dspace.content.Collection; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.IngestionWrapper; import org.dspace.content.InstallItem; import org.dspace.content.WorkspaceItem; import org.dspace.content.packager.PackageDisseminator; @@ -86,9 +86,10 @@ * java org.dspace.app.packager.Packager * -e {ePerson} * -t {PackagerType} - * -c {collection-handle} [ -c {collection} ...] + * -p {parent-handle} [ -p {parent2} ...] * -o {name}={value} [ -o {name}={value} ..] - * [-w] + * [-r] --- use ONLY to restore an AIP + * [-w] --- skip Workflow * {package-filename} * * {PackagerType} must match one of the aliases of the chosen Packager @@ -103,7 +104,7 @@ * -d * -e {ePerson} * -t {PackagerType} - * -i {item-handle} + * -i {identifier-handle-of-object} * -o {name}={value} [ -o {name}={value} ..] * {package-filename} * @@ -131,8 +132,8 @@ public static void main(String[] argv) throws Exception { Options options = new Options(); - options.addOption("c", "collection", true, - "destination collection(s) Handle (repeatable)"); + options.addOption("p", "parent", true, + "Handle(s) of parent Community or Collection into which to ingest object (repeatable)"); options.addOption("e", "eperson", true, "email address of eperson doing importing"); options @@ -141,12 +142,15 @@ "install", false, "disable workflow; install immediately without going through collection's workflow"); + options.addOption("r", "replace", false, "ingest in \"replacment\" mode, e.g. for AIP"); options.addOption("t", "type", true, "package type or MIMEtype"); options .addOption("o", "option", true, "Packager option to pass to plugin, \"name=value\" (repeatable)"); options.addOption("d", "disseminate", false, "Disseminate package (output); default is to submit."); + options.addOption("s", "submit", false, + "Submission package (Input); this is the default. "); options.addOption("i", "item", true, "Handle of item to disseminate."); options.addOption("h", "help", false, "help"); @@ -155,8 +159,9 @@ String sourceFile = null; String eperson = null; - String[] collections = null; + String[] parents = null; boolean useWorkflow = true; + boolean replaceMode = false; String packageType = null; boolean submit = true; String itemHandle = null; @@ -181,10 +186,12 @@ } if (line.hasOption('w')) useWorkflow = false; + if (line.hasOption('r')) + replaceMode = true; if (line.hasOption('e')) eperson = line.getOptionValue('e'); - if (line.hasOption('c')) - collections = line.getOptionValues('c'); + if (line.hasOption('p')) + parents = line.getOptionValues('p'); if (line.hasOption('t')) packageType = line.getOptionValue('t'); if (line.hasOption('i')) @@ -227,7 +234,7 @@ // Sanity checks on arg list: required args if (sourceFile == null || eperson == null || packageType == null - || (submit && collections == null)) + || (submit && parents == null)) { System.err .println("Error - missing a REQUIRED argument or option.\n"); @@ -248,39 +255,51 @@ if (sip == null) usageError("Error, Unknown package type: " + packageType); - // find collections - Collection[] mycollections = null; + System.out.println("Destination parents:"); - System.out.println("Destination collections:"); - - // validate each collection arg to see if it's a real collection - mycollections = new Collection[collections.length]; - for (int i = 0; i < collections.length; i++) - { - // sanity check: did handle resolve, and to a collection? - DSpaceObject dso = HandleManager.resolveToObject(context, - collections[i]); - if (dso == null) - throw new IllegalArgumentException( - "Bad collection list -- " - + "Cannot resolve collection handle \"" - + collections[i] + "\""); - else if (dso.getType() != Constants.COLLECTION) + // validate each parent arg + DSpaceObject parentObjs[] = new DSpaceObject[parents.length]; + for (int i = 0; i < parents.length; i++) + { + // sanity check: did handle resolve? + parentObjs[i] = HandleManager.resolveToObject(context, + parents[i]); + if (parentObjs[i] == null) throw new IllegalArgumentException( - "Bad collection list -- " + "Object at handle \"" - + collections[i] - + "\" is not a collection!"); - mycollections[i] = (Collection) dso; - System.out.println((i == 0 ? " Owning " : " ") - + " Collection: " - + mycollections[i].getMetadata("name")); + "Bad parent list -- " + + "Cannot resolve parent handle \"" + + parents[i] + "\""); + System.out.println((i == 0 ? "Owner: " : "Parent: ") + + parentObjs[i].getHandle()); } try { - WorkspaceItem wi = sip.ingest(context, mycollections[0], + IngestionWrapper iw = sip.ingest(context, parentObjs[0], source, pkgParams, null); - if (useWorkflow) + + if (iw.getType() == Constants.INGESTION_ITEM) + { + WorkspaceItem wi = (WorkspaceItem)iw; + + // replace existing package, if necessary (e.g. ingest AIP) + if (replaceMode) + { + System.err.println("Installing item with Package handle="+wi.getHandle()); + InstallItem.replaceItem(context, wi, wi.getHandle()); + // get new copy of item to reread from RDBMS: + Item item = wi.getItem(); + if (wi.getWithdrawn()) + { + System.err.println("Marking item Withdrawn."); + item.withdraw(); + } + System.out.println("Created and installed item, handle="+ + HandleManager.findHandle(context, item)); + } + + // submit normally, passing along to workflow + else if (useWorkflow) { String handle = null; @@ -299,11 +318,22 @@ else System.out.println("Created and installed item, handle="+handle); } + + // skip workflow, but otherwise normal submission else { - InstallItem.installItem(context, wi); + System.err.println("Installing item with handle="+wi.getHandle()); + InstallItem.installItem(context, wi, wi.getHandle()); + // get new copy of item to reread from RDBMS: + Item item = wi.getItem(); + if (wi.getWithdrawn()) + { + System.err.println("Marking item Withdrawn."); + item.withdraw(); + } System.out.println("Created and installed item, handle=" - + HandleManager.findHandle(context, wi.getItem())); + + HandleManager.findHandle(context, item)); + } } context.complete(); System.exit(0); @@ -311,8 +341,8 @@ catch (Exception e) { // abort all operations - context.abort(); e.printStackTrace(); + context.abort(); System.out.println(e); System.exit(1); } @@ -327,12 +357,12 @@ if (dip == null) usageError("Error, Unknown package type: " + packageType); - DSpaceObject dso = HandleManager.resolveToObject(context, - itemHandle); + DSpaceObject dso = HandleManager.resolveToObject(context, itemHandle); if (dso == null) throw new IllegalArgumentException("Bad Item handle -- " - + "Cannot resolve handle \"" + itemHandle); + + "Cannot resolve handle \"" + itemHandle +"\""); dip.disseminate(context, dso, pkgParams, dest); } + System.exit(0); } } Index: src/org/dspace/content/Bitstream.java =================================================================== --- src/org/dspace/content/Bitstream.java 2006-12-28 03:46:51.000000000 -0500 +++ ../dspace/src/org/dspace/content/Bitstream.java 2007-01-03 21:00:36.000000000 -0500 @@ -44,6 +44,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Iterator; +import java.net.URI; import org.apache.log4j.Logger; import org.dspace.authorize.AuthorizeException; @@ -173,6 +175,72 @@ } /** + * Get an Iterator over all known Bitstreams in the archive. + * + * @return Iterator that returns each Bitstream in turn. + */ + public static Iterator findAll(Context context) + throws SQLException + { + class BitstreamIterator + implements Iterator + { + private TableRowIterator rows; + private Context cachedContext; + + BitstreamIterator(Context context, TableRowIterator rows) + { + cachedContext = context; + this.rows = rows; + } + + public boolean hasNext() + { + try + { + return rows.hasNext(); + } + catch (SQLException e) + { + throw new RuntimeException("Got SQLException error in Bitstream iterator: ", e); + } + } + + public Object next() + { + try + { + if (rows.hasNext()) + { + TableRow row = rows.next(); + + // Check cache + Bitstream fromCache = (Bitstream) cachedContext.fromCache( + Bitstream.class, row.getIntColumn("bitstream_id")); + if (fromCache != null) + return fromCache; + else + return new Bitstream(cachedContext, row); + } + else + return null; + } + catch (SQLException e) + { + throw new RuntimeException("Got SQLException error in Bitstream iterator: ", e); + } + } + + public void remove() + { + } + } + + TableRowIterator rows = BitstreamStorageManager.findAll(context); + return new BitstreamIterator(context, rows); + } + + /** * Create a new bitstream, with a new ID. The checksum and file size are * calculated. This method is not public, and does not check authorisation; * other methods such as Bundle.createBitstream() will check authorisation. @@ -187,7 +255,7 @@ * @throws IOException * @throws SQLException */ - static Bitstream create(Context context, InputStream is) + public static Bitstream create(Context context, InputStream is) throws IOException, SQLException { // Store the bits @@ -250,6 +318,11 @@ return bRow.getIntColumn("bitstream_id"); } + /** + * Bitstreams do not have Handles. Required for DSpaceObject interface. + * + * @return null + */ public String getHandle() { // No Handles for bitstreams @@ -280,6 +353,42 @@ } /** + * Returns a URI of the storage occupied by this bitstream in the + * asset store. It can be resolved by the dereferenceAbsoluteURI() + * method. Note that the "absolute" URI does not depend on the DSpace + * object model or RDBMS storage, it only depends on the asset store + * layer. + * + * @return external-based URI to bitstream. + */ + public URI getAbsoluteURI() + { + URI result = BitstreamStorageManager.getAbsoluteURI(bRow); + if (log.isDebugEnabled()) + log.debug("Bitstream.getAbsoluteURI returning = \""+result+"\""); + return result; + } + + /** + * Returns the Bitstream object containing the file in the asset + * store indicated by the URI, or null if there is none. + * See getAbsoluteURI(). + * + * @param context - the context. + * @param uri a bitstream absolute URI created by getAbsoluteURI() + * @return a Bitstream object or null. + */ + public static Bitstream dereferenceAbsoluteURI(Context context, URI uri) + throws SQLException + { + TableRow row = BitstreamStorageManager.dereferenceAbsoluteURI(context, uri); + if (row == null) + return null; + else + return new Bitstream(context, row); + } + + /** * Get the name of this bitstream - typically the filename, without any path * information * @@ -535,6 +644,18 @@ } /** + * Get the value of the "deleted" bit; has this bitstream been + * deleted but not yet removed (e.g. by cleanup())? + * + * @return true if deleted, false otherwise. + * @throws SQLException + */ + public boolean isDeleted() + { + return bRow.getBooleanColumn("deleted"); + } + + /** * Retrieve the contents of the bitstream * * @return a stream from which the bitstream can be read. Index: src/org/dspace/content/Collection.java =================================================================== --- src/org/dspace/content/Collection.java 2006-12-28 20:28:57.000000000 -0500 +++ ../dspace/src/org/dspace/content/Collection.java 2006-12-27 23:06:26.000000000 -0500 @@ -231,9 +231,29 @@ 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); - c.handle = HandleManager.createHandle(context, c); + c.handle = (handle == null) ? + HandleManager.createHandle(context, c) : + HandleManager.createHandle(context, c, handle); // create the default authorization policy for collections // of 'anonymous' READ @@ -1016,6 +1036,9 @@ wsarray[x].deleteAll(); } + // Remove any Handle + HandleManager.unbindHandle(ourContext, this); + // Delete collection row DatabaseManager.delete(ourContext, collectionRow); Index: src/org/dspace/content/Community.java =================================================================== --- src/org/dspace/content/Community.java 2006-12-28 20:35:22.000000000 -0500 +++ ../dspace/src/org/dspace/content/Community.java 2006-12-26 17:27:44.000000000 -0500 @@ -174,7 +174,7 @@ } /** - * Create a new community, with a new ID. + * Create a new top-level community, with a new ID. * * @param context * DSpace context object @@ -184,9 +184,24 @@ public static Community create(Community parent, Context context) throws SQLException, AuthorizeException { + return create(parent, context, null); + } + + /** + * Create a new top-level community, with a new ID. + * + * @param context + * DSpace context object + * @param handle the pre-determined Handle to assign to the new community + * + * @return the newly created community + */ + public static Community create(Community parent, Context context, String handle) + throws SQLException, AuthorizeException + { // Only administrators and adders can create communities - if (!(AuthorizeManager.isAdmin(context) || AuthorizeManager - .authorizeActionBoolean(context, parent, Constants.ADD))) + if (!(AuthorizeManager.isAdmin(context) || + (parent != null && AuthorizeManager.authorizeActionBoolean(context, parent, Constants.ADD)))) { throw new AuthorizeException( "Only administrators can create communities"); @@ -194,7 +209,9 @@ TableRow row = DatabaseManager.create(context, "community"); Community c = new Community(context, row); - c.handle = HandleManager.createHandle(context, c); + c.handle = (handle == null) ? + HandleManager.createHandle(context, c) : + HandleManager.createHandle(context, c, handle); // create the default authorization policy for communities // of 'anonymous' READ @@ -635,10 +652,23 @@ public Collection createCollection() throws SQLException, AuthorizeException { + return createCollection(null); + } + + /** + * Create a new collection within this community. The collection is created + * without any workflow groups or default submitter group. + * + * @param handle the pre-determined Handle to assign to the new community + * @return the new collection + */ + public Collection createCollection(String handle) throws SQLException, + AuthorizeException + { // Check authorisation AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD); - Collection c = Collection.create(ourContext); + Collection c = Collection.create(ourContext, handle); addCollection(c); return c; @@ -690,10 +720,22 @@ public Community createSubcommunity() throws SQLException, AuthorizeException { + return createSubcommunity(null); + } + + /** + * Create a new sub-community within this community. + * + * @param handle the pre-determined Handle to assign to the new community + * @return the new community + */ + public Community createSubcommunity(String handle) throws SQLException, + AuthorizeException + { // Check authorisation AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD); - Community c = create(this, ourContext); + Community c = create(this, ourContext, handle); addSubcommunity(c); return c; @@ -894,6 +936,9 @@ // Remove all authorization policies AuthorizeManager.removeAllPolicies(ourContext, this); + // Remove any Handle + HandleManager.unbindHandle(ourContext, this); + // Delete community row DatabaseManager.delete(ourContext, communityRow); } Index: src/org/dspace/content/DSpaceObject.java =================================================================== --- src/org/dspace/content/DSpaceObject.java 2006-12-28 04:00:46.000000000 -0500 +++ ../dspace/src/org/dspace/content/DSpaceObject.java 2006-12-28 03:50:15.000000000 -0500 @@ -143,4 +143,18 @@ } return null; } + + /** + * Describe the object in a useful way, by type and database ID or Handle. + * @return string description of object. + */ + public String toString() + { + String hdl = getHandle(); + String prefix = "DSpace "+Constants.typeText[getType()]+" @ "; + if (hdl == null) + return prefix+String.valueOf(getID()); + else + return prefix+getHandle(); + } } Index: src/org/dspace/content/InstallItem.java =================================================================== --- src/org/dspace/content/InstallItem.java 2006-12-28 20:40:35.000000000 -0500 +++ ../dspace/src/org/dspace/content/InstallItem.java 2006-12-22 02:33:52.000000000 -0500 @@ -69,7 +69,10 @@ public static Item installItem(Context c, InProgressSubmission is) throws SQLException, IOException, AuthorizeException { - return installItem(c, is, null); + String handle = null; + if (is instanceof IngestionWrapper) + handle = ((IngestionWrapper)is).getHandle(); + return installItem(c, is, handle); } /** @@ -90,34 +93,84 @@ Item item = is.getItem(); String handle; - // create accession date - DCDate now = DCDate.getCurrent(); - item.addDC("date", "accessioned", null, now.toString()); - item.addDC("date", "available", null, now.toString()); - - // create issue date if not present - DCValue[] currentDateIssued = item.getDC("date", "issued", Item.ANY); - - if (currentDateIssued.length == 0) + // if no previous handle supplied, create one + if (suppliedHandle == null) { - item.addDC("date", "issued", null, now.toString()); + // create handle + handle = HandleManager.createHandle(c, item); + } + else + { + handle = HandleManager.createHandle(c, item, suppliedHandle); + } + populateHandleMetadata(item, handle); + populateMetadata(item); + return finishItem(c, item, is); } - // if no previous handle supplied, create one + /** + * Turn an InProgressSubmission into a fully-archived Item, for + * a "replace" operation such as ingestion of an AIP to restore an + * archive. This does NOT add any descriptive metadata (e.g. for + * provenance) to preserve the transparency of the ingest. The + * ingest mechanism is assumed to have set all relevant technical + * and administrative metadata fields. + * + * @param c current context + * @param is + * submission to install + * @param suppliedHandle + * the existing Handle to give the installed item, or null + * to create a new one. + * + * @return the fully archived Item + */ + public static Item replaceItem(Context c, InProgressSubmission is, + String suppliedHandle) + throws SQLException, IOException, AuthorizeException + { + Item item = is.getItem(); + String handle; + if (suppliedHandle == null) { - // create handle handle = HandleManager.createHandle(c, item); + populateHandleMetadata(item, handle); } else { handle = HandleManager.createHandle(c, item, suppliedHandle); } - String handleref = HandleManager.getCanonicalForm(handle); + return finishItem(c, item, is); + } + // Add handle as identifier.uri DC value - item.addDC("identifier", "uri", null, handleref); + private static void populateHandleMetadata(Item item, String handle) + throws SQLException, IOException, AuthorizeException + { + item.addDC("identifier", "uri", null, + HandleManager.getCanonicalForm(handle)); + } + + + // fill in metadata needed by new Item. + private static void populateMetadata(Item item) + throws SQLException, IOException, AuthorizeException + { + // create accession date + DCDate now = DCDate.getCurrent(); + item.addDC("date", "accessioned", null, now.toString()); + item.addDC("date", "available", null, now.toString()); + + // create issue date if not present + DCValue[] currentDateIssued = item.getDC("date", "issued", Item.ANY); + + if (currentDateIssued.length == 0) + { + item.addDC("date", "issued", null, now.toString()); + } String provDescription = "Made available in DSpace on " + now + " (GMT). " + getBitstreamProvenanceMessage(item); @@ -131,7 +184,13 @@ // Add provenance description item.addDC("description", "provenance", "en", provDescription); + } + // final housekeeping when adding new Item to archive + // common between installing and "restoring" items. + private static Item finishItem(Context c, Item item, InProgressSubmission is) + throws SQLException, IOException, AuthorizeException + { // create collection2item mapping is.getCollection().addItem(item); Index: src/org/dspace/content/Item.java =================================================================== --- src/org/dspace/content/Item.java 2006-12-28 20:42:34.000000000 -0500 +++ ../dspace/src/org/dspace/content/Item.java 2007-01-03 21:04:15.000000000 -0500 @@ -1660,6 +1660,9 @@ // remove all of our authorization policies AuthorizeManager.removeAllPolicies(ourContext, this); + // Remove any Handle + HandleManager.unbindHandle(ourContext, this); + // Finally remove item row DatabaseManager.delete(ourContext, itemRow); } @@ -1946,6 +1949,7 @@ return false; } + public String getName() { DCValue t[] = getMetadata("dc", "title", null, Item.ANY); Index: src/org/dspace/content/WorkspaceItem.java =================================================================== --- src/org/dspace/content/WorkspaceItem.java 2006-12-28 20:48:17.000000000 -0500 +++ ../dspace/src/org/dspace/content/WorkspaceItem.java 2006-12-04 17:36:47.000000000 -0500 @@ -62,23 +62,19 @@ * @author Robert Tansley * @version $Revision: 1.32 $ */ -public class WorkspaceItem implements InProgressSubmission +public class WorkspaceItem + extends IngestionWrapper + implements InProgressSubmission { /** log4j logger */ private static Logger log = Logger.getLogger(WorkspaceItem.class); - /** The item this workspace object pertains to */ - private Item item; - /** Our context */ private Context ourContext; /** The table row corresponding to this workspace item */ private TableRow wiRow; - /** The collection the item is being submitted to */ - private Collection collection; - /** * Construct a workspace item corresponding to the given database row * @@ -89,11 +85,13 @@ */ WorkspaceItem(Context context, TableRow row) throws SQLException { + super(Constants.ITEM); + ourContext = context; wiRow = row; - item = Item.find(context, wiRow.getIntColumn("item_id")); - collection = Collection.find(context, wiRow + wrapped = Item.find(context, wiRow.getIntColumn("item_id")); + parent = Collection.find(context, wiRow .getIntColumn("collection_id")); // Cache ourselves @@ -165,12 +163,19 @@ boolean template) throws AuthorizeException, SQLException, IOException { + return create(c, coll, template, c.getCurrentUser(), null); + } + + public static WorkspaceItem create(Context c, Collection coll, + boolean template, EPerson submitter, String handle) + throws AuthorizeException, SQLException, IOException + { // Check the user has permission to ADD to the collection AuthorizeManager.authorizeAction(c, coll, Constants.ADD); // Create an item Item i = Item.create(c); - i.setSubmitter(c.getCurrentUser()); + i.setSubmitter(submitter); // Now create the policies for the submitter and workflow // users to modify item and contents @@ -180,10 +185,8 @@ Group step2group = coll.getWorkflowGroup(2); Group step3group = coll.getWorkflowGroup(3); - EPerson e = c.getCurrentUser(); - // read permission - AuthorizeManager.addPolicy(c, i, Constants.READ, e); + AuthorizeManager.addPolicy(c, i, Constants.READ, submitter); if (step1group != null) { @@ -201,7 +204,7 @@ } // write permission - AuthorizeManager.addPolicy(c, i, Constants.WRITE, e); + AuthorizeManager.addPolicy(c, i, Constants.WRITE, submitter); if (step1group != null) { @@ -219,7 +222,7 @@ } // add permission - AuthorizeManager.addPolicy(c, i, Constants.ADD, e); + AuthorizeManager.addPolicy(c, i, Constants.ADD, submitter); if (step1group != null) { @@ -237,7 +240,7 @@ } // remove contents permission - AuthorizeManager.addPolicy(c, i, Constants.REMOVE, e); + AuthorizeManager.addPolicy(c, i, Constants.REMOVE, submitter); if (step1group != null) { @@ -285,6 +288,9 @@ WorkspaceItem wi = new WorkspaceItem(c, row); + // pass along handle (if any) to apply when Item is installed. + wi.handle = handle; + return wi; } @@ -464,7 +470,7 @@ "workspace_item_id=" + getID())); // Update the item - item.update(); + ((Item)wrapped).update(); // Update ourselves DatabaseManager.update(ourContext, wiRow); @@ -485,7 +491,7 @@ */ if (!AuthorizeManager.isAdmin(ourContext) && ((ourContext.getCurrentUser() == null) || (ourContext - .getCurrentUser().getID() != item.getSubmitter() + .getCurrentUser().getID() != ((Item)wrapped).getSubmitter() .getID()))) { // Not an admit, not the submitter @@ -494,8 +500,8 @@ } log.info(LogManager.getHeader(ourContext, "delete_workspace_item", - "workspace_item_id=" + getID() + "item_id=" + item.getID() - + "collection_id=" + collection.getID())); + "workspace_item_id=" + getID() + "item_id=" + wrapped.getID() + + "collection_id=" + parent.getID())); //deleteSubmitPermissions(); // Remove from cache @@ -510,7 +516,8 @@ DatabaseManager.delete(ourContext, wiRow); // Delete item - item.delete(); + ((Item)wrapped).delete(); + wrapped = null; } private void deleteEpersonGroup2WorkspaceItem() throws SQLException @@ -525,11 +532,11 @@ IOException { // Check authorisation. We check permissions on the enclosed item. - AuthorizeManager.authorizeAction(ourContext, item, Constants.WRITE); + AuthorizeManager.authorizeAction(ourContext, (Item)wrapped, Constants.WRITE); log.info(LogManager.getHeader(ourContext, "delete_workspace_item", - "workspace_item_id=" + getID() + "item_id=" + item.getID() - + "collection_id=" + collection.getID())); + "workspace_item_id=" + getID() + "item_id=" + wrapped.getID() + + "collection_id=" + parent.getID())); // deleteSubmitPermissions(); // Remove from cache @@ -543,17 +550,27 @@ // InProgressSubmission methods public Item getItem() { - return item; + return (Item)wrapped; } public Collection getCollection() { - return collection; + return (Collection)parent; } + + // Override since we have to set DB table as well + public void setParent(DSpaceObject value) + { + parent = value; + wiRow.setColumn("collection_id", value.getID()); + } + + + public EPerson getSubmitter() throws SQLException { - return item.getSubmitter(); + return (submitter == null) ? ((Item)wrapped).getSubmitter() : submitter; } public boolean hasMultipleFiles() @@ -585,4 +602,12 @@ { wiRow.setColumn("published_before", b); } + + // override to set item's submitter + public void setSubmitter(EPerson value) + { + submitter = value; + ((Item)wrapped).setSubmitter(value); + } + }