diff --git a/ci/mvn-release.sh b/ci/mvn-release.sh
old mode 100644
new mode 100755
diff --git a/ci/setup-git.sh b/ci/setup-git.sh
old mode 100644
new mode 100755
diff --git a/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/css/genericmultifield.css b/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/css/genericmultifield.css
index 5ee1264..c9c824a 100644
--- a/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/css/genericmultifield.css
+++ b/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/css/genericmultifield.css
@@ -28,7 +28,8 @@
.coral-SpectrumMultiField-edit,
.coral-SpectrumMultiField-remove,
-.coral-SpectrumMultiField-move {
+.coral-SpectrumMultiField-move,
+.coral-SpectrumMultiField-copy {
width: 41px;
height: 41px;
}
@@ -65,6 +66,12 @@
right: 0;
}
+.coral-SpectrumMultiField-copy {
+ position: absolute;
+ top: 0;
+ right: 6.3rem;
+}
+
.coral-GenericMultiField .coral-GenericMultiField-listEntry {
border: .0625rem solid rgba(0, 0, 0, 0.15);
position: relative;
diff --git a/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/js/CUI.GenericMultiField.js b/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/js/CUI.GenericMultiField.js
index 8c0a3eb..1c020c4 100644
--- a/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/js/CUI.GenericMultiField.js
+++ b/src/main/resources/SLING-INF/apps/merkle/genericmultifield/clientlibs/js/CUI.GenericMultiField.js
@@ -8,6 +8,7 @@
var removeButton = "";
var editButton = "";
var moveButton = "";
+ var copyButton = "";
/**
* The Merkle.GenericMultiField class represents an editable list
@@ -164,10 +165,12 @@
li.append($(removeButton));
li.append(editButton);
li.append(moveButton);
+ li.append(copyButton);
if (this.readOnly) {
$(".coral-SpectrumMultiField-remove", li).attr("disabled", "disabled");
$(".coral-SpectrumMultiField-edit", li).attr("disabled", "disabled");
$(".coral-SpectrumMultiField-move", li).attr("disabled", "disabled");
+ $(".coral-SpectrumMultiField-copy", li).attr("disabled", "disabled");
}
return li;
},
@@ -196,6 +199,11 @@
that._editItem(currentItem);
});
+ this.$element.on("click", ".js-coral-SpectrumMultiField-copy", function (e) {
+ var currentItem = $(this).closest("li");
+ that._copyItem(currentItem);
+ });
+
this.$element
.fipo("taphold", "mousedown", ".js-coral-SpectrumMultiField-move", function (e) {
@@ -355,6 +363,37 @@
}
},
+ /**
+ * Copies an item by creating a duplicate.
+ *
+ * @param {Object} item List item to be copied.
+ * @private
+ */
+ _copyItem: function (item) {
+ var that = this;
+ var currentElements = this.$element.find("li").length;
+
+ if (!this.maxElements || (currentElements < this.maxElements)) {
+
+ this.ui.prompt(Granite.I18n.get("Copy Item"), Granite.I18n.get("Are you sure you want to copy this item?", this.minElements), "info",
+ [{text: Granite.I18n.get("Cancel")},
+ {
+ text: Granite.I18n.get("Copy"),
+ primary: true,
+ handler: function () {
+ var sourcePath = that.crxPath + "/" + that.itemStorageNode + "/" + item.attr("id");
+ that._copyNode(sourcePath, function (copiedPath) {
+ console.log("Item copied successfully to:", copiedPath);
+ // Refresh the list to show the newly copied item
+ that._updateList(true);
+ });
+ }
+ }]);
+ } else {
+ this.ui.alert(Granite.I18n.get("Maximum reached"), Granite.I18n.get("Maximum number of {0} item(s) reached, you cannot add any additional items.", this.maxElements), "warning");
+ }
+ },
+
/**
* Performs drag and drop reordering and
* executes a sling reordering request on crx items.
@@ -467,6 +506,33 @@
});
},
+ /**
+ * Copies the node at given path.
+ *
+ * @param {String} path of node to be copied.
+ * @param {Function} callback node that has been copied.
+ * @private
+ */
+ _copyNode: function (path, callback) {
+ var that = this;
+ // Create a unique destination path for the copy
+ var timestamp = Date.now();
+ var destinationPath = that.crxPath + "/" + that.itemStorageNode + "/copy_" + timestamp;
+
+ $.ajax({
+ type: "POST",
+ data: ":operation=copy&:dest=" + destinationPath,
+ url: path
+ }).done(function (data) {
+ if ($.isFunction(callback)) {
+ callback(destinationPath);
+ }
+ }).fail(function(xhr, status, error) {
+ console.error("Copy operation failed:", error);
+ that.ui.alert(Granite.I18n.get("Copy Error"), Granite.I18n.get("Failed to copy the item. Please try again."), "error");
+ });
+ },
+
/**
* Triggers the change event with the DOM element as the source.
*