Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
88015b3
Add mapping entity to database model and api.
Bebel00 Nov 3, 2025
68f6a90
Add entity file to last commit.
Bebel00 Nov 3, 2025
03535ee
Add save/load mapping functions to IndexController.
Bebel00 Nov 3, 2025
4d94a89
Separate views for mapping table and the rest.
Bebel00 Nov 3, 2025
3b472ee
Change module version and database schema in Module.php
Bebel00 Nov 3, 2025
d447361
Add controllers for saved mappings and interface.
Bebel00 Nov 3, 2025
11a83f2
Add JS for AJAX mapping selection when importing.
Bebel00 Nov 3, 2025
5d87754
Add new mapping controllers and routes.
Bebel00 Nov 3, 2025
cb464f0
Add options to save mapping and change menu button to select one.
Bebel00 Nov 7, 2025
c5a5d0a
Fix scripts to take in account newly added table.
Bebel00 Nov 7, 2025
25ba1d9
Bump back to v2.6.0 (will be bumped again on main).
Bebel00 Nov 14, 2025
ec581ee
Clean code with gulp fix:module:cs.
Bebel00 Nov 14, 2025
ddb08fc
Bump again to 3.0.0.
Bebel00 Nov 14, 2025
02543ea
Remove useless comments and console logs used for debug.
Bebel00 Nov 14, 2025
7c92dce
Fix SQL update for 3.0.0 and indent lines.
Bebel00 Nov 14, 2025
4a9fbd4
Add 'P' to 'RIMARY KEY'.
Bebel00 Nov 14, 2025
fecbb15
Add unique constraint on mapping name.
Bebel00 Nov 19, 2025
57a99b2
Move saveMapping() to PluginController and add a SaveMapping form for…
Bebel00 Nov 19, 2025
763318c
Create Save mapping sidebar view.
Bebel00 Nov 19, 2025
6b62363
Move save of mapping in past imports table.
Bebel00 Nov 19, 2025
1c75e29
Rename mapping to mapping model.
Bebel00 Jan 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 45 additions & 18 deletions Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,34 @@ public function install(ServiceLocatorInterface $serviceLocator)
{
$connection = $serviceLocator->get('Omeka\Connection');
$sql = <<<'SQL'
CREATE TABLE csvimport_mapping_model (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
created DATETIME NOT NULL,
mapping LONGTEXT NOT NULL COMMENT '(DC2Type:json)',
PRIMARY KEY(id),
UNIQUE INDEX UNIQ_B0D508235E237E06 (name))
DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
CREATE TABLE csvimport_import (
id INT AUTO_INCREMENT NOT NULL,
job_id INT NOT NULL,
undo_job_id INT DEFAULT NULL,
comment VARCHAR(255) DEFAULT NULL,
resource_type VARCHAR(255) NOT NULL,
has_err TINYINT(1) NOT NULL,
stats LONGTEXT NOT NULL COMMENT '(DC2Type:json_array)',
UNIQUE INDEX UNIQ_17B50881BE04EA9 (job_id),
UNIQUE INDEX UNIQ_17B508814C276F75 (undo_job_id),
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB;
id INT AUTO_INCREMENT NOT NULL,
job_id INT NOT NULL,
undo_job_id INT DEFAULT NULL,
comment VARCHAR(255) DEFAULT NULL,
resource_type VARCHAR(255) NOT NULL,
has_err TINYINT(1) NOT NULL,
stats LONGTEXT NOT NULL COMMENT '(DC2Type:json_array)',
UNIQUE INDEX UNIQ_17B50881BE04EA9 (job_id),
UNIQUE INDEX UNIQ_17B508814C276F75 (undo_job_id),
PRIMARY KEY(id))
DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
CREATE TABLE csvimport_entity (
id INT AUTO_INCREMENT NOT NULL,
job_id INT NOT NULL,
entity_id INT NOT NULL,
resource_type VARCHAR(255) NOT NULL,
INDEX IDX_84D382F4BE04EA9 (job_id),
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB;
id INT AUTO_INCREMENT NOT NULL,
job_id INT NOT NULL,
entity_id INT NOT NULL,
resource_type VARCHAR(255) NOT NULL,
INDEX IDX_84D382F4BE04EA9 (job_id),
PRIMARY KEY(id))
DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
ALTER TABLE csvimport_import ADD CONSTRAINT FK_17B50881BE04EA9 FOREIGN KEY (job_id) REFERENCES job (id);
ALTER TABLE csvimport_import ADD CONSTRAINT FK_17B508814C276F75 FOREIGN KEY (undo_job_id) REFERENCES job (id);
ALTER TABLE csvimport_entity ADD CONSTRAINT FK_84D382F4BE04EA9 FOREIGN KEY (job_id) REFERENCES job (id);
Expand All @@ -60,6 +68,7 @@ public function uninstall(ServiceLocatorInterface $serviceLocator)
ALTER TABLE csvimport_import DROP FOREIGN KEY FK_17B50881BE04EA9;
DROP TABLE IF EXISTS csvimport_entity;
DROP TABLE IF EXISTS csvimport_import;
DROP TABLE IF EXISTS csvimport_mapping_model;
SQL;
$sqls = array_filter(array_map('trim', explode(';', $sql)));
foreach ($sqls as $sql) {
Expand All @@ -76,6 +85,24 @@ public function upgrade($oldVersion, $newVersion, ServiceLocatorInterface $servi
ALTER TABLE csvimport_import ADD stats LONGTEXT NOT NULL COMMENT '(DC2Type:json_array)';
UPDATE csvimport_import SET stats = CONCAT('{"processed":{"', resource_type, '":', added_count, '}}');
ALTER TABLE csvimport_import DROP added_count;
SQL;
$sqls = array_filter(array_map('trim', explode(';', $sql)));
foreach ($sqls as $sql) {
$connection->exec($sql);
}
}

if (version_compare($oldVersion, '2.7.0', '<')) {
$connection = $serviceLocator->get('Omeka\Connection');
$sql = <<<'SQL'
CREATE TABLE csvimport_mapping_model (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
created DATETIME NOT NULL,
mapping LONGTEXT NOT NULL COMMENT '(DC2Type:json)',
PRIMARY KEY(id),
UNIQUE INDEX UNIQ_B0D508235E237E06 (name))
DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
SQL;
$sqls = array_filter(array_map('trim', explode(';', $sql)));
foreach ($sqls as $sql) {
Expand Down
139 changes: 74 additions & 65 deletions asset/js/csvimport.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Initially based on Omeka S omeka2importer.js and resource-core.js.
*/
(function ($) {

$(document).ready(function() {
/*
* Init.
Expand All @@ -14,12 +13,85 @@
var defaultSidebarHtml = null;

var actionsHtml = '<ul class="actions">'
+ '<li><a aria-label="' + Omeka.jsTranslate('Remove mapping') + '" title="' + Omeka.jsTranslate('Remove mapping') + '" class="o-icon-delete remove-mapping" href="#" style="display: inline;"></a></li>'
+ '<li><a aria-label="' + Omeka.jsTranslate('Remove mapping model') + '" title="' + Omeka.jsTranslate('Remove mapping model') + '" class="o-icon-delete remove-mapping" href="#" style="display: inline;"></a></li>'
+ '</ul>';

var batchEditCheckboxes = $('.column-select, .select-all');
var batchEditButton = $('#batch-edit-options');

$(document).on('mapping.updated', newTable);

newTable();

function newTable() {
resetActiveColumns();

/*
* Batch edit options.
*/

$('.batch-edit input[type="checkbox"], .batch-edit .select-all').change(function() {
if ($('.column-select:checked').length > 0) {
batchEditButton.removeClass('inactive').addClass('active sidebar-content');
} else {
batchEditButton.addClass('inactive').removeClass('active sidebar-content');
}
});

/*
* Sidebar chooser (buttons on each mappable element).
*/

$('.column-header + .actions a').on('click', function(e) {
e.preventDefault();
if (activeElement !== null) {
activeElement.removeClass('active');
}
if ($('.column-select:checked').length > 0) {
resetActiveColumns();
}
activeElement = $(e.target).closest('tr.mappable');
activeElement.addClass('active');

var actionElement = $(this);
$('.sidebar-chooser li').removeClass('active');
actionElement.parent().addClass('active');
var target = actionElement.data('sidebar-selector');

var sidebar = $(target);
if (!sidebar.hasClass('active') ) {
defaultSidebarHtml = sidebar.html();
}
var columnName = activeElement.data('column');
if (sidebar.find('.column-name').length > 0) {
$('.column-name').text(columnName);
} else {
sidebar.find('h3').append(' <span class="column-name">' + columnName + '</span>');
}

var currentSidebar = $('.sidebar.active');
if (currentSidebar.attr('id') != target) {
currentSidebar.removeClass('active');
sidebar.html(defaultSidebarHtml);
rebindInputs(sidebar);
}

Omeka.openSidebar(sidebar);
populateSidebar();
});

/*
* Actions on mapped columns.
*/

// Remove mapping.
$('.section').on('click', 'a.remove-mapping', function(e) {
e.preventDefault();
e.stopPropagation();
$(this).parents('li.mapping').remove();
});
};

/*
* Rebinding chosen selects and property selector after sidebar hydration.
*/
Expand Down Expand Up @@ -60,47 +132,6 @@
});
}

/*
* Sidebar chooser (buttons on each mappable element).
*/

$('.column-header + .actions a').on('click', function(e) {
e.preventDefault();
if (activeElement !== null) {
activeElement.removeClass('active');
}
if ($('.column-select:checked').length > 0) {
resetActiveColumns();
}
activeElement = $(e.target).closest('tr.mappable');
activeElement.addClass('active');

var actionElement = $(this);
$('.sidebar-chooser li').removeClass('active');
actionElement.parent().addClass('active');
var target = actionElement.data('sidebar-selector');

var sidebar = $(target);
if (!sidebar.hasClass('active') ) {
defaultSidebarHtml = sidebar.html();
}
var columnName = activeElement.data('column');
if (sidebar.find('.column-name').length > 0) {
$('.column-name').text(columnName);
} else {
sidebar.find('h3').append(' <span class="column-name">' + columnName + '</span>');
}

var currentSidebar = $('.sidebar.active');
if (currentSidebar.attr('id') != target) {
currentSidebar.removeClass('active');
sidebar.html(defaultSidebarHtml);
rebindInputs(sidebar);
}

Omeka.openSidebar(sidebar);
populateSidebar();
});

function populateSidebar() {
$('.active.element .options :input:not(:disabled)').each(function() {
Expand All @@ -119,17 +150,6 @@
});
}

/*
* Batch edit options.
*/

$('.batch-edit input[type="checkbox"], .batch-edit .select-all').change(function() {
if ($('.column-select:checked').length > 0) {
batchEditButton.removeClass('inactive').addClass('active sidebar-content');
} else {
batchEditButton.addClass('inactive').removeClass('active sidebar-content');
}
});

$(document).on('click', '#batch-edit-options.active', function() {
defaultSidebarHtml = $('#column-options').html();
Expand Down Expand Up @@ -341,17 +361,6 @@
batchEditButton.removeClass('active sidebar-content').addClass('inactive');
}

/*
* Actions on mapped columns.
*/

// Remove mapping.
$('.section').on('click', 'a.remove-mapping', function(e) {
e.preventDefault();
e.stopPropagation();
$(this).parents('li.mapping').remove();
});

function applyMappings(flagName, flagValue, flagLiClass, flagLabel) {
var hasFlag = activeElement.find('ul.mappings li.' + flagLiClass);
if (flagValue == 'default') {
Expand Down
33 changes: 33 additions & 0 deletions asset/js/mappingselect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(function ($) {
$(document).ready(function() {
$(document).on('click', '#mapping-select-button', function(e) {
e.preventDefault(); // Stop normal form submission

var form = $(this).closest('form');
var formData = form.serialize(); // Collect all form inputs

var sidebar = $(this).parents('.sidebar');
Omeka.closeSidebar(sidebar);

$.ajax({
url: form.attr('action'),
method: 'POST',
data: formData,
success: function(response, status, xhr) {
if (xhr.status === 200 && response.trim().length > 0) {
var newTable = $('<div>').html(response).find('table'); // parse HTML
var currentTable = $('table');

// Replace only the inside of the table
currentTable.replaceWith(newTable);

$(document).trigger("enhance.tablesaw");
$(document).trigger('mapping.updated');
} else {
console.error('Received an empty or invalid response from the server.');
}
}
});
});
});
})(jQuery)
47 changes: 45 additions & 2 deletions config/module.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,29 @@
],
],
'form_elements' => [
'invokables' => [
'CSVImport\Form\MappingModelEditForm' => Form\MappingModelEditForm::class,
'CSVImport\Form\MappingModelSelectForm' => Form\MappingModelSelectForm::class,
],
'factories' => [
'CSVImport\Form\ImportForm' => Service\Form\ImportFormFactory::class,
'CSVImport\Form\MappingForm' => Service\Form\MappingFormFactory::class,
'CSVImport\Form\MappingModelForm' => Service\Form\MappingModelFormFactory::class,
'CSVImport\Form\Element\MappingModelSelect' => Service\Form\Element\MappingModelSelectFactory::class,
'CSVImport\Form\MappingModelSaveForm' => Service\Form\MappingModelSaveFormFactory::class,
],
],
'controllers' => [
'factories' => [
'CSVImport\Controller\Index' => Service\Controller\IndexControllerFactory::class,
'CSVImport\Controller\Admin\MappingModel' => Service\Controller\Admin\MappingModelControllerFactory::class,
],
],
'controller_plugins' => [
'factories' => [
'automapHeadersToMetadata' => Service\ControllerPlugin\AutomapHeadersToMetadataFactory::class,
'findResourcesFromIdentifiers' => Service\ControllerPlugin\FindResourcesFromIdentifiersFactory::class,
'loadMappingModel' => Service\ControllerPlugin\LoadMappingModelFactory::class,
'saveMappingModel' => Service\ControllerPlugin\SaveMappingModelFactory::class,
],
'aliases' => [
'findResourceFromIdentifier' => 'findResourcesFromIdentifiers',
Expand All @@ -47,6 +56,7 @@
'invokables' => [
'csvimport_entities' => Api\Adapter\EntityAdapter::class,
'csvimport_imports' => Api\Adapter\ImportAdapter::class,
'csvimport_mapping_models' => Api\Adapter\MappingModelAdapter::class,
],
],
'service_manager' => [
Expand Down Expand Up @@ -92,6 +102,35 @@
],
],
],
'mapping-model' => [
'type' => 'Segment',
'options' => [
'route' => '/mapping-model[/:action]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
'defaults' => [
'__NAMESPACE__' => 'CSVImport\Controller\Admin',
'controller' => 'MappingModel',
'action' => 'browse',
],
],
],
'mapping-model-id' => [
'type' => 'Segment',
'options' => [
'route' => '/mapping-model/:id[/:action]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '\d+',
],
'defaults' => [
'__NAMESPACE__' => 'CSVImport\Controller\Admin',
'controller' => 'MappingModel',
'action' => 'show',
],
],
],
],
],
],
Expand Down Expand Up @@ -123,6 +162,10 @@
'action' => 'past-imports',
'resource' => 'CSVImport\Controller\Index',
],
[
'label' => 'Mapping Models', // @translate
'route' => 'admin/csvimport/mapping-model',
],
],
],
],
Expand All @@ -138,7 +181,7 @@
],
],
'js_translate_strings' => [
'Remove mapping', // @translate
'Remove mapping model', // @translate
],
'csv_import' => [
'sources' => [
Expand Down
Loading