From 0a9e61f6fa11278488c882a20017c79fc4e9db54 Mon Sep 17 00:00:00 2001
From: Ralf Becker <RalfBecker@outdoor-training.de>
Date: Mon, 31 May 2021 10:38:31 +0200
Subject: [PATCH] * InfoLog: allow to limit list to last N month in
 site-configuration, if ordered by last modified

---
 infolog/inc/class.infolog_bo.inc.php | 45 ++++++++++++++++++++++++++--
 infolog/inc/class.infolog_ui.inc.php |  7 +++++
 infolog/js/app.js                    | 12 ++++++++
 infolog/js/app.ts                    | 14 +++++++++
 infolog/lang/egw_de.lang             |  5 +++-
 infolog/lang/egw_en.lang             |  3 ++
 infolog/templates/default/app.css    |  4 +++
 infolog/templates/default/config.xet | 14 +++++++++
 infolog/templates/pixelegg/app.css   |  5 +++-
 infolog/templates/pixelegg/app.less  |  1 -
 10 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php
index 4de6836937..dfe9bb87a6 100644
--- a/infolog/inc/class.infolog_bo.inc.php
+++ b/infolog/inc/class.infolog_bo.inc.php
@@ -138,6 +138,10 @@ class infolog_bo
 	 * @var int
 	 */
 	var $max_line_chars = 40;
+	/**
+	 * Limit rows ordered by last modified to last N month to improve performance on huge sites
+	 */
+	public $limit_modified_n_month;
 
 	/**
 	 * Available filters
@@ -281,6 +285,8 @@ class infolog_bo
 				$this->implicit_rights = 'edit';
 			}
 			$this->history = $config_data['history'];
+
+			$this->limit_modified_n_month = $config_data['limit_modified_n_month'];
 		}
 		// sort types by there translation
 		foreach($this->enums['type'] as $key => $val)
@@ -1208,6 +1214,15 @@ class infolog_bo
 		return $this->so->anzSubs( $info_id );
 	}
 
+	/**
+	 * Total returned, if search used limit modified optimization
+	 */
+	const LIMIT_MODIFIED_TOTAL = 9999;
+	/**
+	 * Set 2^N to automatic retry N times, if limit modified optimization did not return enough rows
+	 */
+	const LIMIT_MODIFIED_RETRY = 8;
+
 	/**
 	 * searches InfoLog for a certain pattern in $query
 	 *
@@ -1254,8 +1269,34 @@ class infolog_bo
 			}
 		}
 
-		$ret = $this->so->search($query, $no_acl);
-		$this->total = $query['total'];
+		$q = $query;
+		unset($q['limit_modified_n_month']);
+		for($n = 1; $n <= self::LIMIT_MODIFIED_RETRY; $n *= 2)
+		{
+			// apply modified limit only if requested AND we're sorting by modified AND NOT searching
+			if (!empty($query['limit_modified_n_month']) && empty($query['search']) &&
+				$query['order'] === 'info_datemodified' && $query['sort'] === 'DESC' &&
+				isset($query['start']))
+			{
+				$q['col_filter'][99] = 'info_datemodified > '.
+					(new Api\DateTime((-$n*$query['limit_modified_n_month']).' month'))->format('server');
+			}
+			$ret = $this->so->search($q, $no_acl);
+			$this->total = $q['total'];
+			if (!isset($q['col_filter'][99]) || count($ret) >= $query['num_rows'])
+			{
+				if (isset($q['col_filter'][99]))
+				{
+					$this->total = self::LIMIT_MODIFIED_TOTAL;
+				}
+				break;	// --> no modified limit, or got enough rows
+			}
+			// last retry without limit
+			if (2*$n === self::LIMIT_MODIFIED_RETRY)
+			{
+				unset($q['col_filter'][99], $query['limit_modified_n_month']);
+			}
+		}
 
 		if (is_array($ret))
 		{
diff --git a/infolog/inc/class.infolog_ui.inc.php b/infolog/inc/class.infolog_ui.inc.php
index d65eb15975..3bc4ee7c89 100644
--- a/infolog/inc/class.infolog_ui.inc.php
+++ b/infolog/inc/class.infolog_ui.inc.php
@@ -423,7 +423,13 @@ class infolog_ui
 		// do we need to query the cf's
 		$query['custom_fields'] = $this->bo->customfields && (!$columselection || in_array('customfields',$columselection));
 
+		$query['limit_modified_n_month'] = $this->bo->limit_modified_n_month;
 		$infos = $this->bo->search($query);
+		// if limit modified optimization has been used, blur the wrong/not exact total
+		if (!empty($query['limit_modified_n_month']))
+		{
+			Api\Json\Response::get()->call('app.infolog.blurCount', $this->bo->total === infolog_bo::LIMIT_MODIFIED_TOTAL);
+		}
 		$query['col_filter'] = $orginal_colfilter;
 		if (!is_array($infos))
 		{
@@ -2514,6 +2520,7 @@ class infolog_ui
 				Api\Config::save_value('index_load_cfs', implode(',', (array)$content['index_load_cfs']), 'infolog');
 				Api\Config::save_value('sub_prefix', $content['sub_prefix'], 'infolog');
 				Api\Config::save_value('allow_past_due_date', $content['allow_past_due_date'], 'infolog');
+				Api\Config::save_value('limit_modified_n_month', $content['limit_modified_n_month'], 'infolog');
 				// Notifications
 				$notifications =& $config[infolog_tracking::CUSTOM_NOTIFICATION];
 				$notifications[$content['notification_type']] = $content['notification'];
diff --git a/infolog/js/app.js b/infolog/js/app.js
index c652961c86..e5719c916c 100644
--- a/infolog/js/app.js
+++ b/infolog/js/app.js
@@ -90,6 +90,10 @@ var InfologApp = /** @class */ (function (_super) {
                 if (this.egw.user('apps').stylite) {
                     this._get_stylite(function () { this.mailvelopeAvailable(function () { var _a; (_a = app.stylite) === null || _a === void 0 ? void 0 : _a.decrypt_hover(nm); }); });
                 }
+                // blur count, if limit modified optimization used
+                if ((nm === null || nm === void 0 ? void 0 : nm.getValue()).total == 9999) {
+                    this.blurCount(true);
+                }
                 break;
             case 'infolog.edit.print':
                 if (this.et2.getArrayMgr('content').data.info_des.indexOf(this.begin_pgp_message) != -1) {
@@ -727,6 +731,14 @@ var InfologApp = /** @class */ (function (_super) {
             self.egw.message(_err, 'error');
         });
     };
+    /**
+     * Blur NM count (used for limit modified optimization not returning (an exact) count
+     *
+     * @param blur
+     */
+    InfologApp.prototype.blurCount = function (blur) {
+        document.querySelector('div#infolog-index_nm.et2_nextmatch .header_count').classList.toggle('blur_count', blur);
+    };
     return InfologApp;
 }(egw_app_1.EgwApp));
 app.classes.infolog = InfologApp;
diff --git a/infolog/js/app.ts b/infolog/js/app.ts
index 233247cc04..267b3ff3d3 100644
--- a/infolog/js/app.ts
+++ b/infolog/js/app.ts
@@ -91,6 +91,11 @@ class InfologApp extends EgwApp
 				{
 					this._get_stylite(function() {this.mailvelopeAvailable(function() {app.stylite?.decrypt_hover(nm);});});
 				}
+				// blur count, if limit modified optimization used
+				if ((<any>nm?.getValue()).total == 9999)
+				{
+					this.blurCount(true);
+				}
 				break;
 			case 'infolog.edit.print':
 				if (this.et2.getArrayMgr('content').data.info_des.indexOf(this.begin_pgp_message) != -1)
@@ -872,6 +877,15 @@ class InfologApp extends EgwApp
 		});
 	}
 
+	/**
+	 * Blur NM count (used for limit modified optimization not returning (an exact) count
+	 *
+	 * @param blur
+	 */
+	blurCount(blur : boolean)
+	{
+		document.querySelector('div#infolog-index_nm.et2_nextmatch .header_count').classList.toggle('blur_count', blur);
+	}
 }
 
 app.classes.infolog = InfologApp;
diff --git a/infolog/lang/egw_de.lang b/infolog/lang/egw_de.lang
index b2ca80e6a0..ebf214adf5 100644
--- a/infolog/lang/egw_de.lang
+++ b/infolog/lang/egw_de.lang
@@ -72,6 +72,7 @@ billed	infolog	de	abgerechnet
 both	infolog	de	Annahme+erledigt
 call	infolog	de	anrufen
 can be used to show further infolog types in the calendar or limit it to show eg. only tasks.	infolog	de	Kann dazu benutzt werden um weitere InfoLog-Typen im Kalender anzuzeigen oder zu begrenzen dass z.B. nur noch Aufgaben angezeigt werden.
+can not return an exact number of rows and therefore hides the count.	admin	de	Kann keine exakte Anzahl Zeilen liefern und verbirgt deswegen den Zähler.
 cancel	infolog	de	Abbruch
 cancelled	infolog	de	abgesagt
 categories	infolog	de	Kategorien
@@ -276,6 +277,7 @@ leave it empty for a full week	infolog	de	leer lassen, für eine gesamte Woche
 leave without saveing the entry	infolog	de	Abbruch ohne den Eintrag zu speichern
 leaves without saveing	infolog	de	Abbruch ohne speichern
 length	infolog	de	Länge
+limit list to last n month, if ordering by last-modified and not searching.	admin	de	Begrenze die Liste auf die letzten N Monate, wenn nach zuletzt geändert sortiert und nicht nach etwas gesucht wird.
 limit number of description lines (default 5, 0 for no limit)	infolog	de	Begrenze Anzahl Beschreibungszeilen (Vorgabe 5, 0 für keine Grenze)
 limit width of description column ((effective only if lines limit is set), 0 for no limit)	infolog	de	Beschränkt die Breite der Spalte Beschreibung. Setzten Sie den Wert 0, für kein Limit.
 link	infolog	de	Verknüpfung
@@ -354,6 +356,7 @@ path to user and group files has to be outside of the webservers document-root!!
 pattern for search in addressbook	infolog	de	Muster für Suche im Adressbuch
 pattern for search in projects	infolog	de	Muster für Suche des Projekts
 percent completed	infolog	de	Prozent erledigt
+performance optimization for huge infolog tables	admin	de	Performance Optimierung für sehr große InfoLog Tabellen
 permission denied	infolog	de	Zugriff verweigert
 permissions error - %1 could not %2	infolog	de	Fehler in den Zugriffsberechtigungen - %1 nicht möglich %2
 phone	infolog	de	Anruf
@@ -461,8 +464,8 @@ startdate must be before enddate!!!	infolog	de	Startdatum muss vor dem Fälligke
 starting %1	infolog	de	%1 startet
 startrecord	infolog	de	Startdatensatz
 status	infolog	de	Status
-status, percent and date completed are always allowed.	infolog	de	Status, Prozent und Datum erledigt sind immer erlaubt.
 status ...	infolog	de	Status ...
+status, percent and date completed are always allowed.	infolog	de	Status, Prozent und Datum erledigt sind immer erlaubt.
 sub	infolog	de	Unter-<br />einträge
 sub-entries become subs of the parent or main entries, if there's no parent	infolog	de	Untereinträge gehören dann zum übergeordneten Eintrag oder werden Haupteinträge wenn es keinen übergeordneten gibt.
 sub-entries will not be closed	infolog	de	Untereinträge werden nicht geschlossen
diff --git a/infolog/lang/egw_en.lang b/infolog/lang/egw_en.lang
index 055d5979c6..4e16d68353 100644
--- a/infolog/lang/egw_en.lang
+++ b/infolog/lang/egw_en.lang
@@ -72,6 +72,7 @@ billed	infolog	en	Billed
 both	infolog	en	Both
 call	infolog	en	Call
 can be used to show further infolog types in the calendar or limit it to show eg. only tasks.	infolog	en	Can be used to show further InfoLog types in the calendar or limit it to show e.g. only tasks.
+can not return an exact number of rows and therefore hides the count.	admin	en	Can not return an exact number of rows and therefore hides the count.
 cancel	infolog	en	Cancel
 cancelled	infolog	en	Cancelled.
 categories	infolog	en	Categories
@@ -276,6 +277,7 @@ leave it empty for a full week	infolog	en	Leave it empty for a full week
 leave without saveing the entry	infolog	en	Leave without saving the entry
 leaves without saveing	infolog	en	Leaves without saving
 length	infolog	en	Length
+limit list to last n month, if ordering by last-modified and not searching.	admin	en	Limit list to last N month, if ordering by last-modified and not searching.
 limit number of description lines (default 5, 0 for no limit)	infolog	en	Limit number of description lines. Default = 5, 0 = no limit.
 limit width of description column ((effective only if lines limit is set), 0 for no limit)	infolog	en	Limit width of description column. Effective only if lines limit is set. 0 = no limit.
 link	infolog	en	Link
@@ -354,6 +356,7 @@ path to user and group files has to be outside of the webservers document-root!!
 pattern for search in addressbook	infolog	en	Pattern for search in Address book
 pattern for search in projects	infolog	en	Pattern for search in Projects
 percent completed	infolog	en	Percent completed
+performance optimization for huge infolog tables	admin	en	Performance optimization for huge InfoLog tables
 permission denied	infolog	en	Permission denied!
 permissions error - %1 could not %2	infolog	en	Permissions error - %1 could not %2
 phone	infolog	en	Phone call
diff --git a/infolog/templates/default/app.css b/infolog/templates/default/app.css
index 93a2ce3d32..de8864fa7c 100644
--- a/infolog/templates/default/app.css
+++ b/infolog/templates/default/app.css
@@ -106,4 +106,8 @@ div#infolog-edit_infolog-edit-description {
 #infolog_index_cat_id_chzn{
 	min-width: auto;
 	width: 40% !important;
+}
+.header_count.blur_count > span {
+	color: transparent;
+	text-shadow: 0 0 5px rgba(0,0,0,0.5);
 }
\ No newline at end of file
diff --git a/infolog/templates/default/config.xet b/infolog/templates/default/config.xet
index aa96f2200d..153b49e1f4 100644
--- a/infolog/templates/default/config.xet
+++ b/infolog/templates/default/config.xet
@@ -60,6 +60,20 @@
 						<option value="0">No</option>
 					</select>
 				</row>
+				<row>
+					<vbox>
+						<description value="Performance optimization for huge InfoLog tables" label="%s:"/>
+						<description value="Limit list to last N month, if ordering by last-modified and not searching."/>
+						<description value="Can not return an exact number of rows and therefore hides the count."/>
+					</vbox>
+					<select id="limit_modified_n_month">
+						<option value="">{Disabled} ({Default})</option>
+						<option value="1">1 {Month}</option>
+						<option value="3">3 {Month}</option>
+						<option value="6">6 {Month}</option>
+						<option value="12">12 {Month}</option>
+					</select>
+				</row>
 			</rows>
 		</grid>
 	</template>
diff --git a/infolog/templates/pixelegg/app.css b/infolog/templates/pixelegg/app.css
index 3f05fedccd..411bf267cd 100755
--- a/infolog/templates/pixelegg/app.css
+++ b/infolog/templates/pixelegg/app.css
@@ -9,7 +9,6 @@
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  * @author Stefan Reinhard <stefan.reinhard@pixelegg.de>
  * @package infolog
- * @version $Id$
  */
 /* $Id$ */
 /* Minimum width for first column */
@@ -174,6 +173,10 @@ div#infolog-edit_infolog-edit-description {
   min-width: auto;
   width: 40% !important;
 }
+.header_count.blur_count > span {
+  color: transparent;
+  text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
+}
 /*###########################################
 #                                           #
 #       dialogHeader                       #
diff --git a/infolog/templates/pixelegg/app.less b/infolog/templates/pixelegg/app.less
index f5dba5f27b..c6b651e226 100755
--- a/infolog/templates/pixelegg/app.less
+++ b/infolog/templates/pixelegg/app.less
@@ -9,7 +9,6 @@
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  * @author Stefan Reinhard <stefan.reinhard@pixelegg.de>
  * @package infolog
- * @version $Id$
  */