[Libreoffice-commits] online.git: loleaflet/admin.strings.js loleaflet/build loleaflet/dist loleaflet/Makefile.am loleaflet/src wsd/Admin.cpp wsd/AdminModel.cpp wsd/AdminModel.hpp wsd/protocol.txt

Marco Viscido marcoviscido at gmail.com
Wed Apr 19 02:08:57 UTC 2017


 loleaflet/Makefile.am                     |    1 
 loleaflet/admin.strings.js                |    1 
 loleaflet/build/adminDeps.js              |    6 +
 loleaflet/dist/admin/admin.html           |    1 
 loleaflet/dist/admin/adminAnalytics.html  |    5 -
 loleaflet/dist/admin/adminHistory.html    |   85 +++++++++++++++++++++
 loleaflet/dist/admin/adminSettings.html   |    1 
 loleaflet/src/admin/AdminSocketHistory.js |   51 +++++++++++++
 wsd/Admin.cpp                             |    4 +
 wsd/AdminModel.cpp                        |  117 +++++++++++++++++++++++++++---
 wsd/AdminModel.hpp                        |   14 ++-
 wsd/protocol.txt                          |   34 ++++++++
 12 files changed, 304 insertions(+), 16 deletions(-)

New commits:
commit 0bb650e7c48deb65167305d7d11b61f83ea738b1
Author: Marco Viscido <marcoviscido at gmail.com>
Date:   Mon Apr 17 20:36:04 2017 +0200

    keep expired document and query them to get the historical content
    
    A Document has its own snapshots set.
    A snapshot is a string representation of a JSON object.
    AdminModel keeps also the expired document objects.
    Query each document object in order to get their own history.
    Admin accepts an "history" command then returns a json object.
    An administrator checks the history by dashboard.
    
    Change-Id: I73c87eff334cdb5a4a58043b2b66f18a56240b3a
    Reviewed-on: https://gerrit.libreoffice.org/35926
    Reviewed-by: pranavk <pranavk at collabora.co.uk>
    Tested-by: pranavk <pranavk at collabora.co.uk>

diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am
index 0a1880ae..a2d4a617 100644
--- a/loleaflet/Makefile.am
+++ b/loleaflet/Makefile.am
@@ -52,6 +52,7 @@ pot:
 		dist/toolbar/toolbar.js \
 		src/admin/AdminSocketBase.js \
 		src/admin/AdminSocketOverview.js \
+		src/admin/AdminSocketHistory.js \
 		src/admin/AdminSocketSettings.js \
 		src/admin/Util.js \
 		src/control/Control.CharacterMap.js \
diff --git a/loleaflet/admin.strings.js b/loleaflet/admin.strings.js
index fcfb574a..e5116276 100644
--- a/loleaflet/admin.strings.js
+++ b/loleaflet/admin.strings.js
@@ -8,6 +8,7 @@ l10nstrings.strSettings = _('Settings');
 l10nstrings.strOverview = _('Overview');
 l10nstrings.strCurrent = _('(current)');
 l10nstrings.strAnalytics = _('Analytics');
+l10nstrings.strHistory = _('History');
 l10nstrings.strDashboard = _('Dashboard');
 l10nstrings.strUsersOnline = _('Users online');
 l10nstrings.strDocumentsOpened = _('Documents opened');
diff --git a/loleaflet/build/adminDeps.js b/loleaflet/build/adminDeps.js
index 5c2dd5da..f6efdad9 100644
--- a/loleaflet/build/adminDeps.js
+++ b/loleaflet/build/adminDeps.js
@@ -28,6 +28,12 @@ var adminDeps = {
 		src: ['admin/AdminSocketSettings.js'],
 		desc: 'Socket to handle settings from server',
 		deps: ['AdminCore']
+	},
+
+ 	AdminSocketHistory: {
+		src: ['admin/AdminSocketHistory.js'],
+		desc: 'Socket to query document history.',
+		deps: ['AdminCore']
 	}
 };
 
diff --git a/loleaflet/dist/admin/admin.html b/loleaflet/dist/admin/admin.html
index 0d44ea4e..98a99eb8 100644
--- a/loleaflet/dist/admin/admin.html
+++ b/loleaflet/dist/admin/admin.html
@@ -58,6 +58,7 @@
           <ul class="nav nav-sidebar">
             <li class="active"><a href="#"><script>document.write(l10nstrings.strOverview)</script> <span class="sr-only"><script>document.write(l10nstrings.strCurrent)</script></span></a></li>
             <li><a href="adminAnalytics.html"><script>document.write(l10nstrings.strAnalytics)</script></a></li>
+            <li><a href="adminHistory.html"><script>document.write(l10nstrings.strHistory)</script></a></li>
           </ul>
         </div>
         <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
diff --git a/loleaflet/dist/admin/adminAnalytics.html b/loleaflet/dist/admin/adminAnalytics.html
index a0f7beef..2d7a37db 100644
--- a/loleaflet/dist/admin/adminAnalytics.html
+++ b/loleaflet/dist/admin/adminAnalytics.html
@@ -56,8 +56,9 @@
       <div class="row">
         <div class="col-sm-3 col-md-2 sidebar">
           <ul class="nav nav-sidebar">
-            <li><a href="admin.html"><script>document.write(l10nstrings.strOverview)</script> <span class="sr-only"><script>document.write(l10nstrings.strCurrent)</script></span></a></li>
-            <li class="active"><a href="adminAnalytics.html"><script>document.write(l10nstrings.strAnalytics)</script></a></li>
+            <li><a href="admin.html"><script>document.write(l10nstrings.strOverview)</script></a></li>
+            <li class="active"><a href="adminAnalytics.html"><script>document.write(l10nstrings.strAnalytics)</script> <span class="sr-only"><script>document.write(l10nstrings.strCurrent)</script></span></a></li>
+            <li><a href="adminHistory.html"><script>document.write(l10nstrings.strHistory)</script></a></li>
           </ul>
         </div>
         <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
diff --git a/loleaflet/dist/admin/adminHistory.html b/loleaflet/dist/admin/adminHistory.html
new file mode 100644
index 00000000..60f53f6a
--- /dev/null
+++ b/loleaflet/dist/admin/adminHistory.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <title>LibreOffice Online - Admin console</title>
+
+    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+    <!--[if lt IE 9]>
+      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+    <![endif]-->
+    <link rel="localizations" href="/loleaflet/dist/l10n/localizations.json" type="application/vnd.oftn.l10n+json"/>
+
+  </head>
+  <body>
+    <script src="/loleaflet/dist/admin-bundle.js"></script>
+    <script src="/loleaflet/dist/branding.js"></script>
+    <script>if (brandProductName) {l10nstrings.strProductName = brandProductName}</script>
+    <script>document.title = l10nstrings.strProductName + ' - ' + l10nstrings.strAdminConsole</script>
+    <script>
+        if (window.location.protocol == "https:") {
+           host = 'wss://' + window.location.host + '/lool/adminws/'
+        }
+        else {
+           host = 'ws://' + window.location.host + '/lool/adminws/'
+        }
+
+        Admin.History(host)
+    </script>
+
+<nav class="navbar navbar-inverse navbar-fixed-top">
+      <div class="container-fluid">
+        <div class="navbar-header">
+          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+            <span class="sr-only"><script>document.write(l10nstrings.strToggleNavigation)</script></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+          </button>
+          <a class="navbar-brand" href="#"><script>document.write(l10nstrings.strProductName + ' - ' + l10nstrings.strAdminConsole)</script></a>
+        </div>
+        <div id="navbar" class="navbar-collapse collapse">
+          <ul class="nav navbar-nav navbar-right">
+            <li><a href="adminSettings.html"><script>document.write(l10nstrings.strSettings)</script></a></li>
+          </ul>
+        </div>
+      </div>
+    </nav>
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-sm-3 col-md-2 sidebar">
+          <ul class="nav nav-sidebar">
+            <li><a href="admin.html"><script>document.write(l10nstrings.strOverview)</script></a></li>
+            <li><a href="adminAnalytics.html"><script>document.write(l10nstrings.strAnalytics)</script></a></li>
+            <li class="active"><a href="adminHistory.html"><script>document.write(l10nstrings.strHistory)</script> <span class="sr-only"><script>document.write(l10nstrings.strCurrent)</script></span></a></li>
+          </ul>
+        </div>
+
+        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
+          <h1 class="page-header"><script>document.write(l10nstrings.strHistory)</script>
+              <button class="pull-right" id="refreshHistory">refresh</button>
+          </h1>
+            <pre id="json-doc">Documents:<br/><textarea rows="10" cols="100"></textarea></pre>
+            <pre id="json-ex-doc">Expired:<br/><textarea rows="10" cols="100"></textarea></pre>
+        </div>
+      </div>
+    </div>
+
+
+    <!-- Bootstrap core JavaScript
+    ================================================== -->
+    <!-- Placed at the end of the document so the pages load faster -->
+    <script src="../../dist/bootstrap/js/bootstrap.min.js"></script>
+    <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
+    <script src="../../dist/bootstrap/assets/js/vendor/holder.min.js"></script>
+    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+    <script src="../../dist/bootstrap/assets/js/ie10-viewport-bug-workaround.js"></script>
+  </body>
+</html>
diff --git a/loleaflet/dist/admin/adminSettings.html b/loleaflet/dist/admin/adminSettings.html
index 3b74b80e..bba47f90 100644
--- a/loleaflet/dist/admin/adminSettings.html
+++ b/loleaflet/dist/admin/adminSettings.html
@@ -58,6 +58,7 @@
           <ul class="nav nav-sidebar">
             <li><a href="admin.html"><script>document.write(l10nstrings.strOverview)</script> <span class="sr-only"><script>document.write(l10nstrings.strCurrent)</script></span></a></li>
             <li><a href="adminAnalytics.html"><script>document.write(l10nstrings.strAnalytics)</script></a></li>
+            <li><a href="adminHistory.html"><script>document.write(l10nstrings.strHistory)</script></a></li>
           </ul>
           <hr />
           <div style="position:absolute; bottom:0px">
diff --git a/loleaflet/src/admin/AdminSocketHistory.js b/loleaflet/src/admin/AdminSocketHistory.js
new file mode 100644
index 00000000..05f9b063
--- /dev/null
+++ b/loleaflet/src/admin/AdminSocketHistory.js
@@ -0,0 +1,51 @@
+/*
+	Socket to be intialized on opening the history page in Admin console
+*/
+/* global $ nodejson2html Util AdminSocketBase */
+/* eslint no-unused-vars:0 */
+var AdminSocketHistory = AdminSocketBase.extend({
+	constructor: function(host) {
+		this.base(host);
+	},
+
+	refreshHistory: function() {
+		this.socket.send('history');
+	},
+
+	onSocketOpen: function() {
+		// Base class' onSocketOpen handles authentication
+		this.base.call(this);
+
+		var socketHistory = this;
+		$('#refreshHistory').on('click', function () {
+			return socketHistory.refreshHistory();
+		});
+		this.refreshHistory();
+	},
+
+	onSocketMessage: function(e) {
+		//if (e.data == 'InvalidAuthToken' || e.data == 'NotAuthenticated') {
+		//	this.base.call(this);
+		//	this.refreshHistory();
+		//} else {
+		var jsonObj;
+		try {
+			jsonObj = JSON.parse(e.data);
+			var doc = jsonObj['History']['documents'];
+			var exdoc = jsonObj['History']['expiredDocuments'];
+			$('#json-doc').find('textarea').html(JSON.stringify(doc));
+			$('#json-ex-doc').find('textarea').html(JSON.stringify(exdoc));
+		} catch (e) {
+			$('document').alert(e.message);
+		}
+	},
+
+	onSocketClose: function() {
+
+	}
+});
+
+Admin.History = function(host) {
+	return new AdminSocketHistory(host);
+};
+
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index a9039f2b..f5b63b1e 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -108,6 +108,10 @@ void AdminSocketHandler::handleMessage(bool /* fin */, WSOpCode /* code */,
         if (!result.empty())
             sendTextFrame(tokens[0] + ' ' + result);
     }
+    else if (tokens[0] == "history")
+    {
+        sendTextFrame("{ \"History\": " + model.getAllHistory() + "}");
+    }
     else if (tokens[0] == "version")
     {
         // Send LOOL version information
diff --git a/wsd/AdminModel.cpp b/wsd/AdminModel.cpp
index a838cbf3..3429553f 100644
--- a/wsd/AdminModel.cpp
+++ b/wsd/AdminModel.cpp
@@ -50,10 +50,80 @@ int Document::expireView(const std::string& sessionId)
         if (--_activeViews == 0)
             _end = std::time(nullptr);
     }
+    this->takeSnapshot();
 
     return _activeViews;
 }
 
+std::pair<std::time_t, std::string> Document::getSnapshot() const
+{
+    std::time_t ct = std::time(nullptr);
+    std::ostringstream oss;
+    oss << "{";
+    oss << "\"creationTime\"" << ":" << ct << ",";
+    oss << "\"memoryDirty\"" << ":" << this->getMemoryDirty() << ",";
+    oss << "\"activeViews\"" << ":" << this->getActiveViews() << ",";
+
+    oss << "\"views\"" << ":[";
+    std::string separator = "";
+    for (auto view : this->getViews())
+    {
+        oss << separator << "\"";
+        if(view.second.isExpired())
+        {
+            oss << "-";
+        }
+        oss << view.first << "\"";
+        separator = ",";
+    }
+    oss << "],";
+
+    oss << "\"lastActivity\"" << ":" << this->_lastActivity;
+    oss << "}";
+    return std::make_pair(ct, oss.str());
+}
+
+const std::string Document::getHistory() const
+{
+    std::ostringstream oss;
+    oss << "{";
+    oss << "\"docKey\"" << ":\"" << this->_docKey << "\",";
+    oss << "\"filename\"" << ":\"" << this->getFilename() << "\",";
+    oss << "\"start\"" << ":" << this->_start << ",";
+    oss << "\"end\"" << ":" << this->_end << ",";
+    oss << "\"pid\"" << ":" << this->getPid() << ",";
+    oss << "\"snapshots\"" << ":[";
+    std::string separator = "";
+    for (auto s : _snapshots)
+    {
+        oss << separator << s.second;
+        separator = ",";
+    }
+    oss << "]}";
+    return oss.str();
+}
+
+void Document::takeSnapshot()
+{
+    auto p = this->getSnapshot();
+    auto insPoint = _snapshots.upper_bound(p.first);
+    _snapshots.insert(insPoint, p);
+}
+
+std::string Document::to_string() const
+{
+    std::ostringstream oss;
+    std::string encodedFilename;
+    Poco::URI::encode(this->getFilename(), " ", encodedFilename);
+    oss << this->getPid() << ' '
+        << encodedFilename << ' '
+        << this->getActiveViews() << ' '
+        << this->getMemoryDirty() << ' '
+        << this->getElapsedTime() << ' '
+        << this->getIdleTime() << ' ';
+    return oss.str();
+}
+
 bool Subscriber::notify(const std::string& message)
 {
     // If there is no socket, then return false to
@@ -106,6 +176,35 @@ void AdminModel::assertCorrectThread() const
     assert(sameThread);
 }
 
+AdminModel::~AdminModel()
+{
+    Log::debug("History:\n\n" + getAllHistory() + "\n");
+    Log::info("AdminModel dtor.");
+}
+
+std::string AdminModel::getAllHistory() const
+{
+    std::ostringstream oss;
+    oss << "{ \"documents\" : [";
+    std::string separator1 = "";
+    for (auto d : _documents)
+    {
+        oss << separator1;
+        oss << d.second.getHistory();
+        separator1 = ",";
+    }
+    oss << "], \"expiredDocuments\" : [";
+    separator1 = "";
+    for (auto ed : _expiredDocuments)
+    {
+        oss << separator1;
+        oss << ed.second.getHistory();
+        separator1 = ",";
+    }
+    oss << "]}";
+    return oss.str();
+}
+
 std::string AdminModel::query(const std::string& command)
 {
     assertCorrectThread();
@@ -281,6 +380,7 @@ void AdminModel::addDocument(const std::string& docKey, Poco::Process::PID pid,
 
     const auto ret = _documents.emplace(docKey, Document(docKey, pid, filename));
     ret.first->second.addView(sessionId);
+    ret.first->second.takeSnapshot();
     LOG_DBG("Added admin document [" << docKey << "].");
 
     std::string encodedFilename;
@@ -329,11 +429,12 @@ void AdminModel::removeDocument(const std::string& docKey, const std::string& se
             << sessionId;
         notify(oss.str());
 
-        // TODO: The idea is to only expire the document and keep the history
+        // The idea is to only expire the document and keep the history
         // of documents open and close, to be able to give a detailed summary
-        // to the admin console with views. For now, just remove the document.
+        // to the admin console with views.
         if (docIt->second.expireView(sessionId) == 0)
         {
+            _expiredDocuments.emplace(*docIt);
             _documents.erase(docIt);
         }
     }
@@ -355,9 +456,11 @@ void AdminModel::removeDocument(const std::string& docKey)
         {
             // Notify the subscribers
             notify(msg + pair.first);
+            docIt->second.expireView(pair.first);
         }
 
         LOG_DBG("Removed admin document [" << docKey << "].");
+        _expiredDocuments.emplace(*docIt);
         _documents.erase(docIt);
     }
 }
@@ -413,14 +516,7 @@ std::string AdminModel::getDocuments() const
     {
         if (!it.second.isExpired())
         {
-            std::string encodedFilename;
-            Poco::URI::encode(it.second.getFilename(), " ", encodedFilename);
-            oss << it.second.getPid() << ' '
-                << encodedFilename << ' '
-                << it.second.getActiveViews() << ' '
-                << it.second.getMemoryDirty() << ' '
-                << it.second.getElapsedTime() << ' '
-                << it.second.getIdleTime() << " \n ";
+            oss << it.second.to_string() << "\n ";
         }
     }
 
@@ -436,6 +532,7 @@ void AdminModel::updateLastActivityTime(const std::string& docKey)
     {
         if (docIt->second.getIdleTime() >= 10)
         {
+            docIt->second.takeSnapshot(); // I would like to keep the idle time
             docIt->second.updateLastActivityTime();
             notify("resetidle " + std::to_string(docIt->second.getPid()));
         }
diff --git a/wsd/AdminModel.hpp b/wsd/AdminModel.hpp
index 80f0437b..1b37f1a8 100644
--- a/wsd/AdminModel.hpp
+++ b/wsd/AdminModel.hpp
@@ -77,6 +77,12 @@ public:
     bool updateMemoryDirty(int dirty);
     int getMemoryDirty() const { return _memoryDirty; }
 
+    std::pair<std::time_t, std::string> getSnapshot() const;
+    const std::string getHistory() const;
+    void takeSnapshot();
+
+    std::string to_string() const;
+
 private:
     const std::string _docKey;
     const Poco::Process::PID _pid;
@@ -92,6 +98,7 @@ private:
     std::time_t _start;
     std::time_t _lastActivity;
     std::time_t _end = 0;
+    std::map<std::time_t,std::string> _snapshots;
 };
 
 /// An Admin session subscriber.
@@ -144,10 +151,7 @@ public:
         LOG_INF("AdminModel ctor.");
     }
 
-    ~AdminModel()
-    {
-        LOG_INF("AdminModel dtor.");
-    }
+    ~AdminModel();
 
     /// All methods here must be called from the Admin socket-poll
     void setThreadOwner(const std::thread::id &id) { _owner = id; }
@@ -157,6 +161,7 @@ public:
     void assertCorrectThread() const;
 
     std::string query(const std::string& command);
+    std::string getAllHistory() const;
 
     /// Returns memory consumed by all active loolkit processes
     unsigned getKitsMemoryUsage();
@@ -200,6 +205,7 @@ private:
 private:
     std::map<int, Subscriber> _subscribers;
     std::map<std::string, Document> _documents;
+    std::map<std::string, Document> _expiredDocuments;
 
     /// The last N total memory Dirty size.
     std::list<unsigned> _memStats;
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index e686430b..b2404a70 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -490,6 +490,40 @@ documents
     Queries the server for list of opened documents. See `documents` command
     in admin -> client section for format of the response message
 
+history
+
+    Queries the server for list of opened and expired documents with their
+    snapshots. Returns a json object and it looks like:
+
+    { "History" :
+            {
+                "documents": [
+                    {
+                        "filename":"hello-world.odt",
+                        "start":1492104619,
+                        "end":1492104680,
+                        "pid":12302,
+                        "snapshots": [
+                            {
+                                "creationTime":1492104619,
+                                "memoryDirty":0,
+                                "activeViews":1,
+                                "views":["0008", ...],
+                                "lastActivity":1492104619
+                            },
+                            {
+                            ...
+                            }
+                        ]
+                    },
+                    {
+                    ...
+                    }
+                ],
+                "expiredDocuments" : [...]
+            }
+    }
+
 total_mem
 
     Queries for total memory being consumed by the server in kilobytes.


More information about the Libreoffice-commits mailing list