Java tutorial
/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.app.xmlui.aspect.submission.submit; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedMap; import javax.servlet.ServletException; import org.apache.avalon.framework.parameters.Parameters; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.environment.SourceResolver; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.dspace.app.util.DCInput; import org.dspace.app.util.DCInput.ComplexDefinition; import org.dspace.app.util.DCInputSet; import org.dspace.app.util.DCInputsReader; import org.dspace.app.util.DCInputsReaderException; import org.dspace.app.xmlui.utils.UIException; import org.dspace.app.xmlui.aspect.submission.AbstractSubmissionStep; import org.dspace.app.xmlui.aspect.submission.FlowUtils; import org.dspace.app.xmlui.wing.Message; import org.dspace.app.xmlui.wing.WingException; import org.dspace.app.xmlui.wing.element.Body; import org.dspace.app.xmlui.wing.element.CheckBox; import org.dspace.app.xmlui.wing.element.Composite; import org.dspace.app.xmlui.wing.element.Division; import org.dspace.app.xmlui.wing.element.Field; import org.dspace.app.xmlui.wing.element.Hidden; import org.dspace.app.xmlui.wing.element.Instance; import org.dspace.app.xmlui.wing.element.List; import org.dspace.app.xmlui.wing.element.PageMeta; import org.dspace.app.xmlui.wing.element.Params; import org.dspace.app.xmlui.wing.element.Radio; import org.dspace.app.xmlui.wing.element.Select; import org.dspace.app.xmlui.wing.element.Text; import org.dspace.app.xmlui.wing.element.TextArea; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeManager; import org.dspace.content.Collection; import org.dspace.content.DCDate; import org.dspace.content.DCPersonName; import org.dspace.content.DCSeriesNumber; import org.dspace.content.DCValue; import org.dspace.content.Item; import org.dspace.content.authority.MetadataAuthorityManager; import org.dspace.content.authority.ChoiceAuthorityManager; import org.dspace.content.authority.Choice; import org.dspace.content.authority.Choices; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.dspace.services.ConfigurationService; import org.dspace.utils.DSpace; import org.xml.sax.SAXException; import cz.cuni.mff.ufal.dspace.app.util.ACL; /** * This is a step of the item submission processes. The describe step queries * the user for various metadata items about the item. For the most part all the * questions queried are completely configurable via the input-sets.xml file. * This system allows for multiple pages to be defined so this step is different * from all other stages in that it may represent multiple stages within the * submission processes. * * based on class by Scott Phillips and Tim Donohue (updated for Configurable Submission) * modified for LINDAT/CLARIN */ public class DescribeStep extends AbstractSubmissionStep { private static Logger log = Logger.getLogger(DescribeStep.class); /** Language Strings **/ protected static final Message T_head = message("xmlui.Submission.submit.DescribeStep.head"); protected static final Message T_head2 = message("xmlui.Submission.submit.DescribeStep.head2"); protected static final Message T_unknown_field = message("xmlui.Submission.submit.DescribeStep.unknown_field"); protected static final Message T_required_field = message( "xmlui.Submission.submit.DescribeStep.required_field"); protected static final Message T_last_name_help = message( "xmlui.Submission.submit.DescribeStep.last_name_help"); protected static final Message T_first_name_help = message( "xmlui.Submission.submit.DescribeStep.first_name_help"); protected static final Message T_year = message("xmlui.Submission.submit.DescribeStep.year"); protected static final Message T_month = message("xmlui.Submission.submit.DescribeStep.month"); protected static final Message T_day = message("xmlui.Submission.submit.DescribeStep.day"); protected static final Message T_series_name = message("xmlui.Submission.submit.DescribeStep.series_name"); protected static final Message T_report_no = message("xmlui.Submission.submit.DescribeStep.report_no"); /** * A shared resource of the inputs reader. The 'inputs' are the * questions we ask the user to describe an item during the * submission process. The reader is a utility class to read * that configuration file. */ protected static DCInputsReader INPUTS_READER = null; private static final Message T_vocabulary_link = message( "xmlui.Submission.submit.DescribeStep.controlledvocabulary.link"); private java.util.Map<String, String> regexError = null; /** * Ensure that the inputs reader has been initialized, this method may be * called multiple times with no ill-effect. */ private static void initializeInputsReader(DCInputsReader inputs_reader) throws DCInputsReaderException { if (INPUTS_READER == null) { if (inputs_reader != null) INPUTS_READER = inputs_reader; else INPUTS_READER = new DCInputsReader(); } } /** * Return the inputs reader. Note, the reader must have been * initialized before the reader can be accessed. * * @return The input reader. */ protected static DCInputsReader getInputsReader() { return INPUTS_READER; } /** * Establish our required parameters, abstractStep will enforce these. */ public DescribeStep() throws ServletException { this.requireSubmission = true; this.requireStep = true; //Ensure that the InputsReader is initialized. try { initializeInputsReader(null); } catch (DCInputsReaderException e) { throw new ServletException(e); } } public DescribeStep(DCInputsReader input) throws ServletException { this.requireSubmission = true; this.requireStep = true; //Ensure that the InputsReader is initialized. try { initializeInputsReader(input); } catch (DCInputsReaderException e) { throw new ServletException(e); } } public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectModel, src, parameters); this.regexError = getRegexError(parameters); } public void addPageMeta(PageMeta pageMeta) throws SAXException, WingException, UIException, SQLException, IOException, AuthorizeException { super.addPageMeta(pageMeta); int collectionID = submission.getCollection().getID(); pageMeta.addMetadata("choice", "collection").addContent(String.valueOf(collectionID)); pageMeta.addMetadata("include-library", "datepicker"); pageMeta.addMetadata("include-library", "jquery-ui"); pageMeta.addMetadata("include-library", "authority-control"); String jumpTo = submissionInfo.getJumpToField(); if (jumpTo != null) { pageMeta.addMetadata("page", "jumpTo").addContent(jumpTo); } } public void addBody(Body body) throws SAXException, WingException, UIException, SQLException, IOException, AuthorizeException { // Obtain the inputs (i.e. metadata fields we are going to display) Item item = submission.getItem(); Collection collection = submission.getCollection(); String actionURL = contextPath + "/handle/" + collection.getHandle() + "/submit/" + knot.getId() + ".continue"; DCInputSet inputSet; DCInput[] inputs; //set publishedbefore here after hiding initial question page submission.setPublishedBefore(true); submission.update(); context.commit(); try { inputSet = getInputsReader().getInputs(submission.getCollection().getHandle()); inputs = inputSet.getPageRows(getPage() - 1, submission.hasMultipleTitles(), submission.isPublishedBefore()); } catch (DCInputsReaderException se) { throw new UIException(se); } Division div = body.addInteractiveDivision("submit-describe", actionURL, Division.METHOD_POST, "primary submission"); div.setHead(T_submission_head); addSubmissionProgressList(div); List form = div.addList("submit-describe", List.TYPE_FORM); if (this.getPage() == 1) { form.setHead(T_head); } else { form.setHead(message("xmlui.Submission.submit.DescribeStep.head" + this.getPage())); } // Fetch the document type (dc.type) String documentType = ""; if ((item.getMetadata("dc.type") != null) && (item.getMetadata("dc.type").length > 0)) { documentType = item.getMetadata("dc.type")[0].value; } // Iterate over all inputs and add it to the form. for (DCInput dcInput : inputs) { String scope = submissionInfo.isInWorkflow() ? DCInput.WORKFLOW_SCOPE : DCInput.SUBMISSION_SCOPE; boolean readonly = dcInput.isReadOnly(scope); // Omit fields not allowed for this document type if (!dcInput.isAllowedFor(documentType)) { continue; } // If the input is invisible in this scope, then skip it. if (!dcInput.isVisible(scope) && !readonly) { continue; } //skip if acl says so if (!isInputAuthorized(context, dcInput)) { continue; } //add some eye candy if (dcInput.hasACL()) { if (AuthorizeManager.isAdmin(context)) { dcInput.addRend("admin-field"); } else { dcInput.addRend("specialuser-field"); } } String schema = dcInput.getSchema(); String element = dcInput.getElement(); String qualifier = dcInput.getQualifier(); DCValue[] dcValues = item.getMetadata(schema, element, qualifier, Item.ANY); String fieldName = FlowUtils.getFieldName(dcInput); String inputType = dcInput.getInputType(); // if this field is configured as choice control and its // presentation format is SELECT, render it as select field: String fieldKey = MetadataAuthorityManager.makeFieldKey(schema, element, qualifier); ChoiceAuthorityManager cmgr = ChoiceAuthorityManager.getManager(); if (cmgr.isChoicesConfigured(fieldKey) && Params.PRESENTATION_SELECT.equals(cmgr.getPresentation(fieldKey))) { renderChoiceSelectField(form, fieldName, collection, dcInput, dcValues, readonly); } else if (inputType.equals("name")) { renderNameField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("date")) { renderDateField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("series")) { renderSeriesField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("twobox")) { // We don't have a twobox field, instead it's just a // one box field that the theme can render in two columns. renderOneboxField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("qualdrop_value")) { // Determine the real field's values. Since the qualifier is // selected we need to search through all the metadata and see // if any match for another field, if not we assume that this field // should handle it. DCValue[] unfiltered = item.getMetadata(dcInput.getSchema(), dcInput.getElement(), Item.ANY, Item.ANY); ArrayList<DCValue> filtered = new ArrayList<DCValue>(); for (DCValue dcValue : unfiltered) { String unfilteredFieldName = dcValue.element + "." + dcValue.qualifier; if (!inputSet.isFieldPresent(unfilteredFieldName)) { filtered.add(dcValue); } } renderQualdropField(form, fieldName, dcInput, filtered.toArray(new DCValue[filtered.size()]), readonly); } else if (inputType.equals("textarea")) { renderTextArea(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("dropdown")) { renderDropdownField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("list")) { renderSelectFromListField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("onebox")) { renderOneboxField(form, fieldName, dcInput, dcValues, readonly); } else if (inputType.equals("complex")) { renderComplexField(form, fieldName, dcInput, dcValues, readonly); } else { form.addItem(T_unknown_field); } } // add standard control/paging buttons addControlButtons(form); } /** * Each submission step must define its own information to be reviewed * during the final Review/Verify Step in the submission process. * <P> * The information to review should be tacked onto the passed in * List object. * <P> * NOTE: To remain consistent across all Steps, you should first * add a sub-List object (with this step's name as the heading), * by using a call to reviewList.addList(). This sublist is * the list you return from this method! * * @param reviewList * The List to which all reviewable information should be added * @return * The new sub-List object created by this step, which contains * all the reviewable information. If this step has nothing to * review, then return null! */ public List addReviewSection(List reviewList) throws SAXException, WingException, UIException, SQLException, IOException, AuthorizeException { //Create a new list section for this step (and set its heading) List describeSection = reviewList.addList("submit-review-" + this.stepAndPage, List.TYPE_FORM); String suffix = this.getPage() == 1 ? "" : Integer.toString(this.getPage()); describeSection.setHead(message("xmlui.Submission.submit.DescribeStep.head" + suffix)); //Review the values assigned to all inputs //on this page of the Describe step. DCInputSet inputSet = null; try { inputSet = getInputsReader().getInputs(submission.getCollection().getHandle()); } catch (DCInputsReaderException se) { throw new UIException(se); } MetadataAuthorityManager mam = MetadataAuthorityManager.getManager(); DCInput[] inputs = inputSet.getPageRows(getPage() - 1, submission.hasMultipleTitles(), submission.isPublishedBefore()); // Fetch the document type (dc.type) Item item = submission.getItem(); String documentType = ""; if ((item.getMetadata("dc.type") != null) && (item.getMetadata("dc.type").length > 0)) { documentType = item.getMetadata("dc.type")[0].value; } for (DCInput input : inputs) { // If the input is invisible in this scope, then skip it. String scope = submissionInfo.isInWorkflow() ? DCInput.WORKFLOW_SCOPE : DCInput.SUBMISSION_SCOPE; if (!input.isVisible(scope) && !input.isReadOnly(scope)) { continue; } if (!input.isAllowedFor(documentType)) { continue; } String inputType = input.getInputType(); String pairsName = input.getPairsType(); DCValue[] values; if (inputType.equals("qualdrop_value")) { values = submission.getItem().getMetadata(input.getSchema(), input.getElement(), Item.ANY, Item.ANY); } else { values = submission.getItem().getMetadata(input.getSchema(), input.getElement(), input.getQualifier(), Item.ANY); } if (values != null && values.length > 0) { for (DCValue value : values) { String displayValue = null; if (inputType.equals("date")) { DCDate date = new DCDate(value.value); displayValue = date.toString(); } else if (inputType.equals("dropdown")) { displayValue = input.getDisplayString(pairsName, value.value); } else if (inputType.equals("qualdrop_value")) { String qualifier = value.qualifier; String displayQual = input.getDisplayString(pairsName, qualifier); if (displayQual != null && displayQual.length() > 0) { displayValue = displayQual + ":" + value.value; } } else { displayValue = value.value; } //Only display this field if we have a value to display if (displayValue != null && displayValue.length() > 0) { describeSection.addLabel(input.getLabel()); if (mam.isAuthorityControlled(value.schema, value.element, value.qualifier)) { String confidence = (value.authority != null && value.authority.length() > 0) ? Choices.getConfidenceText(value.confidence).toLowerCase() : "blank"; org.dspace.app.xmlui.wing.element.Item authItem = describeSection.addItem( "submit-review-field-with-authority", "ds-authority-confidence cf-" + confidence); authItem.addContent(displayValue); } else { if (inputType.equals("complex")) { displayValue = StringUtils .join(displayValue.split(DCInput.ComplexDefinition.SEPARATOR), ";"); /*describeSection.addItem(); for(String fVal : fieldValues){ describeSection.addItem().addHighlight("label label-default").addContent(fVal); }*/ } //else{ describeSection.addItem(displayValue); //} } } } // For each DCValue } // If values exist } // For each input //return this new "describe" section return describeSection; } /** * Render a Name field to the DRI document. The name field consists of two * text fields, one for the last name and the other for a first name (plus * all other names). * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderNameField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { // The name field is a composite field containing two text fields, one // for first name the other for last name. String rend = dcInput.getRendsAsString(); org.dspace.app.xmlui.wing.element.Item item = form.addItem(null, rend); rend = "submit-name"; Composite fullName = item.addComposite(fieldName, rend); if (isAutocompletable(dcInput)) { rend += " autocomplete"; addAutocompleteComponents(fieldName + "_last", dcInput, item); } Text lastName = fullName.addText(fieldName + "_last", rend); Text firstName = fullName.addText(fieldName + "_first"); String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier()); boolean isAuthorityControlled = MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey); // Setup the field's values if (dcInput.isRepeatable() || dcValues.length > 1) { for (DCValue dcValue : dcValues) { DCPersonName dpn = new DCPersonName(dcValue.value); lastName.addInstance().setValue(dpn.getLastName()); firstName.addInstance().setValue(dpn.getFirstNames()); Instance fi = fullName.addInstance(); fi.setValue(dcValue.value); if (isAuthorityControlled) { if (dcValue.authority == null || dcValue.authority.equals("")) { fi.setAuthorityValue("", "blank"); } else { fi.setAuthorityValue(dcValue.authority, Choices.getConfidenceText(dcValue.confidence)); } } } } else if (dcValues.length == 1) { DCPersonName dpn = new DCPersonName(dcValues[0].value); lastName.setValue(dpn.getLastName()); firstName.setValue(dpn.getFirstNames()); if (isAuthorityControlled) { if (dcValues[0].authority == null || dcValues[0].authority.equals("")) { lastName.setAuthorityValue("", "blank"); } else { lastName.setAuthorityValue(dcValues[0].authority, Choices.getConfidenceText(dcValues[0].confidence)); } } } // Setup the full name fullName.setLabel(dcInput.getLabel()); fullName.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { fullName.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { fullName.addError(dcInput.getWarning()); } else { fullName.addError(T_required_field); } } if (dcInput.isRepeatable() && !readonly) { fullName.enableAddOperation(); } if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { fullName.enableDeleteOperation(); } if (isAuthorityControlled) { fullName.setAuthorityControlled(); fullName.setAuthorityRequired(MetadataAuthorityManager.getManager().isAuthorityRequired(fieldKey)); } if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey)) { fullName.setChoices(fieldKey); fullName.setChoicesPresentation(ChoiceAuthorityManager.getManager().getPresentation(fieldKey)); fullName.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey)); } // Setup the first and last name lastName.setLabel(T_last_name_help); firstName.setLabel(T_first_name_help); if (readonly) { lastName.setDisabled(); firstName.setDisabled(); fullName.setDisabled(); } } /** * Render a date field to the DRI document. The date field consists of * three component fields, a 4 character text field for the year, a select * box for the month, and a 2 character text field for the day. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderDateField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { // The date field consists of three primitive fields: a text field // for the year, followed by a select box of the months, follewed // by a text box for the day. String rend = dcInput.getRendsAsString(); rend += " submit-date"; Composite fullDate = form.addItem().addComposite(fieldName, rend); Text year = fullDate.addText(fieldName + "_year"); Select month = fullDate.addSelect(fieldName + "_month"); Text day = fullDate.addText(fieldName + "_day"); // Set up the full field fullDate.setLabel(dcInput.getLabel()); fullDate.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { fullDate.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { fullDate.addError(dcInput.getWarning()); } else { fullDate.addError(T_required_field); } } if (dcInput.isRepeatable() && !readonly) { fullDate.enableAddOperation(); } if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { fullDate.enableDeleteOperation(); } if (readonly) { year.setDisabled(); month.setDisabled(); day.setDisabled(); } // Setup the year field year.setLabel(T_year); year.setSize(4, 4); // Setup the month field month.setLabel(T_month); month.addOption(0, ""); for (int i = 1; i < 13; i++) { month.addOption(i, org.dspace.content.DCDate.getMonthName(i, Locale.getDefault())); } // Setup the day field day.setLabel(T_day); day.setSize(2, 2); // Setup the field's values if (dcInput.isRepeatable() || dcValues.length > 1) { for (DCValue dcValue : dcValues) { DCDate dcDate = new DCDate(dcValue.value); year.addInstance().setValue(String.valueOf(dcDate.getYear())); month.addInstance().setOptionSelected(dcDate.getMonth()); day.addInstance().setValue(String.valueOf(dcDate.getDay())); fullDate.addInstance().setValue(dcDate.toString()); } } else if (dcValues.length == 1) { DCDate dcDate = new DCDate(dcValues[0].value); year.setValue(String.valueOf(dcDate.getYear())); month.setOptionSelected(dcDate.getMonth()); // Check if the day field is not specified, if so then just // put a blank value in instead of the wiered looking -1. if (dcDate.getDay() == -1) { day.setValue(""); } else { day.setValue(String.valueOf(dcDate.getDay())); } } } /** * Render a series field to the DRI document. The series field conist of * two component text fields. When interpreted each of these fields are * combined back together to be a single value joined together by a * semicolen. The primary use case is for the journal or report number * the left hand side is the journal and the right hand side in a * unique number within the journal. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderSeriesField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { String rend = dcInput.getRendsAsString(); rend += " submit-" + dcInput.getInputType(); // The series field consists of two parts, a series name (text field) // and report or paper number (also a text field). Composite fullSeries = form.addItem().addComposite(fieldName, rend); Text series = fullSeries.addText(fieldName + "_series"); Text number = fullSeries.addText(fieldName + "_number"); // Setup the full field. fullSeries.setLabel(dcInput.getLabel()); fullSeries.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { fullSeries.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { fullSeries.addError(dcInput.getWarning()); } else { fullSeries.addError(T_required_field); } } if (dcInput.isRepeatable() && !readonly) { fullSeries.enableAddOperation(); } if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { fullSeries.enableDeleteOperation(); } series.setLabel(T_series_name); number.setLabel(T_report_no); if (readonly) { fullSeries.setDisabled(); series.setDisabled(); number.setDisabled(); } // Setup the field's values if (dcInput.isRepeatable() || dcValues.length > 1) { for (DCValue dcValue : dcValues) { DCSeriesNumber dcSeriesNumber = new DCSeriesNumber(dcValue.value); series.addInstance().setValue(dcSeriesNumber.getSeries()); number.addInstance().setValue(dcSeriesNumber.getNumber()); fullSeries.addInstance().setValue(dcSeriesNumber.toString()); } } else if (dcValues.length == 1) { DCSeriesNumber dcSeriesNumber = new DCSeriesNumber(dcValues[0].value); series.setValue(dcSeriesNumber.getSeries()); number.setValue(dcSeriesNumber.getNumber()); } } /** * Render a qualdrop field to the DRI document. Qualdrop fields are complicated, * widget wise they are composed of two fields, a select and text box field. * The select field selects the metedata's qualifier and the text box is the * value. This means that that there is not just one metadata element that is * represented so the confusing part is that the name can change. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderQualdropField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { String rend = dcInput.getRendsAsString(); rend += " submit-qualdrop"; Composite qualdrop = form.addItem().addComposite(fieldName, rend); Select qual = qualdrop.addSelect(fieldName + "_qualifier"); Text value = qualdrop.addText(fieldName + "_value"); // Setup the full field. qualdrop.setLabel(dcInput.getLabel()); qualdrop.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { qualdrop.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { qualdrop.addError(dcInput.getWarning()); } else { qualdrop.addError(T_required_field); } } if (dcInput.isRepeatable() && !readonly) { qualdrop.enableAddOperation(); } // Update delete based upon the filtered values. if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { qualdrop.enableDeleteOperation(); } if (readonly) { qualdrop.setDisabled(); qual.setDisabled(); value.setDisabled(); } // Setup the possible options @SuppressWarnings("unchecked") // This cast is correct java.util.List<String> pairs = dcInput.getPairs(); for (int i = 0; i < pairs.size(); i += 2) { String display = pairs.get(i); String returnValue = pairs.get(i + 1); qual.addOption(returnValue, display); } // Setup the field's values if (dcInput.isRepeatable() || dcValues.length > 1) { for (DCValue dcValue : dcValues) { qual.addInstance().setOptionSelected(dcValue.qualifier); value.addInstance().setValue(dcValue.value); qualdrop.addInstance().setValue(dcValue.qualifier + ":" + dcValue.value); } } else if (dcValues.length == 1) { qual.setOptionSelected(dcValues[0].qualifier); value.setValue(dcValues[0].value); } } /** * Render a Text Area field to the DRI document. The text area is a simple * multi row and column text field. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderTextArea(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { String rend = dcInput.getRendsAsString(); rend += " submit-textarea submission-textarea"; // Plain old Textarea TextArea textArea = form.addItem().addTextArea(fieldName, rend); // Setup the text area textArea.setLabel(dcInput.getLabel()); textArea.setHelp(cleanHints(dcInput.getHints())); String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier()); boolean isAuth = MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey); if (isAuth) { textArea.setAuthorityControlled(); textArea.setAuthorityRequired(MetadataAuthorityManager.getManager().isAuthorityRequired(fieldKey)); } if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey)) { textArea.setChoices(fieldKey); textArea.setChoicesPresentation(ChoiceAuthorityManager.getManager().getPresentation(fieldKey)); textArea.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey)); } if (dcInput.isRequired()) { textArea.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { textArea.addError(dcInput.getWarning()); } else { textArea.addError(T_required_field); } } if (dcInput.isRepeatable() && !readonly) { textArea.enableAddOperation(); } if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { textArea.enableDeleteOperation(); } if (readonly) { textArea.setDisabled(); } // Setup the field's values if (dcInput.isRepeatable() || dcValues.length > 1) { for (DCValue dcValue : dcValues) { Instance ti = textArea.addInstance(); ti.setValue(dcValue.value); if (isAuth) { if (dcValue.authority == null || dcValue.authority.equals("")) { ti.setAuthorityValue("", "blank"); } else { ti.setAuthorityValue(dcValue.authority, Choices.getConfidenceText(dcValue.confidence)); } } } } else if (dcValues.length == 1) { textArea.setValue(dcValues[0].value); if (isAuth) { if (dcValues[0].authority == null || dcValues[0].authority.equals("")) { textArea.setAuthorityValue("", "blank"); } else { textArea.setAuthorityValue(dcValues[0].authority, Choices.getConfidenceText(dcValues[0].confidence)); } } } } /** * Render a dropdown field for a choice-controlled input of the * 'select' presentation to the DRI document. The dropdown field * consists of an HTML select box. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderChoiceSelectField(List form, String fieldName, Collection coll, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier()); if (MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey)) { throw new WingException("Field " + fieldKey + " has choice presentation of type \"" + Params.PRESENTATION_SELECT + "\", it may NOT be authority-controlled."); } String rend = dcInput.getRendsAsString(); rend += " submit-select"; // Plain old select list. Select select = form.addItem().addSelect(fieldName, rend); //Setup the select field select.setLabel(dcInput.getLabel()); select.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { select.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { select.addError(dcInput.getWarning()); } else { select.addError(T_required_field); } } if (dcInput.isRepeatable() || dcValues.length > 1) { // Use the multiple functionality from the HTML // widget instead of DRI's version. select.setMultiple(); select.setSize(6); } else { select.setSize(1); } if (readonly) { select.setDisabled(); } Choices cs = ChoiceAuthorityManager.getManager().getMatches(fieldKey, "", coll.getID(), 0, 0, null); if (dcValues.length == 0) { select.addOption(true, "", ""); } for (Choice c : cs.values) { select.addOption(c.value, c.label); } // Setup the field's pre-selected values for (DCValue dcValue : dcValues) { select.setOptionSelected(dcValue.value); } } /** * Render a dropdown field to the DRI document. The dropdown field consists * of an HTML select box. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderDropdownField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { String rend = dcInput.getRendsAsString(); rend += " submit-select"; // Plain old select list. Select select = form.addItem().addSelect(fieldName, rend); //Setup the select field select.setLabel(dcInput.getLabel()); select.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { select.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { select.addError(dcInput.getWarning()); } else { select.addError(T_required_field); } } if (dcInput.isRepeatable() || dcValues.length > 1) { // Use the multiple functionality from the HTML // widget instead of DRI's version. select.setMultiple(); select.setSize(6); } if (readonly) { select.setDisabled(); } // Setup the possible options @SuppressWarnings("unchecked") // This cast is correct java.util.List<String> pairs = dcInput.getPairs(); for (int i = 0; i < pairs.size(); i += 2) { String display = pairs.get(i); String value = pairs.get(i + 1); select.addOption(value, display); } // Setup the field's pre-selected values for (DCValue dcValue : dcValues) { log.info("dc values are present for " + fieldName + " with " + dcValue.value); // jmisutka/UFAL - special hack for iso languages // add the option there artificially if (fieldName.equals("dc_language_iso")) { select.addOption(dcValue.value, dcValue.value); } select.setOptionSelected(dcValue.value); } } /** * Render a select-from-list field to the DRI document. * This field consists of either a series of checkboxes * (if repeatable) or a series of radio buttons (if not repeatable). * <P> * Note: This is NOT the same as a List element * (org.dspace.app.xmlui.wing.element.List). It's just unfortunately * similarly named. * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderSelectFromListField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { Field listField = null; String rend = dcInput.getRendsAsString(); //if repeatable, this list of fields should be checkboxes if (dcInput.isRepeatable()) { listField = form.addItem(null, rend).addCheckBox(fieldName); } else //otherwise this is a list of radio buttons { listField = form.addItem(null, rend).addRadio(fieldName); } if (readonly) { listField.setDisabled(); } // Setup the field listField.setLabel(dcInput.getLabel()); listField.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { listField.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { listField.addError(dcInput.getWarning()); } else { listField.addError(T_required_field); } } //Setup each of the possible options java.util.List<String> pairs = dcInput.getPairs(); for (int i = 0; i < pairs.size(); i += 2) { String display = pairs.get(i); String value = pairs.get(i + 1); if (listField instanceof CheckBox) { ((CheckBox) listField).addOption(value, display); } else if (listField instanceof Radio) { ((Radio) listField).addOption(value, display); } } // Setup the field's pre-selected values for (DCValue dcValue : dcValues) { if (listField instanceof CheckBox) { ((CheckBox) listField).setOptionSelected(dcValue.value); } else if (listField instanceof Radio) { ((Radio) listField).setOptionSelected(dcValue.value); } } } protected void renderComplexField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { String rend = dcInput.getRendsAsString(); org.dspace.app.xmlui.wing.element.Item item = form.addItem(null, rend); rend += "submit-complex"; Composite composite = item.addComposite(fieldName, rend); DCInput.ComplexDefinition definition = dcInput.getComplexDefinition(); java.util.List<Field> fields = new ArrayList<Field>(); for (String name : definition.getInputNames()) { String fullInputName = fieldName + "_" + name; Field field = null; String type = definition.getInput(name).get("type"); if ("text".equals(type)) { String autocomplete = definition.getInput(name).get("autocomplete"); if (isAutocompletable(autocomplete)) { field = composite.addText(fullInputName, rend + " autocomplete"); addAutocompleteComponents(fullInputName, autocomplete, item); } else { field = composite.addText(fullInputName, rend); } } else if ("dropdown".equals(type)) { Select select = composite.addSelect(fullInputName, rend); // Setup the possible options java.util.List<String> pairs = definition.getValuePairsForInput(name); for (int i = 0; i < pairs.size(); i += 2) { String display = pairs.get(i); String value = pairs.get(i + 1); select.addOption(value, display); } field = select; } else { // nothing yet; } String label = definition.getInput(name).get("label"); if (label != null && field != null) { field.setLabel(label); } String help = definition.getInput(name).get("help"); if (help != null && field != null) { field.setHelp(help); } if (field != null) { fields.add(field); } } // Setup the field's values for (DCValue dcValue : dcValues) { java.util.List<String> values = split(dcValue.value); fill(fields, values, true, definition, fieldName); composite.addInstance().setValue(StringUtils.join(values.iterator(), ";")); } if (regexError.containsKey(fieldName)) { // partially fill the form java.util.List<String> values = split(regexError.get(fieldName)); fill(fields, values, false, definition, fieldName); } // Setup the full name composite.setLabel(dcInput.getLabel()); composite.setHelp(cleanHints(dcInput.getHints())); if (dcInput.isRequired()) { composite.setRequired(); } if (isFieldInError(fieldName) || regexError.containsKey(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { composite.addError(dcInput.getWarning()); } else { composite.addError("Fill all the fields, please."); } } if (dcInput.isRepeatable() && !readonly) { composite.enableAddOperation(); } if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { composite.enableDeleteOperation(); } if (readonly) { composite.setDisabled(); for (Field field : fields) { field.setDisabled(); } } } private void fill(java.util.List<Field> fields, java.util.List<String> values, boolean addInstances, ComplexDefinition definition, String fieldName) throws WingException { //fill the form for (int i = 0; i < fields.size(); i++) { Field field = fields.get(i); String value = values.get(i); if (addInstances) { Instance instance = field.addInstance(); //XXX: Branching on type if (field instanceof Select) { instance.setOptionSelected(value); } else { instance.setValue(value); } } else { //currently with error only field.setValue(value); String inputName = StringUtils.difference(fieldName + "_", field.getName()); String regex = definition.getInput(inputName).get("regexp"); if (!DCInput.isAllowedValue(value, regex)) { //attach the regex error to the right field field.addError(String.format( "The field doesn't match the required regular expression (format) \"%s\"", regex)); } } } } private java.util.List<String> split(String value) { // return Arrays.asList(value.split(DCInput.ComplexDefinition.SEPARATOR, -1)); } protected boolean isAutocompletable(DCInput dcInput) { // autocomplete String autocomplete = dcInput.getAutocomplete(); return isAutocompletable(autocomplete); } protected boolean isAutocompletable(String autocomplete) { if (null != autocomplete) { //ConfigurationService cs = new DSpace().getConfigurationService(); //String is_on = cs.getProperty("lr.autocomplete.on"); String is_on = ConfigurationManager.getProperty("lr", "lr.autocomplete.on"); // UI will have an indication that autocomplete is turned on by // a) using the class autocomplete in the main text box // b) the url will be in a hidden attribute which will be marked by the fieldName // c) the type will be also set in a hidden element if (null != is_on && is_on.trim().equals("true")) { return true; } } return false; } protected void addAutocompleteComponents(String fieldName, DCInput dcInput, org.dspace.app.xmlui.wing.element.Item item) throws WingException { String autocomplete = dcInput.getAutocomplete(); addAutocompleteComponents(fieldName, autocomplete, item); } protected void addAutocompleteComponents(String fieldName, String autocomplete, org.dspace.app.xmlui.wing.element.Item item) throws WingException { String[] parts = autocomplete.split("-"); String url_property = String.format("lr.autocomplete.%s.url", parts[0]); //String auto_url = cs.getProperty(url_property); String auto_url = ConfigurationManager.getProperty("lr", url_property); if (auto_url != null) { item.addHidden(fieldName + "-url").setValue(auto_url); item.addHidden(fieldName + "-type").setValue(autocomplete); } else { log.warn(String.format( "autocomplete has been specified but cannot find the actual url in configuration [%s]", url_property)); } } /** * Render a simple text field to the DRI document * * @param form * The form list to add the field too * @param fieldName * The field's name. * @param dcInput * The field's input deffinition * @param dcValues * The field's pre-existing values. */ protected void renderOneboxField(List form, String fieldName, DCInput dcInput, DCValue[] dcValues, boolean readonly) throws WingException { // Both onebox and twobox consist a free form text field // that the user may enter any value. The difference between // the two is that a onebox should be rendered in one column // as twobox should be listed in a two column format. Since this // decision is not something the Aspect can effect we merely place // as a render hint. String rend = dcInput.getRendsAsString(); org.dspace.app.xmlui.wing.element.Item item = form.addItem(null, rend); rend = "submit-text"; if (isAutocompletable(dcInput)) { rend += " autocomplete"; addAutocompleteComponents(fieldName, dcInput, item); } Text text = item.addText(fieldName, rend); if (dcInput.getVocabulary() != null) { String vocabularyUrl = new DSpace().getConfigurationService().getProperty("dspace.url"); vocabularyUrl += "/JSON/controlled-vocabulary?vocabularyIdentifier=" + dcInput.getVocabulary(); //Also hand down the field name so our summoning script knows the field the selected value is to end up in vocabularyUrl += "&metadataFieldName=" + fieldName; item.addXref("vocabulary:" + vocabularyUrl).addContent(T_vocabulary_link); } // Setup the select field text.setLabel(dcInput.getLabel()); text.setHelp(cleanHints(dcInput.getHints())); String fieldKey = MetadataAuthorityManager.makeFieldKey(dcInput.getSchema(), dcInput.getElement(), dcInput.getQualifier()); boolean isAuth = MetadataAuthorityManager.getManager().isAuthorityControlled(fieldKey); if (isAuth) { text.setAuthorityControlled(); text.setAuthorityRequired(MetadataAuthorityManager.getManager().isAuthorityRequired(fieldKey)); } if (ChoiceAuthorityManager.getManager().isChoicesConfigured(fieldKey)) { text.setChoices(fieldKey); text.setChoicesPresentation(ChoiceAuthorityManager.getManager().getPresentation(fieldKey)); text.setChoicesClosed(ChoiceAuthorityManager.getManager().isClosed(fieldKey)); } if (dcInput.isRequired()) { text.setRequired(); } if (isFieldInError(fieldName)) { if (dcInput.getWarning() != null && dcInput.getWarning().length() > 0) { text.addError(dcInput.getWarning()); } else { text.addError(T_required_field); } } // regexp checking - should rather be in dspace-api for (DCValue dcv : dcValues) { if (!dcInput.isAllowedValue(dcv.value)) { if (dcInput.getRegexpWarning() != null && dcInput.getRegexpWarning().length() > 0) { text.addError(dcInput.getRegexpWarning()); } else { text.addError(String.format("This value must match the given regular expression [%s].", dcInput.getRegexp())); } } } if (dcInput.isRepeatable() && !readonly) { text.enableAddOperation(); } if ((dcInput.isRepeatable() || dcValues.length > 1) && !readonly) { text.enableDeleteOperation(); } if (readonly) { text.setDisabled(); } // Setup the field's values if (dcInput.isRepeatable() || dcValues.length > 1) { for (DCValue dcValue : dcValues) { Instance ti = text.addInstance(); ti.setValue(dcValue.value); if (isAuth) { if (dcValue.authority == null || dcValue.authority.equals("")) { ti.setAuthorityValue("", "blank"); } else { ti.setAuthorityValue(dcValue.authority, Choices.getConfidenceText(dcValue.confidence)); } } } } else if (dcValues.length == 1) { text.setValue(dcValues[0].value); if (isAuth) { if (dcValues[0].authority == null || dcValues[0].authority.equals("")) { text.setAuthorityValue("", "blank"); } else { text.setAuthorityValue(dcValues[0].authority, Choices.getConfidenceText(dcValues[0].confidence)); } } } } /** * Check if the given fieldname is listed as being in error. * * @param fieldName * @return */ private boolean isFieldInError(String fieldName) { return (this.errorFields.contains(fieldName)); } /** * There is a problem with the way hints are handled. The class that we use to * read the input-forms.xml configuration will append and prepend HTML to hints. * This causes all sorts of confusion when inserting into the DRI page, so this * method will strip that extra HTML and just leave the cleaned comments. * * * However this method will not remove naughty or sexual innuendoes from the * field's hints. * * * @param dirtyHints HTML-ized hints * @return Hints without HTML. */ private static final String HINT_HTML_PREFIX = "<tr><td colspan=\"4\" class=\"submitFormHelp\">"; private static final String HINT_HTML_POSTFIX = "</td></tr>"; private String cleanHints(String dirtyHints) { String clean = (dirtyHints != null ? dirtyHints : ""); if (clean.startsWith(HINT_HTML_PREFIX)) { clean = clean.substring(HINT_HTML_PREFIX.length()); } if (clean.endsWith(HINT_HTML_POSTFIX)) { clean = clean.substring(0, clean.length() - HINT_HTML_POSTFIX.length()); } return clean; } /** * Adds hidden field with the name of the element to jump to using js after page reload * * @param div * @throws WingException */ protected void addJumpToInput(Division div) throws WingException { String name = submissionInfo.getJumpToField(); Hidden jumpTo = div.addHidden("jump_to"); jumpTo.setValue(name); } /** * should we render the metadata field based on field description? */ protected boolean isInputDisplayable(Context c, DCInput dcInput, String scope, String documentType) { // Omit fields not allowed for this document type if (!dcInput.isAllowedFor(documentType)) { return false; } // If the input is invisible in this scope, then skip it. if (!dcInput.isVisible(scope) && !dcInput.isReadOnly(scope)) { return false; } return true; } /** * should we render the metadata field based on authorization? */ protected boolean isInputAuthorized(Context c, DCInput dcInput) { // If the input is not allowed according to ACL, skip it. if (!dcInput.isAllowedAction(context, ACL.ACTION_READ) && !dcInput.isAllowedAction(context, ACL.ACTION_WRITE)) { return false; } return true; } public Map<String, String> getRegexError(Parameters parameters) { java.util.Map<String, String> fields = new HashMap<String, String>(); String errors = parameters.getParameter("regex_error", ""); if (errors != null && errors.length() > 0) { String[] fvs = errors.split(","); for (int i = 0; i < fvs.length; i += 2) { String field = fvs[i]; //XXX this is obscure, see org.dspace.submit.step.DescribeStep::addRegexError String value = new String(javax.xml.bind.DatatypeConverter.parseBase64Binary(fvs[i + 1])); fields.put(field, value); } } return fields; } }