diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am index 049a0e9773..729fd05e83 100644 --- a/loleaflet/Makefile.am +++ b/loleaflet/Makefile.am @@ -51,7 +51,7 @@ LOLEAFLET_HTML_DST = $(patsubst $(srcdir)/html/%.html,$(DIST_FOLDER)/%.html,$(LO LOLEAFLET_WELCOME_SRC = $(shell find $(srcdir)/welcome -name '*.html') LOLEAFLET_WELCOME_DST = $(patsubst $(srcdir)/welcome/%.html,$(DIST_FOLDER)/welcome/%.html,$(LOLEAFLET_WELCOME_SRC)) -LOLEAFLET_ADMIN_SRC = $(shell find $(srcdir)/admin -name '*.html' -or -name '*.css' -or -name '*.ttf' -or -name 'OFL.txt') +LOLEAFLET_ADMIN_SRC = $(shell find $(srcdir)/admin -name '*.html' -or -name '*.css' -or -name '*.ttf' -or -name 'OFL.txt' -or -name '*.svg') LOLEAFLET_ADMIN_ALL = $(shell find $(srcdir)/admin -name '*') LOLEAFLET_ADMIN_DST = $(patsubst $(srcdir)/admin/%,$(DIST_FOLDER)/admin/%,$(LOLEAFLET_ADMIN_SRC)) diff --git a/loleaflet/admin/admin.html b/loleaflet/admin/admin.html index 82f3c0c41b..0020e9a297 100644 --- a/loleaflet/admin/admin.html +++ b/loleaflet/admin/admin.html @@ -53,11 +53,11 @@
- - + + + - @@ -71,7 +71,7 @@
- + diff --git a/loleaflet/admin/admin.strings.js b/loleaflet/admin/admin.strings.js index 2490a82aec..dc48930d32 100644 --- a/loleaflet/admin/admin.strings.js +++ b/loleaflet/admin/admin.strings.js @@ -14,13 +14,15 @@ l10nstrings.strDashboard = _('Dashboard'); l10nstrings.strUsersOnline = _('Users online'); l10nstrings.strUserName = _('User Name'); l10nstrings.strDocumentsOpened = _('Documents opened'); +l10nstrings.strUsers = _(' user(s).'); +l10nstrings.strUserOpenDocuments = _(' document(s) open.'); l10nstrings.strDocumentNumber = _('Number of Documents'); l10nstrings.strMemoryConsumed = _('Memory consumed'); l10nstrings.strSentBytes = _('Bytes sent'); l10nstrings.strRecvBytes = _('Bytes received'); l10nstrings.strPid = _('PID'); l10nstrings.strDocument = _('Document'); -l10nstrings.strNumberOfViews = _('Number of views'); +l10nstrings.strViewers = _('Views'); l10nstrings.strElapsedTime = _('Elapsed time'); l10nstrings.strIdleTime = _('Idle time'); l10nstrings.strModified = _('Modified'); diff --git a/loleaflet/admin/admintemplate.html b/loleaflet/admin/admintemplate.html index d87c782047..b2d5df8f7f 100644 --- a/loleaflet/admin/admintemplate.html +++ b/loleaflet/admin/admintemplate.html @@ -16,18 +16,38 @@ diff --git a/loleaflet/admin/fonts/Montserrat-Regular.ttf b/loleaflet/admin/font/Montserrat-Regular.ttf similarity index 100% rename from loleaflet/admin/fonts/Montserrat-Regular.ttf rename to loleaflet/admin/font/Montserrat-Regular.ttf diff --git a/loleaflet/admin/fonts/OFL.txt b/loleaflet/admin/font/OFL.txt similarity index 100% rename from loleaflet/admin/fonts/OFL.txt rename to loleaflet/admin/font/OFL.txt diff --git a/loleaflet/admin/src/AdminSocketOverview.js b/loleaflet/admin/src/AdminSocketOverview.js index 653045d048..abca74c1d8 100644 --- a/loleaflet/admin/src/AdminSocketOverview.js +++ b/loleaflet/admin/src/AdminSocketOverview.js @@ -4,12 +4,94 @@ */ /* global DlgYesNo _ vex $ Util AdminSocketBase Admin */ -function appendDocRow(document, $rowContainer, $userContainer, sPid, sName, sViews, sMem, sDocTime, sDocIdle, modified, socket) { - var $sessionCloseCell = $(document.createElement('td')).text('✖'); // This cell will open "Do you want to kill this session?" dialog. - $rowContainer.append($sessionCloseCell); - $sessionCloseCell.addClass('has-text-centered'); - $sessionCloseCell.css('cursor', 'pointer'); - $sessionCloseCell.click(function() { + +function getCollapsibleClass(id) { + var container = document.getElementById(id); + var label = container.children[0]; + var checkBox = container.children[1]; + var list = container.children[2]; + return { + 'addItem': function(itemId, text) { + var listItem = document.createElement('li'); + listItem.id = itemId; + listItem.innerText = text; + list.appendChild(listItem); + }, + 'toggle': function() { + checkBox.checked = !checkBox.checked; + }, + 'expand': function() { + checkBox.checked = true; + }, + 'collapse': function() { + checkBox.checked = false; + }, + 'setText': function(text) { + label.innerText = text; + }, + 'getText': function() { + return label.innerText; + }, + 'checkbox': checkBox, + 'label': label, + 'list': list + }; +} + +// Creates collapsable section with its elements. Requires mcollapsable CSS class. Once created, collapsable element runs without javascript. +function createCollapsable(parentNode, id, text) { + var div = document.createElement('div'); // One div to hold them all. + div.id = id; + // Let's make some magic with CSS. + // This is our checkbox, but it looks like a label. + var checkBox = document.createElement('input'); + checkBox.type = 'checkbox'; + checkBox.className = 'title is-4 mcollapsable'; // Class names come from Bulma.css (except for mcollapsable). We use that library for Admin console. + checkBox.checked = false; + checkBox.style.visibility = 'hidden'; + checkBox.id = id + 'check'; + + var label = document.createElement('label'); + label.innerText = text; + label.className = 'field-label is-5'; + label.setAttribute('for', id + 'check'); + label.style.cursor = 'pointer'; + label.style.textDecoration = 'underline'; + + var list = document.createElement('ul'); + + div.appendChild(label); + div.appendChild(checkBox); + div.appendChild(list); + + parentNode.appendChild(div); + return getCollapsibleClass(id); +} + +// This function takes the list of the users viewing a specific document. Creates an HTML element holding the list. +function createDocumentUserListElement(cell, doc) { + var collapsable = createCollapsable(cell, 'ucontainer' + doc['pid'], String(doc['views'].length) + _(' user(s).')); + for (var i = 0; i < doc['views'].length; i++) { + collapsable.addItem('user' + doc['views'][i]['sessionid'], doc['views'][i]['userName']); + } +} + +function upsertDocsTable(doc, sName, socket) { + var add = false; + var row = document.getElementById('doc' + doc['pid']); + if (row === undefined || row === null) { + row = document.createElement('tr'); + row.id = 'doc' + doc['pid']; + document.getElementById('doclist').appendChild(row); + add = true; + } + + var sessionCloseCell = document.createElement('td'); // This cell will open "Do you want to kill this session?" dialog. + sessionCloseCell.innerText = '✖'; + sessionCloseCell.className = 'has-text-centered'; + sessionCloseCell.style.cursor = 'pointer'; + if (add === true) { row.appendChild(sessionCloseCell); } else { row.cells[0] = sessionCloseCell; } + sessionCloseCell.onclick = function() { var dialog = (new DlgYesNo()) .title(_('Confirmation')) .text(_('Are you sure you want to terminate this session?')) @@ -17,39 +99,93 @@ function appendDocRow(document, $rowContainer, $userContainer, sPid, sName, sVie .noButtonText(_('Cancel')) .type('warning') .yesFunction(function() { - socket.send('kill ' + sPid); + socket.send('kill ' + doc['pid']); }); dialog.open(); - }); + }; - var $pid = $(document.createElement('td')).text(sPid); - $pid.append($userContainer); - $rowContainer.append($pid); + if (add === true) { + var userInfoCell = document.createElement('td'); + userInfoCell.className = 'has-text-left'; + if (add === true) { row.appendChild(userInfoCell); } else { row.cells[1] = userInfoCell; } + createDocumentUserListElement(userInfoCell, doc); + } + else { + var collapsable = getCollapsibleClass('ucontainer' + doc['pid']); + collapsable.addItem('user' + doc['views'][0]['sessionid'], doc['views'][0]['userName']); + collapsable.setText(String(parseInt(collapsable.getText().split(' ')[0]) + 1) + _(' user(s).')); + } - var $name = $(document.createElement('td')).text(sName); - $rowContainer.append($name); + var pidCell = document.createElement('td'); + pidCell.innerText = doc['pid']; + if (add === true) { row.appendChild(pidCell); } else { row.cells[0] = pidCell; } + pidCell.className = 'has-text-centered'; - var $views = $(document.createElement('td')).attr('id', 'docview' + sPid) - .text(sViews); - $rowContainer.append($views); + var nameCell = document.createElement('td'); + nameCell.innerText = sName; + if (add === true) { row.appendChild(nameCell); } else { row.cells[0] = nameCell; } + nameCell.className = 'has-text-left'; - var $mem = $(document.createElement('td')).attr('id', 'docmem' + sPid) - .text(Util.humanizeMem(parseInt(sMem))); - $rowContainer.append($mem); + var memoryCell = document.createElement('td'); + memoryCell.id = 'docmem' + doc['pid']; + memoryCell.innerText = Util.humanizeMem(parseInt(doc['memory'])); + if (add === true) { row.appendChild(memoryCell); } else { row.cells[0] = memoryCell; } + memoryCell.className = 'has-text-centered'; - var $docTime = $(document.createElement('td')).addClass('elapsed_time') - .val(parseInt(sDocTime)) - .text(Util.humanizeSecs(sDocTime)); - $rowContainer.append($docTime); + var eTimeCell = document.createElement('td'); + eTimeCell.innerText = Util.humanizeSecs(doc['elapsedTime']); + if (add === true) { row.appendChild(eTimeCell); } else { row.cells[0] = eTimeCell; } + eTimeCell.className = 'has-text-centered'; - var $docIdle = $(document.createElement('td')).attr('id', 'docidle' + sPid) - .addClass('idle_time') - .val(parseInt(sDocIdle)) - .text(Util.humanizeSecs(sDocIdle)); - $rowContainer.append($docIdle); + var idleCell = document.createElement('td'); + idleCell.id = 'docidle' + doc['pid']; + idleCell.innerText = Util.humanizeSecs(doc['idleTime']); + if (add === true) { row.appendChild(idleCell); } else { row.cells[0] = idleCell; } + idleCell.className = 'has-text-centered'; - var $mod = $(document.createElement('td')).attr('id', 'mod' + sPid).text(modified); - $rowContainer.append($mod); + var isModifiedCell = document.createElement('td'); + isModifiedCell.id = 'mod' + doc['pid']; + isModifiedCell.innerText = doc['modified']; + if (add === true) { row.appendChild(isModifiedCell); } else { row.cells[0] = isModifiedCell; } + isModifiedCell.className = 'has-text-centered'; + + // TODO: Is activeViews always the same with viewer count? We will hide this for now. If they are not same, this will be added to Users column like: 1/2 active/user(s). + if (add === true) { + var viewsCell = document.createElement('td'); + viewsCell.id = 'docview' + doc['pid']; + viewsCell.innerText = doc['activeViews']; + //row.appendChild(viewsCell); + } + else { + //document.getElementById('docview' + doc['pid']).innerText = String(parseInt(document.getElementById('docview' + doc['pid'])) + 1); + } +} + +function upsertUsersTable(docPid, sName, userList) { + for (var i = 0; i < userList.length; i++) { + var encodedUId = encodeURI(userList[i]['userId']); + var row = document.getElementById('usr' + encodedUId); + var collapsable; + if (row === undefined || row === null) { + row = document.createElement('tr'); + row.id = 'usr' + encodedUId; + document.getElementById('userlist').appendChild(row); + + var userNameCell = document.createElement('td'); + userNameCell.innerText = userList[i]['userName']; + row.appendChild(userNameCell); + + var docInfoCell = document.createElement('td'); + row.appendChild(docInfoCell); + collapsable = createCollapsable(docInfoCell, 'docListContainer_' + encodedUId, '1' + ' document(s) open.'); + collapsable.addItem(userList[i]['sessionid'] + '_' + docPid, sName); + } + else { + collapsable = getCollapsibleClass('docListContainer_' + encodedUId); + collapsable.setText(String(parseInt(collapsable.getText()) + 1) + _(' document(s) open.')); + collapsable.addItem(userList[i]['sessionid'] + '_' + docPid, sName); + } + } } var AdminSocketOverview = AdminSocketBase.extend({ @@ -100,38 +236,6 @@ var AdminSocketOverview = AdminSocketBase.extend({ // Dialog uses isExpired()) { std::string encodedFilename; - Poco::URI::encode(it.second->getFilename(), " ", encodedFilename); + Poco::URI::encode(it.second->getFilename(), " ", encodedFilename); // Is encoded name needed? oss << separator1 << '{' << ' ' << "\"pid\"" << ':' << it.second->getPid() << ',' << "\"docKey\"" << ':' << '"' << it.second->getDocKey() << '"' << ','