Skip to content

Commit

Permalink
Merge pull request #1634 from Daniel-KM/feature/template_datatypes_mu…
Browse files Browse the repository at this point in the history
…ltiple

Multiple data types by property in form
  • Loading branch information
zerocrates authored Sep 18, 2020
2 parents 3688539 + dc11c44 commit 4e8dad6
Show file tree
Hide file tree
Showing 18 changed files with 348 additions and 124 deletions.
128 changes: 88 additions & 40 deletions application/asset/js/resource-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
e.preventDefault();
var typeButton = $(this);
var field = typeButton.closest('.resource-values.field');
var value = makeNewValue(field.data('property-term'), null, typeButton.data('type'))
var value = makeNewValue(field.data('property-term'), typeButton.data('type'))
field.find('.values').append(value);
});

Expand Down Expand Up @@ -112,7 +112,7 @@
$('#item-results').find('.resource')
.has('input.select-resource-checkbox:checked').each(function(index) {
if (0 < index) {
value = makeNewValue(field.data('property-term'), null, 'resource');
value = makeNewValue(field.data('property-term'), 'resource');
field.find('.values').append(value);
}
var valueObj = $(this).data('resource-values');
Expand Down Expand Up @@ -183,6 +183,7 @@
errors.push('The following field is required: ' + propLabel);
}
});
thisForm.data('has-error', errors.length > 0);
if (errors.length) {
e.preventDefault();
alert(errors.join("\n"));
Expand Down Expand Up @@ -230,14 +231,17 @@
/**
* Make a new value.
*/
var makeNewValue = function(term, valueObj, type) {
var makeNewValue = function(term, dataType, valueObj) {
var field = $('.resource-values.field[data-property-term="' + term + '"]');
// Get the value node from the templates.
if (typeof type !== 'string') {
type = valueObj['type'];
if (!dataType || typeof dataType !== 'string') {
dataType = valueObj ? valueObj['type'] : field.find('.add-value:visible:first').data('type');
}
var value = $('.value.template[data-data-type="' + type + '"]').clone(true);
var fieldForDataType = field.filter(function() { return $.inArray(dataType, $(this).data('data-types').split(',')) > -1; });
field = fieldForDataType.length ? fieldForDataType.first() : field.first();
var value = $('.value.template[data-data-type="' + dataType + '"]').clone(true);
value.removeClass('template');
value.data('data-term', term);

// Get and display the value's visibility.
var isPublic = true; // values are public by default
Expand All @@ -254,8 +258,8 @@
valueVisibilityButton.attr('aria-label', Omeka.jsTranslate('Make public'));
valueVisibilityButton.attr('title', Omeka.jsTranslate('Make public'));
}

// Prepare the value node.
var count = field.find('.value').length;
var valueLabelID = 'property-' + field.data('property-id') + '-label';
value.find('input.is_public')
.val(isPublic ? 1 : 0);
Expand All @@ -264,15 +268,15 @@
value.find('textarea.input-value')
.attr('aria-labelledby', valueLabelID);
value.attr('aria-labelledby', valueLabelID);
$(document).trigger('o:prepare-value', [type, value, valueObj]);
$(document).trigger('o:prepare-value', [dataType, value, valueObj]);

return value;
};

/**
* Prepare the markup for the default data types.
*/
$(document).on('o:prepare-value', function(e, type, value, valueObj) {
$(document).on('o:prepare-value', function(e, dataType, value, valueObj) {
// Prepare simple single-value form inputs using data-value-key
value.find(':input').each(function () {
var valueKey = $(this).data('valueKey');
Expand All @@ -290,7 +294,7 @@
'resource:itemset',
'resource:media',
];
if (valueObj && -1 !== resourceDataTypes.indexOf(type)) {
if (valueObj && -1 !== resourceDataTypes.indexOf(dataType)) {
value.find('span.default').hide();
var resource = value.find('.selected-resource');
if (typeof valueObj['display_title'] === 'undefined') {
Expand All @@ -310,8 +314,13 @@
/**
* Make a new property field.
*/
var makeNewField = function(property) {
//sort out whether property is the LI that holds data, or the id
var makeNewField = function(property, dataTypes) {
// Prepare data type name of the field.
if (!dataTypes || dataTypes.length < 1) {
dataTypes = ['literal', 'resource', 'uri'];
}

// Sort out whether property is the LI that holds data, or the id.
var propertyLi, propertyId;

switch (typeof property) {
Expand All @@ -329,6 +338,9 @@
propertyLi = $('#property-selector').find("li[data-property-term='" + property + "']");
propertyId = propertyLi.data('property-id');
break;

default:
return null;
}

var term = propertyLi.data('property-term');
Expand All @@ -339,10 +351,12 @@
field.find('.field-description').prepend(propertyLi.find('.field-comment').text());
field.data('property-term', term);
field.data('property-id', propertyId);
field.data('data-types', dataTypes.join(','));
// Adding the attr because selectors need them to find the correct field
// and count when adding more.
field.attr('data-property-term', term);
field.attr('data-property-id', propertyId);
field.attr('data-data-types', dataTypes.join(','));
field.attr('aria-labelledby', 'property-' + propertyId + '-label');
$('div#properties').append(field);

Expand All @@ -357,67 +371,99 @@

/**
* Rewrite a property field following the rules defined by the selected
* resource template.
* resource template property or create a new one.
*/
var rewritePropertyField = function(template) {
var rewritePropertyField = function(templateProperty) {
var properties = $('div#properties');
var propertyId = template['o:property']['o:id'];
var propertyId = templateProperty['o:property']['o:id'];
var dataTypes = templateProperty['o:data_type'] && templateProperty['o:data_type'].length
? templateProperty['o:data_type']
: ['literal', 'resource', 'uri'];

var field = properties.find('[data-property-id="' + propertyId + '"]');
if (field.length == 0) {
field = makeNewField(propertyId);
field = makeNewField(propertyId, dataTypes);
}

var originalLabel = field.find('.field-label');
var originalDescription = field.find('.field-description');
var singleSelector = field.find('div.single-selector');
var defaultSelector = field.find('div.default-selector');
var multipleSelector = field.find('div.multiple-selector');
var singleSelector = field.find('div.single-selector');

if (template['o:is_required']) {
if (templateProperty['o:is_required']) {
field.addClass('required');
}
if (template['o:is_private']) {
if (templateProperty['o:is_private']) {
field.addClass('private');
}
if (template['o:alternate_label']) {
if (templateProperty['o:alternate_label']) {
var altLabel = originalLabel.clone();
var altLabelId = 'property-' + propertyId + '-' + dataTypes.join('-') + '-label';
altLabel.addClass('alternate');
altLabel.text(template['o:alternate_label']);
altLabel.text(templateProperty['o:alternate_label']);
altLabel.insertAfter(originalLabel);
altLabel.attr('id', altLabelId);
field.attr('aria-labelledby', altLabelId);
originalLabel.hide();
}
if (template['o:alternate_comment']) {
if (templateProperty['o:alternate_comment']) {
var altDescription = originalDescription.clone();
altDescription.addClass('alternate');
altDescription.text(template['o:alternate_comment']);
altDescription.text(templateProperty['o:alternate_comment']);
altDescription.insertAfter(originalDescription);
originalDescription.hide();
}

// Remove any unchanged default values for this property so we start fresh.
field.find('.value.default-value').remove();

// Change value selector and add empty value if needed.
if (template['o:data_type']) {
// Use the single selector if the property has a data type.
// Change value selector (multiple, single, or default) and add empty value if needed.
if (templateProperty['o:data_type'].length > 1) {
defaultSelector.hide();
singleSelector.find('a.add-value.button').data('type', template['o:data_type'])
singleSelector.show();
// Add an empty value if none already exist in the property.
if (!field.find('.value').length) {
field.find('.values').append(makeNewValue(field.data('property-term'), null, template['o:data_type']));
singleSelector.hide();
if (!multipleSelector.find('.add-value').length) {
multipleSelector.append(prepareMultipleSelector(templateProperty['o:data_type']));
}
multipleSelector.show();
} else if (templateProperty['o:data_type'].length === 1) {
defaultSelector.hide();
multipleSelector.hide();
singleSelector.find('a.add-value.button').data('type', templateProperty['o:data_type'][0]);
singleSelector.show();
} else {
// Use the default selector if the property has no data type.
multipleSelector.hide();
singleSelector.hide();
defaultSelector.show();
// Add an empty default value if none already exist in the property.
if (!field.find('.value').length) {
field.find('.values').append(makeDefaultValue(field.data('property-term'), 'literal'));
}
}

// Add an empty value if none already exist in the property.
if (!field.find('.value').length) {
field.find('.values').append(makeNewValue(field.data('property-term'), templateProperty['o:data_type'][0]));
}

properties.prepend(field);
};

/**
* Prepare a selector (usualy a html list of buttons) from a list of data types.
*
* @param array dataTypes
* @return string
*/
var prepareMultipleSelector = function(dataTypes) {
var html = '';
dataTypes.forEach(function(dataType) {
var dataTypeTemplate = $('.template.value[data-data-type="' + dataType + '"]');
var label = dataTypeTemplate.data('data-type-label') ? dataTypeTemplate.data('data-type-label') : Omeka.jsTranslate('Add value');
var icon = dataTypeTemplate.data('data-type-icon') ? dataTypeTemplate.data('data-type-icon') : dataType.substring(0, (dataType + ':').indexOf(':'));
html += dataTypeTemplate.data('data-type-button')
? dataTypeTemplate.data('data-type-button') + ' '
: '<a href="#" class="add-value button o-icon-' + icon + '" data-type="' + dataType + '">' + label + '</a> ';
});
return html;
};

/**
* Apply the selected resource template to the form.
*
Expand All @@ -435,10 +481,12 @@
if (!templateId) {
// Using the default resource template, so all properties should use the default
// selector.
fields.find('div.multiple-selector').hide();
fields.find('div.single-selector').hide();
fields.find('div.default-selector').show();
return;
}

var url = templateSelect.data('api-base-url') + '/' + templateId;
return $.get(url)
.done(function(data) {
Expand All @@ -465,6 +513,7 @@
var propertyId = $(this).data('property-id');
if (templatePropertyIds.indexOf(propertyId) === -1) {
var field = $(this);
field.find('div.multiple-selector').hide();
field.find('div.single-selector').hide();
field.find('div.default-selector').show();
}
Expand All @@ -475,8 +524,8 @@
});
}

var makeDefaultValue = function (term, type) {
return makeNewValue(term, null, type)
var makeDefaultValue = function (term, dataType) {
return makeNewValue(term, dataType)
.addClass('default-value')
.one('change', '*', function (event) {
$(event.delegateTarget).removeClass('default-value');
Expand All @@ -496,7 +545,7 @@
$.each(valuesJson, function(term, valueObj) {
var field = makeNewField(term);
$.each(valueObj.values, function(index, value) {
field.find('.values').append(makeNewValue(term, value));
field.find('.values').append(makeNewValue(term, null, value));
});
});
}
Expand All @@ -515,4 +564,3 @@
});
};
})(jQuery);

26 changes: 23 additions & 3 deletions application/asset/js/resource-template-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ new Sortable(propertyList[0], {
handle: ".sortable-handle"
});

// Update the property data types, that are a list in an hidden input.
$('#resourcetemplateform').on('submit', function(e) {
propertyList.find('.property.row').each(function() {
var prop = $(this);
var dataTypes = prop.find('.data-type').val().split(',').filter(function (el) { return el != ''; });
var inputName = prop.find('.data-type').attr('name');
prop.find('.data-type').remove();
dataTypes.forEach(function(dataType) {
prop.append('<input type="hidden" name="' + inputName + '" value="' + dataType +'">');
});
});
});

// Add property row via the property selector.
$('#property-selector .selector-child').click(function(e) {
e.preventDefault();
Expand Down Expand Up @@ -53,6 +66,8 @@ propertyList.on('click', '.property-restore', function(e) {

propertyList.on('click', '.property-edit', function(e) {
e.preventDefault();

// Get values stored in the row.
var prop = $(this).closest('.property');
var propId = prop.data('property-id');
var oriLabel = prop.find('.original-label');
Expand All @@ -61,8 +76,9 @@ propertyList.on('click', '.property-edit', function(e) {
var altComment = prop.find('.alternate-comment');
var isRequired = prop.find('.is-required');
var isPrivate = prop.find('.is-private');
var dataType = prop.find('.data-type');
var dataTypes = prop.find('.data-type');

// Copy values into the sidebar.
$('#original-label').text(oriLabel.val());
$('#alternate-label').val(altLabel.val());
$('#original-comment').text(oriComment.val());
Expand All @@ -71,9 +87,13 @@ propertyList.on('click', '.property-edit', function(e) {
$('#is-description-property').prop('checked', propId == descriptionProperty.val());
$('#is-required').prop('checked', isRequired.val());
$('#is-private').prop('checked', isPrivate.val());
$('#data-type option[value="' + dataType.val() + '"]').prop('selected', true);
$('#data-type option').prop('selected', false);
dataTypes.val().split(',').filter(function (el) { return el != ''; }).forEach(function(selected) {
$('#data-type option[value="' + selected + '"]').prop('selected', true);
});
$('#data-type').trigger('chosen:updated');

// When the sidebar fieldset is applied, store new values in the row.
$('#set-changes').off('click.setchanges').on('click.setchanges', function(e) {
altLabel.val($('#alternate-label').val());
prop.find('.alternate-label-cell').text($('#alternate-label').val());
Expand All @@ -96,7 +116,7 @@ propertyList.on('click', '.property-edit', function(e) {
}
$('#is-required').prop('checked') ? isRequired.val(1) : isRequired.val(null);
$('#is-private').prop('checked') ? isPrivate.val(1) : isPrivate.val(null);
dataType.val($('#data-type').val());
dataTypes.val($('#data-type').val().join(','));
Omeka.closeSidebar($('#edit-sidebar'));
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public function setPosition($position)
/**
* {@inheritDoc}
*/
public function setDataType($dataType)
public function setDataType(array $dataType = NULL)
{

$this->__initializer__ && $this->__initializer__->__invoke($this, 'setDataType', [$dataType]);
Expand Down
2 changes: 1 addition & 1 deletion application/data/install/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ CREATE TABLE `resource_template_property` (
`alternate_label` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`alternate_comment` longtext COLLATE utf8mb4_unicode_ci,
`position` int(11) DEFAULT NULL,
`data_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`data_type` longtext COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '(DC2Type:json_array)',
`is_required` tinyint(1) NOT NULL,
`is_private` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
Expand Down
Loading

0 comments on commit 4e8dad6

Please sign in to comment.