diff --git a/www/js/config.js b/www/js/config.js index ee651d80b75c92cd36ca60b3a911d0707bd568ff..8301fc6ee57fddc074c69280363e2de46c7f87ad 100644 --- a/www/js/config.js +++ b/www/js/config.js @@ -10,9 +10,9 @@ angular.module("cesium.config", []) .constant("csConfig", { "cacheTimeMs": 300000, - "fallbackLanguage": "en", + "fallbackLanguage": "fr-FR", + "defaultLanguage": "fr-FR", "rememberMe": true, - "showUDHistory": false, "timeout": 30000, "timeWarningExpireMembership": 5184000, "timeWarningExpire": 7776000, @@ -22,11 +22,11 @@ angular.module("cesium.config", []) "expertMode": false, "decimalCount": 2, "httpsMode": false, - "shareBaseUrl": "https://g1.duniter.fr", + "shareBaseUrl": "https://g1.le-sou.org", "helptip": { "enable": true, "installDocUrl": { - "fr-FR": "https://duniter.org/fr/wiki/duniter/installer/", + "fr-FR": "https://www.le-sou.org/devenir-noeud/", "en": "https://duniter.org/en/wiki/duniter/install/" } }, @@ -35,30 +35,28 @@ angular.module("cesium.config", []) "en": "license/license_g1-en" }, "node": { - "host": "g1.duniter.org", + "host": "g1.le-sou.org", "port": "443" }, "fallbackNodes": [ { - "host": "g1.duniter.fr", + "host": "g1.duniter.org", "port": "443" }, { - "host": "g1.duniter.org", + "host": "g1.duniter.fr", "port": "443" - } - ], - "developers": [ + }, { - "name": "Benoit Lavenier", - "pubkey": "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE" + "host": "g1.le-sou.org", + "port": "443" } ], "plugins": { "es": { "enable": true, "askEnable": false, - "host": "g1.data.duniter.fr", + "host": "g1.data.le-sou.org", "port": "443", "notifications": { "txSent": true, @@ -70,7 +68,7 @@ angular.module("cesium.config", []) } }, "version": "0.17.6", - "build": "2017-10-03T11:59:41.457Z", + "build": "2017-10-03T21:22:44.315Z", "newIssueUrl": "https://github.com/duniter/cesium/issues/new?labels=bug" }) diff --git a/www/js/directives.js b/www/js/directives.js index 5749e74b4694bdf788dfab341a3dac4caf6386ee..ca8b00558b4a8303246dc34c4bfc3140141018a9 100644 --- a/www/js/directives.js +++ b/www/js/directives.js @@ -174,16 +174,26 @@ angular.module('cesium.directives', []) }; }) - .directive('trustAsHtml', ['$compile', function($compile){ + .directive('trustAsHtml', ['$sce', '$compile', '$parse', function($sce, $compile, $parse){ return { - restrict: 'AE', - link: function(scope, element, attrs) { - var value = attrs.trustAsHtml; - if (value) { - var html = scope.$eval(value); - element.append(html); - $compile(element.contents())(scope); - } + restrict: 'A', + compile: function (tElement, tAttrs) { + var ngBindHtmlGetter = $parse(tAttrs.trustAsHtml); + var ngBindHtmlWatch = $parse(tAttrs.trustAsHtml, function getStringValue(value) { + return (value || '').toString(); + }); + $compile.$$addBindingClass(tElement); + + return function ngBindHtmlLink(scope, element, attr) { + $compile.$$addBindingInfo(element, attr.trustAsHtml); + + scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() { + // we re-evaluate the expr because we want a TrustedValueHolderType + // for $sce, not a string + element.html($sce.getTrustedHtml($sce.trustAsHtml(ngBindHtmlGetter(scope))) || ''); + $compile(element.contents())(scope); + }); + }; } }; }]) diff --git a/www/js/services/http-services.js b/www/js/services/http-services.js index d626e795762ba6657ac4f2645abb84737de1c0e8..e684b0e5e37a3472a2c0e9d545862e2c01bcff92 100644 --- a/www/js/services/http-services.js +++ b/www/js/services/http-services.js @@ -320,7 +320,6 @@ angular.module('cesium.http.services', ['cesium.cache.services']) return Math.floor(moment().utc().valueOf() / 1000); } - function isVersionCompatible(minVersion, actualVersion) { // TODO: add implementation console.debug('[http] TODO: implement check version [{0}] compatible with [{1}]'.format(actualVersion, minVersion)); diff --git a/www/plugins/es/js/controllers/common-controllers.js b/www/plugins/es/js/controllers/common-controllers.js index b0d5f129e4780f14f807bca1d6126af64bfc5a47..5ea4c65b314acd1d357d1612fe7b999e4aacf863 100644 --- a/www/plugins/es/js/controllers/common-controllers.js +++ b/www/plugins/es/js/controllers/common-controllers.js @@ -173,14 +173,12 @@ function ESCommentsController($scope, $timeout, $filter, $state, $focus, UIUtils $scope.comments = data; $scope.comments.hasMore = (data.total > data.result.length); $scope.loading = false; - $scope.service.changes.start(id, data); + $scope.service.changes.start(id, data, $scope); // Set Motion - $timeout(function() { - $scope.motion.show({ - selector: '.comments .item', - ink: false - }); + $scope.motion.show({ + selector: '.comments .item', + ink: false }); }); }; @@ -214,6 +212,7 @@ function ESCommentsController($scope, $timeout, $filter, $state, $focus, UIUtils $scope.focusNewComment(); return $scope.service.save($scope.id, $scope.comments, comment); }) + .catch(UIUtils.onError('REGISTRY.ERROR.FAILED_SAVE_COMMENT')); }; @@ -235,7 +234,7 @@ function ESCommentsController($scope, $timeout, $filter, $state, $focus, UIUtils bindings: { titleKey: 'COMMENTS.POPOVER_SHARE_TITLE', titleValues: {number: index ? index + 1 : 1}, - date: comment.time, + date: comment.creationTime, value: url, postUrl: stateUrl, postMessage: comment.message diff --git a/www/plugins/es/js/entities/comment.js b/www/plugins/es/js/entities/comment.js index 86c240aa973172fd7a9877747a21cd2c342d9c05..4242bdc60a99a12760e050172faa7f609899f684 100644 --- a/www/plugins/es/js/entities/comment.js +++ b/www/plugins/es/js/entities/comment.js @@ -5,8 +5,10 @@ function Comment(id, json) { that.id = id; that.message = null; // set in copyFromJson() + that.html = null; // set in copyFromJson() that.issuer = null; // set in copyFromJson() that.time = null; // set in copyFromJson() + that.creationTime = null; // set in copyFromJson() that.reply_to = null; // set in copyFromJson() that.replyCount = 0; @@ -17,8 +19,10 @@ function Comment(id, json) { that.copy = function(otherComment) { // Mandatory fields that.message = otherComment.message; + that.html = otherComment.html; that.issuer = otherComment.issuer; that.time = otherComment.time; + that.creationTime = otherComment.creationTime || that.time; // fill using time, for backward compatibility // Optional fields that.id = otherComment.id || that.id; @@ -36,6 +40,7 @@ function Comment(id, json) { that.message = json.message; that.issuer = json.issuer; that.time = json.time; + that.creationTime = json.creationTime || that.time; that.reply_to = json.reply_to; }; diff --git a/www/plugins/es/js/services/comment-services.js b/www/plugins/es/js/services/comment-services.js index d22e9e9184ca5608b5df9d91a567e1f527889a05..a0268b2bba3f41bf3aad376616348da880bfc27f 100644 --- a/www/plugins/es/js/services/comment-services.js +++ b/www/plugins/es/js/services/comment-services.js @@ -9,7 +9,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', var DEFAULT_SIZE = 20, fields = { - commons: ["issuer", "time", "message", "reply_to"], + commons: ["issuer", "creationTime", "time", "message", "reply_to"] }, exports = { index: index, @@ -20,8 +20,8 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', search: esHttp.post('/'+index+'/comment/_search'), remove: esHttp.record.remove(index, 'comment'), wsChanges: esHttp.ws('/ws/_changes'), - add: new esHttp.record.post('/'+index+'/comment'), - update: new esHttp.record.post('/'+index+'/comment/:id/_update') + add: new esHttp.record.post('/'+index+'/comment', {creationTime: true}), + update: new esHttp.record.post('/'+index+'/comment/:id/_update', {creationTime: true}) } }; @@ -62,7 +62,9 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', } }, sort : [ - { "time" : {"order" : "desc"}} + // Need desc, because of size+offset (will be sort in 'asc' order later) + { "creationTime" : {"order" : "desc"}}, + { "time" : {"order" : "desc"}} // for backward compatibility ], from: 0, size: 1000, @@ -80,6 +82,8 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', _.forEach(res.hits.hits, function(hit) { var comment = data.mapById[hit._id]; comment.copyFromJson(hit._source); + // Parse URL and hashtags + comment.html = esHttp.util.trustAsHtml(comment.message); delete incompleteCommentIdByParentIds[comment.id]; }); @@ -104,7 +108,9 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', term: { record : recordId} }, sort : [ - { "time" : {"order" : "desc"}} + // Need desc, because of size+offset (will be sort in 'asc' order later) + { "creationTime" : {"order" : "desc"}}, + { "time" : {"order" : "desc"}} // for backward compatibility ], from: options.from, size: options.size, @@ -145,10 +151,10 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', return csWot.extendAll(data.result, 'issuer'); }) - // Sort (time asc) + // Sort (creationTime asc) .then(function() { data.result = data.result.sort(function(cm1, cm2) { - return (cm1.time - cm2.time); + return (cm1.creationTime - cm2.creationTime); }); return data; }); @@ -172,12 +178,14 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', }; }; - exports.raw.startListenChanges = function(recordId, data) { + exports.raw.startListenChanges = function(recordId, data, scope) { data = data || {}; data.result = data.result || []; data.mapById = data.mapById || {}; data.pendings = data.pendings || {}; + scope = scope||$rootScope; + // Add listener to send deletion var onRemoveListener = exports.raw.createOnDeleteListener(data); _.forEach(data.result, function(comment) { @@ -200,35 +208,42 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', console.debug("[ES] [comment] Websocket opened in {0} ms".format(new Date().getTime() - time)); wsChanges.on(function(change) { if (!change) return; - var comment = data.mapById[change._id]; - if (change._operation === 'DELETE') { - if (comment) $rootScope.$apply(comment.remove); - } - else if (change._source && change._source.record === recordId) { - // update - if (comment) { - comment.copyFromJson(change._source); - exports.raw.refreshTreeLinks(data); - } - // create (if not in pending comment) - else if ((!data.pendings || !data.pendings[change._source.time]) && change._source.issuer != csWallet.data.pubkey) { - comment = new Comment(change._id, change._source); - comment.addOnRemoveListener(onRemoveListener); - comment.isnew = true; - data.mapById[change._id] = comment; - exports.raw.refreshTreeLinks(data) - // fill avatars (and uid) - .then(function() { - return csWot.extend(comment, 'issuer'); - }) - .then(function() { - data.result.push(comment); - }); + scope.$apply(function() { + var comment = data.mapById[change._id]; + if (change._operation === 'DELETE') { + if (comment) comment.remove(); } - else { - console.debug("Skip comment received by WS (already in pending)"); + else if (change._source && change._source.record === recordId) { + // update + if (comment) { + comment.copyFromJson(change._source); + // Parse URL and hashtags + comment.html = esHttp.util.trustAsHtml(comment.message); + exports.raw.refreshTreeLinks(data); + } + // create (if not in pending comment) + else if ((!data.pendings || !data.pendings[change._source.creationTime]) && change._source.issuer != csWallet.data.pubkey) { + comment = new Comment(change._id, change._source); + comment.addOnRemoveListener(onRemoveListener); + comment.isnew = true; + // Parse URL and hashtags + comment.html = esHttp.util.trustAsHtml(comment.message); + // fill map by id + data.mapById[change._id] = comment; + exports.raw.refreshTreeLinks(data) + // fill avatars (and uid) + .then(function() { + return csWot.extend(comment, 'issuer'); + }) + .then(function() { + data.result.push(comment); + }); + } + else { + console.debug("Skip comment received by WS (already in pending)"); + } } - } + }); }); }); }; @@ -249,7 +264,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', // Preparing JSON to sent var id = comment.id; var json = { - time: id ? comment.time : esHttp.date.now(), + creationTime: id ? comment.creationTime || comment.time/*for compat*/ : esHttp.date.now(), message: comment.message, record: recordId, issuer: csWallet.data.pubkey @@ -281,24 +296,29 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services', entity = data.mapById[id]; entity.copy(comment); } + + // Parse URL and hashtags entity.html = esHttp.util.trustAsHtml(entity.message); // Send add request if (!id) { data.pendings = data.pendings || {}; - data.pendings[json.time] = json; + data.pendings[json.creationTime] = json; return exports.raw.add(json) .then(function (id) { entity.id = id; data.mapById[id] = entity; - delete data.pendings[json.time]; + delete data.pendings[json.creationTime]; return entity; }); } // Send update request else { - return exports.raw.update(json, {id: id}); + return exports.raw.update(json, {id: id}) + .then(function () { + return entity; + }); } }; diff --git a/www/plugins/es/js/services/http-services.js b/www/plugins/es/js/services/http-services.js index 8c96e5c59231da345d255d25e3d7aee82552c6b1..51edeb75554d00e5b903dd39e3905b1bd0928766 100644 --- a/www/plugins/es/js/services/http-services.js +++ b/www/plugins/es/js/services/http-services.js @@ -226,7 +226,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic content = content.replace('@'+uid, link); }); - $sce.trustAsHtml(content); + //$sce.trustAsHtml(content); } return content; } @@ -240,14 +240,20 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic }); } - function postRecord(path, walletData) { + function postRecord(path, options) { + options = options || {}; var postRequest = that.post(path); return function(record, params) { - return (!walletData ? csWallet.auth() : $q.when(walletData)) + return csWallet.auth() .then(function(walletData) { - if (!record.time) { - record.time = that.date.now(); + if (options.creationTime && !record.creationTime) { + record.creationTime = that.date.now(); } + // Always update the time - fix #572 + // Make sure time is always > previous (required by ES node) + var now = that.date.now(); + record.time = (!record.time || record.time < now) ? now : (record.time+1); + var obj = {}; angular.copy(record, obj); delete obj.signature; diff --git a/www/plugins/es/templates/common/item_comment.html b/www/plugins/es/templates/common/item_comment.html index 3938d6f660b393c8d83b4b18cbe61b94bc124c7d..f9f16578e7b6de407909906e4e2a14c9b5fc8729 100644 --- a/www/plugins/es/templates/common/item_comment.html +++ b/www/plugins/es/templates/common/item_comment.html @@ -39,7 +39,7 @@ <div class="card-footer gray"> <small class="underline"> - <a ng-click="share($event, comment, $index)">{{comment.time | formatFromNow}}</a> + <a ng-click="share($event, comment, $index)">{{comment.creationTime | formatFromNow}}</a> <ng-if ng-if="comment.replyCount"> | <a class="dark" ng-click="toggleExpandedReplies(comment, $index)">{{'COMMENTS.REPLY_COUNT'|translate:comment}}</a> diff --git a/www/plugins/es/templates/common/item_comment_content.html b/www/plugins/es/templates/common/item_comment_content.html index 50b9cb2b95154d3ad7e9e18a8cffee851e39b6c9..4d28f29cdda8e0c4379dd62421e3d86181f62d4f 100644 --- a/www/plugins/es/templates/common/item_comment_content.html +++ b/www/plugins/es/templates/common/item_comment_content.html @@ -2,7 +2,7 @@ <div class="item item-avatar done in"> <span class="avatar" ng-if="::!comment.avatar" - ng-class="{'avatar-member': comment.uid, 'avatar-wallet': !comment.uid}"></span> + ng-class="::{'avatar-member': comment.uid, 'avatar-wallet': !comment.uid}"></span> <span class="avatar" ng-if="::comment.avatar" style="background-image: url({{::comment.avatar.src}})"></span> @@ -17,5 +17,5 @@ {{::comment.issuer|formatPubkey}} </span> </a> - <span class="text-keep-lines" ng-bind-html="comment.html"></span> + <span trust-as-html="comment.html"></span> </div> diff --git a/www/plugins/graph/i18n/locale-fr-FR.json b/www/plugins/graph/i18n/locale-fr-FR.json index 1501f1268fe804ad67849106efdedbe976e37356..00de906b5308c2d9270229e938245e4f57d10ffe 100644 --- a/www/plugins/graph/i18n/locale-fr-FR.json +++ b/www/plugins/graph/i18n/locale-fr-FR.json @@ -91,11 +91,21 @@ } }, "SYNCHRO": { - "EXECUTION": { - "TITLE": "Synchronisation", + "TITLE": "Statistiques de synchronisations", + "COUNT": { + "TITLE": "Volume synchronisé", "INSERTS": "Insertions", "UPDATES": "Mises à jour", "DELETES": "Suppressions" + }, + "PEER": { + "TITLE": "Noeuds requêtés", + "ES_USER_API": "Noeuds données utilisateurs", + "ES_SUBSCRIPTION_API": "Noeuds services en lignes" + }, + "PERFORMANCE": { + "TITLE": "Performances d'execution", + "DURATION": "Temps d'execution (ms)" } } } diff --git a/www/plugins/graph/js/controllers/synchro-controllers.js b/www/plugins/graph/js/controllers/synchro-controllers.js index 580ce9d93ab67a5fd52c4f03f35e5c7e8cc5fd09..f41782fd61a67a0d46bdddd7061ffccfeed93e37 100644 --- a/www/plugins/graph/js/controllers/synchro-controllers.js +++ b/www/plugins/graph/js/controllers/synchro-controllers.js @@ -35,30 +35,64 @@ function GpSynchroController($scope, $controller, $q, $translate, gpColor, gpDat $scope.charts = [ - // User count + // Execution: number of doc { - id: 'user', - title: 'GRAPH.SYNCHRO.EXECUTION.TITLE', + id: 'count', + title: 'GRAPH.SYNCHRO.COUNT.TITLE', series: [ { key: 'inserts', - label: 'GRAPH.SYNCHRO.EXECUTION.INSERTS', + label: 'GRAPH.SYNCHRO.COUNT.INSERTS', color: gpColor.rgba.royal(1), pointHoverBackgroundColor: gpColor.rgba.royal(1) }, { key: 'updates', - label: 'GRAPH.SYNCHRO.EXECUTION.UPDATES', + label: 'GRAPH.SYNCHRO.COUNT.UPDATES', color: gpColor.rgba.calm(1), pointHoverBackgroundColor: gpColor.rgba.calm(1) }, { key: 'deletes', - label: 'GRAPH.SYNCHRO.EXECUTION.DELETES', + label: 'GRAPH.SYNCHRO.COUNT.DELETES', color: gpColor.rgba.assertive(0.5), pointHoverBackgroundColor: gpColor.rgba.assertive(1) } ] + }, + + // Execution: number of peers + { + id: 'peer', + title: 'GRAPH.SYNCHRO.PEER.TITLE', + series: [ + { + key: 'ES_USER_API', + label: 'GRAPH.SYNCHRO.PEER.ES_USER_API', + color: gpColor.rgba.royal(1), + pointHoverBackgroundColor: gpColor.rgba.royal(1) + }, + { + key: 'ES_SUBSCRIPTION_API', + label: 'GRAPH.SYNCHRO.PEER.ES_SUBSCRIPTION_API', + color: gpColor.rgba.gray(0.5), + pointHoverBackgroundColor: gpColor.rgba.gray(1) + } + ] + }, + + // Execution: number of peers + { + id: 'performance', + title: 'GRAPH.SYNCHRO.PERFORMANCE.TITLE', + series: [ + { + key: 'duration', + label: 'GRAPH.SYNCHRO.PERFORMANCE.DURATION', + color: gpColor.rgba.gray(0.5), + pointHoverBackgroundColor: gpColor.rgba.gray(1) + } + ] } ]; diff --git a/www/plugins/graph/js/services/data-services.js b/www/plugins/graph/js/services/data-services.js index b9bfdfdfb178b852367603ce58d3da5a7b70342f..739dfb41756e2216affe61c49c3ba41c64c8a93d 100644 --- a/www/plugins/graph/js/services/data-services.js +++ b/www/plugins/graph/js/services/data-services.js @@ -815,6 +815,24 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. ranges: ranges }, aggs: { + api: { + terms: { + field: "api", + size: 0 + }, + aggs: { + peer_count: { + cardinality: { + field: "peer" + } + } + } + }, + duration: { + sum: { + field: "executionTime" + } + }, result: { nested: { path: "result" @@ -845,6 +863,7 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. // prepare next loop ranges = []; + var apis = {}; if (jobs.length == 10) { console.error('Too many parallel jobs!'); @@ -857,13 +876,20 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. var aggs = res.aggregations; return (aggs.range && aggs.range.buckets || []).reduce(function (res, agg) { - return res.concat({ + var item = { from: agg.from, to: agg.to, inserts: agg.result.inserts.value, updates: agg.result.inserts.value, - deletes: agg.result.deletes.value + deletes: agg.result.deletes.value, + duration: agg.duration.value + }; + _.forEach(agg.api && agg.api.buckets || [], function (api) { + item[api.key] = api.peer_count && api.peer_count.value || 0; + if (!apis[api.key]) apis[api.key] = true; }); + + return res.concat(item); }, []); }) ); @@ -880,12 +906,19 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. res = _.sortBy(res, 'from'); - return { + var series = { times: _.pluck(res, 'from'), inserts: _.pluck(res, 'inserts'), updates: _.pluck(res, 'updates'), - deletes: _.pluck(res, 'deletes') + deletes: _.pluck(res, 'deletes'), + duration: _.pluck(res, 'duration') }; + + _.keys(apis).forEach(function(api) { + series[api] = _.pluck(res, api); + }); + + return series; }); }; diff --git a/www/plugins/graph/templates/synchro/view_stats.html b/www/plugins/graph/templates/synchro/view_stats.html index ec7f0ad311175c9e8c6cfae7f57edf16d4f77fbc..e7275137a062e666d4461b4979a84232befc29dd 100644 --- a/www/plugins/graph/templates/synchro/view_stats.html +++ b/www/plugins/graph/templates/synchro/view_stats.html @@ -1,7 +1,7 @@ <ion-view left-buttons="leftButtons" cache-view="false"> <ion-nav-title> - {{'GRAPH.DOC_STATS.TITLE' | translate}} + {{'GRAPH.SYNCHRO.TITLE' | translate}} </ion-nav-title> <ion-content scroll="true" class="padding" >