Contribute to the DSpace Development Fund

The newly established DSpace Development Fund supports the development of new features prioritized by DSpace Governance. For a list of planned features see the fund wiki page.

Contents

Background

At Leiden University, The Netherlands, we use DSpace among other things for "Dissertations Online" https://openaccess.leidenuniv.nl/handle/1887/492
Sometimes the dissertation is in the repository, but the publisher disallows the distribution of the full text of the dissertation or part of the dissertation. It must be under embargo. Because DSpace 1.3.2 did not support this kind of behaviour, we implemented it.

How it works

Bitstreams all have a resource policy. Most of the time this is a anonymous read policy. If a bitstream has no anonymous read policy then it cannot be read, so effectively it is under embargo.

Now if you could set an end date for that embargo, it would be so much nicer. But this is already possible:

DSpace allows resource policies to have a start date and an end date: so if you set an anonymous read policy with a start date of januari 1 2009, it is effectively under embargo until januari 1 2009.

Now all this change does, is:

  • allow a submitter to set an embargo date when uploading a new file (optional)
  • allow an administrator to set the start and end dates of a resource policy for bitstreams
  • display the embargo date in a nice way when displaying an item with bitstream information, and disabling the link to that bitstream if it is under embargo (an administrator can always link to the bitstream)

See it at

Here you can see the functionality at work:

https://openaccess.leidenuniv.nl/handle/1887/12291

https://openaccess.leidenuniv.nl/handle/1887/12151

Note: our DSpace implementation has lots of other modifications, including a very different look and feel. This is not included in this patch.

Below are some screenshots from the submission process and from the administrators pages

Which DSpace version

The modifications I made were made to Dspace 1.3.2. This is also the version of DSpace were this functionality was tested extensively.

This does not mean that this will not work on DSpace 1.4 or higher, it just is not tested yet.

The installation steps below are for DSpace 1.3.2, but will give some pointers for DSpace 1.4, so it should be possible to implement this on DSpace 1.4. If you do this and have any suggestions, please share them!

How to install

Many files need some changes, so keep a backup copy of the changed files in case it goes sour...

Step 1: Change Java files

Change the following Java files (make a backup copy!)

change file src/org/dspace/app/webui/jsptag/ItemTag.java

  • add some new import statements:
  import org.dspace.authorize.AuthorizeManager;
  import org.dspace.core.Context;
  import java.util.Date;
  import java.util.Calendar
  • change near line 712 from
         out
                 .print(bsLink
                         + LocaleSupport
                                 .getLocalizedMessage(
                                         pageContext,
                                         "org.dspace.app.webui.jsptag.ItemTag.view")
                         + "</a></td></tr>");
    

to this:

   Date date = bitstreams[k].getEmbargoEndDate();
   if (bitstreams[k].isEmbargoed() && (handle != null || date != null))
   \{
     out.println(LocaleSupport.getLocalizedMessage(pageContext,
                          "org.dspace.app.webui.jsptag.ItemTag.underEmbargo"));
     if (date != null)
     \{
       out.print(LocaleSupport.getLocalizedMessage(pageContext,
               "org.dspace.app.webui.jsptag.ItemTag.embargoUntil"));
       Calendar cal = Calendar.getInstance();
       cal.setTime(date);
       out.print(cal.get(Calendar.DAY_OF_MONTH));
       out.print("/");
       out.print(cal.get(Calendar.MONTH) + 1);
       out.print("/");
       out.println(cal.get(Calendar.YEAR));
     \}
   \}
   Context context = null;
   boolean allowed = false;
   try \{
     context = UIUtil.obtainContext(request);
     allowed = AuthorizeManager.authorizeActionBoolean(context,bitstreams[k],Constants.READ);
   \}
   catch (SQLException e) \{
     allowed = false;
   \}
   if (!bitstreams[k].isEmbargoed() || allowed)
   \{
     out.println(bsLink
                    + LocaleSupport
                             .getLocalizedMessage(
                                     pageContext,
                                     "org.dspace.app.webui.jsptag.ItemTag.view")
                     + "</a>");
   \}
  out.println("</td></tr>");

change file src/org/dspace/app/webui/servlet/admin/AuthorizeAdminServlet.java

  • add some new import statements:
  import java.util.Date;
  import java.util.Calendar
  • add a new function:
  private void applyPolicyEndAndStartDate(HttpServletRequest request, ResourcePolicy policy) \{
    int start_year = UIUtil.getIntParameter(request, "start_year");
    int start_month = UIUtil.getIntParameter(request, "start_month");
    int start_day = UIUtil.getIntParameter(request, "start_day");
    int end_year = UIUtil.getIntParameter(request, "end_year");
    int end_month = UIUtil.getIntParameter(request, "end_month");
    int end_day = UIUtil.getIntParameter(request, "end_day");
    Date startDate = null;
    Date endDate = null;
    if (start_year > 0 && start_month > 0 && start_day > 0)
    \{
      Calendar cal = Calendar.getInstance();
      start_month--;
      cal.set(start_year,start_month,start_day,0,0,0);
      startDate = cal.getTime();
    \}
    if (end_year > 0 && end_month > 0 && end_day > 0)
    \{
      Calendar cal = Calendar.getInstance();
      end_month--;
      cal.set(end_year,end_month,end_day,0,0,0);
      endDate = cal.getTime();
    \}
    // modify the policy
    policy.setStartDate(startDate);
    policy.setEndDate(endDate);
  \}
  • change the code starting on line 580 from
  else if (item_id != -1)
  \{
      item = Item.find(c, item_id);
      // modify the policy
      policy.setAction(action_id);
      policy.setGroup(group);
      policy.update();
      // show edit form!
      prepItemEditForm(c, request, item);
      display_page = "/dspace-admin/authorize-item-edit.jsp";
  \}

to this:

  else if (item_id != -1)
  \{
      item = Item.find(c, item_id);
      // modify the policy
      policy.setAction(action_id);
      policy.setGroup(group);
      applyPolicyEndAndStartDate(request,policy);
      policy.update();
      // show edit form!
      prepItemEditForm(c, request, item);
      display_page = "/dspace-admin/authorize-item-edit.jsp";
  \}

So, only "applyPolicyEndAndStartDate(request,policy);" was added below "policy.setGroup(group);"

change file src/org/dspace/app/webui/servlet/SubmitServlet.java

  • add some new import statements:
  import org.dspace.authorize.ResourcePolicy;
  import org.dspace.eperson.Group;
  import java.util.Date;
  import java.util.Calendar
  • add in the function processChooseFile just before (line 1066 in 1.3.2 or line 1073 in 1.4.2):
  // Update to DB
  b.update();
  item.update();

the following lines:

  // set the embargo, if any
  int embargo_year = UIUtil.getIntParameter(wrapper, "embargo_year");
  int embargo_month = UIUtil.getIntParameter(wrapper, "embargo_month");
  int embargo_day = UIUtil.getIntParameter(wrapper, "embargo_day");
  if (embargo_year > 0 && embargo_month > 0 && embargo_day > 0)
  \{
    Date embargoDate = null;
    Calendar cal = Calendar.getInstance();
    embargo_month--;
    cal.set(embargo_year,embargo_month,embargo_day,0,0,0);
    embargoDate = cal.getTime();
    ResourcePolicy rp = ResourcePolicy.create(context);
    Group group = Group.find(context,0);
    rp.setResource(b);
    rp.setAction(Constants.READ);
    rp.setGroup(group);
    rp.setStartDate(embargoDate);
    rp.update();
  \}

Note: sometimes the underscores are not displayed correctly in wiki, but if you copy and paste it, they reappear.

change file src/org/dspace/authorize/ResourcePolicy.java

In this file a bug exists, in version 1.3.2 and also in version 1.4.2. It is clear that this code is not being used, but now it is, so patch the bug:

  • in line 353 change this
      if (now.after(sd))
    
    to this:
      if (now.after(ed))
    

change file src/org/dspace/content/Bitstream.java

  • add the following functions:
  public boolean isEmbargoed() \{
  // may be a better name is: isUnderEmbargo() ?
    ResourcePolicy policy;
    try \{
      policy = getAnonymousReadPolicy();
    \}
    catch (SQLException e) \{
      return true;
    \}
    if (policy == null) \{
      return true;
    \}
    else \{
      return !policy.isDateValid();
    \}
  \}

  public Date getEmbargoEndDate() \{
    ResourcePolicy policy;
    try \{
      policy = getAnonymousReadPolicy();
    \}
    catch (SQLException e) \{
      return null;
    \}
    if (policy == null) \{
      return null;
    \}
    else \{
      return policy.getStartDate();
    \}
  \}

  public ResourcePolicy getAnonymousReadPolicy() throws SQLException \{
    ResourcePolicy thePolicy = null;
    List policies = AuthorizeManager.getPoliciesActionFilter(bContext, this, Constants.READ);
    for (Iterator i = policies.iterator(); i.hasNext(); ) \{
      ResourcePolicy policy = (ResourcePolicy)i.next();
      if (policy.getGroupID() == 0) \{ // anonymous group
        thePolicy = policy;
      \}
    \}
    return thePolicy;
  \}

change file src/org/dspace/content/Item.java

  • change in the function replaceAllBitstreamPolicies the following lines (near line 1613 in 1.3.2 or line 1836 in 1.4.2):
            Bitstream mybitstream = bs[j];
            // change bitstream policies
            AuthorizeManager.removeAllPolicies(ourContext, bs[j]);
            AuthorizeManager.addPolicies(ourContext, newpolicies, bs[j]);
    

with this:

                Bitstream mybitstream = bs[j];
                // keep anonymous read policies with a start date,
                // because that is a policy that enables an embargo
                ResourcePolicy embargo = bs[j].getAnonymousReadPolicy();
                // change bitstream policies
                AuthorizeManager.removeAllPolicies(ourContext, bs[j]);
                AuthorizeManager.addPolicies(ourContext, newpolicies, bs[j]);
                // re-apply the embargo
                if (embargo != null)
                \{
                  ResourcePolicy arp = bs[j].getAnonymousReadPolicy();
                  if (arp == null) \{
                    Group anonymousGroup = Group.find(ourContext, 0);
                    AuthorizeManager.addPolicy(ourContext, bs[j], Constants.READ, anonymousGroup);
                    arp = bs[j].getAnonymousReadPolicy();
                  \}
                  arp.setStartDate(embargo.getStartDate());
                  arp.setEndDate(embargo.getEndDate());
                  arp.update();
                \}

Step 2: Change JSP files

Change the following JSP files:

The best way to do this is by modifying a local copy: so copy a file from jsp/ to jsp/local (include the path), and then modify the local version.

change file jsp/local/dspace-admin/authorize-item-edit.jsp

  • add near line 80
      <%@ page import="java.util.Date"      %>
      <%@ page import="java.util.Calendar"      %>
    
  • change lines 268 thru 270 (or 269 thru 271 for 1.4.2) to
      <th class="oddRowOddCol"><strong><fmt:message key="jsp.dspace-admin.general.period"/></strong></th>
      <th class="oddRowEvenCol"><strong><fmt:message key="jsp.dspace-admin.authorize-item-edit.eperson" /></strong></th>
      <th class="oddRowOddCol"><strong><fmt:message key="jsp.dspace-admin.general.group"/></strong></th>
      <th class="oddRowEvenCol">&nbsp;</th>
    
  • add before line 287 (the <td> with rp.getActionText in it):
      <td class="<%= row %>RowOddCol">
      <%
       Date date;
       if ((date = rp.getStartDate()) != null) \{
         Calendar start_cal = Calendar.getInstance();
         start_cal.setTime(date);
         out.print(start_cal.get(Calendar.DAY_OF_MONTH));
         out.print("/");
         out.print(start_cal.get(Calendar.MONTH) + 1);
         out.print("/");
         out.println(start_cal.get(Calendar.YEAR));
       \}
       out.println("-");
       if ((date = rp.getEndDate()) != null) \{
         Calendar end_cal = Calendar.getInstance();
         end_cal.setTime(date);
         out.print(end_cal.get(Calendar.DAY_OF_MONTH));
         out.print("/");
         out.print(end_cal.get(Calendar.MONTH) + 1);
         out.print("/");
         out.println(end_cal.get(Calendar.YEAR));
       \}
      %>
      </td>
    

change file jsp/local/dspace-admin/authorize-policy-edit.jsp

  • add near line 73
     <%@ page import="java.util.Date"                      %>
     <%@ page import="java.util.Calendar"                  %>
    
  • add near line 81
      Calendar start_cal = null;
      Calendar end_cal = null;
      Date date = null;
      if ((date = policy.getStartDate()) != null) \{
        start_cal = Calendar.getInstance();
        start_cal.setTime(date);
      \}
      if ((date = policy.getEndDate()) != null) \{
        end_cal = Calendar.getInstance();
        end_cal.setTime(date);
      \}
    
  • change line 109 from
     <form action="<%= request.getContextPath() %>/dspace-admin/authorize" method="post">
    
    to this:
      <form action="<%= request.getContextPath() %>/dspace-admin/authorize" method="post" <%=(resourceType == 0)?"onSubmit=\"return validate(this)\"":"" %> >
    
  • add after line 146
          <% if (resourceType == Constants.BITSTREAM) \{ %>
          <tr>
            <th id="t3"><label for="tstartdate"><fmt:message key="jsp.dspace-admin.general.startdate-colon"/></label></th>
            <td headers="t3">
              <% if (start_cal == null)
                 \{ %>
              <input type="text" name="start_day" value="" maxlength="2" size="2"/> /
              <input type="text" name="start_month" value="" maxlength="2" size="2"/> /
              <input type="text" name="start_year" value="" maxlength="4" size="4"/>&nbsp;
              <fmt:message key="jsp.dspace-admin.general.startdate-help"/>
              <% \} else
                 \{ %>
              <input type="text" name="start_day" value="<%=start_cal.get(Calendar.DAY_OF_MONTH)%>" maxlength="2" size="2"/> /
              <input type="text" name="start_month" value="<%=start_cal.get(Calendar.MONTH) + 1%>" maxlength="2" size="2"/> /
              <input type="text" name="start_year" value="<%=start_cal.get(Calendar.YEAR)%>" maxlength="4" size="4"/>&nbsp;
              <fmt:message key="jsp.dspace-admin.general.startdate-help"/>
              <% \} %>
            </td>
          </tr>
          <tr>
            <th id="t4"><label for="tstartdate"><fmt:message key="jsp.dspace-admin.general.enddate-colon"/></label></th>
            <td headers="t4">
              <% if (end_cal == null)
                 \{ %>
              <input type="text" name="end_day" value="" maxlength="2" size="2"/> /
              <input type="text" name="end_month" value="" maxlength="2" size="2"/> /
              <input type="text" name="end_year" value="" maxlength="4" size="4"/>&nbsp;
              <fmt:message key="jsp.dspace-admin.general.enddate-help"/>
              <% \} else
                 \{ %>
              <input type="text" name="end_day" value="<%=end_cal.get(Calendar.DAY_OF_MONTH)%>" maxlength="2" size="2"/> /
              <input type="text" name="end_month" value="<%=end_cal.get(Calendar.MONTH) + 1%>" maxlength="2" size="2"/> /
              <input type="text" name="end_year" value="<%=end_cal.get(Calendar.YEAR)%>" maxlength="4" size="4"/>&nbsp;
              <fmt:message key="jsp.dspace-admin.general.enddate-help"/>
              <% \} %>
            </td>
          </tr>
      <script language="Javascript">
        function validate(aForm) \{
          var ssy = aForm.start_year.value;
          var ssm = aForm.start_month.value;
          var ssd = aForm.start_day.value;
          var sey = aForm.end_year.value;
          var sem = aForm.end_month.value;
          var sed = aForm.end_day.value;
          if ( ! onlyDigits(ssy+ssm+ssd)) \{
            if (!confirm("Start date contains not only digits.\nUse this date anyway?")) \{
              aForm.start_day.focus();
              return false;
            \}
          \}
          if ( ! onlyDigits(sey+sem+sed)) \{
            if (!confirm("End date contains not only digits.\nUse this date anyway?")) \{
              aForm.end_day.focus();
              return false;
            \}
          \}
          var sy = parseInt(ssy,10);
          var sm = parseInt(ssm,10);
          var sd = parseInt(ssd,10);
          var ey = parseInt(sey,10);
          var em = parseInt(sem,10);
          var ed = parseInt(sed,10);
          if ( ! isValidDate(sy,sm,sd)) \{
            if (!confirm("Start date is not a valid date, it should be like dd/mm/yyy.\nUse this date anyway?")) \{
              aForm.start_day.focus();
              return false;
            \}
          \}
          if ( ! isValidDate(ey,em,ed) ) \{
            if (!confirm("End date is not a valid date, it should be like dd/mm/yyy.\nUse this date anyway?")) \{
              aForm.end_day.focus();
              return false;
            \}
          \}
          if (!(isNaN(sy) || isNaN(sm) || isNaN(sd) || isNaN(ey) || isNaN(em) || isNaN(ed))) \{
            if (!(sy <= ey && sm <= em && sd <= ed)) \{
              if (!confirm("Start date is greater than end date. Use these dates anyway?")) \{
                aForm.end_day.focus();
                return false;
              \}
            \}
          \}
          return true;
        \}
        function isValidDate(y,m,d) \{
           if (isNaN(y) && isNaN(m) && isNaN(d)) return true;
           if (isNaN(y) || isNaN(m) || isNaN(d)) return false;
           if (y < 1900 || y > 2100) return false;
           if (m < 1 || m > 12) return false;
           var dof = (y%4==0&&(y%100!=0||y%400==0))?29:28;
           var max = (m==4||m==6||m==9||m==11?30:(m==2?dof:31));
           if (d < 1 || d > max) return false;
           return true;
        \}
        function onlyDigits(s) \{
          for(var i = 0;i<s.length;i++) \{
            if (isNaN(s.charAt(i))) \{
              return false;
            \}
          \}
          return true;
        \}
      </script>
          <% \} %>
    

Step 3: Change configuration

Add the following lines to config/language-packs/Messages.properties:

 jsp.dspace-admin.general.startdate-colon = Start date:
 jsp.dspace-admin.general.enddate-colon = End date:
 jsp.dspace-admin.general.startdate-help = (dd/mm/yyyy or blank)
 jsp.dspace-admin.general.enddate-help = (dd/mm/yyyy or blank)
 jsp.dspace-admin.general.period = Period
 jsp.submit.choose-file.embargohead =If the file is under embargo, please enter the end date of the embargo below
 jsp.submit.choose-file.embargo =Embargo Ends on:
 jsp.submit.choose-file.embargohelp = (dd/mm/yyyy, is optional)
 jsp.submit.upload-file-list.tableheading7 = Embargo
 jsp.submit.upload-file-list.no-embargo = None
 org.dspace.app.webui.jsptag.ItemTag.underEmbargo = Under Embargo
 org.dspace.app.webui.jsptag.ItemTag.embargoUntil = until

Step 4: Change more JSP files (optional)

This step is optional; if you do this the submitter can choose an embargo date when he/she uploads a file.

change file jsp/local/submit/choose-file.jsp

  • add after line 140 (just before </tabel>):
      <tr>
          <td colspan="2">&nbsp;</td>
      </tr>
      <tr>
          <td class="submitFormHelp" colspan="2">
          <fmt:message key="jsp.submit.choose-file.embargohead"/>
          </td>
      </tr>
      <tr>
        <td class="submitFormLabel"><label for="tdescription"><fmt:message key="jsp.submit.choose-file.embargo"/></label></td>
          <td>
            <input type="text" name="embargo_day" value="" maxlength="2" size="2"/> /
            <input type="text" name="embargo_month" value="" maxlength="2" size="2"/> /
            <input type="text" name="embargo_year" value="" maxlength="4" size="4"/>&nbsp;
            <fmt:message key="jsp.submit.choose-file.embargohelp"/>
          </td>
      </tr>

change file jsp/local/submit/show-uploaded-file.jsp

  • add some page imports:
  <%@ page import="org.dspace.authorize.ResourcePolicy" %>
  <%@ page import="java.util.Date"      %>
  <%@ page import="java.util.Calendar"      %>
  • add a table header near line 128 (just before showChecksums code)
   <th id="t8" class="oddRowEvenCol"><fmt:message key="jsp.submit.upload-file-list.tableheading7"/></th>
  • add code after line 159 (just before showChecksums code)
       <td headers="t8" class="evenRowEvenCol">
           <%
              ResourcePolicy rp = bitstream.getAnonymousReadPolicy();
              Date date;
              if ((rp != null) && (date = rp.getStartDate()) != null) \{
                Calendar start_cal = Calendar.getInstance();
                start_cal.setTime(date);
                out.print(start_cal.get(Calendar.DAY_OF_MONTH));
                out.print("/");
                out.print(start_cal.get(Calendar.MONTH) + 1);
                out.print("/");
                out.println(start_cal.get(Calendar.YEAR));
              \}
              else \{
           %>
         <fmt:message key="jsp.submit.upload-file-list.no-embargo"/>
           <%
              \}
           %>
       </td>

change file jsp/local/submit/upload-file-list.jsp

  • add some page imports:
  <%@ page import="org.dspace.authorize.ResourcePolicy" %>
  <%@ page import="java.util.Date"      %>
  <%@ page import="java.util.Calendar"      %>
  • add a table header near line 109 (just before showChecksums code)
   <th id="t8" class="oddRowOddCol"><fmt:message key="jsp.submit.upload-file-list.tableheading7"/></th>
  • add code after line 180 (just before showChecksums code)
    <td headers="t8" class="<%= row %>RowOddCol">
        <%
           ResourcePolicy rp = bitstreams[i].getAnonymousReadPolicy();
           Date date;
           if ((rp != null) && (date = rp.getStartDate()) != null) \{
             Calendar start_cal = Calendar.getInstance();
             start_cal.setTime(date);
             out.print(start_cal.get(Calendar.DAY_OF_MONTH));
             out.print("/");
             out.print(start_cal.get(Calendar.MONTH) + 1);
             out.print("/");
             out.println(start_cal.get(Calendar.YEAR));
           \}
           else \{
        %>
      <fmt:message key="jsp.submit.upload-file-list.no-embargo"/>
        <%
           \}
        %>
    </td>
  • make sure that the Odd/Even Cols are okay again...

Step 5: build / install / restart

Do the stuff you normally do when deploying a new version of DSpace.

batch setting embargoes on a list of handles

With the following perl script you can set or remove the embargo of a list of items in one go:

Missing File:  Setembargo.pl 

The script is executed from the command line and gets 2 arguments:

  • the embargo date, or the word none (all embargoes will be lifted), or the word forever (to get a never ending embargo)
  • the name of a file. The file contains the handles of the items where the bitstreams need an embargo. This file can be a mapfile, so after you batch import some records, you can immediately embargo them.

May be you must alter the script, because of database issues. Please change this:

my $PSQL = "/usr/bin/psql";

in something more appropriate, like:

my $PSQL = "/usr/bin/psql -d dspace -U NAME -h HOSTNAME -p PORT";

Contact me

If you need help, or have any comments, or you just want to inform me that you (are going to) use this, please contact me at: schaik at library dot leidenuniv dot nl

</html>