diff --git a/api/js/etemplate/et2_widget_timestamper.js b/api/js/etemplate/et2_widget_timestamper.js
new file mode 100644
index 0000000000..5016a7c87a
--- /dev/null
+++ b/api/js/etemplate/et2_widget_timestamper.js
@@ -0,0 +1,168 @@
+/**
+ * EGroupware eTemplate2 - JS Timestamp button object
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link http://www.egroupware.org
+ * @author Nathan Gray
+ * @copyright Nathan Gray 2017
+ */
+
+/*egw:uses
+ et2_button;
+*/
+
+/**
+ * Class which implements the "button-timestamper" XET-Tag
+ *
+ * Clicking the button puts the current time and current user at the end of
+ * the provided field.
+ *
+ * @augments et2_button
+ */
+var et2_timestamper = (function(){ "use strict"; return et2_button.extend([],
+{
+ attributes: {
+ target: {
+ name: "Target field",
+ type: "string",
+ default: et2_no_init,
+ description: "Which field to place the timestamp in"
+ },
+ image: {
+ default: "timestamp"
+ },
+ background_image: {
+ default: true
+ }
+ },
+
+ /**
+ * Constructor
+ *
+ * @memberOf et2_button
+ */
+ init: function() {
+ this._super.apply(this, arguments);
+ },
+
+ /**
+ * Overwritten to maintain an internal clicked attribute
+ *
+ * @param _ev
+ * @returns {Boolean}
+ */
+ click: function(_ev) {
+ // ignore click on readonly button
+ if (this.options.readonly) return false;
+
+ this._insert_text();
+
+ if (!this._super.apply(this, arguments))
+ {
+ return false;
+ }
+
+ return true;
+ },
+
+ _insert_text: function() {
+ var text = "";
+ var now = new Date();
+ text += date(egw.preference('dateformat') + ' ' + (egw.preference("timeformat") === "12" ? "h:ia" : "H:i")+' ',now);
+
+ // Get properly formatted user name
+ var user = parseInt(egw.user('account_id'));
+ var accounts = egw.accounts('accounts');
+ for(var j = 0; j < accounts.length; j++)
+ {
+ if(accounts[j].value === user)
+ {
+ text += accounts[j].label;
+ break;
+ }
+ }
+ text += ': ';
+
+ var input = this._get_input(this.target);
+ var scrollPos = input.scrollTop;
+ var browser = ((input.selectionStart || input.selectionStart == "0") ?
+ "standards" : (document.selection ? "ie" : false ) );
+
+ var pos = 0;
+
+ // Find cursor or selection
+ if (browser == "ie")
+ {
+ input.focus();
+ var range = document.selection.createRange();
+ range.moveStart ("character", -input.value.length);
+ pos = range.text.length;
+ }
+ else if (browser == "standards")
+ {
+ pos = input.selectionStart;
+ };
+
+ // Insert the text
+ var front = (input.value).substring(0, pos);
+ var back = (input.value).substring(pos, input.value.length);
+ input.value = front+text+back;
+
+ // If CKEDitor, update it
+ if(CKEDITOR && CKEDITOR.instances[input.id])
+ {
+ window.setTimeout(function() {
+ CKEDITOR.instances[input.id].focus();
+ }, 10);
+ }
+ else
+ {
+ // Clean up a little
+ pos = pos + text.length;
+ if (browser == "ie") {
+ input.focus();
+ var range = document.selection.createRange();
+ range.moveStart ("character", -input.value.length);
+ range.moveStart ("character", pos);
+ range.moveEnd ("character", 0);
+ range.select();
+ }
+ else if (browser == "standards") {
+ input.selectionStart = pos;
+ input.selectionEnd = pos;
+ input.focus();
+ }
+ input.scrollTop = scrollPos;
+ }
+ },
+
+ _get_input: function _get_input(target)
+ {
+ var input = null;
+ var widget = null;
+ if(jQuery('#'+this.target).is('input'))
+ {
+ input = this.target;
+ }
+ else if (typeof target == 'string')
+ {
+ var widget = this.getRoot().getWidgetById(target);
+ }
+ else if (target.instanceOf && target.instanceOf(et2_IInput))
+ {
+ widget = target;
+ }
+ if(widget)
+ {
+ input = widget.input ? widget.input : widget.getDOMNode();
+ }
+ if(input.context)
+ {
+ input = input.get(0);
+ }
+ return input;
+ }
+});}).call(this);
+et2_register_widget(et2_timestamper, ["button-timestamp", "timestamper"]);
\ No newline at end of file
diff --git a/api/js/etemplate/etemplate2.js b/api/js/etemplate/etemplate2.js
index 28d45a1103..67efe9088f 100644
--- a/api/js/etemplate/etemplate2.js
+++ b/api/js/etemplate/etemplate2.js
@@ -38,6 +38,7 @@
et2_widget_htmlarea;
et2_widget_tabs;
et2_widget_taglist;
+ et2_widget_timestamper;
et2_widget_toolbar;
et2_widget_tree;
et2_widget_historylog;
diff --git a/api/templates/default/images/timestamp.png b/api/templates/default/images/timestamp.png
new file mode 100644
index 0000000000..864811b6be
Binary files /dev/null and b/api/templates/default/images/timestamp.png differ
diff --git a/infolog/js/app.js b/infolog/js/app.js
index 084f772877..01fd929d18 100644
--- a/infolog/js/app.js
+++ b/infolog/js/app.js
@@ -273,7 +273,7 @@ app.classes.infolog = AppJS.extend(
nm.dataview.getColumnMgr().columns[i].set_visibility(colData[i].visible);
}
nm.dataview.getColumnMgr().updated = true;
-
+
// Update page - set update_in_progress to true to avoid triggering
// the change handler and looping if the user has a custom field
// column change
@@ -667,7 +667,7 @@ app.classes.infolog = AppJS.extend(
extras.link_id = ids;
break;
}
-
+
egw.open("","timesheet","list", extras, 'timesheet');
},
@@ -697,89 +697,6 @@ app.classes.infolog = AppJS.extend(
if (_widget.get_value()) this.et2._inst.submit();
},
- /**
- * Insert text at the cursor position (or end) of a text field
- *
- * @param {et2_inputWidget|string} widget Either a widget object or it's ID
- * @param {string} text [text=timestamp username] Text to insert
- */
- insert_text: function(widget, text)
- {
- if(typeof widget == 'string')
- {
- et2 = etemplate2.getById('infolog-edit');
- if(et2)
- {
- widget = et2.widgetContainer.getWidgetById(widget);
- }
- }
- if(!widget || !widget.input)
- {
- return;
- }
-
- if(!text)
- {
- var now = new Date();
- text = date(egw.preference('dateformat') + ' ' + (egw.preference("timeformat") === "12" ? "h:ia" : "H:i")+' ',now);
-
- // Get properly formatted user name
- var user = parseInt(egw.user('account_id'));
- var accounts = egw.accounts('accounts');
- for(var j = 0; j < accounts.length; j++)
- {
- if(accounts[j].value === user)
- {
- text += accounts[j].label;
- break;
- }
- }
- text += ': ';
- }
-
- var input = widget.input[0];
- var scrollPos = input.scrollTop;
- var browser = ((input.selectionStart || input.selectionStart == "0") ?
- "standards" : (document.selection ? "ie" : false ) );
-
- var pos = 0;
-
- // Find cursor or selection
- if (browser == "ie")
- {
- input.focus();
- var range = document.selection.createRange();
- range.moveStart ("character", -input.value.length);
- pos = range.text.length;
- }
- else if (browser == "standards")
- {
- pos = input.selectionStart;
- };
-
- // Insert the text
- var front = (input.value).substring(0, pos);
- var back = (input.value).substring(pos, input.value.length);
- input.value = front+text+back;
-
- // Clean up a little
- pos = pos + text.length;
- if (browser == "ie") {
- input.focus();
- var range = document.selection.createRange();
- range.moveStart ("character", -input.value.length);
- range.moveStart ("character", pos);
- range.moveEnd ("character", 0);
- range.select();
- }
- else if (browser == "standards") {
- input.selectionStart = pos;
- input.selectionEnd = pos;
- input.focus();
- }
- input.scrollTop = scrollPos;
- },
-
/**
* Toggle encryption
*
@@ -876,7 +793,7 @@ app.classes.infolog = AppJS.extend(
responsible.set_value(data.data.info_responsible);
}
}
-
+
nm_open_popup(_action, _selected);
},
diff --git a/infolog/templates/default/edit.xet b/infolog/templates/default/edit.xet
index 4e12ca2f40..a18f93c8c5 100644
--- a/infolog/templates/default/edit.xet
+++ b/infolog/templates/default/edit.xet
@@ -187,7 +187,7 @@
-
+
diff --git a/pixelegg/images/timestamp.png b/pixelegg/images/timestamp.png
new file mode 100644
index 0000000000..da67e09ae9
Binary files /dev/null and b/pixelegg/images/timestamp.png differ