:::

DSpace新增input-type——安裝篇

10月 15, 2008 , 1 Comments Edit Post

DSpace新增input-type目錄

  1. 安裝篇
  2. TextEditor篇
  3. FileUpload篇
  4. XMLMetadata篇

目前的DSpace 1.4.2~1.5雖然提供了很多input-type,像onebox(單欄)、textarea(多行)、dropdown(下拉式),但是使用起來還是覺得不夠,尤其是資料儲存無法像樹狀結構一樣靈活,在設計Metadata的時候更是一個很大的限制(雖然很多架設DSpace的人都沒有更動過input-forms的樣子)。

為了改進DSpace的不足,我以DSpace 1.5版開發出以下三種輸入表單型態:

texteditor

把原本的textarea以FCKeditor取代,並設定自動隱藏工具列。最後輸出資料則是html程式碼。

image

fileupload

在edit-metadata的步驟中,透過AJAX技術直接使用DSpace上傳步驟的功能。

上傳前:image

上傳後:image

xmlmetadata

可允許樹狀結構的metadata,並允許特定節點重複,可更靈活地儲存資料。一樣具備onebox、dropdown、textarea、texteditor、fileupload等多種輸入類型。

image

此篇介紹如何修改DSpace 1.5的各個檔案,以呈現出以上的功能,使用與設定則留到其他篇再講吧!


安裝環境與路徑縮寫說明

DSpace 1.5版本跟之前的1.4.2版本各程式存放的路徑上有很大的差別,此處我以1.5版為主,安裝在CentOS 5 Final上,JSP伺服器是Apache Tomcat 6.0.16

以下說明各路徑的簡寫:

[dspace-source]:DSpace原始檔的位置,通常位於「/opt/dspace-1.5.0-src-release/」

[dspace-jspui-webapp]:JSPUI原始碼路徑,通常位於「[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp」之中。根據文件說明,修改之後存放的檔案請放到「[dspace-source]/dspace/modules/jspui/src/main/webapp/」之中。

[dspace-jspui-api]:JSPUI的API,通常位於「/opt/dspace-1.5.0-src-release/dspace-jspui/dspace-jspui-api/src/main/java/org/dspace/app/webui/」。

[dspace-api]:DSpace的API,許多核心程式都擺在這邊,通常位於「[dspace-source]/dspace-api/src/main/java/org/dspace/」

[dspace]:DSpace安裝完成之後的目的地,包括input-forms.xml設定檔都要在這邊修改。預設的位置是「/dspace/」。

[tomcat]:JSP伺服器的安裝位置,通常是在「/opt/apache-tomcat-6.0.16/」裡面。

那麼以下就是安裝與修改的步驟囉!我會列出「路徑與檔名」、「我的程式碼下載」、「程式碼說明」以方便大家修改或安裝。因為細節繁雜,所以有可能有所遺漏,請在下方的回應欄盡量批評吧!


[dspace-jspui-webapp]/extension/...... (extension.20081018.zip)

這是一個新開啟的資料夾,裡面擺放我撰寫的JavaScript、CSS程式,請下載附件extension.zip,並擺放到該路徑去。裡面又分成幾個部份:(以下檔案所指的根目錄就是「[dspace-jspui-webapp]/extension/」,之後其他敘述也同這個道理)

/jquery.js:JavaScript的函式庫jQuery,在此使用的是1.2.1版本,讓JavaScript程式撰寫起來輕鬆很多,我撰寫的JavaScript都會用到jQuery。

/fckeditor/FCKeditor 2.6.3版本,一種JavaScript的網頁內嵌所見即得編輯器,可用類似Word的操作介面、建立HTML程式碼。用於input-type: texteditor,可改善DSpace 1.5只能顯示單調文字的缺點。

/fckeditor/fckeditor_display_toggle.js:用來自動隱藏/顯示工具列的函式。

/ajaxfileupload/ajaxfileupload.js:jQuery的Plugin,我改寫成適合DSpace檔案上傳的版本。

/ajaxfileupload/ajaxfileupload_fn.js:供input-type: fileupload呼叫ajaxfileupload.js的函式,裡面可設定fileupload的參數,但照以下安裝步驟的話是不需要修改此設定的。

/ajaxfileupload/ajaxfileupload_fnXML.js:給XMLMetadata呼叫ajaxfileupload.js的函式。

/xmlmetadata/dspace-inputtype-xml.js:XMLMetadata主要的JavaScript函式程式碼。

/xmlmetadata/dspace-inputtype-xml.css:XMLMetadata在呈現表單跟表格時的樣式表。

/xmlmetadata/ui.datepicker.js:jQuery的UI:Datepicker,可以方便挑選日期,用於XMLMetdata的input-type: date,我想這應該比DSpace原本的date好用很多。

/xmlmetadata/flora.datepicker.css:Datepicker的樣式表。

/display-item/metadata-value-display.js:在Display Item頁面時,針對fileupload跟xmlmetadata兩種資料調整呈現的樣貌。

這些程式只用短短的半個月時間寫完,也還沒做嚴格的測試,如果之後有改良的版本,會直接在此更新,並加上日期版本。


[dspace-jspui-webapp]/submit/...... (submit.20081015.zip)

這邊我修改了在進行上傳步驟的程式碼,以下一一說明。

/edit-metadata.jsp

image

DSpace描述階段的程式碼,為了能夠新增這三種input-type,在這一頁下了很多功夫。以下是修改細項:

修改1:引用所需的JavaScript跟CSS檔案。 請在<dspace>標籤開始之後插入這些引用吧。(紅色部份是新增的程式碼)

<dspace:layout locbar="off" navbar="off" titlekey="jsp.submit.edit-metadata.title">

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/jquery.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fn.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fnXML.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor_display_toggle.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/ui.datepicker.js"></script>
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/flora.datepicker.css" type="text/css" media="screen" title="Flora (Default)">
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.css" type="text/css" media="screen">

修改2:定義要丟給新增函式的變數。包括workspaceItem的ID(目前遞交作業的ID)、nonInternalBistreams的ID(哪些是公開的檔案)、hasMultipleFiles(是否允許多檔案上傳)、defaultValue(XMLMetadata的預設呈現樣貌)。請加在設定inputType、label跟closedVocabulary的後面吧。(紅色部份是新增的程式碼)



       String label = inputs[z].getLabel();
       boolean closedVocabulary = inputs[z].isClosedVocabulary();

>     //For fileupload
     int workspaceItemID = si.getSubmissionItem().getID();
     Bitstream[] bitstreams = si.getSubmissionItem().getItem().getNonInternalBitstreams();
     ArrayList nonInternalBistreamsID = new ArrayList();  //new Int(bitstreams);
     for (int i = 0; i < bitstreams.length; i++)
         nonInternalBistreamsID.add(bitstreams[i].getID());
     boolean hasMultipleFiles = si.getSubmissionItem().hasMultipleFiles();
    
     //For XMLMetadata & other
     String defaultValue = inputs[z].getDefaultValue();

修改3:新增input-type呼叫函式的判斷式。在下方有if else的判斷式,程式會依照input-type的值不同,呼叫不同的函式,那麼我們就把這三個新增的函式也加入判斷之中,新增在list跟onebox之間吧。(紅色部份是新增的程式碼)

       else if (inputType.equals("list"))
       {
          doList(out, item, fieldName, dcSchema, dcElement, dcQualifier,
               repeatable, inputs[z].getPairs(), label);
       }
     else if (inputType.equals("texteditor"))
       {
         doTextEditor(out, item, fieldName, dcSchema, dcElement, dcQualifier,
                repeatable, fieldCountIncr, label, pageContext, vocabulary,
               closedVocabulary, request.getContextPath() + "/extension/fckeditor/");
       }
     else if (inputType.equals("fileupload"))
       {
         doFileUpload(out, item, fieldName, dcSchema, dcElement, dcQualifier,
              repeatable, fieldCountIncr, label, pageContext, vocabulary,
              closedVocabulary, workspaceItemID, nonInternalBistreamsID,
           hasMultipleFiles);
       }
     else if (inputType.equals("xmlmetadata"))
       {
         doXMLMetadata(out, item, fieldName, dcSchema, dcElement, dcQualifier,
                repeatable, fieldCountIncr, label, pageContext, vocabulary,
               closedVocabulary, workspaceItemID,
            nonInternalBistreamsID, hasMultipleFiles, defaultValue);
       }

       else
       {
         doOneBox(out, item, fieldName, dcSchema, dcElement, dcQualifier,
              repeatable, fieldCountIncr, label, pageContext, vocabulary,
              closedVocabulary);
       }

修改4:宣告doTextEditor()、doFileUpload()跟doXMLMetadata()。請寫在doList結尾之後吧(紅色的部份是新增的程式碼):

          }//end doList

    void doTextEditor(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary, String sBasePath)
      throws java.io.IOException
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;

      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++)
      {
   if (i == 0)
      sb.append("<tr><td class=\"submitFormLabel\">")
        .append(label)
        .append("</td>");
   else
      sb.append("<tr><td> </td>");

         if (i < defaults.length)
           val = defaults[i].value;
         else
           val = "";

         sb.append("<td colspan=\"2\">");

         String textareaName = fieldName;
         if (repeatable && i>0)
            textareaName = textareaName + "_" + i;

         sb.append("<textarea name=\"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         sb.append("\" rows=\"4\" cols=\"45\"")
           .append(hasVocabulary(vocabulary)&&closedVocabulary?" readonly=\"readonly\" ":"")
           .append("   >")
           .append(val)
     .append("</textarea>")
     .append(doControlledVocabulary(fieldName + (repeatable?"_" + i:""), pageContext, vocabulary));
      

       //FCKeditor ReplaceTextarea
      
       sb.append("<script type=\"text/javascript\">\n")
         .append("  var oFCKeditor = new FCKeditor( \"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         sb.append("\" ) ;\n")
       .append("  oFCKeditor.Config[\"ToolbarStartExpanded\"] = false ;\n")
         .append("  oFCKeditor.BasePath  = \""+sBasePath+"\" ;\n")
         .append("  oFCKeditor.ReplaceTextarea() ;\n")
         .append("</script>\n");

     sb.append("</td>\n");

   if (repeatable && i < defaults.length)
   {
      // put a remove button next to filled in values
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
        .append("_remove_")
        .append(i)
//        .append("\" value=\"Remove This Entry\"/> </td></tr>");
        .append("\" value=\"")
           .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
           .append("\"/> </td></tr>");
   }
   else if (repeatable && i == fieldCount - 1)
   {
      // put a 'more' button next to the last space
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
//        .append("_add\" value=\"Add More\"/> </td></tr>");
        .append("_add\" value=\"")
        .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
        .append("\"/> </td></tr>");
   }
   else
   {
      // put a blank if nothing else
      sb.append("<td> </td></tr>");
   }
      }

      out.write(sb.toString());
    }  //void doTextEditor()

    void doFileUpload(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary,
    int workspaceItemID, ArrayList nonInternalBistreamsID, boolean hasMultipleFiles)
      throws java.io.IOException
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
    fieldCountIncr = 1;
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;
    
      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++)
      {
   if (i == 0)
      sb.append("<tr><td class=\"submitFormLabel\">")
        .append(label)
        .append("</td>");
   else
      sb.append("<tr><td> </td>");

         if (i < defaults.length)
           val = defaults[i].value.replaceAll("\"", """);
         else
           val = "";
      
    if (!val.equals(""))
    {
      String[] tempID = val.split("/");
      if (tempID.length > 2)
      {
        //String id = tempID[(tempID.length - 2)];
      int id = Integer.parseInt(tempID[(tempID.length - 2)]);
      
      int index = nonInternalBistreamsID.indexOf(id);
      if (index == -1)  continue;
      }
    }
      
         String inputName = fieldName;
         if (repeatable && i>0)
           inputName = inputName + "_" + i;


       sb.append("<td colspan=\"2\">");

    if (hasMultipleFiles == true)
    {
    sb.append("\n<input type=\"text\" id=\""+inputName+"\" name=\""+inputName+"\" value=\""+val+"\" style=\"display:none\"   />\n")
      .append("  <input type=\"file\" onchange=\"jQuery(this).nextAll('button.fileupload-do:first').click()\" />\n")
      .append("  <span></span>\n")
      .append("  <button class=\"fileupload-do\" onclick=\"return ajaxFileUpload(this, "+workspaceItemID+", '"+label+"');\" type=\"button\">Upload</button>\n");
      
       if (!val.equals(""))
       {
      sb.append("  <script type=\"text/javascript\">")
        .append("  ajaxFileUploadExist(\""+inputName+"\");")
        .append("</script>");
       }
    }  //if (hasMultipleFiles == true)
    else
    {
      sb.append("Please check \""+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.initial-questions.elem3")+"\"! \n");
    }
       sb.append("</td>\n");
    

   if (repeatable && i < defaults.length)
   {
      // put a remove button next to filled in values
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
        .append("_remove_")
        .append(i)
//        .append("\" value=\"Remove This Entry\"/> </td></tr>");
        .append("\" value=\"")
           .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
           .append("\" style=\"display:none\" ")
      .append("/>")
      .append("<button type=\"button\" onclick=\"ajaxFileUploadRemove(this, "+workspaceItemID+")\">"+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove")+"</button>")
      .append(" </td></tr>");
   }
   else if (repeatable && i == fieldCount - 1)
   {
      // put a 'more' button next to the last space
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
//        .append("_add\" value=\"Add More\"/> </td></tr>");
        .append("_add\" value=\"")
        .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
        .append("\"/> </td></tr>");
   }
   else
   {
      // put a blank if nothing else
      sb.append("<td>")
      .append("  <button class=\"fileupload-cancel\" onclick=\"return ajaxFileUploadCancel(this, "+workspaceItemID+")\" style=\"display:none\" type=\"button\">"+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove")+"</button>\n")
      .append(" </td></tr>");
   }
      }

      out.write(sb.toString());
    }  //end of doFileUpload

    void doXMLMetadata(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary,
    int workspaceItemID, ArrayList nonInternalBistreamsID, boolean hasMultipleFiles, String defaultValue)
      throws java.io.IOException
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;

      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++)
      {
   if (i == 0)
      sb.append("<tr><td class=\"submitFormLabel\">")
        .append(label)
        .append("</td>");
   else
      sb.append("<tr><td> </td>");

         if (i < defaults.length)
           val = defaults[i].value;
         else if (defaultValue != null)
           val = defaultValue;
     else
       val = "";
    
         sb.append("<td colspan=\"2\">");

         String textareaName = fieldName;
         if (repeatable && i>0)
            textareaName = textareaName + "_" + i;

         sb.append("<textarea id=\""+textareaName+"\" name=\"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         sb.append("\" rows=\"4\" cols=\"45\"")
           .append(hasVocabulary(vocabulary)&&closedVocabulary?" readonly=\"readonly\" ":"")
           .append("   >")
           .append(val)
     .append("</textarea>")
     .append(doControlledVocabulary(fieldName + (repeatable?"_" + i:""), pageContext, vocabulary));
      

       //XMLMetadata Form Creator
       if (!val.equals(""))
     {
       sb.append("\n\n<script type=\"text/javascript\">\n")
         .append("var xm = new XMLMetadata(\""+textareaName+"\");\n")
         .append("xm.workspaceItemID = "+workspaceItemID+";\n")
         .append("xm.fieldTitle = '"+label+"';\n");
       if (hasMultipleFiles == false)
       {
         sb.append("xm.hasMultipleFiles = false;\n");
        sb.append("xm.langNotHasMultipleFiles = \"Please check \'"+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.initial-questions.elem3")+"\'!\";\n");
       }
      
       if (nonInternalBistreamsID.size() != 0)
       {
         String nIBIDary = "[";
         for (int b = 0; b < nonInternalBistreamsID.size(); b++)
         {
          if (b > 0)
            nIBIDary = nIBIDary + ",";
          nIBIDary = nIBIDary + nonInternalBistreamsID.get(b);
         }
         nIBIDary = nIBIDary + "]";
         sb.append("xm.nonInternalBistreamsID = "+nIBIDary+";\n");
       }
       sb.append("xm.createRootForm();\n");
       sb.append("</script>\n");
      }

     sb.append("</td>\n");

   if (repeatable && i < defaults.length)
   {
      // put a remove button next to filled in values
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
        .append("_remove_")
        .append(i)
//        .append("\" value=\"Remove This Entry\"/> </td></tr>");
        .append("\" value=\"")
           .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
           .append("\"/> </td></tr>");
   }
   else if (repeatable && i == fieldCount - 1)
   {
      // put a 'more' button next to the last space
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
//        .append("_add\" value=\"Add More\"/> </td></tr>");
        .append("_add\" value=\"")
        .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
        .append("\"/> </td></tr>");
   }
   else
   {
      // put a blank if nothing else
      sb.append("<td> </td></tr>");
   }
      }

      out.write(sb.toString());
    }  //void doXMLMetadata()

%>

doTextEditor()跟doXMLMetadata()是參考doTextArea()而來的,而doFileUpload()則是參考doOneBox()。

修改5:移除doPersonalName()、doSeriesNumber()跟doTwoBox()的寬度限制。不知為何這些函式都把表格寬度鎖死了,他們裡面都會擁有類似的程式碼,我舉doPersonalName()的設定為例:(紅色為刪除寬度限制的程式碼,附近的註解則是原本有限制寬度的程式碼)

      //Width hints used here to affect whole table
      headers.append("<tr><td> </td>") //.append("<tr><td width=\"40%\"> </td>")
             //.append("<td class=\"submitFormDateLabel\" width=\"5%\">")
       .append("<td class=\"submitFormDateLabel\">")
//             .append("Last name<br>e.g. <strong>Smith</strong></td>")
       .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.lastname"))
       .append("</td>")
             //.append("<td class=\"submitFormDateLabel\" width=\"5%\">")
      .append("<td class=\"submitFormDateLabel\">")
//             .append("First name(s) + \"Jr\"<br> e.g. <strong>Donald Jr</strong></td>")
       .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.firstname"))
       .append("</td>")
             //.append("<td width=\"40%\"> </td>")
      .append("<td> </td>")
             .append("</tr>");
      out.write(headers.toString());

修改6:修改欄位寬度配置。每一列輸入欄位中間都會間隔一個調整欄位寬度用的列,我們可以藉由調整這一列來控制欄位大小,但前提是要先移除掉上面那幾個函式裡面的寬度限制。(紅色部份是修改過後的值,表示左右各保留10%留白,中間則是有80%的寬度。)

<%-- HACK: Using this line to give the browser hints as to the widths of cells --%>
       <tr>
         <td width="10%"> </td>
         <td colspan="2" width="80%"> </td>
         <td width="10%"> </td>
       </tr>
/upload-file-list.jsp跟/get-file-format.jsp

image upload-file-list.jsp

image get-file-format.jsp

前者上傳檔案之後的列表、後者是當上傳檔案格式不明時會提示使用者選擇檔案格式的網頁。當這些網頁被讀取時,我想要轉換成以json格式呈現剛剛上傳檔案的資料(json是JavaSciprt常用的輕量級資料語言)。要讓upload-file-list.jsp跟get-file-format.jsp正常運作,必需要傳送以下參數:

  • workspace_item_id:遞交階段的ID,可由si.getSubmissionItem().getID()取得,這段程式碼寫在edit-metadata的修改2當中。
  • step:上傳檔案的階段,預設是「3」,以下教學會將之改成「2」。可在ajaxfileupload_fn.js裡面設定。
  • page:上傳檔案只會有1頁,輸入「1」即可。可在ajaxfileupload_fn.js裡面設定。
  • jsp:預設是由choose-file.jsp傳送過去,輸入值為「/submit/choose-file.jsp」即可。可在ajaxfileupload_fn.js裡面設定。
  • description:上傳檔案的敘述,可空白或不傳,我在fileupload的設定是會傳label的值。可在ajaxfileupload.js裡面設定。
  • submit_upload:要有值程式才知道是上傳的步驟,我則是固定填入「&gt;」。可在ajaxfileupload.js裡面設定。

修改:用網址設定呈現時轉址到顯示json的網頁。如果網址中包含「action=json」,那麼我就轉到另一個顯示json的網頁去。轉址的這段程式碼插入在<dspace>標籤前後,細節如下:(紅色部份為<dspace>標籤之外新增的程式碼)

  String action = (String) UIUtil.getOriginalURL(request);
if (action.indexOf("action=json") != -1)
{
   JSPManager.showJSP(request, response,
                    "/submit/upload-file-json.jsp");
}
else
{
%>

<dspace:layout locbar="off" navbar="off" titlekey="jsp.submit.get-file-format.title" nocache="true">

……

</dspace:layout>
<%
}  
%>
/upload-file-json.jsp

上面的修改之後會轉到這個網頁,這是我另外撰寫的程式。該程式會去取得最新上傳的檔案(專門指bitstream),回傳filename、url、format等參數。程式也很簡單,只有22行,原始碼如下:

<%@ page contentType="text/html; charset=utf-8" language="java" import="java.sql.*" errorPage="" %>
<%@ page import="org.dspace.core.Context" %>
<%@ page import="org.dspace.app.webui.servlet.SubmissionController" %>
<%@ page import="org.dspace.app.util.SubmissionInfo" %>
<%@ page import="org.dspace.content.Bitstream" %>
<%@ page import="org.dspace.app.webui.util.UIUtil" %>
<%
    // Obtain DSpace context
    Context context = UIUtil.obtainContext(request);    

  //get submission information object
    SubmissionInfo subInfo = SubmissionController.getSubmissionInfo(context, request);

Bitstream[] bitstreams = subInfo.getSubmissionItem().getItem().getNonInternalBitstreams();
Bitstream json_bitsream = bitstreams[0];  //bitstreams[(bitstreams.length-1)];  
%>
{
  error: '',
  filename: '<%= bitstreams[(bitstreams.length-1)].getName() %>',
  url: '<%= request.getContextPath() %>/retrieve/<%= bitstreams[(bitstreams.length-1)].getID() %>/<%= org.dspace.app.webui.util.UIUtil.encodeBitstreamName(bitstreams[(bitstreams.length-1)].getName()) %>',
  format: '<%= bitstreams[(bitstreams.length-1)].getFormatDescription() %>'
}

[dspace-jspui-webapp]/display-item.jsp (display-item.20081015.zip)

image

這是顯示item的頁面。我希望顯示的值能夠動態地調整成指定的樣式,而不是只有文字。像是XMLMetadata我希望能以樹狀結構呈現、fileupload則是呈現檔名。

修改1:引用JavaScript跟CSS檔案。作法同edit-metdata.jsp的修改1,程式碼如下:(紅色部份為新增的程式碼)

<dspace:layout title="<%= title %>">

<%
    if (handle != null)
    {
%>

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/jquery.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/display-item/metadata-value-display.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fn.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fnXML.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor_display_toggle.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/ui.datepicker.js"></script>
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/flora.datepicker.css" type="text/css" media="screen" title="Flora (Default)">
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.css" type="text/css" media="screen">



    <table align="center" class="miscTable">

修改2:呼叫metadataValueDisplay()。全部網頁顯示之後的最後,呼叫metadataValueDisplay()以動態調整內容的顯示。程式碼如下:(紅色的部份為新增的程式碼)

<%
    }
%>
    <p class="submitFormHelp"><fmt:message key="jsp.display-item.copyright"/></p>
  <script type="text/javascript">
  metadataValueDisplay("<%= request.getContextPath() %>");
  </script>

</dspace:layout>

老實說,這並不是很好的作法。建議能從JSP顯示的時候就能呈現檔案或XML的樹狀結構,而不是等網頁讀出來再用JavaScript動態修改。但這部份程式得花另一番功夫了。


[dspace-api]/...... (dspace-api.20081015.zip)

API的三個檔案,不小心改錯的話很容易讓整個DSpace都毀掉喔。

/submit/step/DescribeStep.java

此程式會依照input-type來決定資料的儲存方式,由於我新增的三種input-type都是跟著onebox、textarea來跑的,所以就跟他們一樣的設定即可。程式碼如下:(紅色部份為新增的程式)

            else if ((inputType.equals("onebox"))
                    || (inputType.equals("twobox"))
                    || (inputType.equals("textarea"))
          || (inputType.equals("texteditor"))
          || (inputType.equals("xmlmetadata"))
          || (inputType.equals("fileupload")))

            {
                readText(request, item, schema, element, qualifier, inputs[j]
                        .getRepeatable(), "en");
            }
/app/util/DCInput.java

DSpace並沒有預設值的功能,item template文件樣板只是預設該collection底下的item會擁有固定的值,但這跟我們一般認知的預設值:「每次新增欄位時,某些值是被預先載入的」有段差異,尤其是XMLMetadata是將表單的形式預先寫入才能運作,因此我們要幫他新增取得default-value的功能。

修改1:宣告內用變數defaultValue:(紅色部份是新增的程式碼)

    /** is the entry closed to vocabulary terms? */
    private boolean closedVocabulary = false;
    
    /**  for XMLMetadata  */
    private String defaultValue = null;

修改2:用get取得裡面的資料:(紅色部份是新增的程式碼)

        vocabulary = (String) fieldMap.get("vocabulary");
        //for XMLMetadata
        defaultValue = (String) fieldMap.get("default-value");

        String closedVocabularyStr = (String) fieldMap.get("closedVocabulary");

修改3:宣告公用函式getDefaultValue():(紅色部份是新增的程式碼)

  public boolean isClosedVocabulary() {
    return closedVocabulary;
  }
  
  /** for XMLMetadata */
  public String getDefaultValue()
    {
        return defaultValue;
    }

/core/Utils.java

原本DSpace在display item的頁面中,如果你存的資料裡面有用到一些特殊字元的時候,會被轉換成顯示於HTML的脫逸字元(Escape characters,都會以「&」開頭、「;」結尾),以避免這些可能是HTML程式碼的字元影響版面呈現或程式運作。這種脫逸字元跟C、Java、PHP等會用反斜線「\」來脫逸的方式不同,是HTML獨特的編碼。

在DSpace當中,將會被脫逸掉的字元如下:

  • 「&」→「&amp;」
  • 「"」→「&quot;」
  • 「<」→「&lt;」
  • 「>」→「&gt;」」

※HTML的拖曳字元,可以參考網頁:HTML Escape Characters: Complete List

由於我們用到了由FCKeditor轉換而來的HTML,也會使用XMLMetdata來儲存、顯示資料,我們就不能讓他把這些字元脫逸。接著我們就是addEntities()的內容,程式碼如下:(紅色表示註解掉字元脫逸的程式)

    public static String addEntities(String value)
    {
        if (value==null || value.length() == 0)
            return value;
        
        //value = value.replaceAll("&", "&");
        //value = value.replaceAll("\"", """);

        // actually, &apos; is an XML entity, not in HTML.
        // that's why it's commented out.
        // value = value.replaceAll("'", "&apos;");
        //value = value.replaceAll("<", "<");
        //value = value.replaceAll(">", ">");


        return value;
    }

由於存在同名字檔案在/app/util/Utils.java,千萬要小心不要覆蓋錯囉!


[dspace]/config/...... (config.20081015.zip)

調整DSpace的設定。

/input-forms.dtd

此文件定義了input-forms.xml的格式,我們在此處加上default-value的宣告。

修改1:宣告field裡面的default-value是可選擇性的:(紅色部份為新增的程式碼)

<!ATTLIST page number NMTOKEN #REQUIRED>
<!ELEMENT field (dc-schema, dc-element, dc-qualifier?, repeatable?, label, input-type, hint, required?, vocabulary?, default-value?) >
<!ELEMENT dc-schema (#PCDATA) >

修改2:宣告default-value這個元素:(紅色部份為新增的程式碼)

<!ELEMENT form-value-pairs (value-pairs)* >
<!ELEMENT value-pairs (pair)+ >

<!ELEMENT default-value (#PCDATA) >
/item-submission.xml

此處調整敘述階段(describe)跟上傳階段(upload)的順序。原本的順序如下

  1. 取得開始的資訊:他會問你一些問題,像是是否要上傳多個檔案、是否擁有多個標題之類的,你要打勾來決定。
  2. 敘述資料:填寫由input-forms.xml規定好的表單。
  3. 上傳檔案:上傳bitstream檔案。DSpace的設計理念是前面的敘述階段在敘述這個檔案,例如論文的作者、出版資訊等等。預設此階段是一定上傳資料,不過可以在dspace.cfg裡面取消這個設定。
  4. 確認資料:網頁會列出你之前填寫及上傳的資訊,並讓你回頭去修正。
  5. 授權條款的確認

修改:敘述階段與上傳階段互換。如果要在敘述階段上傳檔案,即使我們在上傳階段並不上傳資料,還是必須先經過上傳階段才行。因此我們將敘述階段跟上傳階段調換過來,程式碼如下:(紅色部份為兩個互換的階段)

     <!--Step 1 will be to gather initial information-->
    <step>
      <heading>submit.progressbar.initial-questions</heading>
    <processing-class>org.dspace.submit.step.InitialQuestionsStep</processing-class>
    <jspui-binding>org.dspace.app.webui.submit.step.JSPInitialQuestionsStep</jspui-binding>         
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.InitialQuestionsStep</xmlui-binding>
      <workflow-editable>true</workflow-editable>
    </step>  

      <!--Step 2 will be to Upload the item-->
      <step>
        <heading>submit.progressbar.upload</heading>
        <processing-class>org.dspace.submit.step.UploadStep</processing-class>
        <jspui-binding>org.dspace.app.webui.submit.step.JSPUploadStep</jspui-binding>
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.UploadStep</xmlui-binding>
        <workflow-editable>true</workflow-editable>
      </step>
          
      <!--Step 3 will be to Describe the item.-->
      <step>
        <heading>submit.progressbar.describe</heading>
        <processing-class>org.dspace.submit.step.DescribeStep</processing-class>
        <jspui-binding>org.dspace.app.webui.submit.step.JSPDescribeStep</jspui-binding>
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.DescribeStep</xmlui-binding>
        <workflow-editable>true</workflow-editable>
      </step>
   

    <!--Step 4 will be to Verify/Review everything -->
    <step>
        <heading>submit.progressbar.verify</heading>
        <processing-class>org.dspace.submit.step.VerifyStep</processing-class>
      <jspui-binding>org.dspace.app.webui.submit.step.JSPVerifyStep</jspui-binding>
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.ReviewStep</xmlui-binding>
    <workflow-editable>true</workflow-editable>
      </step>
/dspace.cfg
此處有很多DSpace的設定,也包含了是否限制一定要上傳檔案的設定。修改的程式碼如下:(紅色部份為新增的設定) ##### Settings for Submission Process #####

# Should the submit UI block submissions marked as theses?
webui.submit.blocktheses = false

# Whether or not we REQUIRE that a file be uploaded
# during the 'Upload' step in the submission process
# Defaults to true; If set to 'false', submitter has option to skip upload
#webui.submit.upload.required = true
webui.submit.upload.required = false


DSpace操作步驟說明

以上修改完畢之後,還需要進行重新安裝的步驟。

DSpace 1.5版重新安裝:出處在說明文件DSpace System Documentation: Configuration and Customization ([dspace-source]/dspace/docs/configure.html#jspui,dspace.1.5.0.configure.zip),大致上步驟如下:

  1. 進入指令模式
  2. 移動到你的[dspace-source]/dspace/目錄
    > cd [dspace-source]/dspace/
  3. 執行ant編譯
    > mvn package
  4. 移動到你的[dspace-source]/dspace/target/dspace-[version].dir目錄
    > [dspace-source]/dspace/target/dspace-[version].dir
  5. 執行ant打包
    > ant -Dconfig=[dspace]/config/dspace.cfg update
  6. 停止你的Tomcat伺服器
    > cd [tomcat]/bin/
    > ./shutdown.sh
  7. 複製新的.war檔案到Tomcat的webpapps目錄:
    > \cp -rf [dspace]/webapps/* [tomcat]/webapps/
  8. 重新啟動Tomcat伺服器
    > cd [tomcat]/bin/
    > ./startup.sh


參考資料


本來想要一篇之內寫完的,沒想到內容已經長到令人看不下去的地步,連Windows Live Writer連帶的都變得很慢,這就是警告我該換一篇寫了。繼續努力!

總共1 則留言, (我要發問)

  1. 更新extension.20081018.zip
    因為寫到XMLMetadata篇時,又重新偵錯了一下
    下載位置是http://puddingchen.35.googlepages.com/extension.20081018.zip

    有問題請盡量回報

    回覆刪除

留言工具: