diff --git a/api/js/etemplate/Et2Button/Et2ButtonCopy.ts b/api/js/etemplate/Et2Button/Et2ButtonCopy.ts
new file mode 100644
index 0000000000..135cb1e811
--- /dev/null
+++ b/api/js/etemplate/Et2Button/Et2ButtonCopy.ts
@@ -0,0 +1,20 @@
+/**
+ * EGroupware eTemplate2 - Button widget
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link https://www.egroupware.org
+ * @author Nathan Gray
+ */
+
+
+import {Et2Widget} from "../Et2Widget/Et2Widget";
+import {SlCopyButton} from "@shoelace-style/shoelace";
+import {customElement} from "lit/decorators/custom-element.js";
+
+@customElement('et2-button-copy')
+export class Et2Button extends Et2Widget(SlCopyButton)
+{
+
+}
\ No newline at end of file
diff --git a/api/js/etemplate/Et2Dialog/Et2Dialog.ts b/api/js/etemplate/Et2Dialog/Et2Dialog.ts
index 5718c03d1d..77b7e4c3f3 100644
--- a/api/js/etemplate/Et2Dialog/Et2Dialog.ts
+++ b/api/js/etemplate/Et2Dialog/Et2Dialog.ts
@@ -834,10 +834,10 @@ export class Et2Dialog extends Et2Widget(SlDialog)
this._template_widget.DOMContainer.setAttribute('id', this.__template.replace(/^(.*\/)?([^/]+?)(\.xet)?(\?.*)?$/, '$2').replace(/\./g, '-'));
// Look for buttons after load
- this._contentNode.addEventListener("load", this._adoptTemplateButtons);
+ this._template_promise.then(() => {this._adoptTemplateButtons();});
// Default autofocus to first input if autofocus is not set
- this._contentNode.addEventListener("load", this._setDefaultAutofocus);
+ this._template_promise.then(() => {this._setDefaultAutofocus();});
// Need to update to pick up changes
this.requestUpdate();
@@ -1333,6 +1333,7 @@ export class Et2Dialog extends Et2Widget(SlDialog)
let log = null;
let progressbar = null;
let cancel = false;
+ let skip_all = false;
let totals = {
success: 0,
skipped: 0,
@@ -1358,6 +1359,11 @@ export class Et2Dialog extends Et2Widget(SlDialog)
log.appendChild(div);
totals.failed++;
+ if(skip_all)
+ {
+ totals.skipped++;
+ break;
+ }
// Ask to retry / ignore / abort
let retry = new Et2Dialog(dialog.egw());
@@ -1372,7 +1378,11 @@ export class Et2Dialog extends Et2Widget(SlDialog)
break;
case 'dialog[skip]':
totals.skipped++;
- break
+ break;
+ case 'dialog[skip_all]':
+ totals.skipped++;
+ skip_all = true;
+ break;
default:
// Try again with previous index
retry_index = index - 1;
@@ -1384,7 +1394,8 @@ export class Et2Dialog extends Et2Widget(SlDialog)
// These ones will use the callback, just like normal
{label: dialog.egw().lang("Abort"), id: 'dialog[cancel]'},
{label: dialog.egw().lang("Retry"), id: 'dialog[retry]'},
- {label: dialog.egw().lang("Skip"), id: 'dialog[skip]', default: true}
+ {label: dialog.egw().lang("Skip"), id: 'dialog[skip]', default: true},
+ {label: dialog.egw().lang("Skip all"), id: 'dialog[skip_all]'}
],
dialog_type: Et2Dialog.ERROR_MESSAGE
});
diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts
index d1128a3c4f..4106bdac7d 100644
--- a/api/js/etemplate/etemplate2.ts
+++ b/api/js/etemplate/etemplate2.ts
@@ -31,6 +31,7 @@ import './Layout/Et2Tabs/Et2TabsMobile';
import './Et2Avatar/Et2Avatar';
import './Et2Avatar/Et2AvatarGroup';
import './Et2Button/Et2Button';
+import './Et2Button/Et2ButtonCopy';
import './Et2Button/Et2ButtonIcon';
import './Et2Button/Et2ButtonScroll';
import './Et2Button/Et2ButtonTimestamper';
diff --git a/api/templates/default/long_task.xet b/api/templates/default/long_task.xet
index 82bf757498..57d9446a01 100644
--- a/api/templates/default/long_task.xet
+++ b/api/templates/default/long_task.xet
@@ -10,6 +10,8 @@
+
+
#long_task > div {