[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-2-0' - 3 commits - loleaflet/admin.strings.js loleaflet/dist loleaflet/src wsd/Admin.cpp wsd/Admin.hpp wsd/AdminModel.cpp wsd/AdminModel.hpp wsd/ClientSession.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/protocol.txt

Tor Lillqvist tml at collabora.com
Tue Jan 10 16:52:12 UTC 2017


 loleaflet/admin.strings.js                 |    1 
 loleaflet/dist/admin/admin.html            |    1 
 loleaflet/src/admin/AdminSocketOverview.js |   35 +++++++-
 loleaflet/src/admin/Util.js                |   12 ++
 loleaflet/src/map/Map.js                   |  122 +++++++++++++++++++----------
 wsd/Admin.cpp                              |    5 +
 wsd/Admin.hpp                              |    2 
 wsd/AdminModel.cpp                         |   16 +++
 wsd/AdminModel.hpp                         |   10 ++
 wsd/ClientSession.cpp                      |    1 
 wsd/DocumentBroker.cpp                     |    5 +
 wsd/DocumentBroker.hpp                     |    2 
 wsd/protocol.txt                           |    9 +-
 13 files changed, 171 insertions(+), 50 deletions(-)

New commits:
commit 90a86850e8249afad1565e2cb814427a01b6b163
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Dec 14 18:02:10 2016 +0200

    Grey-out ("dim") after 10 minutes if idle even while having focus
    
    (The greying-out without focus happens, as before, already after 30
    seconds.)
    
    Factor out the dimming code to a separate function _dim. When we have
    focus, check once a minute if ten minutes has passed without user
    activity. Keep track of when last user activity happened.
    
    Change-Id: I19160a82f5de860fc609dad391b168acfba560ce
    (cherry picked from commit 8f98206926e49fcf563b4c4f2dc171d04c01bd69)

diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 6c2e504..d699a73 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -20,6 +20,8 @@ L.Map = L.Evented.extend({
 		urlPrefix: 'lool'
 	},
 
+	lastActiveTime: Date.now(),
+
 	initialize: function (id, options) { // (HTMLElement or String, Object)
 		options = L.setOptions(this, options);
 
@@ -737,17 +739,20 @@ L.Map = L.Evented.extend({
 	},
 
 	_activate: function () {
+		// console.log('_activate:');
 		clearTimeout(vex.timer);
 
 		if (!this._active) {
 			// Only activate when we are connected.
 			if (this._socket.connected()) {
+				// console.log('  sending useractive');
 				this._socket.sendMessage('useractive');
 				this._active = true;
 				this._docLayer._onMessage('invalidatetiles: EMPTY', null);
 				if (vex.dialogID > 0) {
 					var id = vex.dialogID;
 					vex.dialogID = -1;
+					this._startInactiveTimer();
 					return vex.close(id);
 				}
 			} else {
@@ -755,10 +760,81 @@ L.Map = L.Evented.extend({
 			}
 		}
 
+		this._startInactiveTimer();
 		return false;
 	},
 
+	_dim: function() {
+		// console.log('_dim:');
+		if (window.devtools.open || !map._socket.connected()) {
+			return;
+		}
+
+		// console.log('  cont');
+		map._active = false;
+		clearTimeout(vex.timer);
+
+		var options = $.extend({}, vex.defaultOptions, {
+			contentCSS: {'background':'rgba(0, 0, 0, 0)',
+				     'font-size': 'xx-large',
+				     'color': '#fff',
+				     'text-align': 'center'},
+			content: _('Inactive document - please click to resume editing')
+		});
+		options.id = vex.globalID;
+		vex.dialogID = options.id;
+		vex.globalID += 1;
+		options.$vex = $('<div>').addClass(vex.baseClassNames.vex).addClass(options.className).css(options.css).data({
+			vex: options
+		});
+		options.$vexOverlay = $('<div>').addClass(vex.baseClassNames.overlay).addClass(options.overlayClassName).css(options.overlayCSS).data({
+			vex: options
+		});
+
+		options.$vexOverlay.bind('click.vex', function(e) {
+			// console.log('click.vex function');
+			if (e.target !== this) {
+				// console.log('  early return');
+				return 0;
+			}
+			// console.log('  calling _activate');
+			return map._activate();
+		});
+		options.$vex.append(options.$vexOverlay);
+
+		options.$vexContent = $('<div>').addClass(vex.baseClassNames.content).addClass(options.contentClassName).css(options.contentCSS).text(options.content).data({
+			vex: options
+		});
+		options.$vex.append(options.$vexContent);
+
+		$(options.appendLocation).append(options.$vex);
+		vex.setupBodyClassName(options.$vex);
+
+		map._doclayer && map._docLayer._onMessage('textselection:', null);
+		// console.log('  sending userinactive');
+		map._socket.sendMessage('userinactive');
+	},
+
+	_dimIfInactive: function () {
+		// console.log('_dimIfInactive: diff=' + (Date.now() - map.lastActiveTime));
+		if ((Date.now() - map.lastActiveTime) >= 10 * 60 * 1000) { // Dim 10 minutes after last user activity
+			map._dim();
+		} else {
+			map._startInactiveTimer();
+		}
+	},
+
+	_startInactiveTimer: function () {
+		// console.log('_startInactiveTimer:');
+		clearTimeout(vex.timer);
+		var map = this;
+		vex.timer = setTimeout(function() {
+			map._dimIfInactive();
+		}, 1 * 60 * 1000); // Check once a minute
+	},
+
 	_deactivate: function () {
+		// console.log('_deactivate:');
 		clearTimeout(vex.timer);
 
 		if (!window.devtools.open && (!this._active || vex.dialogID > 0)) {
@@ -767,6 +843,7 @@ L.Map = L.Evented.extend({
 			this._active = false;
 			this._docLayer && this._docLayer._onMessage('textselection:', null);
 			if (this._socket.connected()) {
+				// console.log('  sending userinactive');
 				this._socket.sendMessage('userinactive');
 			}
 
@@ -775,46 +852,7 @@ L.Map = L.Evented.extend({
 
 		var map = this;
 		vex.timer = setTimeout(function() {
-			if (window.devtools.open || !map._socket.connected()) {
-				return;
-			}
-
-			map._active = false;
-			clearTimeout(vex.timer);
-
-			var options = $.extend({}, vex.defaultOptions, {
-				contentCSS: {'background':'rgba(0, 0, 0, 0)',
-				             'font-size': 'xx-large',
-				             'color': '#fff',
-				             'text-align': 'center'},
-				content: _('Inactive document - please click to resume editing')
-			});
-			options.id = vex.globalID;
-			vex.dialogID = options.id;
-			vex.globalID += 1;
-			options.$vex = $('<div>').addClass(vex.baseClassNames.vex).addClass(options.className).css(options.css).data({
-				vex: options
-			});
-			options.$vexOverlay = $('<div>').addClass(vex.baseClassNames.overlay).addClass(options.overlayClassName).css(options.overlayCSS).data({
-				vex: options
-			});
-
-			options.$vex.bind('click.vex', function(e) {
-				return map._activate();
-			});
-			options.$vex.append(options.$vexOverlay);
-
-			options.$vexContent = $('<div>').addClass(vex.baseClassNames.content).addClass(options.contentClassName).css(options.contentCSS).text(options.content).data({
-				vex: options
-			});
-			options.$vex.append(options.$vexContent);
-
-			$(options.appendLocation).append(options.$vex);
-			vex.setupBodyClassName(options.$vex);
-
-			map._doclayer && map._docLayer._onMessage('textselection:', null);
-			map._socket.sendMessage('userinactive');
-
+			map._dim();
 		}, 30 * 1000); // Dim in 30 seconds.
 	},
 
@@ -839,8 +877,10 @@ L.Map = L.Evented.extend({
 	},
 
 	_onGotFocus: function () {
+		// console.log('_onGotFocus:');
 		if (!this._loaded) { return; }
 
+		// console.log('  cont');
 		var doclayer = this._docLayer;
 		if (doclayer &&
 		    typeof doclayer._isCursorOverlayVisibleOnLostFocus !== 'undefined' &&
@@ -891,6 +931,8 @@ L.Map = L.Evented.extend({
 	_handleDOMEvent: function (e) {
 		if (!this._loaded || !this._enabled || L.DomEvent._skipped(e)) { return; }
 
+		this.lastActiveTime = Date.now();
+
 		// find the layer the event is propagating from
 		var target = this._targets[L.stamp(e.target || e.srcElement)],
 			//type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
commit 561d1b8516f6f07aa822aa3bdb6b07fc7d75e7c1
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Dec 5 17:17:15 2016 +0200

    Display leading zero of minutes or seconds properly
    
    (cherry picked from commit 49c505f327b74498f7ef74141cc779dcd333b679)

diff --git a/loleaflet/src/admin/Util.js b/loleaflet/src/admin/Util.js
index acc5db5..35f6c81 100644
--- a/loleaflet/src/admin/Util.js
+++ b/loleaflet/src/admin/Util.js
@@ -38,9 +38,17 @@ var Util = Base.extend({
 		}
 
 		if (hrs) {
-			res = hrs + ':' + mins + _(' hrs');
+			if (mins < 10) {
+				res = hrs + ':0' + mins + _(' hrs');
+			} else {
+				res = hrs + ':' + mins + _(' hrs');
+			}
 		} else if (mins) {
-			res = mins + ':' + secs + _(' mins');
+			if (secs < 10) {
+				res = mins + ':0' + secs + _(' mins');
+			} else {
+				res = mins + ':' + secs + _(' mins');
+			}
 		} else if (secs) {
 			res = secs + _(' s');
 		} else {
commit b8e5c60d2829f07154c2dffd55586f3d936eb2d7
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Dec 5 22:11:07 2016 +0200

    Add an 'Idle time' column to the Admin console
    
    Use a new protocol message, 'resetidle' to inform Admin clients
    whenever a user has done anything in a document view. This is a
    message that Admin clients need to subscribe to.
    
    Also add the current idle time for each document to the 'documents'
    message.
    
    To reduce protocol chatter, the idle time is updated at most once per
    10 s.
    
    Change-Id: I418e82b05048a3628f21dcd240ccd974b3a01356
    Reviewed-on: https://gerrit.libreoffice.org/31653
    Reviewed-by: Tor Lillqvist <tml at collabora.com>
    Tested-by: Tor Lillqvist <tml at collabora.com>
    (cherry picked from commit e221388c795e43159dab15602856223aa1eb500e)

diff --git a/loleaflet/admin.strings.js b/loleaflet/admin.strings.js
index c9bfbbb..fcfb574 100644
--- a/loleaflet/admin.strings.js
+++ b/loleaflet/admin.strings.js
@@ -16,6 +16,7 @@ l10nstrings.strPid = _('PID');
 l10nstrings.strDocument = _('Document');
 l10nstrings.strNumberOfViews = _('Number of views');
 l10nstrings.strElapsedTime = _('Elapsed time');
+l10nstrings.strIdleTime = _('Idle time');
 l10nstrings.strKill = _('Kill');
 l10nstrings.strGraphs = _('Graphs');
 l10nstrings.strSave = _('Save');
diff --git a/loleaflet/dist/admin/admin.html b/loleaflet/dist/admin/admin.html
index 8e41f12..0d44ea4 100644
--- a/loleaflet/dist/admin/admin.html
+++ b/loleaflet/dist/admin/admin.html
@@ -88,6 +88,7 @@
 		  <th><script>document.write(l10nstrings.strNumberOfViews)</script></th>
 		  <th><script>document.write(l10nstrings.strMemoryConsumed)</script></th>
 		  <th><script>document.write(l10nstrings.strElapsedTime)</script></th>
+		  <th><script>document.write(l10nstrings.strIdleTime)</script></th>
 		</tr>
 	      </thead>
 	      <tbody id="doclist">
diff --git a/loleaflet/src/admin/AdminSocketOverview.js b/loleaflet/src/admin/AdminSocketOverview.js
index 068c60d..3bfdafe 100644
--- a/loleaflet/src/admin/AdminSocketOverview.js
+++ b/loleaflet/src/admin/AdminSocketOverview.js
@@ -23,7 +23,7 @@ var AdminSocketOverview = AdminSocketBase.extend({
 		this.base.call(this);
 
 		this.socket.send('documents');
-		this.socket.send('subscribe adddoc rmdoc');
+		this.socket.send('subscribe adddoc rmdoc resetidle');
 
 		this._getBasicStats();
 		var socketOverview = this;
@@ -39,6 +39,11 @@ var AdminSocketOverview = AdminSocketBase.extend({
 				$(this).val(newSecs);
 				$(this).html(Util.humanizeSecs(newSecs));
 			});
+			$('td.idle_time').each(function() {
+				var newSecs = parseInt($(this).val()) + 1;
+				$(this).val(newSecs);
+				$(this).html(Util.humanizeSecs(newSecs));
+			});
 		}, 1000);
 
 		// Allow table rows to have a context menu for terminating sessions
@@ -80,7 +85,7 @@ var AdminSocketOverview = AdminSocketBase.extend({
 		}
 
 		var $rowContainer;
-		var $pid, $name, $views, $mem, $docTime, $doc, $a;
+		var $pid, $name, $views, $mem, $docTime, $docIdle, $doc, $a;
 		var nViews, nTotalViews;
 		var docProps, sPid, sName, sViews, sMem, sDocTime;
 		if (textMsg.startsWith('documents')) {
@@ -93,7 +98,9 @@ var AdminSocketOverview = AdminSocketBase.extend({
 				sViews = docProps[2];
 				sMem = docProps[3];
 				sDocTime = docProps[4];
+				sDocIdle = docProps[5];
 
+				$doc = $('#doc' + sPid);
 				$rowContainer = $(document.createElement('tr')).attr('id', 'doc' + sPid);
 
 				$pid = $(document.createElement('td')).text(sPid);
@@ -103,20 +110,32 @@ var AdminSocketOverview = AdminSocketBase.extend({
 				$rowContainer.append($name);
 
 				$views = $(document.createElement('td')).attr('id', 'docview' + sPid)
-					                                    .text(sViews);
+									    .text(sViews);
 				$rowContainer.append($views);
 
 				$mem = $(document.createElement('td')).text(Util.humanizeMem(parseInt(sMem)));
 				$rowContainer.append($mem);
 
 				$docTime = $(document.createElement('td')).addClass('elapsed_time')
-					                                      .val(parseInt(sDocTime))
-					                                      .text(Util.humanizeSecs(sDocTime));
+									      .val(parseInt(sDocTime))
+									      .text(Util.humanizeSecs(sDocTime));
 				$rowContainer.append($docTime);
 
+				$docIdle = $(document.createElement('td')).attr('id', 'docidle' + sPid)
+									      .addClass('idle_time')
+									      .val(parseInt(sDocIdle))
+									      .text(Util.humanizeSecs(sDocIdle));
+				$rowContainer.append($docIdle);
 				$('#doclist').append($rowContainer);
 			}
 		}
+		else if (textMsg.startsWith('resetidle')) {
+			textMsg = textMsg.substring('resetidle'.length);
+			docProps = textMsg.trim().split(' ');
+			sPid = docProps[0];
+			var $idle = $(document.getElementById('docidle' + sPid));
+			$idle.val(0).text(Util.humanizeSecs(0));
+		}
 		else if (textMsg.startsWith('adddoc')) {
 			textMsg = textMsg.substring('adddoc'.length);
 			docProps = textMsg.trim().split(' ');
@@ -147,6 +166,12 @@ var AdminSocketOverview = AdminSocketBase.extend({
 					                                      .text(Util.humanizeSecs(0));
 				$rowContainer.append($docTime);
 
+				$docIdle = $(document.createElement('td')).attr('id', 'docidle' + sPid)
+									      .addClass('idle_time')
+					                                      .val(0)
+					                                      .text(Util.humanizeSecs(0));
+				$rowContainer.append($docIdle);
+
 				$('#doclist').append($rowContainer);
 
 				$a = $(document.getElementById('active_docs_count'));
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 09a561e..73b23f7 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -388,4 +388,9 @@ AdminModel& Admin::getModel()
     return _model;
 }
 
+void Admin::updateLastActivityTime(const std::string& docKey)
+{
+    _model.updateLastActivityTime(docKey);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/Admin.hpp b/wsd/Admin.hpp
index cb5d83f..2c88cd2 100644
--- a/wsd/Admin.hpp
+++ b/wsd/Admin.hpp
@@ -94,6 +94,8 @@ public:
 
     std::unique_lock<std::mutex> getLock() { return std::unique_lock<std::mutex>(_modelMutex); }
 
+    void updateLastActivityTime(const std::string& docKey);
+
 private:
     Admin();
 
diff --git a/wsd/AdminModel.cpp b/wsd/AdminModel.cpp
index 1099a0d..2f71486 100644
--- a/wsd/AdminModel.cpp
+++ b/wsd/AdminModel.cpp
@@ -351,11 +351,25 @@ std::string AdminModel::getDocuments() const
                 << encodedFilename << ' '
                 << it.second.getActiveViews() << ' '
                 << Util::getMemoryUsage(it.second.getPid()) << ' '
-                << it.second.getElapsedTime() << " \n ";
+                << it.second.getElapsedTime() << ' '
+                << it.second.getIdleTime() << " \n ";
         }
     }
 
     return oss.str();
 }
 
+void AdminModel::updateLastActivityTime(const std::string& docKey)
+{
+    auto docIt = _documents.find(docKey);
+    if (docIt != _documents.end())
+    {
+        if (docIt->second.getIdleTime() >= 10)
+        {
+            docIt->second.updateLastActivityTime();
+            notify("resetidle " + std::to_string(docIt->second.getPid()));
+        }
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/AdminModel.hpp b/wsd/AdminModel.hpp
index f2163b5..1dce29c 100644
--- a/wsd/AdminModel.hpp
+++ b/wsd/AdminModel.hpp
@@ -49,7 +49,8 @@ public:
         : _docKey(docKey),
           _pid(pid),
           _filename(filename),
-          _start(std::time(nullptr))
+          _start(std::time(nullptr)),
+          _lastActivity(_start)
     {
     }
 
@@ -61,6 +62,8 @@ public:
 
     std::time_t getElapsedTime() const { return std::time(nullptr) - _start; }
 
+    std::time_t getIdleTime() const { return std::time(nullptr) - _lastActivity; }
+
     void addView(const std::string& sessionId);
 
     int expireView(const std::string& sessionId);
@@ -69,6 +72,8 @@ public:
 
     const std::map<std::string, View>& getViews() const { return _views; }
 
+    void updateLastActivityTime() { _lastActivity = std::time(nullptr); }
+
 private:
     const std::string _docKey;
     const Poco::Process::PID _pid;
@@ -80,6 +85,7 @@ private:
     std::string _filename;
 
     std::time_t _start;
+    std::time_t _lastActivity;
     std::time_t _end = 0;
 };
 
@@ -166,6 +172,8 @@ public:
     void removeDocument(const std::string& docKey, const std::string& sessionId);
     void removeDocument(const std::string& docKey);
 
+    void updateLastActivityTime(const std::string& docKey);
+
 private:
     std::string getMemStats();
 
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 4bccd87..5adcf3a 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -101,6 +101,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     {
         // Keep track of timestamps of incoming client messages that indicate user activity.
         updateLastActivityTime();
+        docBroker->updateLastActivityTime();
     }
 
     if (tokens[0] == "loolclient")
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 6aaea39..21baad3 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1036,4 +1036,9 @@ void DocumentBroker::closeDocument(const std::string& reason)
     terminateChild(lock, reason);
 }
 
+void DocumentBroker::updateLastActivityTime()
+{
+    Admin::instance().updateLastActivityTime(_docKey);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index feb9697..0ea84b8 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -299,6 +299,8 @@ public:
 
     std::unique_lock<std::mutex> getLock() { return std::unique_lock<std::mutex>(_mutex); }
 
+    void updateLastActivityTime();
+
 private:
     /// Sends the .uno:Save command to LoKit.
     bool sendUnoSave(const bool dontSaveIfUnmodified);
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index f43f76c..d20b598 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -551,6 +551,11 @@ section). Others are just response messages to some client command.
     <memory consumed> in kilobytes sent from admin -> client after every
     mem_stats_interval (see `set` command for list of settings)
 
+[*] resetidle <pid>
+
+    <pid> process id hosting the document
+    reset the idle time counter for the document
+
 InvalidAuthToken
 
     This is sent when invalid auth token is provided in 'auth' command. See
@@ -560,11 +565,13 @@ NotAuthenticated
 
     When client sends an admin command that requires authentication.
 
-documents <pid> <filename> <number of views> <memory consumed> <elapsed time>
+documents <pid> <filename> <number of views> <memory consumed> <elapsed time> <idle time>
 <pid> <filename> ....
 ...
 
     <elapsed time> is in seconds since the first view of the document was opened
+    <idle time> is in seconds since some user did something in his view of the document (even just moving
+        the insertion cursor)
     <number of views> Number of users/views opening this(<pid>) document
     Other parameters are same as mentioned in `adddoc`
 


More information about the Libreoffice-commits mailing list