From 30bcb06d9cf3892f17572af3d460004c0f3c227d Mon Sep 17 00:00:00 2001
From: Benoit Lavenier <benoit.lavenier@e-is.pro>
Date: Tue, 3 Mar 2020 12:53:51 +0100
Subject: [PATCH] [fix] Blockchain explorer: optimize refresh using a block id;
 [fix] UI: fix top refresher background, in wallet and identity views; [fix]
 Websocket: make sure socket is not closed, before reuse it; [enh] ES (user
 and page): allow user to send like and abuse report. [enh] ES Graph: use the
 optimized extension controller, for extension point; [enh] ES Graph: remove
 stats button, on the identity view (keep it onl in the TX history)

---
 doc/privacy_policy.md                         |   4 +-
 scss/ionic.app.scss                           |  99 ++----
 www/css/style.css                             |  11 +-
 www/i18n/locale-en-GB.json                    |   1 -
 www/i18n/locale-en.json                       |   1 -
 www/i18n/locale-eo-EO.json                    |   3 +-
 www/i18n/locale-es-ES.json                    |   1 -
 www/i18n/locale-fr-FR.json                    |   5 +-
 www/i18n/locale-it-IT.json                    |   1 -
 www/i18n/locale-nl-NL.json                    |   1 -
 www/index.html                                |   1 +
 www/js/controllers/wallet-controllers.js      |   5 +-
 www/js/controllers/wot-controllers.js         |  21 +-
 www/js/entities/block.js                      |   2 +
 www/js/services/bma-services.js               |   4 +-
 www/js/services/wallet-services.js            |  62 ++--
 www/js/services/wot-services.js               |   6 +-
 www/plugins/es/css/style.css                  |  16 +
 www/plugins/es/i18n/locale-en-GB.json         |  42 ++-
 www/plugins/es/i18n/locale-en.json            |  42 ++-
 www/plugins/es/i18n/locale-eo-EO.json         |  46 ++-
 www/plugins/es/i18n/locale-es-ES.json         |   2 +-
 www/plugins/es/i18n/locale-fr-FR.json         |  60 +++-
 www/plugins/es/i18n/locale-it-IT.json         |   2 +-
 www/plugins/es/i18n/locale-nl-NL.json         |   2 +-
 .../es/js/controllers/common-controllers.js   | 281 ++++++++++++++++++
 .../es/js/controllers/registry-controllers.js |  18 +-
 .../es/js/controllers/wallet-controllers.js   |  39 ++-
 .../es/js/controllers/wot-controllers.js      |  64 +++-
 www/plugins/es/js/entities/notification.js    |  95 +++++-
 www/plugins/es/js/services.js                 |   3 +-
 .../es/js/services/comment-services.js        |   2 +-
 www/plugins/es/js/services/http-services.js   |  47 +--
 www/plugins/es/js/services/like-services.js   | 153 ++++++++++
 .../es/js/services/profile-services.js        |   7 +-
 .../es/js/services/registry-services.js       |   5 +-
 .../templates/common/popup_report_abuse.html  |  37 +++
 .../es/templates/common/view_likes.html       |  25 ++
 .../templates/network/item_content_peer.html  |   2 +-
 .../notification/view_notifications.html      |   2 +-
 .../registry/view_popover_actions.html        |  15 +-
 .../es/templates/registry/view_record.html    |  39 ++-
 .../templates/wallet/view_wallet_extend.html  |  12 +-
 .../templates/wot/view_identity_extend.html   |  27 +-
 .../templates/wot/view_popover_actions.html   |  40 +++
 .../js/controllers/account-controllers.js     |  42 +--
 .../account/view_identity_extend.html         |   9 -
 .../account/view_identity_tx_extend.html      |   4 +-
 .../account/view_wallet_tx_extend.html        |   4 +-
 .../templates/currency/tab_blocks_extend.html |   2 +-
 .../currency/view_currency_extend.html        |   6 +-
 .../network/view_es_peer_extend.html          |   2 +-
 .../templates/network/view_peer_extend.html   |   2 +-
 www/templates/blockchain/list_blocks_lg.html  |   2 +-
 www/templates/wallet/view_wallet.html         |   5 +-
 www/templates/wot/view_identity.html          |  12 +-
 56 files changed, 1167 insertions(+), 276 deletions(-)
 create mode 100644 www/plugins/es/js/services/like-services.js
 create mode 100644 www/plugins/es/templates/common/popup_report_abuse.html
 create mode 100644 www/plugins/es/templates/common/view_likes.html
 create mode 100644 www/plugins/es/templates/wot/view_popover_actions.html
 delete mode 100644 www/plugins/graph/templates/account/view_identity_extend.html

diff --git a/doc/privacy_policy.md b/doc/privacy_policy.md
index 3c35672c6..3560e3ba1 100644
--- a/doc/privacy_policy.md
+++ b/doc/privacy_policy.md
@@ -1,6 +1,6 @@
 ## Privacy policy
 
-Android build allow user to upload user profile (avatar, pictures) used by the Cesium+ extension.
+Cesium Android allow user to upload user profile (avatar, pictures), when the Cesium+ extension has been enable in settings.
 
 ### Cesium+ profile
 
@@ -8,4 +8,4 @@ Privacy policy are :
  
 - Profile data and avatar are public data;
 - Profile data and avatar on an [ES Duniter4j node](https://github.com/duniter/duniter4j); Open Cesium+ settings to known the node address;  
-- To totally remove your profile and avatar (if you use our official server [data.gtest.duniter.fr](http://data.gtest.duniter.fr)), please contact us at https://github.com/blavenie.
\ No newline at end of file
+- User can remove profile and avatar (open the Settings page, then use the options menu).
\ No newline at end of file
diff --git a/scss/ionic.app.scss b/scss/ionic.app.scss
index c5827b92e..868ee51ba 100644
--- a/scss/ionic.app.scss
+++ b/scss/ionic.app.scss
@@ -910,86 +910,17 @@ html, body {
 * 'ion-refresher'
 *******/
 
-body {
-  // Set default refresher background height (for android)
-  --refresher-bg-height: 100px;
-
-  // Set default refresher background
-  --refresher-background-color: inherit;
-
-}
-
-.refresher-positive-900-bg {
-  --refresher-background-color: #{$positive-900-bg};
-}
-
-.refresher-dark-100-bg {
-  --refresher-background-color: #{$dark-100-bg};
-}
-
 .platform-android {
-  // Workaround to control background of 'ion-refresher'
-  .refresher-positive-900-bg:before,
-  .refresher-dark-100-bg:before {
-    background-color: var(--refresher-background-color);
-    position: absolute;
-    display: block;
-    top: 0;
-    width: 100%;
-    height: var(--refresher-bg-height, 100px);
-    z-index: -10;
-    content: " ";
-  }
-
   .scroll-refresher {
     z-index: 50;
   }
 }
 
-.platform-ios {
-  .refresher-positive-900-bg,
-  .refresher-dark-100-bg {
-
-    // use light color for icon and text
-    //@extend .refresher-light;
-
-    background-color: var(--refresher-background-color) !important;
-
-    .scroll {
-      background-color: #fff;
-      bottom: auto;
-      min-height: 100%;
-    }
-  }
-}
-
-// Lighter style for refresher (icon and text)
-.refresher-light {
-  .scroll-refresher {
-    .ionic-refresher-content {
-      color: $light;
-    }
-
-    .spinner {
-      stroke: $light;
-      fill: $light;
-    }
-  }
-}
-
-.refresher-positive-900-bg,
-.refresher-dark-100-bg {
-  @extend .refresher-light;
-}
-
 /******
 * Wallet view
 *******/
 .view-wallet {
 
-  // Set refresher background height (for android)
-  --refresher-bg-height: 200px;
-
   .hero {
     height: 200px;
 
@@ -1113,7 +1044,6 @@ body {
 *******/
 @media screen and (max-width: $screen-sm-max) {
   .view-wallet-tx {
-    --refresher-bg-height: 100px;
 
     .hero {
       height: 100px;
@@ -1123,7 +1053,6 @@ body {
 
 @media screen and (min-width: $screen-md) {
   .view-wallet-tx {
-    --refresher-bg-height: 140px;
 
     .hero {
       height: 140px;
@@ -1230,20 +1159,16 @@ body {
 *******/
 
 .view-identity {
-  // Set refresher background height (for android)
-  --refresher-bg-height: 200px;
-
   // Default wallet color
-  --refresher-background-color: #{$dark-100-bg};
+  --background-color: #{$dark-100-bg};
 
-  // if member wallet, change wallet color
   .member {
-    --refresher-background-color: #{$positive-900-bg};
+    --background-color: #{$positive-900-bg};
   }
 
   .hero {
     height: 200px;
-    background-color: var(--refresher-background-color);
+    background-color: var(--background-color);
   }
 
 
@@ -2606,3 +2531,21 @@ div[dropzone]:hover {
     }
   }
 }
+
+/* -- likes -- */
+.hero .likes {
+  .gray {
+    color: white !important;
+  }
+  a.positive,
+  .positive a,
+  .positive i,
+  .positive {
+    color: $calm !important;
+  }
+}
+
+.view-wallet .hero .likes a {
+  user-select: none !important;
+  pointer-events: none;
+}
diff --git a/www/css/style.css b/www/css/style.css
index f8bcc3291..3cb65f89e 100644
--- a/www/css/style.css
+++ b/www/css/style.css
@@ -145,6 +145,14 @@
     display:none;
 }
 
+.no-margin {
+  margin: 0 !important;
+}
+
+/**********
+   Settings items
+**********/
+
 .settings .item-divider {
     background-color: #f5f5f5;
 }
@@ -164,9 +172,6 @@
 .settings .item .badge {
   top: 22px;
 }
-.no-margin {
-  margin: 0 !important;
-}
 
 /**********
    Network/Peer items
diff --git a/www/i18n/locale-en-GB.json b/www/i18n/locale-en-GB.json
index 60c0deb56..9aabaca37 100644
--- a/www/i18n/locale-en-GB.json
+++ b/www/i18n/locale-en-GB.json
@@ -694,7 +694,6 @@
     "SEND_CERTIFICATION_FAILED": "Could not certify identity.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "You could not send certification, because your account is <b>not a member account</b>.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "You could not send certification now, because your are <b>not a member</b> yet.<br/><br/>You still need certification to become a member.",
-    "NOT_MEMBER_FOR_CERTIFICATION": "Your account is not a member account yet.",
     "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "This account could not be certified. No registration found, or need to renew.",
     "LOGIN_FAILED": "Error while sign in.",
     "LOAD_IDENTITY_FAILED": "Could not load identity.",
diff --git a/www/i18n/locale-en.json b/www/i18n/locale-en.json
index 725db3dbf..7213317ac 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -694,7 +694,6 @@
     "SEND_CERTIFICATION_FAILED": "Could not certify identity.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "You could not send certification, because your account is <b>not a member account</b>.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "You could not send certification now, because your are <b>not a member</b> yet.<br/><br/>You still need certification to become a member.",
-    "NOT_MEMBER_FOR_CERTIFICATION": "Your account is not a member account yet.",
     "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "This account could not be certified. No registration found, or need to renew.",
     "LOGIN_FAILED": "Error while sign in.",
     "LOAD_IDENTITY_FAILED": "Could not load identity.",
diff --git a/www/i18n/locale-eo-EO.json b/www/i18n/locale-eo-EO.json
index e27d28d7e..e8287d68e 100644
--- a/www/i18n/locale-eo-EO.json
+++ b/www/i18n/locale-eo-EO.json
@@ -435,7 +435,7 @@
       "MESSAGE": "<i class=\"ion-android-time\"></i> Vi estis <b>malkonektita</b> aÅ­tomate, pro tro longa senaktiveco.",
       "BTN_RELOGIN": "Rekonektiĝi",
       "IDLE_WARNING": "Vi estos malkonektita... {{countdown}}"
-    },			
+    },
     "METHOD": {
       "SCRYPT_DEFAULT": "Sekreta identigilo kaj pasvorto",
       "SCRYPT_ADVANCED": "Sperta salumado",
@@ -694,7 +694,6 @@
     "SEND_CERTIFICATION_FAILED": "Atestado malsukcesa",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "Vi ne povas efektivigi atestadon, ĉar via konto <b>ne estas membro</b>.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "Vi ne povas efektivigi atestadon, ĉar via konto ankoraŭ ne estas membro.<br/><br/>Ankoraŭ mankas al vi atestaĵoj, aŭ tiuj ĉi ankoraŭ ne estis validigitaj.",
-    "NOT_MEMBER_FOR_CERTIFICATION": "Via konto ankoraÅ­ ne estas membro.",
     "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "Konto ne atestebla. Neniu aliĝo-peto estis farita, aŭ la aliĝo ne estis revalidigita.",
     "LOGIN_FAILED": "Eraro dum konektiĝo.",
     "LOAD_IDENTITY_FAILED": "Eraro por ŝarĝi la identecon.",
diff --git a/www/i18n/locale-es-ES.json b/www/i18n/locale-es-ES.json
index b34ee8085..aa1c119bb 100644
--- a/www/i18n/locale-es-ES.json
+++ b/www/i18n/locale-es-ES.json
@@ -643,7 +643,6 @@
     "SEND_CERTIFICATION_FAILED": "Error de la certificación.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "No se puede certificar, porque su cuenta no <b>es miembro</b>.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "No se puede certificar, porque su cuenta ya no es miembro.<br/><br/>Todavía faltan certificaciones, o ahora no son validas.",
-    "NOT_MEMBER_FOR_CERTIFICATION": "Su cuenta todavía no es miembro.",
     "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "Cuenta no certificable. No se ha solicitado la adhesión, o no fue renovada.",
     "LOGIN_FAILED": "Error durante la autentificación.",
     "LOAD_IDENTITY_FAILED": "Error de carga de la identidad.",
diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index 1ba9781ba..7307bce2e 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -692,9 +692,8 @@
     "SALT_NOT_CONFIRMED": "Ne correspond pas à l'identifiant secret",
     "SEND_IDENTITY_FAILED": "Échec de l'inscription",
     "SEND_CERTIFICATION_FAILED": "Échec de la certification",
-    "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "Vous ne pouvez pas effectuer de certification, car votre compte n'est <b>pas membre</b>.",
-    "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "Vous ne pouvez pas effectuer de certification, car votre compte n'est pas encore membre.<br/><br/>Il vous manque encore des certifications, ou bien celles-ci n'ont pas encore été validées.",
-    "NOT_MEMBER_FOR_CERTIFICATION": "Votre compte n'est pas encore membre.",
+    "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "Vous ne pouvez pas effectuer de certification, car ce compte n'est <b>pas membre</b>.",
+    "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "Vous ne pouvez pas effectuer de certification, car ce compte n'est pas encore membre.<br/><br/>Il vous manque encore des certifications, ou bien celles-ci n'ont pas encore été validées.",
     "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "Compte non certifiable. Aucune demande d'adhésion n'a été faite, ou bien elle n'a pas été renouvelée.",
     "LOGIN_FAILED": "Erreur lors de la connexion.",
     "LOAD_IDENTITY_FAILED": "Erreur de chargement de l'identité.",
diff --git a/www/i18n/locale-it-IT.json b/www/i18n/locale-it-IT.json
index 4d3f3407d..9baa8dacb 100644
--- a/www/i18n/locale-it-IT.json
+++ b/www/i18n/locale-it-IT.json
@@ -630,7 +630,6 @@
      "SEND_CERTIFICATION_FAILED": "Certificazione fallita.",
      "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "Non puoi inviare certificazioni perche tuo conto <b>non è ancora un conto membro</b>.",
      "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "Non puoi inviare certificazioni adesso perche <b>non sei ancora membro</b>.<br/><br/>Devi ancora entrare nella WOT.",
-     "NOT_MEMBER_FOR_CERTIFICATION": "Tuo conto non è ancora un conto membro.",
      "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "Impossibile certificare questo conto. Nessuna richiesta di certificazione trovata o bisogna rinnovarla.",
      "LOGIN_FAILED": "Errore di login.",
      "LOAD_IDENTITY_FAILED": "Impossibile caricare la tua identità.",
diff --git a/www/i18n/locale-nl-NL.json b/www/i18n/locale-nl-NL.json
index 1384445cc..9949b11df 100644
--- a/www/i18n/locale-nl-NL.json
+++ b/www/i18n/locale-nl-NL.json
@@ -434,7 +434,6 @@
     "SEND_CERTIFICATION_FAILED": "Could not certify identity.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY": "You could not send certification, because your account is <b>not a member account</b>.",
     "NEED_MEMBER_ACCOUNT_TO_CERTIFY_HAS_SELF": "You could not send certification now, because your are <b>not a member</b> yet.<br/><br/>You still need certification to become a member.",
-    "NOT_MEMBER_FOR_CERTIFICATION": "Your account is not a member account yet.",
     "IDENTITY_TO_CERTIFY_HAS_NO_SELF": "This account could not be certified. No registration found, or need to renew.",
     "LOGIN_FAILED": "Error while sign in.",
     "LOAD_IDENTITY_FAILED": "Could not load identity.",
diff --git a/www/index.html b/www/index.html
index d742b7bd3..9fc89e506 100644
--- a/www/index.html
+++ b/www/index.html
@@ -211,6 +211,7 @@
     <script src="dist/dist_js/plugins/es/js/services/geo-services.js"></script>
     <script src="dist/dist_js/plugins/es/js/services/document-services.js"></script>
     <script src="dist/dist_js/plugins/es/js/services/network-services.js"></script>
+    <script src="dist/dist_js/plugins/es/js/services/like-services.js"></script>
     <script src="dist/dist_js/plugins/es/js/controllers/common-controllers.js"></script>
     <script src="dist/dist_js/plugins/es/js/controllers/app-controllers.js"></script>
     <script src="dist/dist_js/plugins/es/js/controllers/settings-controllers.js"></script>
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
index a7710f06a..e14831fa9 100644
--- a/www/js/controllers/wallet-controllers.js
+++ b/www/js/controllers/wallet-controllers.js
@@ -65,7 +65,10 @@ function WalletController($scope, $rootScope, $q, $ionicPopup, $timeout, $state,
   $scope.settings = csSettings.data;
   $scope.qrcodeId = 'qrcode-wallet-' + $scope.$id;
   $scope.toggleQRCode = false;
-
+  $scope.likeData = {
+    likes: {},
+    abuses: {}
+  };
 
   var wallet;
 
diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js
index 2e95963f7..f7fc54dd4 100644
--- a/www/js/controllers/wot-controllers.js
+++ b/www/js/controllers/wot-controllers.js
@@ -254,7 +254,7 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
       type: undefined
     };
 
-    if ($scope.search.type == 'text') {
+    if ($scope.search.type === 'text') {
       var text = $scope.search.text.trim();
       if (text.match(/^#[\wḡĞǦğàáâãäåçèéêëìíîïðòóôõöùúûüýÿ]+$/)) {
         stateParams.hash = text.substr(1);
@@ -283,16 +283,15 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
   };
 
   $scope.doSearch = function() {
-    $scope.search.loading = true;
     var text = $scope.search.text.trim();
     if ((UIUtils.screen.isSmall() && text.length < 3) || !text.length) {
       $scope.search.results = undefined;
-      $scope.search.loading = false;
       $scope.search.type = 'none';
       $scope.search.total = undefined;
       return $q.when();
     }
 
+    $scope.search.loading = true;
     $scope.search.type = 'text';
     return csWot.search(text)
       .then(function(idties){
@@ -1040,7 +1039,20 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s
   $scope.motion = UIUtils.motion.fadeSlideInRight;
   $scope.qrcodeId = 'qrcode-wot-' + $scope.$id;
 
+  // Init likes here, to be able to use in extension
+  $scope.options = $scope.options || {};
+  $scope.options.like = {
+    kinds: ['LIKE', 'ABUSE'],
+    index: 'user',
+    type: 'profile'
+  };
+  $scope.likeData = {
+    likes: {},
+    abuses: {}
+  };
+
   $scope.$on('$ionicView.enter', function(e, state) {
+
     var onLoadSuccess = function() {
       $scope.doMotion();
       if (state.stateParams && state.stateParams.action) {
@@ -1049,6 +1061,9 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s
         }, 100);
 
         $scope.removeActionParamInLocationHref(state);
+
+        // Need by like controller
+        $scope.likeData.id = $scope.formData.pubkey;
       }
 
       $scope.showQRCode();
diff --git a/www/js/entities/block.js b/www/js/entities/block.js
index 399578121..e3958e0b6 100644
--- a/www/js/entities/block.js
+++ b/www/js/entities/block.js
@@ -31,6 +31,8 @@ function Block(json, attributes) {
   that.transactionsCount = that.transactions ? that.transactions.length : 0;
 
   that.empty = that.isEmpty();
+
+  that.id = [that.number, that.hash].join('-');
 }
 
 Block.prototype.isEmpty = function(){
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index 5ce668f05..cd80ab65e 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -147,7 +147,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
 
     get = function (path, cacheTime) {
 
-      cacheTime = that.useCache && cacheTime;
+      cacheTime = that.useCache && cacheTime || 0 /* no cache*/ ;
       var cacheKey = path + (cacheTime ? ('#'+cacheTime) : '');
 
       var getRequestFn = function(params) {
@@ -415,7 +415,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
         }
       },
       wot: {
-        lookup: get('/wot/lookup/:search', csHttp.cache.MEDIUM),
+        lookup: get('/wot/lookup/:search'),
         certifiedBy: get('/wot/certified-by/:pubkey'),
         certifiersOf: get('/wot/certifiers-of/:pubkey'),
         member: {
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index c2aa7b25d..dc704cd2d 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -11,7 +11,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
   var defaultBMA = BMA;
   var service;
 
-  function factory(id, BMA) {
+  function csWallet(id, BMA) {
 
     BMA = BMA || defaultBMA;
     var
@@ -241,7 +241,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
           return keepAuth ? data : angular.merge({}, data, authData);
         })
         .catch(function(err) {
-          if (err == 'RETRY' && (!options || !options.authData)) {
+          if (err === 'RETRY' && (!options || !options.authData)) {
             return $timeout(function(){
               return login(options);
             }, 300);
@@ -385,7 +385,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     // store pubkey and uid
     store = function(pubkey) {
       pubkey = pubkey && typeof pubkey == 'string' ? pubkey : data.pubkey;
-      if (settings.useLocalStorage) {
+      if (settings && settings.useLocalStorage) {
 
         if (isLogin() && settings.rememberMe) {
 
@@ -450,7 +450,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       localStorage.put(constants.STORAGE_PUBKEY, null);
       localStorage.put(constants.STORAGE_UID, null);
 
-      if (settings.useLocalStorage) {
+      if (settings && settings.useLocalStorage) {
         // Clean data (only in the session storage - keep local)
         return pubkey ? sessionStorage.put(constants.STORAGE_DATA_PREFIX + pubkey, null) : $q.when();
       }
@@ -467,7 +467,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     storeData = function() {
       if (!isLogin()) throw {message:'ERROR.NEED_LOGIN_FIRST'};
 
-      var useEncryption = settings.useLocalStorageEncryption;
+      var useEncryption = settings && settings.useLocalStorageEncryption;
       var storageKey = constants.STORAGE_DATA_PREFIX + data.pubkey;
 
       var content; // Init only if used
@@ -648,7 +648,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
           _.forEach(content.children, function(child) {
             if (!pubkeys[child.pubkey]) { // make sure wallet is unique by pubkey
               pubkeys[child.pubkey] = true;
-              var wallet = getNewChildrenInstance();
+              var wallet = newChildInstance();
               wallet.data.pubkey = child.pubkey;
               wallet.data.localName = child.localName;
               wallet.data.uid = child.uid;
@@ -1614,6 +1614,12 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
           var keypair = res[0];
           var currency = res[1];
           var block = res[2];
+
+          // Check if member account
+          if (!data.isMember && !csConfig.initPhase) {
+            throw {message:'ERROR.ONLY_MEMBER_CAN_EXECUTE_THIS_ACTION'};
+          }
+
           // Create the self part to sign
           var cert = 'Version: '+ constants.CERT_VERSION +'\n' +
             'Type: Certification\n' +
@@ -1948,8 +1954,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     },
 
     createNewChildWallet = function(options) {
-      var walletId = getChildrenWalletCount()+1;
-      var wallet = service.instance(walletId);
+      var wallet = newChildInstance();
       addChildWallet(wallet, options);
       return wallet;
     },
@@ -1988,7 +1993,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
 
     removeChildWalletById = function(id, options) {
       data.children = data.children || [];
-      var childIndex = _.findIndex(data.children, function(child) {return child.id == id;});
+      var childIndex = _.findIndex(data.children, function(child) {return child.id === id;});
       if (childIndex === -1) {
         console.warn('[wallet] Unable to remove child wallet {'+id+'} (not found)');
         return;
@@ -2006,7 +2011,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     },
 
     getChildWalletById = function(id) {
-      return (id !== 'default') && _.find(data.children|| [], function(child) {return child.id == id;}) || undefined;
+      return (id !== 'default') && _.find(data.children|| [], function(child) {return child.id === id;}) || undefined;
     },
 
     getChildWalletByPubkey = function(pubkey) {
@@ -2021,12 +2026,13 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       return angular.isDefined(data.childrenCount) ? data.childrenCount : (data.children && data.children.length || 0);
     },
 
-    getNewChildrenInstance =  function() {
+    newChildInstance =  function() {
       // Return max(id) + 1
-      var walletId = (data.children && data.children.reduce(function(res, wallet) {
+      var walletId = (data.children && data.children.reduce(function(res, wallet) {
           return Math.max(res, wallet.id);
         }, 0) || data.childrenCount || 0 )+ 1;
-      return service.instance(walletId, BMA);
+      var childrenWallet = service.instance(walletId, BMA);
+      return childrenWallet;
     },
 
     getAllChildrenWallet = function() {
@@ -2036,6 +2042,20 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
         });
     },
 
+    getAllPubkeys = function() {
+      if (!data.pubkey) throw new Error('User not login!');
+      return (data.children || []).reduce(function(res, wallet) {
+        return wallet.data.pubkey ? res.concat(wallet.data.pubkey) : res;
+      }, [data.pubkey])
+    }
+
+    getByPubkey = function(pubkey) {
+      if (!pubkey) throw new Error("Missing 'pubkey' argument !");
+      if (!data.pubkey) throw new Error('User not login!');
+      if (data.pubkey === pubkey) return exports; // main wallet
+      return getChildWalletByPubkey(pubkey);
+    }
+
     downloadChildrenWalletFile = function() {
       return $q.all([
         getAllChildrenWallet(),
@@ -2144,7 +2164,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
 
     checkAuthIdle = function(isAuthResult) {
       isAuthResult = angular.isDefined(isAuthResult) ? isAuthResult : isAuth();
-      var newEnableAuthIdle = isAuthResult && settings.keepAuthIdle > 0 && settings.keepAuthIdle != csSettings.constants.KEEP_AUTH_IDLE_SESSION;
+      var newEnableAuthIdle = isAuthResult && settings && settings.keepAuthIdle > 0 && settings.keepAuthIdle != csSettings.constants.KEEP_AUTH_IDLE_SESSION;
       var changed = (enableAuthIdle != newEnableAuthIdle);
 
       // need start/top watching
@@ -2170,7 +2190,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       }
 
       // Make sure to store seckey, in the session storage for secret key -fix #372
-      var storeSecKey = isAuthResult && settings.keepAuthIdle == csSettings.constants.KEEP_AUTH_IDLE_SESSION && true;
+      var storeSecKey = isAuthResult && settings && settings.keepAuthIdle == csSettings.constants.KEEP_AUTH_IDLE_SESSION && true;
       if (storeSecKey) {
         sessionStorage.put(constants.STORAGE_SECKEY, CryptoUtils.util.encode_base58(data.keypair.signSk));
       }
@@ -2181,7 +2201,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     };
 
     function getWalletSettings(settings) {
-      return {
+      return settings && {
         useLocalStorage: settings.useLocalStorage,
         useLocalStorageEncryption: settings.useLocalStorageEncryption,
         rememberMe: settings.rememberMe,
@@ -2192,7 +2212,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     function onSettingsChanged(allSettings) {
       var newSettings = getWalletSettings(allSettings);
       var hasChanged = !angular.equals(settings, newSettings);
-      if (!hasChanged) return; // skip
+      if (!hasChanged || !settings) return; // skip
 
       var useEncryptionChanged = !angular.equals(settings.useLocalStorageEncryption, newSettings.useLocalStorageEncryption);
       var useStorageChanged = !angular.equals(settings.useLocalStorage, newSettings.useLocalStorage) || useEncryptionChanged;
@@ -2394,6 +2414,8 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       recoverId: recoverId,
       downloadRevocation: downloadRevocation,
       downloadKeyFile: downloadKeyFile,
+      pubkeys: getAllPubkeys,
+      getByPubkey: getByPubkey,
       membership: {
         inside: membership(true),
         out: membership(false)
@@ -2412,7 +2434,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
         setParent: setParentWallet,
         count: getChildrenWalletCount,
         hasPubkey: hasChildrenWithPubkey,
-        instance: getNewChildrenInstance,
+        instance: newChildInstance,
         downloadFile: downloadChildrenWalletFile
       },
       api: api
@@ -2420,8 +2442,8 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
     return exports;
   }
 
-  service = factory('default', BMA);
-  service.instance = factory;
+  service = csWallet('default', BMA);
+  service.instance = csWallet;
 
   return service;
 });
diff --git a/www/js/services/wot-services.js b/www/js/services/wot-services.js
index a04095080..4ad2f3f98 100644
--- a/www/js/services/wot-services.js
+++ b/www/js/services/wot-services.js
@@ -5,7 +5,7 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
 .factory('csWot', function($rootScope, $q, $timeout, BMA, Api, CacheFactory, csConfig, csCurrency, csSettings, csCache) {
   'ngInject';
 
-  function factory(id) {
+  function csWot(id) {
 
     var
       api = new Api(this, "csWot-" + id),
@@ -1209,8 +1209,8 @@ angular.module('cesium.wot.services', ['ngApi', 'cesium.bma.services', 'cesium.c
     };
   }
 
-  var service = factory('default', BMA);
+  var service = csWot('default', BMA);
 
-  service.instance = factory;
+  service.instance = csWot;
   return service;
 });
diff --git a/www/plugins/es/css/style.css b/www/plugins/es/css/style.css
index c95bc0e82..edeb37ef8 100644
--- a/www/plugins/es/css/style.css
+++ b/www/plugins/es/css/style.css
@@ -272,6 +272,22 @@
   padding-top: 1px;
 }
 
+/**********
+   Common > report abuse
+**********/
+
+.popup-report-abuse .popup {
+  width: 370px !important;
+}
+.popup-report-abuse .popup .item-toggle .toggle {
+  right: 0 !important;
+}
+.popup-report-abuse .popup .item-toggle .input-label {
+  width: 280px;
+  max-width: 100%;
+  white-space: normal;
+}
+
 /**********
    Add specific Icons
 **********/
diff --git a/www/plugins/es/i18n/locale-en-GB.json b/www/plugins/es/i18n/locale-en-GB.json
index 489525697..eec0d9c4f 100644
--- a/www/plugins/es/i18n/locale-en-GB.json
+++ b/www/plugins/es/i18n/locale-en-GB.json
@@ -3,8 +3,15 @@
     "CATEGORY": "Category",
     "CATEGORIES": "Categories",
     "CATEGORY_SEARCH_HELP": "Search",
+    "COMMENT_HELP": "Comments",
     "LAST_MODIFICATION_DATE": "Updated on ",
     "SUBMIT_BY": "Submitted by",
+    "BTN_LIKE": "I like",
+    "BTN_LIKE_REMOVE": "I don't like anymore",
+    "LIKES_TEXT": "{{total}} {{total > 1 ? 'people' : 'person'}} liked this page",
+    "ABUSES_TEXT": "{{total}} {{total > 1 ? 'people' : 'person'}} reported a problem on this page",
+    "BTN_REPORT_ABUSE_DOTS": "Report a problem or an abuse...",
+    "BTN_REMOVE_REPORTED_ABUSE": "Cancel my problem report",
     "BTN_PUBLISH": "Publish",
     "BTN_PICTURE_DELETE": "Delete",
     "BTN_PICTURE_FAVORISE": "Default",
@@ -20,6 +27,15 @@
       "NO_RESULT": "No notification",
       "SHOW_ALL": "Show all",
       "LOAD_NOTIFICATIONS_FAILED": "Could not load notifications"
+    },
+    "REPORT_ABUSE": {
+      "TITLE": "Report a problem",
+      "SUB_TITLE": "Please explain briefly the problem:",
+      "REASON_HELP": "I explain the problem...",
+      "ASK_DELETE": "Request removal?",
+      "CONFIRM": {
+        "SENT": "Request sent. Thnak you!"
+      }
     }
   },
   "MENU": {
@@ -118,6 +134,8 @@
     "REPLY_TO_DELETED_COMMENT": "In response to a deleted comment",
     "REPLY_COUNT": "{{replyCount}} responses",
     "DELETED_COMMENT": "Comment deleted",
+    "MODIFIED_ON": "modified on {{time|formatDate}}",
+    "MODIFIED_PARENTHESIS": "(modified then)",
     "ERROR": {
       "FAILED_SAVE_COMMENT": "Saving comment failed",
       "FAILED_REMOVE_COMMENT": "Deleting comment failed"
@@ -497,11 +515,31 @@
     "TX_RECEIVED_MULTI": "You received a payment from <b>{{params[1]}}</b>.",
     "CERT_SENT": "Your <b>certification</b> to <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> was executed.",
     "CERT_RECEIVED": "You  have <b>received a certification</b> from <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-    "REGISTRY": {
+    "USER": {
+      "LIKE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> like your profile",
+      "FOLLOW_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> follows your activity",
+      "STAR_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> rated you ({{params[3]}} <i class=\"ion-star\">)",
+      "MODERATION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> asks you for a moderation on the profile: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> reported a profile to be deleted: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "ABUSE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has requested moderation on your profile"
+    },
+    "PAGE": {
       "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has commented on your referencing: <b>{{params[2]}}</b>",
       "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his comment on your referencing: <b>{{params[2]}}</b>",
       "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has replied to your comment on the referencing: <b>{{params[2]}}</b>",
-      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his answer to your comment, on the referencing: <b>{{params[2]}}</b>"
+      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his answer to your comment, on the referencing: <b>{{params[2]}}</b>",
+      "FOLLOW_NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has commented on the page: <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his comment on the page: <b>{{params[2]}}</b>",
+      "FOLLOW_NEW": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> added a page: <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> updated the page: <b>{{params[2]}}</b>",
+      "MODERATION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> asks you for a moderation on the page: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> reported a page to be deleted: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "ABUSE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has requested moderation on your page: <b>{{params[2]}}</b>"
+    }
+  },
+  "LIKE": {
+    "ERROR": {
+      "FAILED_TOGGLE_LIKE": "Unable to execute this action."
     }
   },
   "CONFIRM": {
diff --git a/www/plugins/es/i18n/locale-en.json b/www/plugins/es/i18n/locale-en.json
index 489525697..eec0d9c4f 100644
--- a/www/plugins/es/i18n/locale-en.json
+++ b/www/plugins/es/i18n/locale-en.json
@@ -3,8 +3,15 @@
     "CATEGORY": "Category",
     "CATEGORIES": "Categories",
     "CATEGORY_SEARCH_HELP": "Search",
+    "COMMENT_HELP": "Comments",
     "LAST_MODIFICATION_DATE": "Updated on ",
     "SUBMIT_BY": "Submitted by",
+    "BTN_LIKE": "I like",
+    "BTN_LIKE_REMOVE": "I don't like anymore",
+    "LIKES_TEXT": "{{total}} {{total > 1 ? 'people' : 'person'}} liked this page",
+    "ABUSES_TEXT": "{{total}} {{total > 1 ? 'people' : 'person'}} reported a problem on this page",
+    "BTN_REPORT_ABUSE_DOTS": "Report a problem or an abuse...",
+    "BTN_REMOVE_REPORTED_ABUSE": "Cancel my problem report",
     "BTN_PUBLISH": "Publish",
     "BTN_PICTURE_DELETE": "Delete",
     "BTN_PICTURE_FAVORISE": "Default",
@@ -20,6 +27,15 @@
       "NO_RESULT": "No notification",
       "SHOW_ALL": "Show all",
       "LOAD_NOTIFICATIONS_FAILED": "Could not load notifications"
+    },
+    "REPORT_ABUSE": {
+      "TITLE": "Report a problem",
+      "SUB_TITLE": "Please explain briefly the problem:",
+      "REASON_HELP": "I explain the problem...",
+      "ASK_DELETE": "Request removal?",
+      "CONFIRM": {
+        "SENT": "Request sent. Thnak you!"
+      }
     }
   },
   "MENU": {
@@ -118,6 +134,8 @@
     "REPLY_TO_DELETED_COMMENT": "In response to a deleted comment",
     "REPLY_COUNT": "{{replyCount}} responses",
     "DELETED_COMMENT": "Comment deleted",
+    "MODIFIED_ON": "modified on {{time|formatDate}}",
+    "MODIFIED_PARENTHESIS": "(modified then)",
     "ERROR": {
       "FAILED_SAVE_COMMENT": "Saving comment failed",
       "FAILED_REMOVE_COMMENT": "Deleting comment failed"
@@ -497,11 +515,31 @@
     "TX_RECEIVED_MULTI": "You received a payment from <b>{{params[1]}}</b>.",
     "CERT_SENT": "Your <b>certification</b> to <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> was executed.",
     "CERT_RECEIVED": "You  have <b>received a certification</b> from <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-    "REGISTRY": {
+    "USER": {
+      "LIKE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> like your profile",
+      "FOLLOW_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> follows your activity",
+      "STAR_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> rated you ({{params[3]}} <i class=\"ion-star\">)",
+      "MODERATION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> asks you for a moderation on the profile: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> reported a profile to be deleted: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "ABUSE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has requested moderation on your profile"
+    },
+    "PAGE": {
       "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has commented on your referencing: <b>{{params[2]}}</b>",
       "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his comment on your referencing: <b>{{params[2]}}</b>",
       "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has replied to your comment on the referencing: <b>{{params[2]}}</b>",
-      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his answer to your comment, on the referencing: <b>{{params[2]}}</b>"
+      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his answer to your comment, on the referencing: <b>{{params[2]}}</b>",
+      "FOLLOW_NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has commented on the page: <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has modified his comment on the page: <b>{{params[2]}}</b>",
+      "FOLLOW_NEW": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> added a page: <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> updated the page: <b>{{params[2]}}</b>",
+      "MODERATION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> asks you for a moderation on the page: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> reported a page to be deleted: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "ABUSE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> has requested moderation on your page: <b>{{params[2]}}</b>"
+    }
+  },
+  "LIKE": {
+    "ERROR": {
+      "FAILED_TOGGLE_LIKE": "Unable to execute this action."
     }
   },
   "CONFIRM": {
diff --git a/www/plugins/es/i18n/locale-eo-EO.json b/www/plugins/es/i18n/locale-eo-EO.json
index fe8978016..8572520b2 100644
--- a/www/plugins/es/i18n/locale-eo-EO.json
+++ b/www/plugins/es/i18n/locale-eo-EO.json
@@ -3,8 +3,14 @@
     "CATEGORY": "Kategorio",
     "CATEGORIES": "Kategorioj",
     "CATEGORY_SEARCH_HELP": "Serĉado",
+    "COMMENT_HELP": "Komento",
     "LAST_MODIFICATION_DATE": "Äœisdatigita la",
     "SUBMIT_BY": "Submetita de",
+    "BTN_LIKE": "Mi ŝatas",
+    "LIKES_TEXT": "{{total}} persono{{total > 1 ? 'j' : ''}} ŝatis tiun ĉi paĝon",
+    "ABUSES_TEXT": "{{total}} persono{{total > 1 ? 'j' : ''}} atentigis pri problemo",
+    "BTN_REPORT_ABUSE_DOTS": "Atentigi pri problemo aÅ­ misuzo...",
+    "BTN_REMOVE_REPORTED_ABUSE": "Nuligi mian atentigon",
     "BTN_PUBLISH": "Publikigi",
     "BTN_PICTURE_DELETE": "Forigi",
     "BTN_PICTURE_FAVORISE": "Precipa",
@@ -20,6 +26,15 @@
       "NO_RESULT": "Neniu avizo",
       "SHOW_ALL": "Vidi ĉion",
       "LOAD_NOTIFICATIONS_FAILED": "Malsukceso por ŝarĝi la avizojn"
+    },
+    "REPORT_ABUSE": {
+      "TITLE": "Atentigi pri problemo",
+      "SUB_TITLE": "Bonvolu klarigi rapide la problemon:",
+      "REASON_HELP": "Mi klarigas la problemon...",
+      "ASK_DELETE": "Peti la forigon?",
+      "CONFIRM": {
+        "SENT": "Atentigo sendita. Dankon!"
+      }
     }
   },
   "MENU": {
@@ -118,6 +133,8 @@
     "REPLY_TO_DELETED_COMMENT": "Responde al forigita komento",
     "REPLY_COUNT": "{{replyCount}} respondoj",
     "DELETED_COMMENT": "Komento forigita",
+    "MODIFIED_ON": "modifita la {{time|formatDate}}",
+    "MODIFIED_PARENTHESIS": "(modifita poste)",
     "ERROR": {
       "FAILED_SAVE_COMMENT": "Eraro dum la konservo de la komento",
       "FAILED_REMOVE_COMMENT": "Eraro dum la forigo de la komento"
@@ -464,6 +481,7 @@
       "TITLE": "Serĉado de dokumentoj",
       "BTN_ACTIONS": "Agoj",
       "SEARCH_HELP": "Sendanto:AAA*, tempo:1508406169",
+      "LAST_DOCUMENTS_DOTS": "Lastaj dokumentoj:",
       "LAST_DOCUMENTS": "Lastaj dokumentoj",
       "SHOW_QUERY": "Vidi la informpeton",
       "HIDE_QUERY": "Kaŝi la informpeton",
@@ -472,6 +490,8 @@
       "HEADER_RECIPIENT": "Ricevonto",
       "READ": "Legita",
       "BTN_REMOVE": "Forigi tiun ĉi dokumenton",
+      "BTN_COMPACT": "Densigi",
+      "HAS_CREATE_OR_UPDATE_PROFILE": "kreis aÅ­ modifis sian profilon",
       "POPOVER_ACTIONS": {
         "TITLE": "Agoj",
         "REMOVE_ALL": "Forigi tiujn ĉi dokumentojn..."
@@ -523,6 +543,13 @@
       "RECIPIENT_IS_MANDATORY": "Adresito estas deviga por la ĉifrado."
     }
   },
+  "ES_PEER": {
+    "NAME": "Nomo",
+    "DOCUMENTS": "Dokumentoj",
+    "SOFTWARE": "Programo",
+    "DOCUMENT_COUNT": "Nombro de dokumentoj",
+    "EMAIL_SUBSCRIPTION_COUNT": "{{emailSubscription}} abonantoj pri avizoj per retmesaĝoj"
+  },
   "EVENT": {
     "NODE_STARTED": "Via nodo ES API <b>{{params[0]}}</b> ekis",
     "NODE_BMA_DOWN": "La nodo <b>{{params[0]}}:{{params[1]}}</b> (uzata de via nodo ES API) estas <b>neatingebla</b>.",
@@ -538,11 +565,26 @@
     "TX_RECEIVED_MULTI": "Vi <b>ricevis pagon</b> de <b>{{params[1]}}</b>.",
     "CERT_SENT": "Via <b>atestado</b> al <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> efektiviĝis.",
     "CERT_RECEIVED": "Vi <b>ricevis atestaĵon</b> de <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-    "REGISTRY": {
+    "USER": {
+      "LIKE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ŝatas vian profilon",
+      "FOLLOW_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> sekvas viajn agojn",
+      "STAR_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> notis vin ({{params[3]}} <b class=\"ion-star\">)",
+      "MODERATION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> petas de vi moderigon pri la profilo: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> atentigis pri profilo foriginda: <b>{{params[2]}}</b>",
+      "ABUSE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> atentigis pri via profilo"
+    },
+    "PAGE": {
       "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> komentis vian anoncon: <b>{{params[2]}}</b>",
       "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> modifis sian komenton pri via anonco: <b>{{params[2]}}</b>",
       "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> respondis al via komento pri la anonco: <b>{{params[2]}}</b>",
-      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> modifis sian respondon al via komento pri la anonco: <b>{{params[2]}}</b>"
+      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> modifis sian respondon al via komento pri la anonco: <b>{{params[2]}}</b>",
+      "FOLLOW_NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> komentis la paĝon: <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> modifis sian komenton ĉe la paĝo: <b>{{params[2]}}</b>",
+      "FOLLOW_NEW": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> aldonis la paĝon: <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> modifis la paĝon: <b>{{params[2]}}</b>",
+      "MODERATION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> petas de vis moderigon pri la paĝo: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> atentigis pri paĝo foriginda: <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "ABUSE_RECEIVED": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> atentigis pri via paĝo: <b>{{params[2]}}</b>"
     }
   },
   "CONFIRM": {
diff --git a/www/plugins/es/i18n/locale-es-ES.json b/www/plugins/es/i18n/locale-es-ES.json
index 49be7a994..50077c822 100644
--- a/www/plugins/es/i18n/locale-es-ES.json
+++ b/www/plugins/es/i18n/locale-es-ES.json
@@ -499,7 +499,7 @@
     "TX_RECEIVED_MULTI": "Ha <b>recibido un pago</b> de <b>{{params[1]}}</b>.",
     "CERT_SENT": "Su <b>certificación</b> a <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> fue efectuada.",
     "CERT_RECEIVED": "Ha <b>recibido una certificación</b> de <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-    "REGISTRY": {
+    "PAGE": {
       "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ha comentado su referencia : <b>{{params[2]}}</b>",
       "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ha modificado su comentario sobre su referencia : <b>{{params[2]}}</b>",
       "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ha contestado a su comentario sobre el referencia : <b>{{params[2]}}</b>",
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index 2b3175d28..34c229111 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -3,8 +3,15 @@
     "CATEGORY": "Catégorie",
     "CATEGORIES": "Catégories",
     "CATEGORY_SEARCH_HELP": "Recherche",
+    "COMMENT_HELP": "Commentaire",
     "LAST_MODIFICATION_DATE": "Mise à jour le",
     "SUBMIT_BY": "Soumis par",
+    "BTN_LIKE": "J'aime",
+    "BTN_LIKE_REMOVE": "Je n'aime plus",
+    "LIKES_TEXT": "{{total}} personne{{total > 1 ? 's' : ''}} {{total > 1 ? 'ont' : 'a'}} aimé cette page",
+    "ABUSES_TEXT": "{{total}} personne{{total > 1 ? 's' : ''}} {{total > 1 ? 'ont' : 'a'}} signalé un problème",
+    "BTN_REPORT_ABUSE_DOTS": "Signaler un problème ou un abus...",
+    "BTN_REMOVE_REPORTED_ABUSE": "Annuler mon signalement",
     "BTN_PUBLISH": "Publier",
     "BTN_PICTURE_DELETE": "Supprimer",
     "BTN_PICTURE_FAVORISE": "Principale",
@@ -20,6 +27,15 @@
       "NO_RESULT": "Aucune notification",
       "SHOW_ALL": "Voir tout",
       "LOAD_NOTIFICATIONS_FAILED": "Erreur de chargement des notifications"
+    },
+    "REPORT_ABUSE": {
+      "TITLE": "Signaler un problème",
+      "SUB_TITLE": "Merci d'expliquer succintement le problème :",
+      "REASON_HELP": "J'explique le problème...",
+      "ASK_DELETE": "Demander la suppression ?",
+      "CONFIRM": {
+        "SENT": "Signalement envoyé. Merci !"
+      }
     }
   },
   "MENU": {
@@ -118,6 +134,8 @@
     "REPLY_TO_DELETED_COMMENT": "En réponse à un commentaire supprimé",
     "REPLY_COUNT": "{{replyCount}} réponses",
     "DELETED_COMMENT": "Commentaire supprimé",
+    "MODIFIED_ON": "modifié le {{time|formatDate}}",
+    "MODIFIED_PARENTHESIS": "(modifié ensuite)",
     "ERROR": {
       "FAILED_SAVE_COMMENT": "Erreur lors de la sauvegarde du commentaire",
       "FAILED_REMOVE_COMMENT": "Erreur lors de la suppression du commentaire"
@@ -136,7 +154,7 @@
       "MESSAGE_RECEIVED": "Vous avez <b>reçu un message</b><br/>de"
     },
     "LIST": {
-      "INBOX": "Boite de réception",
+      "INBOX": "Boîte de réception",
       "OUTBOX": "Messages envoyés",
       "LAST_INBOX": "Nouveaux messages",
       "LAST_OUTBOX": "Messages envoyés",
@@ -341,7 +359,7 @@
       "LOAD_CATEGORY_FAILED": "Erreur de chargement de la liste des activités",
       "LOAD_RECORD_FAILED": "Erreur lors du chargement de la page",
       "LOOKUP_RECORDS_FAILED": "Erreur lors de l'exécution de la recherche",
-      "REMOVE_RECORD_FAILED": "Erreur de la suppression de la page",
+      "REMOVE_RECORD_FAILED": "Erreur lors de la suppression de la page",
       "SAVE_RECORD_FAILED": "Erreur lors de la sauvegarde",
       "RECORD_NOT_EXISTS": "Page inexistante",
       "GEO_LOCATION_NOT_FOUND": "Ville ou code postal non trouvé"
@@ -448,9 +466,9 @@
       "DELETE_SUBSCRIPTION_FAILED": "Erreur lors de la suppression de l'abonnement"
     },
     "MODAL_EMAIL": {
-      "TITLE" : "Notification par email",
-      "HELP" : "Remplissez ce formulaire pour <b>être notifié par email</b> des événements de votre compte.<br/>Votre adresse email sera chiffrée pour n'être visible que par le prestataire de service.",
-      "EMAIL_LABEL" : "Votre email :",
+      "TITLE": "Notification par email",
+      "HELP": "Remplissez ce formulaire pour <b>être notifié par email</b> des événements de votre compte.<br/>Votre adresse email sera chiffrée pour n'être visible que par le prestataire de service.",
+      "EMAIL_LABEL": "Votre email :",
       "EMAIL_HELP": "jean.dupond@domaine.com",
       "FREQUENCY_LABEL": "Fréquence des notifications :",
       "FREQUENCY_DAILY": "Journalier",
@@ -531,7 +549,7 @@
     "DOCUMENTS": "Documents",
     "SOFTWARE": "Logiciel",
     "DOCUMENT_COUNT": "Nombre de documents",
-    "EMAIL_SUBSCRIPTION_COUNT": "{{emailSubscription}} abonnés aux notifications par email"
+    "EMAIL_SUBSCRIPTION_COUNT": "{{emailSubscription}} abonné{{emailSubscription ? 's' : ''}} aux notifications par email"
   },
   "EVENT": {
     "NODE_STARTED": "Votre noeud ES API <b>{{params[0]}}</b> est démarré",
@@ -548,11 +566,31 @@
     "TX_RECEIVED_MULTI": "Vous avez <b>reçu un paiement</b> de <b>{{params[1]}}</b>.",
     "CERT_SENT": "Votre <b>certification</b> à <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a été effectuée.",
     "CERT_RECEIVED": "Vous avez <b>reçu une certification</b> de <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-    "REGISTRY": {
-      "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a commenté votre référencement : <b>{{params[2]}}</b>",
-      "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a modifié son commentaire sur votre référencement : <b>{{params[2]}}</b>",
-      "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a répondu à votre commentaire sur le référencement : <b>{{params[2]}}</b>",
-      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a modifié sa réponse à votre commentaire sur le référencement : <b>{{params[2]}}</b>"
+    "USER": {
+      "LIKE_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> aime votre profil",
+      "FOLLOW_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> suit votre activité",
+      "STAR_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> vous a noté ({{params[3]}} <b class=\"ion-star\">)",
+      "MODERATION_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> vous demande une modération sur le profil : <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> demande la suppression d'un profil : <b>{{params[2]}}</b>",
+      "ABUSE_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a signalé votre profil"
+    },
+    "PAGE": {
+      "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a commenté votre page : <b>{{params[2]}}</b>",
+      "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a modifié son commentaire sur votre page : <b>{{params[2]}}</b>",
+      "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a répondu à votre commentaire sur la page : <b>{{params[2]}}</b>",
+      "UPDATE_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> a modifié sa réponse à votre commentaire sur la page : <b>{{params[2]}}</b>",
+      "FOLLOW_NEW_COMMENT": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a commenté la page : <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE_COMMENT": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a modifié son commentaire sur la page : <b>{{params[2]}}</b>",
+      "FOLLOW_NEW": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a ajouté la page : <b>{{params[2]}}</b>",
+      "FOLLOW_UPDATE": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a modifié la page : <b>{{params[2]}}</b>",
+      "MODERATION_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> vous demande une modération sur la page : <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "DELETION_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a signalé une page à supprimer : <b>{{params[2]}}</b><br/><b class=\"dark ion-quote\"> </b><span class=\"text-italic\">{{params[3]}}</span>",
+      "ABUSE_RECEIVED": "<span class=\"positive\"><i class=\"icon ion-person\"></i>&thinsp;{{name||params[1]}}</span> a signalé votre page : <b>{{params[2]}}</b>"
+    }
+  },
+  "LIKE": {
+    "ERROR": {
+      "FAILED_TOGGLE_LIKE": "Impossible d'executer cette action."
     }
   },
   "CONFIRM": {
diff --git a/www/plugins/es/i18n/locale-it-IT.json b/www/plugins/es/i18n/locale-it-IT.json
index 9227ffe45..e0e79b488 100644
--- a/www/plugins/es/i18n/locale-it-IT.json
+++ b/www/plugins/es/i18n/locale-it-IT.json
@@ -518,7 +518,7 @@
      "TX_RECEIVED_MULTI": "Ha ricevuto un pagamento da <b>{{params[1]}}</b>.",
      "CERT_SENT": "Sua <b>certificazione</b> a favore di <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> è stata eseguita.",
      "CERT_RECEIVED": "Ha ricevuto <b>una certificazione</b> da parte di <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-     "REGISTRY": {
+     "PAGE": {
        "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ha scritto un commento sul suo riferimento: <b>{{params[2]}}</b>",
        "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ha modificato il suo commento sul suo riferimento: <b>{{params[2]}}</b>",
        "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> ha risposto al suo commento sul riferimento: <b>{{params[2]}}</b>",
diff --git a/www/plugins/es/i18n/locale-nl-NL.json b/www/plugins/es/i18n/locale-nl-NL.json
index 4d6894532..4b04e73f6 100644
--- a/www/plugins/es/i18n/locale-nl-NL.json
+++ b/www/plugins/es/i18n/locale-nl-NL.json
@@ -239,7 +239,7 @@
     "TX_RECEIVED_MULTI": "Je hebt een <b>betaling ontvangen</b> van <b>{{params[1]}}</b>.",
     "CERT_SENT": "Je <b>certificatie</b> van <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\" ><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> is uitgevoerd.",
     "CERT_RECEIVED": "Je hebt een <b>certificatie ontvangen</b> van <span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid}\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span>.",
-    "REGISTRY": {
+    "PAGE": {
       "NEW_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> heeft gereageerd op jouw referentie: <b>{{params[2]}}</b>",
       "UPDATE_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> heeft zijn/aar reactie op jouw referentie bewerkt: <b>{{params[2]}}</b>",
       "NEW_REPLY_COMMENT": "<span ng-class=\"{'gray': !notification.uid, 'positive':notification.uid }\"><i class=\"icon\" ng-class=\"{'ion-person': notification.uid, 'ion-key': !notification.uid}\"></i>&thinsp;{{name||uid||params[1]}}</span> hheeft gereageerd op jouw commentaar op referentie: <b>{{params[2]}}</b>",
diff --git a/www/plugins/es/js/controllers/common-controllers.js b/www/plugins/es/js/controllers/common-controllers.js
index 415602f89..0d96ac610 100644
--- a/www/plugins/es/js/controllers/common-controllers.js
+++ b/www/plugins/es/js/controllers/common-controllers.js
@@ -21,6 +21,8 @@ angular.module('cesium.es.common.controllers', ['ngResource', 'cesium.es.service
   .controller('ESSearchPositionItemCtrl', ESSearchPositionItemController)
 
   .controller('ESSearchPositionModalCtrl', ESSearchPositionModalController)
+
+  .controller('ESLikesCtrl', ESLikesController)
 ;
 
 
@@ -973,3 +975,282 @@ function ESSearchPositionModalController($scope, $q, $translate, esGeo, paramete
   };
 
 }
+
+
+function ESLikesController($scope, $q, $timeout, $translate, $ionicPopup, UIUtils, Modals, csWallet, esHttp, esLike) {
+  'ngInject';
+
+  $scope.entered = false;
+  $scope.abuseData = {};
+  $scope.abuseLevels = [
+    {value: 1, label: 'LOW'},
+    {value: 2, label: 'LOW'}
+  ];
+  $scope.staring = false;
+  $scope.options = $scope.options || {};
+  $scope.options.like = $scope.options.like || {
+    kinds: esLike.constants.KINDS,
+    index: undefined,
+    type: undefined,
+    id: undefined
+  };
+
+  $scope.$on('$recordView.enter', function(e, state) {
+    // First enter
+    if (!$scope.entered) {
+      $scope.entered = false;
+      // Nothing to do: main controller will trigger '$recordView.load' event
+    }
+    // second call (e.g. if cache)
+    else if ($scope.id) {
+      $scope.loadLikes($scope.id);
+    }
+  });
+
+  $scope.$on('$recordView.load', function(event, id) {
+    $scope.id = id || $scope.id;
+    if ($scope.id) {
+      $scope.loadLikes($scope.id);
+    }
+  });
+
+  // Init Like service
+  $scope.initLikes = function() {
+    if (!$scope.likeData) {
+      throw new Error("Missing 'likeData' in scope. Cannot load likes counter");
+    }
+    if (!$scope.options.like.service) {
+      if (!$scope.options.like.index || !$scope.options.like.type) {
+        throw new Error("Missing 'options.like.index' or 'options.like.type' in scope. Cannot load likes counter");
+      }
+      $scope.options.like.service = esLike.instance($scope.options.like.index, $scope.options.like.type);
+    }
+    if (!$scope.options.like.kinds) {
+      // Get scope's kinds (e.g. defined in the parent scope)
+      $scope.options.like.kinds = _.filter(esLike.constants.KINDS, function (kind) {
+        var key = kind.toLowerCase() + 's';
+        return angular.isDefined($scope.likeData[key]);
+      });
+    }
+  };
+
+  $scope.loadLikes = function(id) {
+    if ($scope.likeData.loading) return;// Skip
+
+    id = id || $scope.likeData.id;
+    $scope.initLikes();
+
+    var kinds = $scope.options.like.kinds || [];
+    if (!kinds.length) return; // skip
+
+    $scope.likeData.loading = true;
+    var now = Date.now();
+    console.debug("[ES] Loading counter of {0}... ({1})".format(id.substring(0,8), kinds));
+
+    var issuers = csWallet.isLogin() ? csWallet.pubkeys() : undefined;
+
+    return $q.all(_.map(kinds, function(kind) {
+      var key = kind.toLowerCase() + 's';
+      return $scope.options.like.service.count(id, {issuers: issuers, kind: kind})
+        .then(function (res) {
+          // Store result to scope
+          if ($scope.likeData[key]) {
+            angular.merge($scope.likeData[key], res);
+          }
+        });
+    }))
+      .then(function () {
+        $scope.likeData.id = id;
+        console.debug("[ES] Loading counter of {0} [OK] in {1}ms".format(id.substring(0,8), Date.now()-now));
+
+        if (_.contains(kinds, 'VIEW') && !$scope.canEdit) {
+          $scope.markAsView();
+        }
+
+        // Publish to parent scope (to be able to use it in action popover's buttons)
+        if ($scope.$parent) {
+          console.debug("[ES] [likes] Adding data and functions to parent scope");
+          $scope.$parent.toggleLike = $scope.toggleLike;
+          $scope.$parent.reportAbuse = $scope.reportAbuse;
+        }
+
+        $scope.likeData.loading = false;
+      })
+      .catch(function (err) {
+        console.error(err && err.message || err);
+        $scope.likeData.loading = false;
+      });
+  };
+
+  $scope.toggleLike = function(event, options) {
+    $scope.initLikes();
+    if (!$scope.likeData.id) throw new Error("Missing 'likeData.id' in scope. Cannot apply toggle");
+
+    options = options || {};
+    options.kind = options.kind && options.kind.toUpperCase() || 'LIKE';
+    var key = options.kind.toLowerCase() + 's';
+
+    $scope.likeData[key] = $scope.likeData[key] || {};
+
+    // Avoid too many call
+    if ($scope.likeData[key].loading === true || $scope.likeData.loading) {
+      event.preventDefault();
+      return $q.reject();
+    }
+
+    // Select the wallet, if many
+    if (!options.pubkey) {
+      return (csWallet.children.count() ? Modals.showSelectWallet({displayBalance: false}) : $q.when(csWallet))
+        .then(function(wallet) {
+          if (!wallet) throw 'CANCELLED';
+          options.pubkey = wallet.data.pubkey;
+          return $scope.toggleLike(event, options); // Loop
+        });
+    }
+
+    var wallet = csWallet.getByPubkey(options.pubkey);
+    if (!wallet) return;
+
+    $scope.likeData[key].loading = true;
+    return wallet.auth({minData: true})
+      .then(function(walletData) {
+        if (!walletData) {
+          UIUtils.loading.hide();
+          return;
+        }
+
+        // Check if member account
+        if (!walletData.isMember) {
+          // TODO: enable this
+          //throw {message: "ERROR.ONLY_MEMBER_CAN_EXECUTE_THIS_ACTION"};
+        }
+
+        // Apply like
+        options.id = $scope.likeData.id;
+        return $scope.options.like.service.toggle($scope.likeData.id, options);
+      })
+      .then(function(delta) {
+        UIUtils.loading.hide();
+        if (delta !== 0) {
+          $scope.likeData[key].total = ($scope.likeData[key].total || 0) + delta;
+          $scope.likeData[key].wasHitByPubkey = $scope.likeData[key].wasHitByPubkey || {};
+          $scope.likeData[key].wasHitByPubkey[options.pubkey] = delta > 0;
+          $scope.likeData[key].wasHitCount += delta;
+        }
+        $timeout(function() {
+          $scope.likeData[key].loading = false;
+          $scope.$broadcast('$$rebind::like'); // notify binder
+        }, 1000);
+      })
+      .catch(function(err) {
+        $scope.likeData[key].loading = false;
+        if (err === 'CANCELLED') return; // User cancelled
+        console.error(err);
+        UIUtils.onError('LIKE.ERROR.FAILED_TOGGLE_LIKE')(err);
+        event.preventDefault();
+      });
+  };
+
+  $scope.setAbuseForm = function(form) {
+    $scope.abuseForm = form;
+  };
+
+  $scope.showAbuseCommentPopover = function(event) {
+    return $translate(['COMMON.REPORT_ABUSE.TITLE', 'COMMON.REPORT_ABUSE.SUB_TITLE','COMMON.BTN_SEND', 'COMMON.BTN_CANCEL'])
+      .then(function(translations) {
+
+        UIUtils.loading.hide();
+
+        return $ionicPopup.show({
+          templateUrl: 'plugins/es/templates/common/popup_report_abuse.html',
+          title: translations['COMMON.REPORT_ABUSE.TITLE'],
+          subTitle: translations['COMMON.REPORT_ABUSE.SUB_TITLE'],
+          cssClass: 'popup-report-abuse',
+          scope: $scope,
+          buttons: [
+            {
+              text: translations['COMMON.BTN_CANCEL'],
+              type: 'button-stable button-clear gray'
+            },
+            {
+              text: translations['COMMON.BTN_SEND'],
+              type: 'button button-positive  ink',
+              onTap: function(e) {
+                $scope.abuseForm.$submitted=true;
+                if(!$scope.abuseForm.$valid || !$scope.abuseData.comment) {
+                  //don't allow the user to close unless he enters a uid
+                  e.preventDefault();
+                } else {
+                  return $scope.abuseData;
+                }
+              }
+            }
+          ]
+        });
+      })
+      .then(function(res) {
+        $scope.abuseData = {};
+        if (!res || !res.comment) { // user cancel
+          UIUtils.loading.hide();
+          return undefined;
+        }
+        return res;
+      });
+  };
+
+  $scope.reportAbuse = function(event, options) {
+    if (!$scope.likeData || !$scope.likeData.abuses || $scope.likeData.abuses.wasHitCount) return; // skip
+    if ($scope.likeData.abuses.wasHitCount) return; // already report
+
+    options = options || {};
+
+    // Select the wallet, if many
+    if (!options.pubkey) {
+      return (csWallet.children.count() ? Modals.showSelectWallet({displayBalance: false}) : $q.when(csWallet))
+        .then(function(wallet) {
+          if (!wallet) throw 'CANCELLED';
+          options.pubkey = wallet.data.pubkey;
+          return $scope.reportAbuse(event, options); // Loop
+        });
+    }
+
+    var wallet = csWallet.getByPubkey(options.pubkey);
+
+    // Check if member account
+    if (!wallet || !wallet.isMember()) {
+      UIUtils.alert.info("ERROR.ONLY_MEMBER_CAN_EXECUTE_THIS_ACTION");
+      return;
+    }
+
+    if (!options.comment) {
+      // Ask a comment
+      return $scope.showAbuseCommentPopover(event)
+        // Loop, with options.comment filled
+        .then(function(res) {
+          if (!res || !res.comment) return; // Empty comment: skip
+          options.comment = res.comment;
+          options.level = res.level || (res.delete && 5) || undefined;
+          return $scope.reportAbuse(event, options) // Loop, with the comment
+        });
+    }
+
+    // Send abuse report
+    options.kind = 'ABUSE';
+    return $scope.toggleLike(event, options)
+      .then(function() {
+        UIUtils.toast.show('COMMON.REPORT_ABUSE.CONFIRM.SENT')
+      })
+  };
+
+  csWallet.api.data.on.reset($scope, function() {
+    _.forEach($scope.options.like.kinds||[], function(kind) {
+      var key = kind.toLowerCase() + 's';
+      if ($scope.likeData[key]) {
+        $scope.likeData[key].wasHitByPubkey = {};
+        $scope.likeData[key].wasHitCount = 0;
+      }
+    })
+    $scope.$broadcast('$$rebind::like'); // notify binder
+  }, this);
+
+}
diff --git a/www/plugins/es/js/controllers/registry-controllers.js b/www/plugins/es/js/controllers/registry-controllers.js
index 04b3c29fb..7d1cc4c22 100644
--- a/www/plugins/es/js/controllers/registry-controllers.js
+++ b/www/plugins/es/js/controllers/registry-controllers.js
@@ -775,7 +775,7 @@ function ESWalletPagesController($scope, $controller, $timeout, UIUtils, esModal
 }
 
 
-function ESRegistryRecordViewController($scope, $rootScope, $state, $q, $timeout, $ionicPopover, $ionicHistory, $translate,
+function ESRegistryRecordViewController($scope, $rootScope, $state, $q, $timeout, $ionicPopover, $ionicHistory, $translate, $controller,
                                         $anchorScroll, csConfig, csWallet, esRegistry, UIUtils, esHttp) {
   'ngInject';
 
@@ -788,6 +788,21 @@ function ESRegistryRecordViewController($scope, $rootScope, $state, $q, $timeout
   $scope.loading = true;
   $scope.motion = UIUtils.motion.fadeSlideIn;
 
+  // Init likes here, to be able to use in extension
+  $scope.options = $scope.options || {};
+  $scope.options.like = {
+    kinds: ['LIKE', 'ABUSE'],
+    index: 'page',
+    type: 'record'
+  };
+  $scope.likeData = {
+    likes: {},
+    abuses: {}
+  };
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('ESLikesCtrl', {$scope: $scope}));
+
   $scope.$on('$ionicView.beforeEnter', function (event, viewData) {
     // Enable back button (workaround need for navigation outside tabs - https://stackoverflow.com/a/35064602)
     viewData.enableBack = UIUtils.screen.isSmall() ? true : viewData.enableBack;
@@ -954,6 +969,7 @@ function ESRegistryRecordViewController($scope, $rootScope, $state, $q, $timeout
       $scope.actionsPopover.hide();
       $scope.actionsPopover = null;
     }
+    return true;
   };
 
   $scope.showSharePopover = function(event) {
diff --git a/www/plugins/es/js/controllers/wallet-controllers.js b/www/plugins/es/js/controllers/wallet-controllers.js
index ad1ba4bfb..eb1d12a5b 100644
--- a/www/plugins/es/js/controllers/wallet-controllers.js
+++ b/www/plugins/es/js/controllers/wallet-controllers.js
@@ -8,9 +8,13 @@ angular.module('cesium.es.wallet.controllers', ['cesium.es.services'])
       PluginServiceProvider
         .extendStates(['app.view_wallet', 'app.view_wallet_by_id'], {
           points: {
+            'hero': {
+              templateUrl: "plugins/es/templates/wallet/view_wallet_extend.html",
+              controller: 'ESWalletLikesCtrl'
+            },
             'after-general': {
               templateUrl: "plugins/es/templates/wallet/view_wallet_extend.html",
-              controller: 'ESWalletCtrl'
+              controller: 'ESWalletViewCtrl'
             }
           }
         })
@@ -30,11 +34,13 @@ angular.module('cesium.es.wallet.controllers', ['cesium.es.services'])
   })
 
 
-  .controller('ESWalletCtrl', ESWalletController)
+  .controller('ESWalletViewCtrl', ESWalletViewController)
+
+  .controller('ESWalletLikesCtrl', ESWalletLikesController)
 
 ;
 
-function ESWalletController($scope, $controller, $state, esModals, csWallet) {
+function ESWalletViewController($scope, $controller, $state, csWallet, esModals) {
   'ngInject';
 
   // Initialize the super class and extend it.
@@ -44,7 +50,7 @@ function ESWalletController($scope, $controller, $state, esModals, csWallet) {
 
   /* -- modals -- */
 
-  $scope.showNewPageModal = function() {
+  $scope.showNewPageModal = function(event) {
     var wallet = ($state.params && $state.params.id) ? csWallet.children.get($state.params.id) : csWallet;
     if (!wallet) {
       UIUtils.alert.error('ERROR.UNKNOWN_WALLET_ID');
@@ -55,3 +61,28 @@ function ESWalletController($scope, $controller, $state, esModals, csWallet) {
   };
 }
 
+
+function ESWalletLikesController($scope, $controller, UIUtils, esHttp, esProfile) {
+  'ngInject';
+
+  $scope.options = $scope.options || {};
+  $scope.options.like = $scope.options.like || {
+    index: 'user',
+    type: 'profile',
+    service: esProfile.like
+  };
+  $scope.canEdit = true; // Avoid to change like counter itself
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('ESLikesCtrl', {$scope: $scope}));
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('ESExtensionCtrl', {$scope: $scope}));
+
+  // Load likes, when profile loaded
+  $scope.$watch('formData.pubkey', function(pubkey) {
+    if (pubkey) {
+      $scope.loadLikes(pubkey);
+    }
+  });
+}
diff --git a/www/plugins/es/js/controllers/wot-controllers.js b/www/plugins/es/js/controllers/wot-controllers.js
index 44f9461da..fd7c9fd9d 100644
--- a/www/plugins/es/js/controllers/wot-controllers.js
+++ b/www/plugins/es/js/controllers/wot-controllers.js
@@ -27,6 +27,14 @@ angular.module('cesium.es.wot.controllers', ['cesium.es.services'])
 
         .extendStates(['app.wot_identity', 'app.wot_identity_uid'], {
           points: {
+            'hero': {
+              templateUrl: "plugins/es/templates/wot/view_identity_extend.html",
+              controller: 'ESWotIdentityViewCtrl'
+            },
+            'general': {
+              templateUrl: "plugins/es/templates/wot/view_identity_extend.html",
+              controller: 'ESWotIdentityViewCtrl'
+            },
             'after-general': {
               templateUrl: "plugins/es/templates/wot/view_identity_extend.html",
               controller: 'ESWotIdentityViewCtrl'
@@ -35,6 +43,10 @@ angular.module('cesium.es.wot.controllers', ['cesium.es.services'])
               templateUrl: "plugins/es/templates/wot/view_identity_extend.html",
               controller: 'ESWotIdentityViewCtrl'
             },
+            'after-buttons': {
+              templateUrl: "plugins/es/templates/wot/view_identity_extend.html",
+              controller: 'ESWotIdentityViewCtrl'
+            },
             'buttons-top-fab': {
               templateUrl: "plugins/es/templates/wot/view_identity_extend.html",
               controller: 'ESWotIdentityViewCtrl'
@@ -55,8 +67,6 @@ angular.module('cesium.es.wot.controllers', ['cesium.es.services'])
           }
         })
       ;
-
-
     }
 
   })
@@ -88,9 +98,21 @@ function ESWotLookupExtendController($scope, $controller, $state) {
 }
 
 function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIUtils, Modals, csWallet,
-                                     esModals, esWallet, esInvitation) {
+                                     esHttp, esModals, esWallet, esProfile, esInvitation) {
   'ngInject';
 
+  $scope.options = $scope.options || {};
+  $scope.options.like = $scope.options.like || {
+    kinds: esHttp.constants.like.KINDS,
+    index: 'user',
+    type: 'profile',
+    service: esProfile.like
+  };
+  $scope.smallscreen = angular.isDefined($scope.smallscreen) ? $scope.smallscreen : UIUtils.screen.isSmall();
+
+  // Initialize the super class and extend it.
+  angular.extend(this, $controller('ESLikesCtrl', {$scope: $scope}));
+
   // Initialize the super class and extend it.
   angular.extend(this, $controller('ESExtensionCtrl', {$scope: $scope}));
 
@@ -100,6 +122,7 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIU
 
   $scope.showNewMessageModal = function(confirm) {
 
+    // note: not need to select wallet here, because message modal will do it, if need
     return csWallet.login({minData: true, method: 'default'})
       .then(function() {
         UIUtils.loading.hide();
@@ -122,8 +145,8 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIU
           destPub: $scope.formData.pubkey,
           destUid: $scope.formData.name||$scope.formData.uid
         })
-        .then(function(send) {
-          if (send) UIUtils.toast.show('MESSAGE.INFO.MESSAGE_SENT');
+        .then(function(sent) {
+          if (sent) UIUtils.toast.show('MESSAGE.INFO.MESSAGE_SENT');
         });
       });
   };
@@ -196,6 +219,7 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIU
     var identities;
     return (csWallet.children.count() ? Modals.showSelectWallet({displayBalance: false}) : $q.when(csWallet))
       .then(function(wallet) {
+        if (!wallet) throw 'CANCELLED';
         return wallet.auth({minData: true});
       })
       .then(function(walletData) {
@@ -287,6 +311,15 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIU
       });
   };
 
+  /* -- likes -- */
+
+  // Load likes, when profile loaded
+  $scope.$watch('formData.pubkey', function(pubkey) {
+    if (pubkey) {
+      $scope.loadLikes(pubkey);
+    }
+  });
+
   /* -- Popover -- */
 
   $scope.showCertificationActionsPopover = function(event) {
@@ -305,6 +338,26 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIU
       $scope.certificationActionsPopover.hide();
       $scope.certificationActionsPopover = null;
     }
+    return true;
+  };
+
+  $scope.showActionsPopover = function (event) {
+    UIUtils.popover.show(event, {
+      templateUrl: 'plugins/es/templates/wot/view_popover_actions.html',
+      scope: $scope,
+      autoremove: true,
+      afterShow: function(popover) {
+        $scope.actionsPopover = popover;
+      }
+    });
+  };
+
+  $scope.hideActionsPopover = function() {
+    if ($scope.actionsPopover) {
+      $scope.actionsPopover.hide();
+      $scope.actionsPopover = null;
+    }
+    return true;
   };
 
   if ($scope.extensionPoint === 'buttons-top-fab') {
@@ -323,4 +376,3 @@ function ESWotIdentityViewController($scope, $ionicPopover, $q, $controller, UIU
     $scope.showSuggestCertificationModal();
   }, 1000);*/
 }
-
diff --git a/www/plugins/es/js/entities/notification.js b/www/plugins/es/js/entities/notification.js
index f928c3200..eee62d735 100644
--- a/www/plugins/es/js/entities/notification.js
+++ b/www/plugins/es/js/entities/notification.js
@@ -2,7 +2,8 @@
 function EsNotification(json, markAsReadCallback) {
 
   var messagePrefixes = {
-    'registry': 'EVENT.REGISTRY.'
+    'user': 'EVENT.USER.',
+    'page': 'EVENT.PAGE.'
   };
 
   var that = this;
@@ -49,7 +50,7 @@ function EsNotification(json, markAsReadCallback) {
   // TX
   else if (json.code.startsWith('TX_')) {
     that.avatarIcon = 'ion-card';
-    that.icon = (json.code == 'TX_SENT') ? 'ion-paper-airplane dark' : 'ion-archive balanced';
+    that.icon = (json.code === 'TX_SENT') ? 'ion-paper-airplane dark' : 'ion-archive balanced';
     that.medianTime = that.time;
     pubkeys = json.params.length > 0 ? json.params[0] : null;
     if (pubkeys && pubkeys.indexOf(',') == -1) {
@@ -61,13 +62,13 @@ function EsNotification(json, markAsReadCallback) {
 
   // Certifications
   else if (json.code.startsWith('CERT_')) {
-    that.avatarIcon = (json.code == 'CERT_RECEIVED') ? 'ion-ribbon-b' : 'ion-ribbon-a';
-    that.icon = (json.code == 'CERT_RECEIVED') ? 'ion-ribbon-b balanced' : 'ion-ribbon-a gray';
+    that.avatarIcon = (json.code === 'CERT_RECEIVED') ? 'ion-ribbon-b' : 'ion-ribbon-a';
+    that.icon = (json.code === 'CERT_RECEIVED') ? 'ion-ribbon-b balanced' : 'ion-ribbon-a gray';
     that.pubkey = json.params.length > 0 ? json.params[0] : null;
     that.medianTime = that.time;
     that.state = 'app.wallet_cert';
     that.stateParams = {
-      type: (json.code == 'CERT_RECEIVED') ? 'received' : 'given'
+      type: (json.code === 'CERT_RECEIVED') ? 'received' : 'given'
     };
   }
 
@@ -76,15 +77,62 @@ function EsNotification(json, markAsReadCallback) {
     that.avatarIcon = 'ion-email';
     that.icon = 'ion-email dark';
     pubkeys = json.params.length > 0 ? json.params[0] : null;
-    if (pubkeys && pubkeys.indexOf(',') == -1) {
+    if (pubkeys && pubkeys.indexOf(',') === -1) {
       that.pubkey = pubkeys;
     }
     that.id = json.reference.id; // Do not care about notification ID, because notification screen use message _id
   }
 
+  // user profile record
+  else if (json.reference && json.reference.index === 'user' && json.reference.type === 'profile') {
+    that.pubkey = json.params.length > 0 ? json.params[0] : null;
+    that.state = 'app.wot_identity';
+    that.stateParams = {
+      pubkey: that.pubkey,
+      uid: json.params && json.params[3],
+    };
+    if (json.code.startsWith('LIKE_')) {
+      that.avatarIcon = 'ion-person';
+      that.icon = 'ion-ios-heart positive';
+    }
+    else if (json.code.startsWith('STAR_')) {
+      that.avatarIcon = 'ion-person';
+      that.icon = 'ion-star gray';
+    }
+    else if (json.code.startsWith('FOLLOW_')) {
+      that.avatarIcon = 'ion-person';
+      that.icon = 'ion-ios-people gray';
+    }
+    else if (json.code.startsWith('ABUSE_')) {
+      that.avatarIcon = 'ion-person';
+      that.icon = 'ion-android-warning assertive';
+    }
+    else if (json.code.startsWith('MODERATION_')) {
+      that.state = 'app.wot_identity';
+      that.stateParams = {
+        pubkey: json.reference.id,
+        uid: json.params && json.params[3],
+      };
+      that.avatarIcon = 'ion-alert-circled';
+      that.icon = 'ion-alert-circled energized';
+
+      // If deletion has been asked, change the message
+      var level = json.params && json.params[4] || 0;
+      if (json.code === 'MODERATION_RECEIVED' && level == 5) {
+        that.message = 'EVENT.USER.DELETION_RECEIVED';
+        that.icon = 'ion-trash-a assertive';
+      }
+    }
+    else {
+      that.icon = 'ion-person dark';
+    }
+
+  }
+
   // page record
-  else if (json.reference && json.reference.index == 'page') {
-    that.avatarIcon = 'ion-ios-book';
+  else if (json.reference && json.reference.index === 'page') {
+    that.pubkey = json.params.length > 0 ? json.params[0] : null;
+    that.avatarIcon = 'ion-social-buffer';
     if (json.reference.anchor) {
       that.icon = 'ion-ios-chatbubble-outline dark';
       that.state = 'app.view_page_anchor';
@@ -95,26 +143,47 @@ function EsNotification(json, markAsReadCallback) {
       };
     }
     else {
-      that.icon = 'ion-ios-book dark';
+      that.icon = 'ion-social-buffer dark';
       that.state = 'app.view_page';
       that.stateParams = {
         id: json.reference.id,
-        title: json.params[1]};
+        title: json.params[1]
+      };
+    }
+
+    if (json.code.startsWith('LIKE_')) {
+      that.icon = 'ion-ios-heart positive';
+    }
+    else if (json.code.startsWith('FOLLOW_')) {
+      that.avatarIcon = 'ion-person';
+    }
+    else if (json.code.startsWith('ABUSE_')) {
+      that.icon = 'ion-alert-circled energized';
+    }
+    else if (json.code.startsWith('MODERATION_')) {
+      that.avatarIcon = 'ion-alert-circled';
+      that.icon = 'ion-alert-circled energized';
+
+      // If deletion has been asked, change the message
+      if (json.code === 'MODERATION_RECEIVED' && level == 5) {
+        that.message = 'EVENT.PAGE.DELETION_RECEIVED';
+        that.icon = 'ion-trash-a assertive';
+      }
     }
   }
 
   // info message
-  else if (json.type == 'INFO') {
+  else if (json.type === 'INFO') {
     that.avatarIcon = 'ion-information';
     that.icon = 'ion-information-circled positive';
   }
   // warn message
-  else if (json.type == 'WARN') {
+  else if (json.type === 'WARN') {
     that.avatarIcon = 'ion-alert-circled';
     that.icon = 'ion-alert-circled energized';
   }
   // error message
-  else if (json.type == 'ERROR') {
+  else if (json.type === 'ERROR') {
     that.avatarIcon = 'ion-close';
     that.icon = 'ion-close-circled assertive';
   }
diff --git a/www/plugins/es/js/services.js b/www/plugins/es/js/services.js
index 0bb32bb2e..ff48151ce 100644
--- a/www/plugins/es/js/services.js
+++ b/www/plugins/es/js/services.js
@@ -20,6 +20,7 @@ angular.module('cesium.es.services', [
     'cesium.es.tx.services',
     'cesium.es.geo.services',
     'cesium.es.document.services',
-    'cesium.es.network.services'
+    'cesium.es.network.services',
+    'cesium.es.like.services'
   ])
 ;
diff --git a/www/plugins/es/js/services/comment-services.js b/www/plugins/es/js/services/comment-services.js
index 5044924bb..b5f5c40d4 100644
--- a/www/plugins/es/js/services/comment-services.js
+++ b/www/plugins/es/js/services/comment-services.js
@@ -1,7 +1,7 @@
 angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services',
   'cesium.es.http.services', 'cesium.es.profile.services'])
 
-  .factory('esComment', function($rootScope, $q, UIUtils, BMA, esHttp, csWallet, csWot) {
+  .factory('esComment', function($rootScope, $q, esHttp, csWallet, csWot) {
     'ngInject';
 
     function EsComment(index) {
diff --git a/www/plugins/es/js/services/http-services.js b/www/plugins/es/js/services/http-services.js
index b4a7e6e01..a1fa44289 100644
--- a/www/plugins/es/js/services/http-services.js
+++ b/www/plugins/es/js/services/http-services.js
@@ -213,8 +213,14 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
     that.ws = function(path) {
       return function() {
         var sock = that.cache.wsByPath[path];
-        if (!sock) {
+        if (!sock || sock.isClosed()) {
           sock =  csHttp.ws(that.host, that.port, path, that.useSsl);
+
+          // When close, remove from cache
+          sock.onclose = function() {
+            delete that.cache.wsByPath[path];
+          };
+
           that.cache.wsByPath[path] = sock;
         }
         return sock;
@@ -504,13 +510,13 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
     function postRecord(path, options) {
       options = options || {};
       var postRequest = that.post(path);
-      return function(record, options) {
-        options = options || {};
-        var wallet = options.wallet || (options.walletId && csWallet.children.get(options.walletId)) ||
-          ((!options.pubkey || csWallet.isUserPubkey(options.pubkey)) && csWallet) ||
-          (options.pubkey && csWallet.children.getByPubkey(options.pubkey));
+      return function(record, params) {
+        params = params || {};
+        var wallet = params.wallet || (params.walletId && csWallet.children.get(params.walletId)) ||
+          ((!params.pubkey || csWallet.isUserPubkey(params.pubkey)) && csWallet) ||
+          (params.pubkey && csWallet.children.getByPubkey(params.pubkey));
 
-        var keypair = options.keypair || wallet && wallet.data && wallet.data.keypair;
+        var keypair = params.keypair || wallet && wallet.data && wallet.data.keypair;
 
         if (!keypair && !wallet) {
           throw new Error('Missing wallet or keypair, to sign record');
@@ -518,15 +524,15 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
 
         // Create the POSt request params,
         // but BEFORE, remove protected options
-        delete options.wallet;
-        delete options.walletId;
-        delete options.keypair;
-        var params = angular.copy(options);
+        delete params.wallet;
+        delete params.walletId;
+        delete params.keypair;
+        var params = angular.copy(params);
         params.pubkey = params.pubkey || wallet.data.pubkey;
 
         return (wallet.isAuth() ? $q.when(wallet.data) : wallet.auth({silent: true, minData: true}))
           .then(function() {
-            if (options.creationTime && !record.creationTime) {
+            if (params.creationTime && !record.creationTime) {
               record.creationTime = moment().utc().unix();
             }
             // Always update the time - fix #572
@@ -577,11 +583,14 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
       };
     }
 
-    function countRecord(index, type) {
-      return that.get("/{0}/{1}/_search?size=0".format(index, type))
-        .then(function(res) {
-          return res && res.hits && res.hits.total;
-        });
+    function countRecords(index, type, cacheTime) {
+      var getRequest = that.get("/{0}/{1}/_search?size=0".format(index, type), cacheTime);
+      return function(params) {
+        return getRequest(params)
+            .then(function(res) {
+              return res && res.hits && res.hits.total;
+            });
+      };
     }
 
     function removeRecord(index, type) {
@@ -755,7 +764,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
       record: {
         post: postRecord,
         remove: removeRecord,
-        count : countRecord
+        count : countRecords
       },
       image: {
         fromAttachment: imageFromAttachment,
@@ -785,7 +794,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
 
   service.lightInstance = function(host, port, useSsl, timeout) {
     port = port || 80;
-    useSsl = angular.isDefined(useSsl) ? useSsl : (port == 443);
+    useSsl = angular.isDefined(useSsl) ? useSsl : (+port === 443);
 
     function countHits(path, params) {
       return csHttp.get(host, port, path)(params)
diff --git a/www/plugins/es/js/services/like-services.js b/www/plugins/es/js/services/like-services.js
new file mode 100644
index 000000000..8d0b489c7
--- /dev/null
+++ b/www/plugins/es/js/services/like-services.js
@@ -0,0 +1,153 @@
+angular.module('cesium.es.like.services', ['ngResource', 'cesium.services',
+  'cesium.es.http.services'])
+
+  .factory('esLike', function($q, csWallet, esHttp) {
+    'ngInject';
+
+    function EsLike(index, type) {
+
+      var
+        raw = {
+          getSearch: esHttp.get('/like/record/_search?_source=false&q=:q'),
+          searchBaseQueryString: 'index:{0} AND type:{1} AND id:'.format(index, type),
+          postSearch: esHttp.post("/like/record/_search"),
+          postRecord: esHttp.record.post('/{0}/{1}/:id/_like'.format(index, type)),
+          removeRecord: esHttp.record.remove('like', 'record')
+        },
+        constants = {
+          KINDS: ['LIKE', 'ABUSE']
+        }
+      ;
+
+      function getLikeIds(id, options) {
+        options = options || {};
+        options.kind = options.kind || 'LIKE';
+        var queryString = raw.searchBaseQueryString + id;
+        if (options.kind) queryString += ' AND kind:' + options.kind.toUpperCase();
+        if (options.issuer) queryString += ' AND issuer:' + options.issuer;
+
+        return raw.getSearch({q: queryString})
+          .then(function(res) {
+            return (res && res.hits && res.hits.hits || []).map(function(hit) {
+              return hit._id;
+            });
+          });
+      }
+
+      function addLike(id, options) {
+        options = options || {};
+        options.kind = options.kind && options.kind.toUpperCase() || 'LIKE';
+        if (!csWallet.isLogin()) return $q.reject('Wallet must be login before sending record to ES node');
+        var record = {
+          version: 2,
+          index: index,
+          type: type,
+          id: id,
+          kind: options.kind
+        };
+        if (options.comment) record.comment = options.comment;
+        if (angular.isDefined(options.level)) record.level = options.level;
+
+        return raw.postRecord(record, options);
+      }
+
+      function toggleLike(id, options) {
+          options = options || {};
+          options.kind = options.kind || 'LIKE';
+          var pubkey = options.pubkey || options.wallet && options.wallet.data.pubkey || (csWallet.isLogin() && csWallet.data.pubkey);
+          if (!pubkey) return $q.reject('User not log in!');
+          options.wallet = options.wallet || csWallet.getByPubkey(pubkey);
+          return getLikeIds(id, {kind: options.kind, issuer: pubkey})
+            .then(function(existingLikeIds) {
+              // User already like: so remove it
+              if (existingLikeIds && existingLikeIds.length) {
+                return $q.all(_.map(existingLikeIds, function(likeId) {
+                  return removeLike(likeId, options)
+                }))
+                  // Return the deletion, as a delta
+                  .then(function() {
+                    return -1 * existingLikeIds.length;
+                  });
+              }
+              // User not like, so add it
+              else {
+                return addLike(id, options)
+                  // Return the insertion, as a delta
+                  .then(function() {
+                    return +1;
+                  });
+              }
+          });
+      }
+
+      function removeLike(id, options) {
+        if (!id) throw new Error("Missing 'id' argument");
+        return raw.removeRecord(id, options);
+      }
+
+      function countLike(id, options) {
+        options = options || {};
+        options.kind = options.kind || 'LIKE';
+
+        var request = {
+          query: {
+            bool: {
+              filter: [
+                {term: {index: index}},
+                {term: {type: type}},
+                {term: {id: id}},
+                {term: {kind: options.kind.toUpperCase()}}
+              ]
+            }
+          },
+          size: 0
+        };
+
+        // To known if the user already like, add 'should' on issuers
+        var issuers = options.issuer ? [options.issuer] : options.issuers;
+        if (issuers && issuers.length) {
+          request.query.bool.should = {terms: {issuer: issuers}};
+          request.size = issuers.length;
+          request._source = ["issuer"];
+        }
+
+        return raw.postSearch(request)
+          .then(function(res) {
+            var hits = res && res.hits;
+            var result = {
+              total: hits && hits.total || 0,
+              wasHitByPubkey: {},
+              wasHitCount: 0
+            };
+
+            // Check is issuer is return (because of size=1 and should filter)
+            _.forEach(issuers, function(issuer) {
+              var issuerHitIndex =  hits ? _.findIndex(hits.hits || [], function(hit) {
+                return hit._source.issuer === issuer;
+              }) : -1;
+
+              result.wasHitByPubkey[issuer] = issuerHitIndex !== -1 || false;
+              result.wasHitCount += issuerHitIndex !== -1 ? 1 : 0;
+            })
+
+            return result;
+          })
+      }
+
+      // Expose functions
+      return {
+        index: index,
+        type: type,
+        constants: constants,
+        toggle: toggleLike,
+        add: addLike,
+        remove: removeLike,
+        count: countLike
+      };
+    }
+
+    return {
+      instance: EsLike
+    };
+  })
+;
diff --git a/www/plugins/es/js/services/profile-services.js b/www/plugins/es/js/services/profile-services.js
index c36f3123b..95cbe011e 100644
--- a/www/plugins/es/js/services/profile-services.js
+++ b/www/plugins/es/js/services/profile-services.js
@@ -1,4 +1,4 @@
-angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http.services'])
+angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http.services', 'cesium.es.like.services'])
   .config(function(PluginServiceProvider, csConfig) {
     'ngInject';
 
@@ -10,7 +10,7 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
 
   })
 
-  .factory('esProfile', function($rootScope, $q, esHttp, SocialUtils, csWot, csWallet, csPlatform, esSettings) {
+  .factory('esProfile', function($rootScope, $q, esHttp, SocialUtils, csWot, csWallet, csPlatform, esSettings, esLike) {
     'ngInject';
 
     var
@@ -385,9 +385,10 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
       get: getProfile,
       add: esHttp.record.post('/user/profile', {tagFields: ['title', 'description']}),
       update: esHttp.record.post('/user/profile/:id/_update', {tagFields: ['title', 'description']}),
+      remove: esHttp.record.remove("user","profile"),
       avatar: esHttp.get('/user/profile/:id?_source=avatar'),
       fillAvatars: fillAvatars,
-      remove: esHttp.record.remove("user","profile")
+      like: esLike.instance('user', 'profile')
     };
   })
 ;
diff --git a/www/plugins/es/js/services/registry-services.js b/www/plugins/es/js/services/registry-services.js
index 800da207c..1007bfff1 100644
--- a/www/plugins/es/js/services/registry-services.js
+++ b/www/plugins/es/js/services/registry-services.js
@@ -1,4 +1,4 @@
-angular.module('cesium.es.registry.services', ['ngResource', 'cesium.services', 'cesium.es.http.services'])
+angular.module('cesium.es.registry.services', ['ngResource', 'cesium.services', 'cesium.es.http.services', 'cesium.es.like.services'])
 .config(function(PluginServiceProvider, csConfig) {
   'ngInject';
 
@@ -10,7 +10,7 @@ angular.module('cesium.es.registry.services', ['ngResource', 'cesium.services',
 
 })
 
-.factory('esRegistry', function($rootScope, $q, csPlatform, csSettings, csWallet, csWot, esHttp, esComment, esGeo) {
+.factory('esRegistry', function($rootScope, $q, csPlatform, csSettings, csWallet, csWot, esHttp, esComment, esLike, esGeo) {
   'ngInject';
 
   var
@@ -289,6 +289,7 @@ angular.module('cesium.es.registry.services', ['ngResource', 'cesium.services',
       picture: {
         all: esHttp.get('/page/record/:id?_source=pictures')
       },
+      like: esLike.instance('page', 'record'),
       comment: esComment.instance('page')
     };
   that.currency = {
diff --git a/www/plugins/es/templates/common/popup_report_abuse.html b/www/plugins/es/templates/common/popup_report_abuse.html
new file mode 100644
index 000000000..6e2c9f93f
--- /dev/null
+++ b/www/plugins/es/templates/common/popup_report_abuse.html
@@ -0,0 +1,37 @@
+<form name="abuseForm" ng-submit="">
+    <div class="list" ng-init="setAbuseForm(abuseForm)">
+
+        <!-- reason -->
+        <label class="item item-input"
+               ng-class="{'item-input-error': abuseForm.$submitted && abuseForm.comment.$invalid}">
+            <textarea class="padding" style="background-color: transparent;"
+                      name="comment" type="text" placeholder="{{'COMMON.REPORT_ABUSE.REASON_HELP' | translate}}"
+                      rows="3"
+                      ng-model="abuseData.comment"
+                      ng-minlength="8"
+                      required></textarea>
+        </label>
+        <div class="form-errors"
+             ng-if="abuseForm.$submitted && abuseForm.comment.$error"
+             ng-messages="abuseForm.comment.$error">
+            <div class="form-error" ng-message="required">
+                <span translate="ERROR.FIELD_REQUIRED"></span>
+            </div>
+            <div class="form-error" ng-message="minlength">
+                <span translate="ERROR.FIELD_TOO_SHORT"></span>
+            </div>
+        </div>
+
+        <div class="item item-toggle item-text-wrap dark">
+            <div class="input-label" translate>COMMON.REPORT_ABUSE.ASK_DELETE</div>
+            <label class="toggle toggle-royal">
+                <input type="checkbox" ng-model="abuseData.delete" >
+                <div class="track">
+                    <div class="handle"></div>
+                </div>
+            </label>
+        </div>
+    </div>
+</form>
+
+
diff --git a/www/plugins/es/templates/common/view_likes.html b/www/plugins/es/templates/common/view_likes.html
new file mode 100644
index 000000000..5441724dd
--- /dev/null
+++ b/www/plugins/es/templates/common/view_likes.html
@@ -0,0 +1,25 @@
+
+<div class="likes">
+
+  <!-- likes -->
+  <ng-if ng-if="likeData.likes && likeData.likes.total">
+      <span ng-class="{'gray': !likeData.likes.wasHitCount, 'positive': likeData.likes.wasHitCount}">
+        <a title="{{'COMMON.LIKES_TEXT'|translate: likeData.likes }}"
+           ng-click="!canEdit && toggleLike($event, {kind: 'like'})">
+            {{likeData.likes.total}}
+            <i class="icon ion-heart"></i>
+        </a>
+      </span>
+  </ng-if>
+
+  <!-- abuses -->
+  <ng-if ng-if="likeData.abuses && likeData.abuses.total">
+    <span class="gray" ng-if="likeData.likes && likeData.likes.total">&nbsp;|&nbsp;</span>
+    <a ng-class="{'assertive': likeData.abuses.wasHitCount}"
+       ng-click="!canEdit && reportAbuse($event)"
+       title="{{'COMMON.ABUSES_TEXT'|translate: likeData.abuses }}">
+        {{likeData.abuses.total}}
+        <i class="icon ion-android-warning"></i>
+    </a>
+  </ng-if>
+</div>
diff --git a/www/plugins/es/templates/network/item_content_peer.html b/www/plugins/es/templates/network/item_content_peer.html
index efbad7b39..69de08a63 100644
--- a/www/plugins/es/templates/network/item_content_peer.html
+++ b/www/plugins/es/templates/network/item_content_peer.html
@@ -28,7 +28,7 @@
           </span>
           <span ng-if=":rebind:peer.hasEndpoint('ES_SUBSCRIPTION_API')"
                title="{{'ES_PEER.EMAIL_SUBSCRIPTION_COUNT'|translate: peer.docCount }}">
-            <i class="ion-email"></i> {{:rebind:peer.docCount.emailSubscription || '?'}}
+            <i class="ion-email"></i> {{:rebind:peer.docCount.emailSubscription}}
           </span>
         </div>
         <div ng-if=":rebind:peer.isTor()">
diff --git a/www/plugins/es/templates/notification/view_notifications.html b/www/plugins/es/templates/notification/view_notifications.html
index b77c2b9fe..98226cfec 100644
--- a/www/plugins/es/templates/notification/view_notifications.html
+++ b/www/plugins/es/templates/notification/view_notifications.html
@@ -10,7 +10,7 @@
       </button>
   </ion-nav-buttons>
 
-  <ion-content class="padding no-padding-xs no-padding-sm" scroll="true">
+  <ion-content scroll="true">
     <ion-refresher pulling-text="{{'COMMON.BTN_REFRESH' | translate}}"
                    on-refresh="refresh(true)">
     </ion-refresher>
diff --git a/www/plugins/es/templates/registry/view_popover_actions.html b/www/plugins/es/templates/registry/view_popover_actions.html
index 4a972edb2..3f69517d1 100644
--- a/www/plugins/es/templates/registry/view_popover_actions.html
+++ b/www/plugins/es/templates/registry/view_popover_actions.html
@@ -5,18 +5,29 @@
   <ion-content scroll="false">
     <div class="list item-text-wrap">
 
-      <a class="item item-icon-left ink"
+      <a class="item item-icon-left ink visible-xs visible-sm"
          ng-click="showSharePopover($event)">
         <i class="icon ion-android-share-alt"></i>
         {{'COMMON.BTN_SHARE' | translate}}
       </a>
 
-      <a class="item item-icon-left assertive ink"
+      <a class="item item-icon-left assertive ink visible-xs visible-sm"
          ng-if="canEdit"
          ng-click="delete()">
         <i class="icon ion-trash-a"></i>
         {{'COMMON.BTN_DELETE' | translate}}
       </a>
+
+      <!-- report abuse -->
+      <ng-if ng-if="!canEdit && likeData.abuses">
+        <button class="item item-icon-left ink"
+           ng-disabled="!!likeData.abuses.wasHitCount"
+           ng-class="{'gray': !!likeData.abuses.wasHitCount}"
+           ng-click="hideActionsPopover() && reportAbuse($event)">
+          <i class="icon ion-android-warning"></i>
+          {{'COMMON.BTN_REPORT_ABUSE_DOTS' | translate}}
+        </button>
+      </ng-if>
     </div>
   </ion-content>
 </ion-popover-view>
diff --git a/www/plugins/es/templates/registry/view_record.html b/www/plugins/es/templates/registry/view_record.html
index 74cecad9d..03730c7cb 100644
--- a/www/plugins/es/templates/registry/view_record.html
+++ b/www/plugins/es/templates/registry/view_record.html
@@ -23,15 +23,28 @@
         <i class="avatar cion-page-{{formData.type}}" ng-if="!formData.avatar"></i>
         <i class="avatar" ng-style="{{avatarStyle}}" ng-if="formData.avatar"></i>
         <h3><span class="dark" ng-bind-html="formData.title"></span></h3>
-        <h4>&nbsp;</h4>
+        <h4 class="gray">
+          <span ng-if="formData.city" class="gray hidden-xs hidden-sm">
+            <i class="icon ion-location"></i> <span ng-bind-html="formData.city"></span>
+          </span>
+          <!-- likes -->
+          <small ng-include="'plugins/es/templates/common/view_likes.html'"></small>
+        </h4>
+
       </div>
+
       <h4 class="content dark" ng-if="loading">
         <ion-spinner icon="android"></ion-spinner>
       </h4>
-      <h4 class="content gray hidden-xs hidden-sm" ng-if="formData.city">
-        <i class="icon ion-location"></i>
-        <span ng-bind-html="formData.city"></span>
-      </h4>
+    </div>
+
+    <!-- Top fab button -->
+    <div class="visible-xs visible-sm">
+      <!-- like -->
+      <button class="button button-fab button-fab-top-right button-stable"
+              ng-click="toggleLike($event)">
+        <i class="icon ion-heart"  ng-class="{'gray': !likeData.likes.wasHitCount, 'calm': likeData.likes.wasHitCount}"></i>
+      </button>
     </div>
 
     <div class="row no-padding-xs no-padding-sm">
@@ -66,7 +79,7 @@
                 {{issuer.pubkey|formatPubkey}}
               </span>
             </a>
-            <span >
+            <span>
                 {{formData.time|formatFromNow}}
                 <h4 class="gray hidden-xs">|
                   {{formData.time | formatDate}}
@@ -81,6 +94,14 @@
           <button class="button button-stable button-small-padding icon ion-android-share-alt"
                   ng-click="showSharePopover($event)">
           </button>
+          <!-- Like button -->
+          <button class="button button-stable button-small-padding ink-dark"
+                  ng-if="!canEdit"
+                  title="{{'COMMON.BTN_LIKE' | translate }}"
+                  ng-click="toggleLike($event)">
+            <i class="icon ion-heart" ng-class="{'gray': !likeData.likes.wasHitCount, 'calm': likeData.likes.wasHitCount}"></i>
+          </button>
+
           <button class="button button-calm ink-dark"
                   ng-if="showTransfer"
                   ng-click="showTransferModal({pubkey:formData.pubkey, uid: formData.title})">
@@ -97,6 +118,12 @@
                   ng-click="edit()">
             {{'COMMON.BTN_EDIT' | translate}}
           </button>
+
+          <!-- options -->
+          <button class="button button-stable button-small-padding icon ion-android-more-vertical"
+                  ng-if="!canEdit"
+                  ng-click="showActionsPopover($event)">
+          </button>
         </div>
 
         <div class="item">
diff --git a/www/plugins/es/templates/wallet/view_wallet_extend.html b/www/plugins/es/templates/wallet/view_wallet_extend.html
index 7fe4439c4..ef2db1aa6 100644
--- a/www/plugins/es/templates/wallet/view_wallet_extend.html
+++ b/www/plugins/es/templates/wallet/view_wallet_extend.html
@@ -1,4 +1,12 @@
-<ng-if ng-if=":state:enable">
+<ng-if ng-if=":state:enable && extensionPoint === 'hero'" >
+  <!-- likes -->
+  <small class="light" style="display: inline-block;"
+         ng-include="'plugins/es/templates/common/view_likes.html'"
+         ng-init="canEdit=true"></small>
+</ng-if>
+
+<ng-if ng-if=":state:enable && extensionPoint === 'after-general'">
+
   <!-- profile -->
   <div class="item item-divider item-divider-top-border">
     <span>
@@ -93,7 +101,7 @@
 
     <a class="badge button button-text button-small button-small-padding "
        ng-if="!formData.pages.count"
-       ng-click="showNewPageModal()">
+       ng-click="showNewPageModal($event)">
       <i class="icon ion-edit"></i>
       <span translate>REGISTRY.BTN_NEW</span>
     </a>
diff --git a/www/plugins/es/templates/wot/view_identity_extend.html b/www/plugins/es/templates/wot/view_identity_extend.html
index a5aa6df8a..0c47c382b 100644
--- a/www/plugins/es/templates/wot/view_identity_extend.html
+++ b/www/plugins/es/templates/wot/view_identity_extend.html
@@ -1,9 +1,8 @@
-<!-- Buttons section -->
-<ng-if ng-if=":state:enable && extensionPoint === 'buttons'">
-  <button class="button button-stable button-small-padding icon ion-compose"
-          ng-click="showNewMessageModal()"
-          title="{{'MESSAGE.BTN_WRITE' | translate}}">
-  </button>
+<!-- Hero -->
+<ng-if ng-if=":state:enable && extensionPoint === 'hero'">
+  <!-- likes -->
+  <small class="light" style="display: inline-block;"
+         ng-include="'plugins/es/templates/common/view_likes.html'"></small>
 </ng-if>
 
 <!-- Top fab buttons -->
@@ -16,6 +15,22 @@
   </button>
 </ng-if>
 
+<!-- Buttons section -->
+<ng-if ng-if=":state:enable && extensionPoint === 'buttons'">
+  <button class="button button-stable button-small-padding icon ion-compose"
+          ng-click="showNewMessageModal()"
+          title="{{'MESSAGE.BTN_WRITE' | translate}}">
+  </button>
+</ng-if>
+
+<!-- End of buttons section -->
+<ng-if ng-if=":state:enable && extensionPoint === 'after-buttons'">
+  <!-- options -->
+  <button class="button button-stable button-small-padding icon ion-android-more-vertical"
+          ng-click="showActionsPopover($event)">
+  </button>
+</ng-if>
+
 <!-- General section -->
 <ng-if ng-if=":state:enable && extensionPoint === 'after-general'">
 
diff --git a/www/plugins/es/templates/wot/view_popover_actions.html b/www/plugins/es/templates/wot/view_popover_actions.html
new file mode 100644
index 000000000..c3ba6ad5f
--- /dev/null
+++ b/www/plugins/es/templates/wot/view_popover_actions.html
@@ -0,0 +1,40 @@
+<ion-popover-view class="fit has-header">
+  <ion-header-bar>
+    <h1 class="title"  translate>COMMON.POPOVER_ACTIONS_TITLE</h1>
+  </ion-header-bar>
+  <ion-content scroll="false">
+    <div class="list item-text-wrap">
+
+      <a class="item item-icon-left ink visible-xs visible-sm"
+         ng-click="showSharePopover($event)">
+        <i class="icon ion-android-share-alt"></i>
+        {{'COMMON.BTN_SHARE' | translate}}
+      </a>
+
+      <!--<a class="item item-icon-left assertive ink "
+         ng-if="canEdit"
+         ng-click="delete()">
+        <i class="icon ion-trash-a"></i>
+        {{'COMMON.BTN_DELETE' | translate}}
+      </a>-->
+
+      <!-- Like -->
+      <a class="item item-icon-left ink"
+         ng-if="!canEdit && likeData.likes"
+         ng-click="hideActionsPopover() && toggleLike($event)">
+        <i class="icon " ng-class="{'ion-heart-broken': likeData.likes.wasHit, 'ion-heart': !likeData.likes.wasHit}"></i>
+        {{(likeData.likes.wasHit ? 'COMMON.BTN_LIKE_REMOVE' : 'COMMON.BTN_LIKE' )| translate}}
+      </a>
+
+      <!-- report abuse -->
+      <a class="item item-icon-left ink"
+         ng-if="!canEdit && likeData.abuses"
+         ng-disabled="!!likeData.abuses.wasHitCount"
+         ng-class="{'gray': !!likeData.abuses.wasHitCount}"
+         ng-click="hideActionsPopover() && reportAbuse($event)">
+        <i class="icon ion-android-warning"></i>
+        {{'COMMON.BTN_REPORT_ABUSE_DOTS' | translate}}
+      </a>
+    </div>
+  </ion-content>
+</ion-popover-view>
diff --git a/www/plugins/graph/js/controllers/account-controllers.js b/www/plugins/graph/js/controllers/account-controllers.js
index 5f5b3fa9b..5504af3a9 100644
--- a/www/plugins/graph/js/controllers/account-controllers.js
+++ b/www/plugins/graph/js/controllers/account-controllers.js
@@ -12,7 +12,7 @@ angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.se
           points: {
             'buttons': {
               templateUrl: "plugins/graph/templates/account/view_wallet_tx_extend.html",
-              controller: 'GpExtendCtrl'
+              controller: 'ESExtensionCtrl'
             }
           }
         })
@@ -21,16 +21,7 @@ angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.se
           points: {
             'buttons': {
               templateUrl: "plugins/graph/templates/account/view_wallet_tx_extend.html",
-              controller: 'GpExtendCtrl'
-            }
-          }
-        })
-
-        .extendState('app.wot_identity', {
-          points: {
-            'buttons': {
-              templateUrl: "plugins/graph/templates/account/view_identity_extend.html",
-              controller: 'GpExtendCtrl'
+              controller: 'ESExtensionCtrl'
             }
           }
         })
@@ -39,7 +30,7 @@ angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.se
           points: {
             'buttons': {
               templateUrl: "plugins/graph/templates/account/view_identity_tx_extend.html",
-              controller: 'GpExtendCtrl'
+              controller: 'ESExtensionCtrl'
             }
           }
         })
@@ -81,40 +72,13 @@ angular.module('cesium.graph.account.controllers', ['chart.js', 'cesium.graph.se
     }
   })
 
-  .controller('GpExtendCtrl', GpExtendController)
-
   .controller('GpAccountBalanceCtrl', GpAccountBalanceController)
 
   .controller('GpAccountSumTxCtrl', GpAccountSumTxController)
 
   .controller('GpAccountCertificationCtrl', GpAccountCertificationController)
-
 ;
 
-function GpExtendController($scope, PluginService, esSettings, $state, csWallet) {
-  'ngInject';
-
-  $scope.extensionPoint = PluginService.extensions.points.current.get();
-  $scope.enable = esSettings.isEnable();
-
-  esSettings.api.state.on.changed($scope, function(enable) {
-    $scope.enable = enable;
-  });
-
-  $scope.showIdentityStats = function() {
-    if ($scope.formData && $scope.formData.pubkey) {
-      $state.go('app.wot_identity_stats', {pubkey: $scope.formData.pubkey});
-    }
-  };
-
-  $scope.showWalletStats = function() {
-    if (csWallet.isLogin()) {
-      $state.go('app.wot_identity_stats', {pubkey: csWallet.data.pubkey});
-    }
-  };
-}
-
-
 function GpAccountBalanceController($scope, $controller, $q, $state, $filter, $translate, csWot, gpData, gpColor, csWallet) {
   'ngInject';
 
diff --git a/www/plugins/graph/templates/account/view_identity_extend.html b/www/plugins/graph/templates/account/view_identity_extend.html
deleted file mode 100644
index 65ff216db..000000000
--- a/www/plugins/graph/templates/account/view_identity_extend.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!-- Buttons section -->
-<ng-if ng-if="extensionPoint === 'buttons'">
-
-  <button class="button button-stable button-small-padding icon ion-stats-bars"
-          ng-click="showIdentityStats()"
-          title="{{'GRAPH.ACCOUNT.BTN_SHOW_STATS' | translate}}">
-  </button>
-
-</ng-if>
diff --git a/www/plugins/graph/templates/account/view_identity_tx_extend.html b/www/plugins/graph/templates/account/view_identity_tx_extend.html
index 65ff216db..1c4463581 100644
--- a/www/plugins/graph/templates/account/view_identity_tx_extend.html
+++ b/www/plugins/graph/templates/account/view_identity_tx_extend.html
@@ -1,8 +1,8 @@
 <!-- Buttons section -->
-<ng-if ng-if="extensionPoint === 'buttons'">
+<ng-if ng-if=":state:enable && extensionPoint === 'buttons'">
 
   <button class="button button-stable button-small-padding icon ion-stats-bars"
-          ng-click="showIdentityStats()"
+          ui-sref="app.wot_identity_stats({pubkey: formData.pubkey})"
           title="{{'GRAPH.ACCOUNT.BTN_SHOW_STATS' | translate}}">
   </button>
 
diff --git a/www/plugins/graph/templates/account/view_wallet_tx_extend.html b/www/plugins/graph/templates/account/view_wallet_tx_extend.html
index b8cd1c04a..1c4463581 100644
--- a/www/plugins/graph/templates/account/view_wallet_tx_extend.html
+++ b/www/plugins/graph/templates/account/view_wallet_tx_extend.html
@@ -1,8 +1,8 @@
 <!-- Buttons section -->
-<ng-if ng-if="extensionPoint === 'buttons'">
+<ng-if ng-if=":state:enable && extensionPoint === 'buttons'">
 
   <button class="button button-stable button-small-padding icon ion-stats-bars"
-          ng-click="showWalletStats()"
+          ui-sref="app.wot_identity_stats({pubkey: formData.pubkey})"
           title="{{'GRAPH.ACCOUNT.BTN_SHOW_STATS' | translate}}">
   </button>
 
diff --git a/www/plugins/graph/templates/currency/tab_blocks_extend.html b/www/plugins/graph/templates/currency/tab_blocks_extend.html
index f6c4c5e19..11fa547c8 100644
--- a/www/plugins/graph/templates/currency/tab_blocks_extend.html
+++ b/www/plugins/graph/templates/currency/tab_blocks_extend.html
@@ -1,5 +1,5 @@
 <!-- buttons -->
-<ng-if ng-if="enable && extensionPoint === 'buttons'">
+<ng-if ng-if=":state:enable && extensionPoint === 'buttons'">
   <div class="item item-divider">
     <a class="badge button button-text button-small button-small-padding ink" ui-sref="app.currency.tab_blocks_stats">
       <i class="icon ion-stats-bars"></i>
diff --git a/www/plugins/graph/templates/currency/view_currency_extend.html b/www/plugins/graph/templates/currency/view_currency_extend.html
index 088160024..5b6317b37 100644
--- a/www/plugins/graph/templates/currency/view_currency_extend.html
+++ b/www/plugins/graph/templates/currency/view_currency_extend.html
@@ -1,6 +1,6 @@
 
 <!-- section actual parameters -->
-<ng-if ng-if="enable && extensionPoint === 'parameters-actual'" >
+<ng-if ng-if=":state:enable && extensionPoint === 'parameters-actual'" >
 
   <ng-if ng-if="!smallscreen">
     <div class="item padding-left padding-right no-padding-xs no-padding-sm"
@@ -26,7 +26,7 @@
 </ng-if>
 
 <!-- section Wot -->
-<ng-if ng-if="enable && extensionPoint === 'wot-actual'" >
+<ng-if ng-if=":state:enable && extensionPoint === 'wot-actual'" >
 
   <ng-if ng-if="!smallscreen">
     <div class="item padding-left padding-right no-padding-xs no-padding-sm"
@@ -51,7 +51,7 @@
 </ng-if>
 
 <!-- section Wot -->
-<ng-if ng-if="enable && extensionPoint === 'network-actual'" >
+<ng-if ng-if=":state:enable && extensionPoint === 'network-actual'" >
 
   <div class="item padding-left padding-right no-padding-xs no-padding-sm"
        ng-if="!smallscreen"
diff --git a/www/plugins/graph/templates/network/view_es_peer_extend.html b/www/plugins/graph/templates/network/view_es_peer_extend.html
index e150d7795..71a1891fe 100644
--- a/www/plugins/graph/templates/network/view_es_peer_extend.html
+++ b/www/plugins/graph/templates/network/view_es_peer_extend.html
@@ -1,5 +1,5 @@
 <!-- Buttons section -->
-<ng-if ng-if="enable && extensionPoint === 'general'">
+<ng-if ng-if=":state:enable && extensionPoint === 'general'">
 
   <a class="item item-icon-left item-icon-right item-text-wrap ink"
      ng-if="isReachable"
diff --git a/www/plugins/graph/templates/network/view_peer_extend.html b/www/plugins/graph/templates/network/view_peer_extend.html
index f4f62451b..2e07444c2 100644
--- a/www/plugins/graph/templates/network/view_peer_extend.html
+++ b/www/plugins/graph/templates/network/view_peer_extend.html
@@ -1,5 +1,5 @@
 <!-- Buttons section -->
-<ng-if ng-if="enable && extensionPoint === 'general'">
+<ng-if ng-if=":state:enable && extensionPoint === 'general'">
 
   <a class="item item-icon-left item-icon-right item-text-wrap ink"
     ui-sref="app.view_peer_stats({pubkey: node.pubkey})">
diff --git a/www/templates/blockchain/list_blocks_lg.html b/www/templates/blockchain/list_blocks_lg.html
index 8c9d6763e..8d8d1ca97 100644
--- a/www/templates/blockchain/list_blocks_lg.html
+++ b/www/templates/blockchain/list_blocks_lg.html
@@ -22,7 +22,7 @@
       BLOCKCHAIN.LOOKUP.NO_BLOCK
     </div>
     <!-- blocks -->
-    <ng-repeat ng-repeat="block in search.results"
+    <ng-repeat ng-repeat="block in search.results track by block.id"
                ng-include="!block.empty ? 'templates/blockchain/item_block_lg.html' : 'templates/blockchain/item_block_empty_lg.html'">
     </ng-repeat>
   </ion-list>
diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html
index 18d39744f..0b1e7d4cb 100644
--- a/www/templates/wallet/view_wallet.html
+++ b/www/templates/wallet/view_wallet.html
@@ -63,8 +63,9 @@
           </a>
         </h3>
 
-        <h4 class="assertive">
-          <span ng-if=":rebind:(formData.name || formData.uid) && !formData.isMember" translate>WOT.NOT_MEMBER_PARENTHESIS</span>
+        <h4>
+          <span class="assertive" ng-if=":rebind:(formData.name || formData.uid) && !formData.isMember" translate>WOT.NOT_MEMBER_PARENTHESIS</span>
+          <cs-extension-point name="hero"></cs-extension-point>
         </h4>
       </div>
       <h4 class="content light" ng-if="loading">
diff --git a/www/templates/wot/view_identity.html b/www/templates/wot/view_identity.html
index 055a26520..3a6604907 100644
--- a/www/templates/wot/view_identity.html
+++ b/www/templates/wot/view_identity.html
@@ -3,8 +3,7 @@
   </ion-nav-title>
 
   <ion-content scroll="true"
-               class="refresher-light"
-               ng-class="{'member refresher-positive-900-bg': !loading && formData.isMember, 'refresher-dark-100-bg': loading || !formData.isMember}">
+               ng-class="{'member': !loading && formData.isMember}">
 
     <ion-refresher pulling-text="{{'COMMON.BTN_REFRESH' | translate}}"
                    on-refresh="doUpdate(true)">
@@ -25,9 +24,10 @@
           <h3 class="light" ng-if=":rebind:formData.uid">{{:rebind:formData.uid}}</h3>
           <h3 class="light" ng-if=":rebind:!formData.uid"><i class="ion-key"></i> {{:rebind:formData.pubkey | formatPubkey}}</h3>
         </ng-if>
-        <h4 class="assertive">
-          <ng-if ng-if=":rebind:(formData.name || formData.uid) && !formData.isMember && revoked" translate>WOT.IDENTITY_REVOKED_PARENTHESIS</ng-if>
-          <ng-if ng-if=":rebind:(formData.name || formData.uid) && formData.isMember && revoked" translate>WOT.MEMBER_PENDING_REVOCATION_PARENTHESIS</ng-if>
+        <h4>
+          <ng-if class="assertive" ng-if=":rebind:(formData.name || formData.uid) && !formData.isMember && revoked" translate>WOT.IDENTITY_REVOKED_PARENTHESIS</ng-if>
+          <ng-if class="assertive" ng-if=":rebind:(formData.name || formData.uid) && formData.isMember && revoked" translate>WOT.MEMBER_PENDING_REVOCATION_PARENTHESIS</ng-if>
+          <cs-extension-point name="hero"></cs-extension-point>
         </h4>
 
       </div>
@@ -65,6 +65,8 @@
               ng-click="showTransferModal({pubkey:formData.pubkey, uid: formData.name||formData.uid})">
         {{'COMMON.BTN_SEND_MONEY' | translate}}
       </button>
+
+      <cs-extension-point name="after-buttons"></cs-extension-point>
     </div>
 
     <!-- fab buttons -->
-- 
GitLab