From ae000be2bc147d717c92dcb5e56d1ca6fd1ec570 Mon Sep 17 00:00:00 2001 From: nathangray Date: Tue, 26 Feb 2019 15:13:47 -0700 Subject: [PATCH 01/14] Instead of new and old values, store unified diff in database for multiline strings --- api/src/Storage/Tracking.php | 15 +++++++++++++++ composer.json | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/api/src/Storage/Tracking.php b/api/src/Storage/Tracking.php index 3ca3aa97ec..f539eaf951 100644 --- a/api/src/Storage/Tracking.php +++ b/api/src/Storage/Tracking.php @@ -18,6 +18,8 @@ use EGroupware\Api; // explicitly reference classes still in phpgwapi or otherwise outside api use notifications; +use Text_Diff_Renderer_unified; + /** * Abstract base class for trackering: * - logging all modifications of an entry @@ -451,6 +453,19 @@ abstract class Tracking $this->historylog->add($name,$data[$this->id_field],$added[$i],$removed[$i]); } } + else if (is_string($data[$name]) && is_string($old[$name]) && ( + strpos($data[$name], PHP_EOL) !== FALSE || strpos($old[$name], PHP_EOL) !== FALSE)) + { + // Multiline string, just store diff + $diff = new \Text_Diff('auto', array(explode("\n",$data[$name]), explode("\n",$old[$name]))); + $renderer = new \Text_Diff_Renderer_unified(); + $this->historylog->add( + $status, + $data[$this->id_field], + $renderer->render($diff), + '***diff***' + ); + } else { //error_log(__METHOD__.__LINE__.' IDField:'.array2string($this->id_field).' ->'.$data[$this->id_field].' New:'.$data[$name].' Old:'.$old[$name]); diff --git a/composer.json b/composer.json index 2f46c925d4..fdf33590f6 100644 --- a/composer.json +++ b/composer.json @@ -84,7 +84,8 @@ "egroupware/tracker": "self.version", "egroupware/z-push-dev": "self.version", "egroupware/activesync": "self.version", - "egroupware/adodb-php": "self.version" + "egroupware/adodb-php": "self.version", + "pear-pear.horde.org/Horde_Text_Diff": "^2.2" }, "require-dev": { }, From 39e7820520d08a35817ced858ae290a39e027bce Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 09:03:28 -0700 Subject: [PATCH 02/14] Just send unified diff to client, not full old & new value --- api/src/Storage/History.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api/src/Storage/History.php b/api/src/Storage/History.php index f3be3807d1..f21393506c 100644 --- a/api/src/Storage/History.php +++ b/api/src/Storage/History.php @@ -260,6 +260,17 @@ class History $row[$field] = explode(Tracking::ONE2N_SEPERATOR,$row[$field]); } } + if ($row['history_old_value'] !== Tracking::DIFF_MARKER && ( + $this->needs_diff($row['history_status'], $row['history_old_value']) || + $this->needs_diff($row['history_status'], $row['history_old_value']) + )) + { + // Larger text stored with full old / new value - calculate diff and just send that + $diff = new \Text_Diff('auto', array(explode("\n",$row['history_new_value']), explode("\n",$row['history_old_value']))); + $renderer = new \Text_Diff_Renderer_unified(); + $row['history_new_value'] = $renderer->render($diff); + $row['history_old_value'] = Tracking::DIFF_MARKER; + } // Get information needed for proper display if($row['history_appname'] == 'filemanager') { @@ -297,4 +308,10 @@ class History return $total; } + + protected function needs_diff($name, $value) + { + return $name == 'note' || $name == 'description' || + ($value && (strlen($value) > 50 || strstr($value, "\n") !== FALSE)); + } } From 73600bb541fcf612a11ac40ee324037820994b85 Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 09:04:17 -0700 Subject: [PATCH 03/14] Use a proper constant for diff marker --- api/src/Storage/Tracking.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/src/Storage/Tracking.php b/api/src/Storage/Tracking.php index f539eaf951..4dffe64ad5 100644 --- a/api/src/Storage/Tracking.php +++ b/api/src/Storage/Tracking.php @@ -191,6 +191,12 @@ abstract class Tracking */ const ONE2N_SEPERATOR = '~|~'; + /** + * Marker for change stored as unified diff, not old/new value + * Diff is in the new value, marker in old value + */ + const DIFF_MARKER = '***diff***'; + /** * Config name for custom notification message */ @@ -463,7 +469,7 @@ abstract class Tracking $status, $data[$this->id_field], $renderer->render($diff), - '***diff***' + self::DIFF_MARKER ); } else From 0ddcd3e9f8eda8322cc01ab5e6053d5644299dcd Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 09:05:07 -0700 Subject: [PATCH 04/14] Start to get new diff viewer in, not working yet --- api/js/etemplate/et2_widget_historylog.js | 12 ++---------- composer.json | 3 ++- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/api/js/etemplate/et2_widget_historylog.js b/api/js/etemplate/et2_widget_historylog.js index d3cd995022..8c629d9ac2 100644 --- a/api/js/etemplate/et2_widget_historylog.js +++ b/api/js/etemplate/et2_widget_historylog.js @@ -500,20 +500,12 @@ var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([e { // Large text value - span both columns, and show a nice diff var jthis = jQuery(this); - if(i == self.NEW_VALUE) + if(i === self.NEW_VALUE) { // Diff widget widget = self.diff.widget; nodes = self.diff.nodes.clone(); - if (typeof _data[self.columns[self.NEW_VALUE].id] == "string") - { - value = _data[self.columns[i].id] = { - 'old': _data[self.columns[i+1].id], - 'new': value - }; - } - // Skip column 4 jthis.parents("td").attr("colspan", 2) .css("border-right", "none"); @@ -571,7 +563,7 @@ var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([e this.egw().debug("warn", "Crazy diff value", value); return false; } - return columnName == 'note' || columnName == 'description' || (value && (value.length > 50 || value.match(/\n/g))); + return value=== '***diff***'; }, resize: function (_height) diff --git a/composer.json b/composer.json index fdf33590f6..29bf265579 100644 --- a/composer.json +++ b/composer.json @@ -85,7 +85,8 @@ "egroupware/z-push-dev": "self.version", "egroupware/activesync": "self.version", "egroupware/adodb-php": "self.version", - "pear-pear.horde.org/Horde_Text_Diff": "^2.2" + "pear-pear.horde.org/Horde_Text_Diff": "^2.2", + "bower-asset/diff2html": "^2.7" }, "require-dev": { }, From 7e7e1982992bcb5ba40c23bc1d15b5cdf3345d33 Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 10:48:10 -0700 Subject: [PATCH 05/14] Make diff check static --- api/src/Storage/History.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/Storage/History.php b/api/src/Storage/History.php index f21393506c..0cfd397025 100644 --- a/api/src/Storage/History.php +++ b/api/src/Storage/History.php @@ -261,8 +261,8 @@ class History } } if ($row['history_old_value'] !== Tracking::DIFF_MARKER && ( - $this->needs_diff($row['history_status'], $row['history_old_value']) || - $this->needs_diff($row['history_status'], $row['history_old_value']) + static::needs_diff($row['history_status'], $row['history_old_value']) || + static::needs_diff($row['history_status'], $row['history_old_value']) )) { // Larger text stored with full old / new value - calculate diff and just send that @@ -309,7 +309,7 @@ class History return $total; } - protected function needs_diff($name, $value) + protected static function needs_diff($name, $value) { return $name == 'note' || $name == 'description' || ($value && (strlen($value) > 50 || strstr($value, "\n") !== FALSE)); From ea9a2def40fa302f4b1414c047ce6ecfed9aee17 Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 10:48:57 -0700 Subject: [PATCH 06/14] Use new diff visualization library --- api/js/etemplate/et2_widget_diff.js | 61 +++++++---------------------- 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/api/js/etemplate/et2_widget_diff.js b/api/js/etemplate/et2_widget_diff.js index 4b49182b16..da622514fa 100644 --- a/api/js/etemplate/et2_widget_diff.js +++ b/api/js/etemplate/et2_widget_diff.js @@ -13,8 +13,8 @@ /*egw:uses /vendor/bower-asset/jquery/dist/jquery.js; /vendor/bower-asset/jquery-ui/jquery-ui.js; - lib/jsdifflib/difflib; - lib/jsdifflib/diffview; + /vendor/bower-asset/diff2html/dist/diff2html.min.js; + /vendor/bower-asset/diff2html/dist/diff2html-ui.min.js; et2_core_valueWidget; */ @@ -31,6 +31,11 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe } }, + diff_options: { + "inputFormat":"diff", + "matching": "words" + }, + /** * Constructor * @@ -48,51 +53,13 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe set_value: function(value) { jQuery(this.div).empty(); - if(typeof value['old'] == 'string' && typeof value['new'] == 'string') { - // Build diff - var old_text = difflib.stringAsLines(value['old'].toString()); - var new_text = difflib.stringAsLines(value['new'].toString()); - var sm = new difflib.SequenceMatcher(old_text, new_text); - var opcodes = sm.get_opcodes(); - var view = diffview.buildView({ - baseTextLines: old_text, - newTextLines: new_text, - opcodes: opcodes, - baseTextName: '',//this.egw().lang('Old value'), - newTextName: '',//this.egw().lang('New value'), - viewType: 1 - }); - jQuery(this.div).append(view); - if(this.mini) { - view = jQuery(view); - this.minify(view); - var self = this; - jQuery(' ') - .appendTo(self.div) - .css("cursor", "pointer") - .click({diff: view, div: self.div}, function(e) { - var diff = e.data.diff; - var div = e.data.div; - self.un_minify(diff); - var dialog_div = jQuery('
') - .append(diff); - dialog_div.dialog({ - title: self.options.label, - width: 'auto', - autoResize: true, - modal: true, - buttons: [{text: self.egw().lang('ok'), click: function() {jQuery(this).dialog("close");}}], - close: function(event, ui) { - // Need to destroy the dialog, etemplate widget needs divs back where they were - dialog_div.dialog("destroy"); - self.minify(this); - - // Put it back where it came from, or et2 will error when clear() is called - diff.prependTo(div); - } - }); - }); - } + if(typeof value == 'string') { + jQuery(this.div).append( + Diff2Html.getPrettyHtml( + value, + this.diff_options + ) + ); } else if(typeof value != 'object') { From 3c76c5a10a02f632c89efd5c28471901810b5047 Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 16:08:20 -0700 Subject: [PATCH 07/14] Fix diff argument order --- api/src/Storage/History.php | 2 +- api/src/Storage/Tracking.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/Storage/History.php b/api/src/Storage/History.php index 0cfd397025..cfae21c5a5 100644 --- a/api/src/Storage/History.php +++ b/api/src/Storage/History.php @@ -266,7 +266,7 @@ class History )) { // Larger text stored with full old / new value - calculate diff and just send that - $diff = new \Text_Diff('auto', array(explode("\n",$row['history_new_value']), explode("\n",$row['history_old_value']))); + $diff = new \Text_Diff('auto', array(explode("\n",$row['history_old_value']), explode("\n",$row['history_new_value']))); $renderer = new \Text_Diff_Renderer_unified(); $row['history_new_value'] = $renderer->render($diff); $row['history_old_value'] = Tracking::DIFF_MARKER; diff --git a/api/src/Storage/Tracking.php b/api/src/Storage/Tracking.php index 4dffe64ad5..2ef70432d1 100644 --- a/api/src/Storage/Tracking.php +++ b/api/src/Storage/Tracking.php @@ -463,7 +463,7 @@ abstract class Tracking strpos($data[$name], PHP_EOL) !== FALSE || strpos($old[$name], PHP_EOL) !== FALSE)) { // Multiline string, just store diff - $diff = new \Text_Diff('auto', array(explode("\n",$data[$name]), explode("\n",$old[$name]))); + $diff = new \Text_Diff('auto', array(explode("\n",$old[$name]), explode("\n",$data[$name]))); $renderer = new \Text_Diff_Renderer_unified(); $this->historylog->add( $status, From d4f614e3afabfa0f2bfd6daa03e3fb1f4e6c794b Mon Sep 17 00:00:00 2001 From: nathangray Date: Wed, 27 Feb 2019 16:09:45 -0700 Subject: [PATCH 08/14] Adapt diff widget to new display library --- api/js/etemplate/et2_widget_diff.js | 55 ++++++++++++++++++++++++---- api/templates/default/etemplate2.css | 22 ++++++++++- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/api/js/etemplate/et2_widget_diff.js b/api/js/etemplate/et2_widget_diff.js index da622514fa..18c314ebeb 100644 --- a/api/js/etemplate/et2_widget_diff.js +++ b/api/js/etemplate/et2_widget_diff.js @@ -14,7 +14,6 @@ /vendor/bower-asset/jquery/dist/jquery.js; /vendor/bower-asset/jquery-ui/jquery-ui.js; /vendor/bower-asset/diff2html/dist/diff2html.min.js; - /vendor/bower-asset/diff2html/dist/diff2html-ui.min.js; et2_core_valueWidget; */ @@ -46,7 +45,7 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe this.mini = true; // included via etemplate2.css - //this.egw().includeCSS('etemplate/js/lib/jsdifflib/diffview.css'); + //this.egw().includeCSS('../../../vendor/bower-asset/dist/dist2html.css'); this.div = document.createElement("div"); jQuery(this.div).addClass('diff'); }, @@ -54,19 +53,59 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe set_value: function(value) { jQuery(this.div).empty(); if(typeof value == 'string') { - jQuery(this.div).append( - Diff2Html.getPrettyHtml( - value, - this.diff_options - ) - ); + + // Diff2Html likes to have files, we don't have them + if(value.indexOf('---') !== 0) + { + value = "--- diff\n+++ diff\n"+value; + } + var diff = Diff2Html.getPrettyHtml(value, this.diff_options); + // var ui = new Diff2HtmlUI({diff: diff}); + // ui.draw(jQuery(this.div), this.diff_options); + jQuery(this.div).append(diff); } else if(typeof value != 'object') { jQuery(this.div).append(value); } + this.check_mini(); }, + check_mini: function() { + if(!this.mini) + { + return false; + } + var view = jQuery(this.div).children(); + this.minify(view); + var self = this; + jQuery(' ') + .appendTo(self.div) + .css("cursor", "pointer") + .click({diff: view, div: self.div}, function(e) { + var diff = e.data.diff; + var div = e.data.div; + self.un_minify(diff); + var dialog_div = jQuery('
') + .append(diff); + + dialog_div.dialog({ + title: self.options.label, + width: 'auto', + autoResize: true, + modal: true, + buttons: [{text: self.egw().lang('ok'), click: function() {jQuery(this).dialog("close");}}], + close: function(event, ui) { + // Need to destroy the dialog, etemplate widget needs divs back where they were + dialog_div.dialog("destroy"); + self.minify(this); + + // Put it back where it came from, or et2 will error when clear() is called + diff.prependTo(div); + } + }); + }); + }, set_label: function(_label) { this.options.label = _label; diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 602d53f418..2636534cd6 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -13,7 +13,7 @@ /*@import url("../../js/jquery/blueimp/css/blueimp-gallery.min.css");*/ /*@import url("../../js/dhtmlxtree/codebase/dhtmlxtree.css");*/ /*@import url("../../js/egw_action/test/skins/dhtmlxmenu_egw.css");*/ -/*@import url("../../js/etemplate/lib/jsdifflib/diffview.css");*/ +/*@import url("../../../vendor/bower-asset/diff2html/dist/diff2html.css");*/ /*@import url("../../../vendor/bower-asset/cropper/dist/cropper.min.css");*/ /*@import url("css/flags.css");*/ /*@import url("css/htmlarea.css");*/ @@ -567,10 +567,28 @@ for printing /** * Diff widget */ +div.diff { + width: 100%; +} .diff thead, -.author { +.author, +.d2h-file-header, +.d2h-file-info, +.d2h-info, +.diff .d2h-cntx +{ display: none; } +.d2h-file-diff { + white-space:normal; +} +.diff .d2h-file-diff { + overflow-x: hidden; +} +.ui-widget-content .d2h-file-diff { + overflow-x: visible; + overflow-y: visible; +} .diff .ui-icon { margin-top: -16px; float: right; From b7934f699c4b6af6e172a9054b148a164d60be5f Mon Sep 17 00:00:00 2001 From: nathangray Date: Thu, 28 Feb 2019 15:15:26 -0700 Subject: [PATCH 09/14] Finish up UI - CSS - Popup sizing & formatting - diff resize when popup resizes --- api/js/etemplate/et2_widget_diff.js | 11 +++++++++-- api/js/etemplate/et2_widget_historylog.js | 10 +++++++++- api/src/Storage/Tracking.php | 5 ++--- api/templates/default/etemplate2.css | 3 +++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/api/js/etemplate/et2_widget_diff.js b/api/js/etemplate/et2_widget_diff.js index 18c314ebeb..1aea479e37 100644 --- a/api/js/etemplate/et2_widget_diff.js +++ b/api/js/etemplate/et2_widget_diff.js @@ -82,7 +82,7 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe jQuery(' ') .appendTo(self.div) .css("cursor", "pointer") - .click({diff: view, div: self.div}, function(e) { + .click({diff: view, div: self.div, label: self.options.label}, function(e) { var diff = e.data.diff; var div = e.data.div; self.un_minify(diff); @@ -90,11 +90,18 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe .append(diff); dialog_div.dialog({ - title: self.options.label, + title: e.data.label, width: 'auto', autoResize: true, modal: true, buttons: [{text: self.egw().lang('ok'), click: function() {jQuery(this).dialog("close");}}], + open: function() { + if(jQuery(this).parent().height() > jQuery(window).height()) + { + jQuery(this).height(jQuery(window).height() *0.7); + } + jQuery(this).dialog({position: "center"}); + }, close: function(event, ui) { // Need to destroy the dialog, etemplate widget needs divs back where they were dialog_div.dialog("destroy"); diff --git a/api/js/etemplate/et2_widget_historylog.js b/api/js/etemplate/et2_widget_historylog.js index 8c629d9ac2..bd7438c2f2 100644 --- a/api/js/etemplate/et2_widget_historylog.js +++ b/api/js/etemplate/et2_widget_historylog.js @@ -473,7 +473,9 @@ var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([e widget = undefined; value = _data['share_email']; } - if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined') + // Get widget from list, unless it needs a diff widget + if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined' && i < self.NEW_VALUE || + i >= self.NEW_VALUE &&!self._needsDiffWidget(_data['status'], _data[self.columns[self.OLD_VALUE].id])) { widget = self.fields[_data.status].widget; if(!widget._children.length) @@ -586,6 +588,12 @@ var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([e this.div.trigger('resize.' +this.options.value.app + this.options.value.id); } } + // Resize diff widgets to match new space + if(this.dataview) + { + var columns = this.dataview.getColumnMgr().columnWidths; + jQuery('.diff', this.div).parent().width(columns[this.NEW_VALUE] + columns[this.OLD_VALUE]); + } } });}).call(this); et2_register_widget(et2_historylog, ['historylog']); diff --git a/api/src/Storage/Tracking.php b/api/src/Storage/Tracking.php index 2ef70432d1..fbbe51617b 100644 --- a/api/src/Storage/Tracking.php +++ b/api/src/Storage/Tracking.php @@ -18,7 +18,6 @@ use EGroupware\Api; // explicitly reference classes still in phpgwapi or otherwise outside api use notifications; -use Text_Diff_Renderer_unified; /** * Abstract base class for trackering: @@ -463,8 +462,8 @@ abstract class Tracking strpos($data[$name], PHP_EOL) !== FALSE || strpos($old[$name], PHP_EOL) !== FALSE)) { // Multiline string, just store diff - $diff = new \Text_Diff('auto', array(explode("\n",$old[$name]), explode("\n",$data[$name]))); - $renderer = new \Text_Diff_Renderer_unified(); + $diff = new \Horde_Text_Diff('auto', array(explode("\n",$old[$name]), explode("\n",$data[$name]))); + $renderer = new \Horde_Text_Diff_Renderer_Unified(); $this->historylog->add( $status, $data[$this->id_field], diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 2636534cd6..9bc072141d 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -585,6 +585,9 @@ div.diff { .diff .d2h-file-diff { overflow-x: hidden; } +.ui-widget-content .d2h-code-line-ctn { + white-space:normal; +} .ui-widget-content .d2h-file-diff { overflow-x: visible; overflow-y: visible; From 59ab76ef5e3d9eb95b4e8ad7b8d21473e5915bb5 Mon Sep 17 00:00:00 2001 From: nathangray Date: Thu, 28 Feb 2019 15:22:50 -0700 Subject: [PATCH 10/14] Use correct class --- api/src/Storage/History.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/Storage/History.php b/api/src/Storage/History.php index cfae21c5a5..b50f3ae4cf 100644 --- a/api/src/Storage/History.php +++ b/api/src/Storage/History.php @@ -266,8 +266,8 @@ class History )) { // Larger text stored with full old / new value - calculate diff and just send that - $diff = new \Text_Diff('auto', array(explode("\n",$row['history_old_value']), explode("\n",$row['history_new_value']))); - $renderer = new \Text_Diff_Renderer_unified(); + $diff = new \Horde_Text_Diff('auto', array(explode("\n",$row['history_old_value']), explode("\n",$row['history_new_value']))); + $renderer = new \Horde_Text_Diff_Renderer_Unified(); $row['history_new_value'] = $renderer->render($diff); $row['history_old_value'] = Tracking::DIFF_MARKER; } From 0abec539c32aad2cb1cc27f8550f04319a70a632 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 1 Mar 2019 15:46:54 +0100 Subject: [PATCH 11/14] * All apps: store history of multiline fields as diff only (migration can take 15min!) --- api/setup/setup.inc.php | 2 +- api/setup/tables_update.inc.php | 54 +++++++++++++++++++++++++++++++++ api/src/Storage/History.php | 6 ++-- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/api/setup/setup.inc.php b/api/setup/setup.inc.php index 20ce891239..b547569510 100644 --- a/api/setup/setup.inc.php +++ b/api/setup/setup.inc.php @@ -11,7 +11,7 @@ /* Basic information about this app */ $setup_info['api']['name'] = 'api'; $setup_info['api']['title'] = 'EGroupware API'; -$setup_info['api']['version'] = '17.1.005'; +$setup_info['api']['version'] = '17.1.006'; $setup_info['api']['versions']['current_header'] = '1.29'; // maintenance release in sync with changelog in doc/rpm-build/debian.changes $setup_info['api']['versions']['maintenance_release'] = '17.1.20190222'; diff --git a/api/setup/tables_update.inc.php b/api/setup/tables_update.inc.php index c78a0ed59d..bcdc14b049 100644 --- a/api/setup/tables_update.inc.php +++ b/api/setup/tables_update.inc.php @@ -516,3 +516,57 @@ function api_upgrade17_1_004() return $GLOBALS['setup_info']['api']['currentver'] = '17.1.005'; } + +/** + * Store multiline history content as diff + * + * Benchmark on Ralf's Laptop (OS X) + * - auto/Native: 66k rows in 15mins + * + * @return string new version + */ +function api_upgrade17_1_005() +{ + $renderer = new Horde_Text_Diff_Renderer_Unified(); + $start = microtime(true); + $junk_size = 200; // 2*200*160KB = 64MB + $total = $saved = 0; + do { + $n = 0; + foreach($GLOBALS['egw_setup']->db->select('egw_history_log', 'history_id,history_new_value,history_old_value', array( + 'history_old_value != '.$GLOBALS['egw_setup']->db->quote(Api\Storage\Tracking::DIFF_MARKER), + // if one is empty, no need to store diff + "(LENGTH(history_new_value) > 0 AND LENGTH(history_old_value) > 0)", + "(history_status LIKE '%description' OR history_status='De' OR history_status='note'". + " OR LENGTH(history_new_value) > 200 OR LENGTH(history_old_value) > 200)", + ), __LINE__, __FILE__, 0, 'ORDER BY history_id', false, $junk_size) as $row) + { + // use OS diff command for big texts, if available + $diff = new Horde_Text_Diff('auto', array( + explode("\n", $row['history_old_value']), + explode("\n", $row['history_new_value']), + )); + $diff_str = $renderer->render($diff); + + $saved += strlen($row['history_old_value'])+strlen($row['history_new_value']) + -strlen($diff_str)-strlen(Api\Storage\Tracking::DIFF_MARKER); + + $GLOBALS['egw_setup']->db->update('egw_history_log', array( + 'history_new_value' => $diff_str, + 'history_old_value' => Api\Storage\Tracking::DIFF_MARKER, + ), array( + 'history_id' => $row['history_id'], + ), __LINE__, __FILE__); + + $n++; + $total++; + } + } + while($n == $junk_size); + $saved = number_format($saved/(1024.0*1024.0), 1); + $time = number_format((microtime(true)-$start)/60, 1); + echo "$total history-records converted in $time minutes to diff with a total of $saved MB saved\n"; + + return $GLOBALS['setup_info']['api']['currentver'] = '17.1.006'; +} + diff --git a/api/src/Storage/History.php b/api/src/Storage/History.php index b50f3ae4cf..9d29df9f60 100644 --- a/api/src/Storage/History.php +++ b/api/src/Storage/History.php @@ -311,7 +311,9 @@ class History protected static function needs_diff($name, $value) { - return $name == 'note' || $name == 'description' || - ($value && (strlen($value) > 50 || strstr($value, "\n") !== FALSE)); + return $name == 'note' || // Addressbook + strpos($name, 'description') !== false || // Calendar, Records, Timesheet, ProjectManager, Resources + $name == 'De' || // Tracker, InfoLog + ($value && (strlen($value) > 200 || strstr($value, "\n") !== FALSE)); } } From b39f4e0aef30971d40fc96b96a50dbf70f9a7824 Mon Sep 17 00:00:00 2001 From: nathangray Date: Fri, 1 Mar 2019 10:30:53 -0700 Subject: [PATCH 12/14] Fix js TypeError for missing widget for 'user_agent_action' --- api/js/etemplate/et2_widget_historylog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/js/etemplate/et2_widget_historylog.js b/api/js/etemplate/et2_widget_historylog.js index bd7438c2f2..0245c2bb22 100644 --- a/api/js/etemplate/et2_widget_historylog.js +++ b/api/js/etemplate/et2_widget_historylog.js @@ -474,8 +474,8 @@ var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([e value = _data['share_email']; } // Get widget from list, unless it needs a diff widget - if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined' && i < self.NEW_VALUE || - i >= self.NEW_VALUE &&!self._needsDiffWidget(_data['status'], _data[self.columns[self.OLD_VALUE].id])) + if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined' && (i < self.NEW_VALUE || + i >= self.NEW_VALUE &&!self._needsDiffWidget(_data['status'], _data[self.columns[self.OLD_VALUE].id]))) { widget = self.fields[_data.status].widget; if(!widget._children.length) From 8765b9a084e8d2eb05f1691ca926986e2c353a9c Mon Sep 17 00:00:00 2001 From: nathangray Date: Fri, 1 Mar 2019 10:31:52 -0700 Subject: [PATCH 13/14] Change diff colors to match github, provided by Ralf --- api/js/etemplate/et2_widget_diff.js | 4 ++-- api/templates/default/etemplate2.css | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/api/js/etemplate/et2_widget_diff.js b/api/js/etemplate/et2_widget_diff.js index 1aea479e37..24ab728b27 100644 --- a/api/js/etemplate/et2_widget_diff.js +++ b/api/js/etemplate/et2_widget_diff.js @@ -47,7 +47,7 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe // included via etemplate2.css //this.egw().includeCSS('../../../vendor/bower-asset/dist/dist2html.css'); this.div = document.createElement("div"); - jQuery(this.div).addClass('diff'); + jQuery(this.div).addClass('et2_diff'); }, set_value: function(value) { @@ -100,7 +100,7 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe { jQuery(this).height(jQuery(window).height() *0.7); } - jQuery(this).dialog({position: "center"}); + jQuery(this).addClass('et2_diff').dialog({position: "center"}); }, close: function(event, ui) { // Need to destroy the dialog, etemplate widget needs divs back where they were diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 9bc072141d..8ac518815b 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -567,22 +567,22 @@ for printing /** * Diff widget */ -div.diff { +div.et2_diff { width: 100%; } -.diff thead, +.et2_diff thead, .author, .d2h-file-header, .d2h-file-info, .d2h-info, -.diff .d2h-cntx +.et2_diff .d2h-cntx { display: none; } .d2h-file-diff { white-space:normal; } -.diff .d2h-file-diff { +.et2_diff .d2h-file-diff { overflow-x: hidden; } .ui-widget-content .d2h-code-line-ctn { @@ -592,10 +592,23 @@ div.diff { overflow-x: visible; overflow-y: visible; } -.diff .ui-icon { +.et2_diff .ui-icon { margin-top: -16px; float: right; } +.et2_diff .d2h-del, .et2_diff.d2h-del.d2h-change, .et2_diff .d2h-file-diff .d2h-del.d2h-change { + background-color: #ffeef0; +} +.et2_diff .d2h-code-line del, .et2_diff .d2h-code-side-line del { + background-color: #fdb8c0; +} +.et2_diff .d2h-ins, .et2_diff.d2h-ins.d2h-change, .et2_diff .d2h-file-diff .d2h-ins.d2h-change { + background-color: #e6ffed; +} +.et2_diff .d2h-code-line ins, .et2_diff .d2h-code-side-line ins { + background-color: #acf2bd; +} + /** Display a loading icon **/ .loading { background-position: center; From d4d36651c49cc696cd3f14f485a0feaa64cea3bf Mon Sep 17 00:00:00 2001 From: nathangray Date: Fri, 1 Mar 2019 10:53:40 -0700 Subject: [PATCH 14/14] Missed CSS class name change --- api/js/etemplate/et2_widget_historylog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/js/etemplate/et2_widget_historylog.js b/api/js/etemplate/et2_widget_historylog.js index 0245c2bb22..4c2de75535 100644 --- a/api/js/etemplate/et2_widget_historylog.js +++ b/api/js/etemplate/et2_widget_historylog.js @@ -592,7 +592,7 @@ var et2_historylog = (function(){ "use strict"; return et2_valueWidget.extend([e if(this.dataview) { var columns = this.dataview.getColumnMgr().columnWidths; - jQuery('.diff', this.div).parent().width(columns[this.NEW_VALUE] + columns[this.OLD_VALUE]); + jQuery('.et2_diff', this.div).parent().width(columns[this.NEW_VALUE] + columns[this.OLD_VALUE]); } } });}).call(this);