diff --git a/app/config.json b/app/config.json
index 545c4299ca367096acf4896077c0581beb96414d..a64d8caf2897b74f95c63f503672d0b858324a50 100644
--- a/app/config.json
+++ b/app/config.json
@@ -27,6 +27,13 @@
       "es-ES": "license/license_g1-es-ES",
       "eo-EO": "license/license_g1-eo-EO"
     },
+    "feed": {
+      "jsonFeed": {
+        "fr-FR": "feed.json",
+        "en": "feed.json"
+      },
+      "maxContentLength": 650
+    },
     "node": {
       "host": "g1.duniter.org",
       "port": 443
@@ -162,75 +169,6 @@
     }
   },
 
-  "le-sou": {
-    "cacheTimeMs": 300000,
-    "fallbackLanguage": "fr-FR",
-    "defaultLanguage": "fr-FR",
-    "rememberMe": true,
-    "timeout": 30000,
-    "timeWarningExpireMembership": 5184000,
-    "timeWarningExpire": 7776000,
-    "keepAuthIdle": 600,
-    "useLocalStorage": true,
-    "useRelative": false,
-    "expertMode": false,
-    "decimalCount": 2,
-    "httpsMode": false,
-    "shareBaseUrl": "https://g1.le-sou.org",
-    "helptip": {
-      "enable": true,
-      "installDocUrl": {
-        "fr-FR": "https://www.le-sou.org/devenir-noeud/",
-        "en": "https://duniter.org/en/wiki/duniter/install/"
-      }
-    },
-    "license": {
-      "fr-FR": "license/license_g1-fr-FR",
-      "en": "license/license_g1-en"
-    },
-    "node": {
-      "host": "g1.le-sou.org",
-      "port": 443
-    },
-    "fallbackNodes": [
-      {
-        "host": "g1.duniter.org",
-        "port": 443
-      },
-      {
-        "host": "g1.duniter.fr",
-        "port": 443
-      }
-    ],
-    "plugins":{
-      "es": {
-        "enable": true,
-        "askEnable": true,
-        "useRemoteStorage": true,
-        "host": "g1.data.le-sou.org",
-        "port": 443,
-        "fallbackNodes": [
-          {
-            "host": "g1.data.le-sou.org",
-            "port": 443
-          },
-          {
-            "host": "g1.data.duniter.fr",
-            "port": 443
-          }
-        ],
-        "notifications": {
-          "txSent": true,
-          "txReceived": true,
-          "certSent": true,
-          "certReceived": true
-        },
-        "defaultCountry": "France"
-      }
-    }
-  },
-
-
   "g1-test": {
     "cacheTimeMs": 300000,
     "fallbackLanguage": "en",
diff --git a/doc/privacy_policy.md b/doc/privacy_policy.md
index 3c35672c60c7ae695ae799db8db85ba0f0c73da5..3560e3ba1e8e6fa36f99f9ba29f9a231895df1b7 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 c5827b92e6f38ea1ad47545bedfc5aef9a56f960..b4146080f137112ff3660f93001d36ae896747a7 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,74 @@ 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;
+}
+
+/* -- feed (home page) -- */
+.feed {
+  .card {
+    background-color: rgba(0,0,0,0.3);
+    color: lightgrey;
+  }
+
+  h1, h2, h3, h4, h5 {
+    color: white !important;
+  }
+
+  ul {
+    list-style: unset;
+    padding-left: 40px;
+  }
+
+  .feed_header,
+  .card .header {
+    height: 25px;
+    color: grey !important;
+    a  {
+      color: inherit;
+    }
+    .avatar {
+      margin-top: -6px;
+      margin-left: -6px;
+      height: 30px;
+      width: 30px;
+      border: 1px solid $positive;
+    }
+    .avatar-left-padding {
+      padding-left: 30px;
+    }
+  }
+
+  .feed-title,
+  .card .title {
+    margin-top: 5px;
+    font-size: 18pt;
+    a {
+      color: white !important;;
+    }
+  }
+
+  .feed-content,
+  .card .content {
+    text-align: start;
+    color: lightgrey !important;
+  }
+
+}
+
diff --git a/www/api/index.html b/www/api/index.html
index dddac1f7e8889296ba9392e7ef60e18c0a0ce0f3..d3c3c2d29ee94e797e56feab123060eba72a76b2 100644
--- a/www/api/index.html
+++ b/www/api/index.html
@@ -44,22 +44,22 @@
 
 
 <!-- ionic/angularjs js -->
-<script src="../lib/ionic/js/ionic.js"></script>
-<script src="../lib/angular/angular.js"></script>
-<script src="../lib/angular-animate/angular-animate.js"></script>
-<script src="../lib/angular-sanitize/angular-sanitize.js"></script>
-<script src="../lib/angular-ui-router/release/angular-ui-router.js"></script>
-<script src="../lib/ionic/js/ionic-angular.js"></script>
-<script src="../lib/ionic-material/dist/ionic.material.js"></script>
-<script src="../lib/angular-resource/angular-resource.js"></script>
-<script src="../lib/angular-translate/angular-translate.js"></script>
-<script src="../lib/angular-messages/angular-messages.js"></script>
+<script src="../lib/ionic/js/ionic.min.js"></script>
+<script src="../lib/angular/angular.min.js"></script>
+<script src="../lib/angular-animate/angular-animate.min.js"></script>
+<script src="../lib/angular-sanitize/angular-sanitize.min.js"></script>
+<script src="../lib/angular-ui-router/release/angular-ui-router.min.js"></script>
+<script src="../lib/ionic/js/ionic-angular.min.js"></script>
+<script src="../lib/ionic-material/dist/ionic.material.min.js"></script>
+<script src="../lib/angular-resource/angular-resource.min.js"></script>
+<script src="../lib/angular-translate/angular-translate.min.js"></script>
+<script src="../lib/angular-messages/angular-messages.min.js"></script>
 <script src="../lib/angular-api/angular-api.js"></script>
-<script src="../lib/angular-cache/dist/angular-cache.js"></script>
+<script src="../lib/angular-cache/dist/angular-cache.min.js"></script>
 <script src="../lib/angular-screenmatch/dist/angular-screenmatch.min.js"></script>
 <script src="../lib/angular-bind-notifier/dist/angular-bind-notifier.min.js"></script>
-<script src="../lib/angular-file-saver/dist/angular-file-saver.bundle.js"></script>
-<script src="../lib/ng-idle/angular-idle.js"></script>
+<script src="../lib/angular-file-saver/dist/angular-file-saver.bundle.min.js"></script>
+<script src="../lib/ng-idle/angular-idle.min.js"></script>
 <script src="../lib/angular-fullscreen/src/angular-fullscreen.js"></script>
 
   <script src="../js/vendor/base58.js" async></script>
diff --git a/www/css/style.css b/www/css/style.css
index f8bcc3291f97fd076feaeb9af87b52816740a00d..7f6bd56b27160896c2cd308486e67b15260b29c6 100644
--- a/www/css/style.css
+++ b/www/css/style.css
@@ -10,24 +10,14 @@
     line-height: inherit;
 }
 
-#home h1 {
+#home .main-container h1 {
     padding-top: 15px;
     text-align: center;
 }
-
-#home h2 {
-    font-size: 22px;
-    padding-top: 15px;
-    padding-bottom: 15px;
-    text-align: center;
+#home .main-container h4 {
     color: #fff;
 }
-
-#home h4 {
-    color: #fff;
-}
-
-#home h4 a {
+#home .main-container h4 a {
   color: inherit;
 }
 
@@ -145,6 +135,14 @@
     display:none;
 }
 
+.no-margin {
+  margin: 0 !important;
+}
+
+/**********
+   Settings items
+**********/
+
 .settings .item-divider {
     background-color: #f5f5f5;
 }
@@ -164,9 +162,6 @@
 .settings .item .badge {
   top: 22px;
 }
-.no-margin {
-  margin: 0 !important;
-}
 
 /**********
    Network/Peer items
diff --git a/www/feed.json b/www/feed.json
new file mode 100644
index 0000000000000000000000000000000000000000..9c71e6821d986a33f24f1db9cdc5fdf7f698ad69
--- /dev/null
+++ b/www/feed.json
@@ -0,0 +1,35 @@
+{
+  "version": "https://jsonfeed.org/version/1",
+  "user_comment": "This feed allows you to read the posts from this site in any feed reader that supports the JSON Feed format. To add this feed to your reader, copy the following URL — https://jsonfeed.org/feed.json — and add it your reader.",
+  "title": "Actualités",
+  "description": "Cesium News",
+  "home_page_url": "https://forum.monnaie-libre.fr/tag/cesium",
+  "feed_url": "/feed.json",
+  "author": {
+    "name": "Benoit Lavenier",
+    "url": "@BenoitLavenier",
+    "avatar": "http://localhost:9200/user/profile/38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE/_image/avatar.png"
+  },
+  "items": [
+    {
+      "title": "Fermeture prochaine de Cesium-web",
+      "author": {
+        "name": "Moul",
+        "url": "@moul",
+        "avatar": "https://forum.monnaie-libre.fr/user_avatar/forum.monnaie-libre.fr/moul/45/1246_2.png"
+      },
+      "date_published": "2020-02-07T09:42:00+01:00",
+      "id": "https://forum.monnaie-libre.fr/t/fermeture-prochaine-de-cesium-web-1er-mai/9474",
+      "url": "https://forum.monnaie-libre.fr/t/fermeture-prochaine-de-cesium-web-1er-mai/9474",
+      "content_html": "<p>Au <b>1er mai 2020</b>, il ne sera <b>plus possible de vous connecter à Ğ1 via la version web de Cesium</b> (Cesium-web), pour faire des transactions et certifier de nouveaux membres. Cesium-web restera cependant disponible en lecture, pour consulter vos comptes et vos certifications. Cette décision a été prise par les développeurs, en raison de potentielles failles de sécurité, qui pourraient affecter l’évolution de cette monnaie.</p><p>Le présent article a pour objectif : <ul><li>de vous présenter les causes et conséquences de cette décision,</li>\n<li>de détailler les procédures d’installation sur les différentes plateformes.</li></ul></p><p><h4>Pourquoi fermer Cesium-Web ?</h2></p><h5>Rappels concernant la Ğ1</h5><p>La monnaie libre Ğ1 est une monnaie cryptographique, c’est-à-dire qu’elle utilise des procédés de chiffrement pour garantir la légitimité de chaque transaction.</p>\n"
+    },
+    {
+      "title": "Un site officiel simple pour Cesium !",
+      "date_published": "2019-02-21T22:55:00+01:00",
+      "id": "https://cesium.app",
+      "url": "https://cesium.app",
+      "image": "https://cesium.app/i18n/fr_FR/contents/accueil/Cesium-G1-maquette.png",
+      "content_html": "<p><ul><li>Vous aimeriez vous inscrire à la Ğ1 (<i>June</i>), mais vous ne savez pas comment vous y prendre ?</li><li>Vous voulez savoir <b>comment utiliser</b> Cesium depuis votre smartphone ou ordinateur ?</li><li>On vous a dit que Cesium faisait aussi le café (en plus de gérer votre portefeuille en monnaie libre), mais vous n'en êtes pas sûr ? :-)</li></ul></p><p>Tutos, téléchargements, documentation: tout est désormais accessible depuis le <b>site officiel</b> : <b>https://cesium.app</b> !</p>"
+    }
+  ]
+}
diff --git a/www/i18n/locale-en-GB.json b/www/i18n/locale-en-GB.json
index 60c0deb561146e3e342e5bb16326b4351fc7615a..a9e6b89b33ac6de9b103346939d170c58a90c1f4 100644
--- a/www/i18n/locale-en-GB.json
+++ b/www/i18n/locale-en-GB.json
@@ -120,14 +120,17 @@
   "HOME": {
     "TITLE": "Cesium",
     "WELCOME": "Welcome to the Cesium Application!",
-    "MESSAGE": "Follow your {{currency|abbreviate}} wallets easily",
+    "MESSAGE": "Receive and send libre currency {{currency|abbreviate}}",
     "BTN_CURRENCY": "Explore currency",
     "BTN_ABOUT": "about",
     "BTN_HELP": "Help",
     "REPORT_ISSUE": "Report an issue",
     "NOT_YOUR_ACCOUNT_QUESTION" : "You do not own the account <b><i class=\"ion-key\"></i> {{pubkey|formatPubkey}}</b>?",
     "BTN_CHANGE_ACCOUNT": "Disconnect this account",
-    "CONNECTION_ERROR": "Peer <b>{{server}}</b> unreachable or invalid address.<br/><br/>Check your Internet connection, or change node <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in the settings</a>."
+    "CONNECTION_ERROR": "Peer <b>{{server}}</b> unreachable or invalid address.<br/><br/>Check your Internet connection, or change node <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in the settings</a>.",
+    "SHOW_ALL_FEED": "Show all",
+    "READ_MORE": "Read more",
+    "FEED_SOURCE": "Source"
   },
   "SETTINGS": {
     "TITLE": "Settings",
@@ -694,7 +697,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 725db3dbffde55625fbb51841eaffb925306f927..bfca958813ce9f3fa8435cb83286967bcf25cc6b 100644
--- a/www/i18n/locale-en.json
+++ b/www/i18n/locale-en.json
@@ -120,14 +120,17 @@
   "HOME": {
     "TITLE": "Cesium",
     "WELCOME": "Welcome to the Cesium Application!",
-    "MESSAGE": "Follow your {{currency|abbreviate}} wallets easily",
+    "MESSAGE": "Receive and send libre currency {{currency|abbreviate}}",
     "BTN_CURRENCY": "Explore currency",
     "BTN_ABOUT": "about",
     "BTN_HELP": "Help",
     "REPORT_ISSUE": "Report an issue",
     "NOT_YOUR_ACCOUNT_QUESTION" : "You do not own the account <b><i class=\"ion-key\"></i> {{pubkey|formatPubkey}}</b>?",
     "BTN_CHANGE_ACCOUNT": "Disconnect this account",
-    "CONNECTION_ERROR": "Peer <b>{{server}}</b> unreachable or invalid address.<br/><br/>Check your Internet connection, or change node <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in the settings</a>."
+    "CONNECTION_ERROR": "Peer <b>{{server}}</b> unreachable or invalid address.<br/><br/>Check your Internet connection, or change node <a class=\"positive\" ng-click=\"doQuickFix('settings')\">in the settings</a>.",
+    "SHOW_ALL_FEED": "Show all",
+    "READ_MORE": "Read more",
+    "FEED_SOURCE": "Source"
   },
   "SETTINGS": {
     "TITLE": "Settings",
@@ -694,7 +697,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 e27d28d7e861904aaa2dff8b0effe9b29a623025..e8287d68ea88fb620ae320f37ff57c7b82521a71 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 b34ee808529324ebdaa0bc8ef42caa6248bd825d..aa1c119bb06dea4e1b7064e0884a896886c919f8 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 1ba9781ba5796ddd1e8aa0cbc1a01da7b08a2002..1f472e5831ef5b8aaab68c70bb7d7d6af2d51292 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -49,7 +49,7 @@
     "NO_ACCOUNT_QUESTION": "Pas encore de compte ? Créez-en un gratuitement !",
     "SEARCH_NO_RESULT": "Aucun résultat trouvé",
     "LOADING": "Veuillez patienter...",
-    "LOADING_WAIT": "Veuillez patienter...<br/><small>(Attente de disponibilité du noeud)</small>",
+    "LOADING_WAIT": "Veuillez patienter...<br/><small>(Cesium interroge le nœud Duniter)</small>",
     "SEARCHING": "Recherche en cours...",
     "FROM": "De",
     "TO": "À",
@@ -127,7 +127,10 @@
     "REPORT_ISSUE": "anomalie",
     "NOT_YOUR_ACCOUNT_QUESTION" : "Vous n'êtes pas propriétaire du compte <b><i class=\"ion-key\"></i> {{pubkey|formatPubkey}}</b> ?",
     "BTN_CHANGE_ACCOUNT": "Déconnecter ce compte",
-    "CONNECTION_ERROR": "Nœud <b>{{server}}</b> injoignable ou adresse invalide.<br/><br/>Vérifiez votre connexion Internet, ou changer de nœud <a class=\"positive\" ng-click=\"doQuickFix('settings')\">dans les paramètres</a>."
+    "CONNECTION_ERROR": "Nœud <b>{{server}}</b> injoignable ou adresse invalide.<br/><br/>Vérifiez votre connexion Internet, ou changer de nœud <a class=\"positive\" ng-click=\"doQuickFix('settings')\">dans les paramètres</a>.",
+    "SHOW_ALL_FEED": "Voir tout",
+    "READ_MORE": "Lire la suite",
+    "FEED_SOURCE": "Source"
   },
   "SETTINGS": {
     "TITLE": "Paramètres",
@@ -692,9 +695,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 4d3f3407d0b4b475b004a7a50c58d6c2ddef8ff7..9baa8dacb71edea3db2491c2ad08413e6c0e0ddf 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 1384445ccedc94524f50e41d838e8e3086146cec..9949b11df3c29b2be72e21e54e3d14a93a6e0dc7 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 d742b7bd3be4718e4c75f6cc93113ac917d2ec92..fe43c3f752a3952427c05955b6d60f527ac2dd05 100644
--- a/www/index.html
+++ b/www/index.html
@@ -74,30 +74,30 @@
     <script src="js/vendor/numeral.eo.js"></script>
     <script src="lib/socket.io-client/dist/socket.io.min.js"></script>
     <script src="lib/underscore/underscore-min.js"></script>
-    <script src="lib/qrcode.js/qrcode.js"></script>
+    <script src="lib/qrcode.js/qrcode.min.js"></script>
     <script src="lib/aes-js/index.js"></script>
     <script src="lib/chart.js/dist/Chart.min.js"></script>
 
     <!-- ionic/angular js -->
-    <script src="lib/ionic/js/ionic.js"></script>
-    <script src="lib/angular/angular.js"></script>
-    <script src="lib/angular-animate/angular-animate.js"></script>
-    <script src="lib/angular-sanitize/angular-sanitize.js"></script>
-    <script src="lib/angular-ui-router/release/angular-ui-router.js"></script>
-    <script src="lib/ionic/js/ionic-angular.js"></script>
-    <script src="lib/ionic-material/dist/ionic.material.js"></script>
-    <script src="lib/angular-resource/angular-resource.js"></script>
-    <script src="lib/angular-translate/angular-translate.js"></script>
-    <script src="lib/angular-messages/angular-messages.js"></script>
+    <script src="lib/ionic/js/ionic.min.js"></script>
+    <script src="lib/angular/angular.min.js"></script>
+    <script src="lib/angular-animate/angular-animate.min.js"></script>
+    <script src="lib/angular-sanitize/angular-sanitize.min.js"></script>
+    <script src="lib/angular-ui-router/release/angular-ui-router.min.js"></script>
+    <script src="lib/ionic/js/ionic-angular.min.js"></script>
+    <script src="lib/ionic-material/dist/ionic.material.min.js"></script>
+    <script src="lib/angular-resource/angular-resource.min.js"></script>
+    <script src="lib/angular-translate/angular-translate.min.js"></script>
+    <script src="lib/angular-messages/angular-messages.min.js"></script>
     <script src="lib/angular-api/angular-api.js"></script>
-    <script src="lib/angular-cache/dist/angular-cache.js"></script>
+    <script src="lib/angular-cache/dist/angular-cache.min.js"></script>
     <script src="lib/angular-screenmatch/dist/angular-screenmatch.min.js"></script>
     <script src="lib/angular-bind-notifier/dist/angular-bind-notifier.min.js"></script>
     <script src="lib/angular-image-crop/image-crop.js"></script>
-    <script src="lib/angular-file-saver/dist/angular-file-saver.bundle.js"></script>
-    <script src="lib/ng-idle/angular-idle.js"></script>
-    <script src="lib/angular-simple-logger/dist/angular-simple-logger.light.js"></script>
-    <script src="lib/ui-leaflet/dist/ui-leaflet.js"></script>
+    <script src="lib/angular-file-saver/dist/angular-file-saver.bundle.min.js"></script>
+    <script src="lib/ng-idle/angular-idle.min.js"></script>
+    <script src="lib/angular-simple-logger/dist/angular-simple-logger.light.min.js"></script>
+    <script src="lib/ui-leaflet/dist/ui-leaflet.min.no-header.js"></script>
     <script src="lib/ion-digit-keyboard/dist/ion-digit-keyboard.min.js"></script>
     <script src="lib/angular-chart.js/dist/angular-chart.min.js"></script>
     <script src="lib/angular-fullscreen/src/angular-fullscreen.js"></script>
@@ -118,7 +118,7 @@
     <!--removeIf(no-plugin)-->
     <script src="lib/leaflet/dist/leaflet.js"></script>
     <script src="lib/Leaflet.awesome-markers/dist/leaflet.awesome-markers.min.js"></script>
-    <script src="lib/leaflet-search/dist/leaflet-search.src.js"></script>
+    <script src="lib/leaflet-search/dist/leaflet-search.min.js"></script>
     <script src="lib/Leaflet.EasyButton/src/easy-button.js"></script>
     <script src="lib/leaflet.loading/src/Control.Loading.js"></script>
     <script src="lib/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
@@ -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>
@@ -227,6 +228,7 @@
     <script src="dist/dist_js/plugins/es/js/controllers/invitation-controllers.js"></script>
     <script src="dist/dist_js/plugins/es/js/controllers/subscription-controllers.js"></script>
     <script src="dist/dist_js/plugins/es/js/controllers/document-controllers.js"></script>
+    <script src="dist/dist_js/plugins/es/js/controllers/like-controllers.js"></script>
 
     <!-- Graph plugin -->
     <script src="dist/dist_js/plugins/graph/js/plugin.js"></script>
diff --git a/www/js/config.js b/www/js/config.js
index a543dbff15a6721818d5189dcb4bf5f8786e9c80..7cc4a683878e616f2e3e07e0ef930e2c2ec1961a 100644
--- a/www/js/config.js
+++ b/www/js/config.js
@@ -33,7 +33,15 @@ angular.module("cesium.config", [])
 	"license": {
 		"en": "license/license_g1-en",
 		"fr-FR": "license/license_g1-fr-FR",
-		"es-ES": "license/license_g1-es-ES"
+		"es-ES": "license/license_g1-es-ES",
+		"eo-EO": "license/license_g1-eo-EO"
+	},
+	"feed": {
+		"jsonFeed": {
+			"fr-FR": "feed.json",
+			"en": "feed.json"
+		},
+		"maxContentLength": 650
 	},
 	"node": {
 		"host": "g1.duniter.org",
@@ -90,7 +98,7 @@ angular.module("cesium.config", [])
 		}
 	},
 	"version": "1.5.3",
-	"build": "2020-01-27T13:59:40.149Z",
+	"build": "2020-03-04T18:00:55.476Z",
 	"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
 })
 
diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js
index 806047b979bf7c3ac36441d566a1b4fc4efa3019..f0e6a97cc99389bd9fbc6a3d4e09c02d4281dbab 100644
--- a/www/js/controllers/app-controllers.js
+++ b/www/js/controllers/app-controllers.js
@@ -443,6 +443,15 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     $event.stopPropagation();
     $event.preventDefault();
 
+    // Read URL like '@UID' (Used by home page, in feed's author url)
+    if (uri && uri.startsWith('@')) {
+      var uid = uri.substr(1);
+      if (BMA.regexp.USER_ID.test(uid)) {
+        $state.go('app.wot_identity_uid', {uid: uid});
+        return false;
+      }
+    }
+
     options = options || {};
 
     // If unable to open, just copy value
@@ -502,11 +511,12 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
 }
 
 
-function HomeController($scope, $state, $timeout, $ionicHistory, $translate, UIUtils, csPlatform, csCurrency, csSettings) {
+function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $http, UIUtils, csConfig, csPlatform, csCurrency, csSettings) {
   'ngInject';
 
   $scope.loading = true;
   $scope.locales = angular.copy(csSettings.locales);
+  $scope.smallscreen = UIUtils.screen.isSmall();
 
   $scope.enter = function(e, state) {
     if (ionic.Platform.isIOS()) {
@@ -533,10 +543,11 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, UIU
         notify: false});
     }
     else {
-      // Start platform
+      // Wait platform to be ready
       csPlatform.ready()
         .then(function() {
           $scope.loading = false;
+          $scope.loadFeeds();
         })
         .catch(function(err) {
           $scope.node =  csCurrency.data.node;
@@ -554,6 +565,55 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, UIU
     $timeout($scope.enter, 200);
   };
 
+  $scope.loadFeeds = function() {
+    var feedUrl = csSettings.getFeedUrl();
+    if (!feedUrl || typeof feedUrl !== 'string') return; // Skip
+
+    var maxContentLength = (csConfig.feed && csConfig.feed.maxContentLength) || 650;
+
+    var now = Date.now();
+    console.debug("[home] Loading feeds from {0}...".format(feedUrl));
+
+    $http.get(feedUrl, {responseType: 'json'})
+      .success(function(feed) {
+        console.debug('[home] Feeds loaded in {0}ms'.format(Date.now()-now));
+        if (!feed || !feed.items || !feed.items.length) return // skip if empty
+
+        feed.items = feed.items.reduce(function(res, item) {
+          if (!item || (!item.title && !item.content_text && !item.content_html)) return res; // Skip
+
+          // Convert UTC time
+          if (item.date_published) {
+            item.time = moment.utc(item.date_published).unix();
+          }
+          // Convert content to HTML
+          if (item.content_html) {
+            item.content = item.content_html;
+          }
+          else {
+            item.content = (item.content_text||'').replace(/\n/g, '<br/>');
+          }
+
+          // Trunc content, if need
+          if (maxContentLength !== -1 && item.content && item.content.length > maxContentLength) {
+            var endIndex = Math.max(item.content.lastIndexOf(" ", maxContentLength), item.content.lastIndexOf("<", maxContentLength));
+            item.content = item.content.substr(0, endIndex) + ' (...)';
+            item.truncated = true;
+          }
+
+          // If author is missing, copy the main author
+          item.author = item.author || feed.author;
+
+          return res.concat(item);
+        }, []);
+        $scope.feed = feed;
+      })
+      .error(function(data, status) {
+        console.error('[home] Failed to load feeds.');
+        $scope.feed = null;
+      });
+  }
+
   /**
    * Catch click for quick fix
    * @param event
@@ -571,6 +631,7 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, UIU
     $translate.use(langKey);
     $scope.hideLocalesPopover();
     csSettings.data.locale = _.findWhere($scope.locales, {id: langKey});
+    $scope.loadFeeds();
   };
 
   /* -- show/hide locales popup -- */
diff --git a/www/js/controllers/blockchain-controllers.js b/www/js/controllers/blockchain-controllers.js
index 65f3073fd0a81b787dabe4a1c8c63ab8b5cf6889..7db5956958accf0210b84c5025f18f460f1a5652 100644
--- a/www/js/controllers/blockchain-controllers.js
+++ b/www/js/controllers/blockchain-controllers.js
@@ -135,7 +135,7 @@ function BlockLookupController($scope, $timeout, $focus, $filter, $state, $ancho
           useTor: useTor
         };
         var serverParts = state.stateParams.server.split(':');
-        if (serverParts.length == 2) {
+        if (serverParts.length === 2) {
           node.host = serverParts[0];
           node.port = serverParts[1];
         }
diff --git a/www/js/controllers/network-controllers.js b/www/js/controllers/network-controllers.js
index 6db2363158674dbcca69313ff26745139e00674e..35ca9fda82b47b71ede1c54bdfd3d6e91de225db 100644
--- a/www/js/controllers/network-controllers.js
+++ b/www/js/controllers/network-controllers.js
@@ -589,7 +589,7 @@ function PeerViewController($scope, $q, $window, $state, UIUtils, csWot, BMA) {
             var peer = new Peer(json);
             return (peer.getEndpoints('BASIC_MERKLED_API') || []).reduce(function(res, ep) {
               var bma = BMA.node.parseEndPoint(ep);
-              if((bma.dns == node.host || bma.ipv4 == node.host || bma.ipv6 == node.host) && (
+              if((bma.dns === node.host || bma.ipv4 === node.host || bma.ipv6 === node.host) && (
                 bma.port == node.port)) {
                 peer.bma = bma;
                 return res.concat(peer);
diff --git a/www/js/controllers/wallet-controllers.js b/www/js/controllers/wallet-controllers.js
index a7710f06afa7e91a814611d6a4de629d29d5584b..e14831fa9e5b263178720ee7eba9e59ace634b6f 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 2e95963f74a1644840268045ae74858a5d77a452..f7fc54dd4a81d7f957983700b4d16ab502daa108 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 3995781211e3fa885a28c636600eb0667214ec99..e3958e0b6164bb7eaaca57ed8b17a3783379730f 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 5ce668f05b591f860b7ff3e02f06f0b88188b481..5bd2decd1513c85025a5ab60445c7e2b78cc2ad3 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,9 +415,9 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
         }
       },
       wot: {
-        lookup: get('/wot/lookup/:search', csHttp.cache.MEDIUM),
-        certifiedBy: get('/wot/certified-by/:pubkey'),
-        certifiersOf: get('/wot/certifiers-of/:pubkey'),
+        lookup: get('/wot/lookup/:search'),
+        certifiedBy: get('/wot/certified-by/:pubkey', csHttp.cache.SHORT),
+        certifiersOf: get('/wot/certifiers-of/:pubkey', csHttp.cache.SHORT),
         member: {
           all: get('/wot/members', csHttp.cache.LONG),
           pending: get('/wot/pending', csHttp.cache.SHORT)
diff --git a/www/js/services/currency-services.js b/www/js/services/currency-services.js
index 828754ca38c7e8b18c9a4ce2ff1f8927d690fc49..a65054d00054a83f2e69969b345bb2c1fb415887 100644
--- a/www/js/services/currency-services.js
+++ b/www/js/services/currency-services.js
@@ -4,358 +4,345 @@ angular.module('cesium.currency.services', ['ngApi', 'cesium.bma.services'])
 .factory('csCurrency', function($rootScope, $q, $timeout, BMA, Api, csSettings) {
   'ngInject';
 
-  var defaultBMA = BMA;
+  var
+    constants = {
+      // Avoid to many call on well known currencies
+      WELL_KNOWN_CURRENCIES: {
+        g1: {
+          firstBlockTime: 1488987127,
+          medianTimeOffset: 3600
+        }
+      }
+    },
 
-  function CsCurrency(id, BMA) {
+    data = {},
+    started = false,
+    startPromise,
+    listeners,
+    api = new Api(this, "csCurrency");
 
-    BMA = BMA || defaultBMA;
+  function powBase(amount, base) {
+    return base <= 0 ? amount : amount * Math.pow(10, base);
+  }
 
-    var
-      constants = {
-        // Avoid to many call on well known currencies
-        WELL_KNOWN_CURRENCIES: {
-          g1: {
-            firstBlockTime: 1488987127,
-            medianTimeOffset: 3600
-          }
-        }
-      },
+  function resetData() {
+    data.name = null;
+    data.parameters = null;
+    data.firstBlockTime = null;
+    data.membersCount = null;
+    data.cache = {};
+    data.node = BMA;
+    data.currentUD = null;
+    data.medianTimeOffset = 0;
+    started = false;
+    startPromise = undefined;
+    api.data.raise.reset(data);
+  }
 
-      data = {},
-      started = false,
-      startPromise,
-      listeners,
-      api = new Api(this, "csCurrency-" + id);
+  function loadData() {
 
-    function powBase(amount, base) {
-      return base <= 0 ? amount : amount * Math.pow(10, base);
-    }
+    // Load currency from default node
+    return $q.all([
 
-    function resetData() {
-      data.name = null;
-      data.parameters = null;
-      data.firstBlockTime = null;
-      data.membersCount = null;
-      data.cache = {};
-      data.node = BMA;
-      data.currentUD = null;
-      data.medianTimeOffset = 0;
-      started = false;
-      startPromise = undefined;
-      api.data.raise.reset(data);
-    }
+      // get parameters
+      loadParameters()
+        .then(function(parameters) {
+          // load first block info
+          return loadFirstBlock(parameters.currency);
+        }),
 
-    function loadData() {
+      // get current UD
+      loadCurrentUD(),
 
-      // Load currency from default node
-      return $q.all([
+      // call extensions
+      api.data.raisePromise.load(data)
+    ])
+    .catch(function(err) {
+      resetData();
+      throw err;
+    });
+  }
 
-        // get parameters
-        loadParameters()
-          .then(function(parameters) {
-            // load first block info
-            return loadFirstBlock(parameters.currency);
-          }),
+  function loadParameters() {
+    return BMA.blockchain.parameters()
+      .then(function(res){
+        data.name = res.currency;
+        data.parameters = res;
+        data.medianTimeOffset = res.avgGenTime * res.medianTimeBlocks / 2;
+        return res;
+      });
+  }
 
-        // get current UD
-        loadCurrentUD(),
+  function loadFirstBlock(currencyName) {
+    // Well known currencies
+    if (constants.WELL_KNOWN_CURRENCIES[currencyName]){
+      angular.merge(data, constants.WELL_KNOWN_CURRENCIES[currencyName]);
+      return $q.when();
+    }
 
-        // call extensions
-        api.data.raisePromise.load(data)
-      ])
+    return BMA.blockchain.block({block:0})
+      .then(function(json) {
+        // Need by graph plugin
+        data.firstBlockTime = json.medianTime;
+      })
       .catch(function(err) {
-        resetData();
+        // Special case, when currency not started yet
+        if (err && err.ucode === BMA.errorCodes.BLOCK_NOT_FOUND) {
+          data.firstBlockTime = 0;
+          data.initPhase = true;
+          console.warn('[currency] Blockchain not launched: Enable init phase mode');
+          return;
+        }
         throw err;
       });
-    }
-
-    function loadParameters() {
-      return BMA.blockchain.parameters()
-        .then(function(res){
-          data.name = res.currency;
-          data.parameters = res;
-          data.medianTimeOffset = res.avgGenTime * res.medianTimeBlocks / 2;
-          return res;
-        });
-    }
+  }
 
-    function loadFirstBlock(currencyName) {
-      // Well known currencies
-      if (constants.WELL_KNOWN_CURRENCIES[currencyName]){
-        angular.merge(data, constants.WELL_KNOWN_CURRENCIES[currencyName]);
-        return $q.when();
-      }
+  function loadCurrentUD() {
+    return BMA.blockchain.stats.ud()
+      .then(function(res) {
+        // Special case for currency init
+        if (!res.result.blocks.length) {
+          data.currentUD = data.parameters ? data.parameters.ud0 : -1;
+          return data.currentUD ;
+        }
+        return _safeLoadCurrentUD(res, res.result.blocks.length - 1);
+      })
+      .catch(function(err) {
+        data.currentUD = null;
+        throw err;
+      });
+  }
 
-      return BMA.blockchain.block({block:0})
-        .then(function(json) {
-          // Need by graph plugin
-          data.firstBlockTime = json.medianTime;
-        })
-        .catch(function(err) {
-          // Special case, when currency not started yet
-          if (err && err.ucode === BMA.errorCodes.BLOCK_NOT_FOUND) {
-            data.firstBlockTime = 0;
-            data.initPhase = true;
-            console.warn('[currency] Blockchain not launched: Enable init phase mode');
-            return;
-          }
-          throw err;
-        });
+  /**
+   * Load the last UD, with a workaround if last block with UD is not found in the node
+   * @param res
+   * @param blockIndex
+   * @returns {*}
+   * @private
+   */
+  function _safeLoadCurrentUD(res, blockIndex) {
+    // Special case for currency init
+    if (!res.result.blocks.length || blockIndex < 0) {
+      data.currentUD = data.parameters ? data.parameters.ud0 : -1;
+      return data.currentUD ;
     }
-
-    function loadCurrentUD() {
-      return BMA.blockchain.stats.ud()
-        .then(function(res) {
-          // Special case for currency init
-          if (!res.result.blocks.length) {
-            data.currentUD = data.parameters ? data.parameters.ud0 : -1;
-            return data.currentUD ;
-          }
-          return _safeLoadCurrentUD(res, res.result.blocks.length - 1);
+    else {
+      var lastBlockWithUD = res.result.blocks[blockIndex];
+      return BMA.blockchain.block({ block: lastBlockWithUD })
+        .then(function(block){
+          data.currentUD = powBase(block.dividend, block.unitbase);
+          return data.currentUD;
         })
         .catch(function(err) {
+          console.error("[currency] Unable to load last block with UD, with number {0}".format(lastBlockWithUD));
+          if (blockIndex > 0) {
+            console.error("[currency] Retrying to load UD from a previous block...");
+            return _safeLoadCurrentUD(res, blockIndex-1);
+          }
           data.currentUD = null;
           throw err;
         });
     }
+  }
 
-    /**
-     * Load the last UD, with a workaround if last block with UD is not found in the node
-     * @param res
-     * @param blockIndex
-     * @returns {*}
-     * @private
-     */
-    function _safeLoadCurrentUD(res, blockIndex) {
-      // Special case for currency init
-      if (!res.result.blocks.length || blockIndex < 0) {
-        data.currentUD = data.parameters ? data.parameters.ud0 : -1;
-        return data.currentUD ;
-      }
-      else {
-        var lastBlockWithUD = res.result.blocks[blockIndex];
-        return BMA.blockchain.block({ block: lastBlockWithUD })
-          .then(function(block){
-            data.currentUD = powBase(block.dividend, block.unitbase);
-            return data.currentUD;
-          })
-          .catch(function(err) {
-            console.error("[currency] Unable to load last block with UD, with number {0}".format(lastBlockWithUD));
-            if (blockIndex > 0) {
-              console.error("[currency] Retrying to load UD from a previous block...");
-              return _safeLoadCurrentUD(res, blockIndex-1);
-            }
-            data.currentUD = null;
-            throw err;
-          });
-      }
+  function getData() {
+
+    if (started) { // load only once
+      return $q.when(data);
     }
 
-    function getData() {
+    // Previous load not finished: return the existing promise - fix #452
+    return startPromise || start();
+  }
 
+  function getDataField(field) {
+    return function() {
       if (started) { // load only once
-        return $q.when(data);
+        return $q.when(data[field]);
       }
 
       // Previous load not finished: return the existing promise - fix #452
-      return startPromise || start();
-    }
-
-    function getDataField(field) {
-      return function() {
-        if (started) { // load only once
-          return $q.when(data[field]);
-        }
-
-        // Previous load not finished: return the existing promise - fix #452
-        return startPromise || start() // load only once
-            .then(function(){
-              return data[field];
-            });
-      };
-    }
-
-    function onBlock(json) {
-      var block = new Block(json);
-      block.cleanData(); // Remove unused content (arrays...) and keep items count
+      return startPromise || start() // load only once
+          .then(function(){
+            return data[field];
+          });
+    };
+  }
 
-      //console.debug('[currency] Received new block', block);
-      console.debug('[currency] Received new block {' + block.number + '-' + block.hash + '}');
+  function onBlock(json) {
+    var block = new Block(json);
+    block.cleanData(); // Remove unused content (arrays...) and keep items count
 
-      data.currentBlock = block;
-      data.currentBlock.receivedAt = moment().utc().unix();
+    //console.debug('[currency] Received new block', block);
+    console.debug('[currency] Received new block {' + block.number + '-' + block.hash + '}');
 
-      data.medianTime = block.medianTime;
-      data.membersCount = block.membersCount;
+    data.currentBlock = block;
+    data.currentBlock.receivedAt = moment().utc().unix();
 
-      // Update UD
-      if (block.dividend) {
-        data.currentUD = block.dividend;
-      }
+    data.medianTime = block.medianTime;
+    data.membersCount = block.membersCount;
 
-      // Dispatch to extensions
-      api.data.raise.newBlock(block);
+    // Update UD
+    if (block.dividend) {
+      data.currentUD = block.dividend;
     }
 
-    function addListeners() {
-      listeners = [
-        // Listen if node changed
-        BMA.api.node.on.restart($rootScope, restart, this),
-        // open web socket on block
-        BMA.websocket.block().onListener(onBlock)
-      ];
-    }
+    // Dispatch to extensions
+    api.data.raise.newBlock(block);
+  }
 
-    function removeListeners() {
-      _.forEach(listeners, function(remove){
-        remove();
-      });
-      listeners = [];
-    }
+  function addListeners() {
+    listeners = [
+      // Listen if node changed
+      BMA.api.node.on.restart($rootScope, restart, this),
+      // open web socket on block
+      BMA.websocket.block().onListener(onBlock)
+    ];
+  }
 
-    function ready() {
-      if (started) return $q.when(data);
-      return startPromise || start();
-    }
+  function removeListeners() {
+    _.forEach(listeners, function(remove){
+      remove();
+    });
+    listeners = [];
+  }
 
-    function stop() {
-      console.debug('[currency] Stopping...');
-      removeListeners();
-      resetData();
-    }
+  function ready() {
+    if (started) return $q.when(data);
+    return startPromise || start();
+  }
 
-    function restart() {
-      stop();
-      return $timeout(start, 200);
-    }
+  function stop() {
+    console.debug('[currency] Stopping...');
+    removeListeners();
+    resetData();
+  }
 
-    function start() {
-      console.debug('[currency] Starting...');
-      var now = Date.now();
+  function restart() {
+    stop();
+    return $timeout(start, 200);
+  }
 
-      startPromise = BMA.ready()
+  function start() {
+    console.debug('[currency] Starting...');
+    var now = Date.now();
 
-        // Load data
-        .then(loadData)
+    startPromise = BMA.ready()
 
-        // Emit ready event
-        .then(function() {
-          addListeners();
+      // Load data
+      .then(loadData)
 
-          console.debug('[currency] Started in ' + (Date.now() - now) + 'ms');
+      // Emit ready event
+      .then(function() {
+        addListeners();
 
-          started = true;
-          startPromise = null;
+        console.debug('[currency] Started in ' + (Date.now() - now) + 'ms');
 
-          // Emit event (used by plugins)
-          api.data.raise.ready(data);
-        })
-        .then(function(){
-          return data;
-        });
+        started = true;
+        startPromise = null;
 
-      return startPromise;
-    }
+        // Emit event (used by plugins)
+        api.data.raise.ready(data);
+      })
+      .then(function(){
+        return data;
+      });
 
-    var currentBlockField = getDataField('currentBlock');
+    return startPromise;
+  }
 
-    function getCurrent(cache) {
-      // Get field (and make sure service is started)
-      return currentBlockField()
+  var currentBlockField = getDataField('currentBlock');
 
-        .then(function(currentBlock) {
+  function getCurrent(cache) {
+    // Get field (and make sure service is started)
+    return currentBlockField()
 
-          var now = moment().utc().unix();
+      .then(function(currentBlock) {
 
-          if (cache) {
-            if (currentBlock && currentBlock.receivedAt && (now - currentBlock.receivedAt) < 60/*1min*/) {
-              //console.debug('[currency] Use current block #'+ currentBlock.number +' from cache (age='+ (now - currentBlock.receivedAt) + 's)');
-              return currentBlock;
-            }
+        var now = moment().utc().unix();
 
-            if (!currentBlock) {
-              // Should never occur, if websocket /ws/block works !
-              console.warn('[currency] No current block in cache: get it from network. Websocket [/ws/block] may not be started ?');
-            }
+        if (cache) {
+          if (currentBlock && currentBlock.receivedAt && (now - currentBlock.receivedAt) < 60/*1min*/) {
+            //console.debug('[currency] Use current block #'+ currentBlock.number +' from cache (age='+ (now - currentBlock.receivedAt) + 's)');
+            return currentBlock;
           }
 
-          return BMA.blockchain.current(false)
-            .catch(function(err){
-              // Special case for currency init (root block not exists): use fixed values
-              if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
-                return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH, medianTime: now};
-              }
-              throw err;
-            })
-            .then(function(current) {
-              data.currentBlock = current;
-              data.currentBlock.receivedAt = now;
-              return current;
-            });
-        });
-    }
-
-    function getLastValidBlock() {
-      if (csSettings.data.blockValidityWindow <= 0) {
-        return getCurrent(true);
-      }
+          if (!currentBlock) {
+            // Should never occur, if websocket /ws/block works !
+            console.warn('[currency] No current block in cache: get it from network. Websocket [/ws/block] may not be started ?');
+          }
+        }
 
-      return getCurrent(true)
-        .then(function(current) {
-          var number = current.number - csSettings.data.blockValidityWindow;
-          return (number > 0) ? BMA.blockchain.block({block: number}) : current;
-        });
-    }
+        return BMA.blockchain.current(false)
+          .catch(function(err){
+            // Special case for currency init (root block not exists): use fixed values
+            if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
+              return {number: 0, hash: BMA.constants.ROOT_BLOCK_HASH, medianTime: now};
+            }
+            throw err;
+          })
+          .then(function(current) {
+            data.currentBlock = current;
+            data.currentBlock.receivedAt = now;
+            return current;
+          });
+      });
+  }
 
-    // Get time in second (UTC - medianTimeOffset)
-    function getDateNow() {
-      return moment().utc().unix() - (data.medianTimeOffset || constants.WELL_KNOWN_CURRENCIES.g1.medianTimeOffset);
+  function getLastValidBlock() {
+    if (csSettings.data.blockValidityWindow <= 0) {
+      return getCurrent(true);
     }
 
-    // TODO register new block event, to get new UD value
-
-    // Register extension points
-    api.registerEvent('data', 'ready');
-    api.registerEvent('data', 'load');
-    api.registerEvent('data', 'reset');
-    api.registerEvent('data', 'newBlock');
-
-    // init data
-    resetData();
+    return getCurrent(true)
+      .then(function(current) {
+        var number = current.number - csSettings.data.blockValidityWindow;
+        return (number > 0) ? BMA.blockchain.block({block: number}) : current;
+      });
+  }
 
-    // Default action
-    //start();
-
-    return {
-      ready: ready,
-      start: start,
-      stop: stop,
-      data: data,
-      get: getData,
-      name: getDataField('name'),
-      parameters: getDataField('parameters'),
-      currentUD: getDataField('currentUD'),
-      medianTimeOffset: getDataField('medianTimeOffset'),
-      blockchain: {
-        current: getCurrent,
-        lastValid: getLastValidBlock
-      },
-      date: {
-        now: getDateNow
-      },
-      // api extension
-      api: api,
-      // deprecated methods
-      default: function() {
-        console.warn('[currency] \'csCurrency.default()\' has been DEPRECATED - Please use \'csCurrency.get()\' instead.');
-        return getData();
-      }
-    };
+  // Get time in second (UTC - medianTimeOffset)
+  function getDateNow() {
+    return moment().utc().unix() - (data.medianTimeOffset || constants.WELL_KNOWN_CURRENCIES.g1.medianTimeOffset);
   }
 
-  var service = new CsCurrency('default');
-  service.instance = function(id, bma) {
-    return new CsCurrency(id, bma);
+  // TODO register new block event, to get new UD value
+
+  // Register extension points
+  api.registerEvent('data', 'ready');
+  api.registerEvent('data', 'load');
+  api.registerEvent('data', 'reset');
+  api.registerEvent('data', 'newBlock');
+
+  // init data
+  resetData();
+
+  // Default action
+  //start();
+
+  return {
+    ready: ready,
+    start: start,
+    stop: stop,
+    data: data,
+    get: getData,
+    name: getDataField('name'),
+    parameters: getDataField('parameters'),
+    currentUD: getDataField('currentUD'),
+    medianTimeOffset: getDataField('medianTimeOffset'),
+    blockchain: {
+      current: getCurrent,
+      lastValid: getLastValidBlock
+    },
+    date: {
+      now: getDateNow
+    },
+    // api extension
+    api: api,
+    // deprecated methods
+    default: function() {
+      console.warn('[currency] \'csCurrency.default()\' has been DEPRECATED - Please use \'csCurrency.get()\' instead.');
+      return getData();
+    }
   };
-  return service;
 });
diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js
index cdb1df2078ec8cb56580c78c93c8a78bf4170bf7..4788424358174a9d8f991fb94b4aadc363e16926 100644
--- a/www/js/services/device-services.js
+++ b/www/js/services/device-services.js
@@ -2,8 +2,7 @@ var App;
 
 angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.settings.services'])
 
-  .factory('Device',
-    function($rootScope, $translate, $ionicPopup, $q,
+  .factory('Device', function($rootScope, $translate, $ionicPopup, $q,
       // removeIf(no-device)
       $cordovaClipboard, $cordovaBarcodeScanner, $cordovaCamera,
       // endRemoveIf(no-device)
diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js
index 037345ccfedab352211e1bc857c089111941e87b..7cdee80e5aa80009b025bf4226e32d5627730068 100644
--- a/www/js/services/network-services.js
+++ b/www/js/services/network-services.js
@@ -4,855 +4,843 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
 .factory('csNetwork', function($rootScope, $q, $interval, $timeout, $window, csConfig, BMA, csHttp, csCurrency, Api) {
   'ngInject';
 
-  function CsNetwork(id) {
-
-    var
-      interval,
-      constants = {
-        UNKNOWN_BUID: -1,
-        MAX_BLOCK_OFFSET: 1000
-      },
-      isHttpsMode = $window.location.protocol === 'https:',
-      api = new Api(this, "csNetwork-" + id),
-
-      data = {
-        bma: null,
-        listeners: [],
-        loading: true,
-        peers: [],
-        filter: {
-          member: true,
-          mirror: true,
-          endpoint: null,
-          online: false,
-          bma: false,
-          ssl: undefined,
-          tor: undefined
-        },
-        sort:{
-          type: null,
-          asc: true,
-          compact: true
-        },
-        groupBy: 'pubkey',
-        expertMode: false,
-        knownBlocks: [],
-        mainBlock: null,
-        minOnlineBlockNumber: 0,
-        uidsByPubkeys: null,
-        searchingPeersOnNetwork: false,
-        difficulties: null,
-        ws2pHeads: null,
-        timeout: csConfig.timeout
-      },
-
-      // Return the block uid
-      buid = function(block) {
-        return block && [block.number, block.hash].join('-');
+   var
+    interval,
+    constants = {
+      UNKNOWN_BUID: -1,
+      MAX_BLOCK_OFFSET: 1000
+    },
+    isHttpsMode = $window.location.protocol === 'https:',
+    api = new Api(this, "csNetwork"),
+
+    data = {
+      bma: null,
+      listeners: [],
+      loading: true,
+      peers: [],
+      filter: {
+        member: true,
+        mirror: true,
+        endpoint: null,
+        online: false,
+        bma: false,
+        ssl: undefined,
+        tor: undefined
       },
-
-      resetData = function() {
-        data.bma = null;
-        data.listeners = [];
-        data.peers.splice(0);
-        data.filter = {
-          member: true,
-          mirror: true,
-          endpoint: null,
-          online: true,
-          bma: false,
-          ssl: undefined,
-          tor: undefined
-        };
-        data.sort = {
-          type: null,
-          asc: true
-        };
-        data.groupBy = 'pubkey';
-        data.expertMode = false;
-        data.memberPeersCount = 0;
-        data.knownBlocks = [];
-        data.mainBlock = null;
-        data.minOnlineBlockNumber = 0;
-        data.uidsByPubkeys = {};
-        data.loading = true;
-        data.searchingPeersOnNetwork = false;
-        data.difficulties = null;
-        data.ws2pHeads = null;
-        data.timeout = csConfig.timeout;
-      },
-
-      hasPeers = function() {
-        return data.peers && data.peers.length > 0;
+      sort:{
+        type: null,
+        asc: true,
+        compact: true
       },
-
-      getPeers = function() {
-        return data.peers;
-      },
-
-      isBusy = function() {
-        return data.loading;
-      },
-
-      getKnownBlocks = function() {
-        return data.knownBlocks;
-      },
-
-      // Load WS2P heads
-      loadW2spHeads = function() {
-        return data.bma.network.ws2p.heads()
-          .then(function (res) {
-            data.ws2pHeads = res.heads ? res.heads.reduce(function (res, hit) {
-              if (hit.message && hit.sig) {
-                try {
-                  var head = new Ws2pMessage(hit.message);
-                  res[[head.pubkey, head.ws2pid].join('-')] = head;
-                }
-                catch(err) {
-                  // just log, then ignore
-                  console.error('[network] Ignoring WS2P head.', err && err.message || err);
-                }
+      groupBy: 'pubkey',
+      expertMode: false,
+      knownBlocks: [],
+      mainBlock: null,
+      minOnlineBlockNumber: 0,
+      uidsByPubkeys: null,
+      searchingPeersOnNetwork: false,
+      difficulties: null,
+      ws2pHeads: null,
+      timeout: csConfig.timeout
+    },
+
+    // Return the block uid
+    buid = function(block) {
+      return block && [block.number, block.hash].join('-');
+    },
+
+    resetData = function() {
+      data.bma = null;
+      data.listeners = [];
+      data.peers.splice(0);
+      data.filter = {
+        member: true,
+        mirror: true,
+        endpoint: null,
+        online: true,
+        bma: false,
+        ssl: undefined,
+        tor: undefined
+      };
+      data.sort = {
+        type: null,
+        asc: true
+      };
+      data.groupBy = 'pubkey';
+      data.expertMode = false;
+      data.memberPeersCount = 0;
+      data.knownBlocks = [];
+      data.mainBlock = null;
+      data.minOnlineBlockNumber = 0;
+      data.uidsByPubkeys = {};
+      data.loading = true;
+      data.searchingPeersOnNetwork = false;
+      data.difficulties = null;
+      data.ws2pHeads = null;
+      data.timeout = csConfig.timeout;
+    },
+
+    hasPeers = function() {
+      return data.peers && data.peers.length > 0;
+    },
+
+    getPeers = function() {
+      return data.peers;
+    },
+
+    isBusy = function() {
+      return data.loading;
+    },
+
+    getKnownBlocks = function() {
+      return data.knownBlocks;
+    },
+
+    // Load WS2P heads
+    loadW2spHeads = function() {
+      return data.bma.network.ws2p.heads()
+        .then(function (res) {
+          data.ws2pHeads = res.heads ? res.heads.reduce(function (res, hit) {
+            if (hit.message && hit.sig) {
+              try {
+                var head = new Ws2pMessage(hit.message);
+                res[[head.pubkey, head.ws2pid].join('-')] = head;
+              }
+              catch(err) {
+                // just log, then ignore
+                console.error('[network] Ignoring WS2P head.', err && err.message || err);
               }
-              return res;
-            }, {}) : {};
-          })
-          .catch(function(err) {
-            // When too many request, retry in 3s
-            if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
-              return $timeout(function() {
-                return loadW2spHeads();
-              }, 3000);
             }
-            console.error(err); // can occur on duniter v1.6
-            data.ws2pHeads = {};
-          });
-      },
+            return res;
+          }, {}) : {};
+        })
+        .catch(function(err) {
+          // When too many request, retry in 3s
+          if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
+            return $timeout(function() {
+              return loadW2spHeads();
+            }, 3000);
+          }
+          console.error(err); // can occur on duniter v1.6
+          data.ws2pHeads = {};
+        });
+    },
+
+    // Load personal difficulties
+    loadDifficulties = function() {
+      return data.bma.blockchain.stats.difficulties()
+        .then(function (res) {
+          data.difficulties = res.levels ? res.levels.reduce(function (res, hit) {
+            if (hit.uid && hit.level) res[hit.uid] = hit.level;
+            return res;
+          }, {}) : {};
+        })
+        .catch(function(err) {
+          // When too many request, retry in 3s
+          if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
+            return $timeout(function() {
+              return loadDifficulties();
+            }, 3000);
+          }
+          console.error(err);
+          data.difficulties = {};
+        });
+    },
 
-      // Load personal difficulties
-      loadDifficulties = function() {
-        return data.bma.blockchain.stats.difficulties()
-          .then(function (res) {
-            data.difficulties = res.levels ? res.levels.reduce(function (res, hit) {
-              if (hit.uid && hit.level) res[hit.uid] = hit.level;
-              return res;
-            }, {}) : {};
-          })
-          .catch(function(err) {
-            // When too many request, retry in 3s
-            if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
-              return $timeout(function() {
-                return loadDifficulties();
-              }, 3000);
-            }
-            console.error(err);
-            data.difficulties = {};
-          });
-      },
+    loadPeers = function() {
+      data.peers = [];
+      data.searchingPeersOnNetwork = true;
+      data.loading = true;
+      data.bma = data.bma || BMA;
+      var newPeers = [];
 
-      loadPeers = function() {
-        data.peers = [];
-        data.searchingPeersOnNetwork = true;
-        data.loading = true;
-        data.bma = data.bma || BMA;
-        var newPeers = [];
+      if (interval) {
+        $interval.cancel(interval);
+      }
 
-        if (interval) {
+      interval = $interval(function() {
+        // not same job instance
+        if (newPeers.length) {
+          flushNewPeersAndSort(newPeers);
+        }
+        else if (data.loading && !data.searchingPeersOnNetwork) {
+          data.loading = false;
           $interval.cancel(interval);
+          // The peer lookup end, we can make a clean final report
+          sortPeers(true/*update main buid*/);
+
+          console.debug('[network] Finish: {0} peers found.'.format(data.peers.length));
         }
+      }, 1000);
 
-        interval = $interval(function() {
-          // not same job instance
-          if (newPeers.length) {
-            flushNewPeersAndSort(newPeers);
-          }
-          else if (data.loading && !data.searchingPeersOnNetwork) {
-            data.loading = false;
-            $interval.cancel(interval);
-            // The peer lookup end, we can make a clean final report
-            sortPeers(true/*update main buid*/);
+      var initJobs = [
+        // Load uids
+        data.bma.wot.member.uids()
+          .then(function(uids) {
+            data.uidsByPubkeys = uids;
+          })
+          .catch(function(err) {
+            console.error(err);
+            data.uidsByPubkeys = {};
+          }),
 
-            console.debug('[network] Finish: {0} peers found.'.format(data.peers.length));
-          }
-        }, 1000);
+        // Load WS2P heads
+        loadW2spHeads()
+      ];
 
-        var initJobs = [
-          // Load uids
-          data.bma.wot.member.uids()
-            .then(function(uids) {
-              data.uidsByPubkeys = uids;
-            })
-            .catch(function(err) {
-              console.error(err);
-              data.uidsByPubkeys = {};
-            }),
+      // Get difficulties (expert mode only)
+      if (data.expertMode) {
+        initJobs.push(loadDifficulties());
+      }
 
-          // Load WS2P heads
-          loadW2spHeads()
-        ];
+      return $q.all(initJobs)
+        .then(function() {
+          return data.bma && data.bma.network.peers();
+        })
+        .then(function(res){
+          if (!res || !res.peers || !res.peers.length) return;
 
-        // Get difficulties (expert mode only)
-        if (data.expertMode) {
-          initJobs.push(loadDifficulties());
-        }
-
-        return $q.all(initJobs)
-          .then(function() {
-            return data.bma && data.bma.network.peers();
-          })
-          .then(function(res){
-            if (!res || !res.peers || !res.peers.length) return;
-
-            // If filter online peers
-            if (data.filter.online) {
-              var jobs = [];
-              _.forEach(res.peers, function(json) {
-                // Exclude, if not UP or on a too old block
-                if (json.status !== 'UP') return;
-                json.blockNumber = json.block && parseInt(json.block.split('-')[0]);
-                if (json.blockNumber && json.blockNumber < data.minOnlineBlockNumber) {
-                  console.debug("[network] Exclude a too old peer document, on pubkey {0}".format(json.pubkey.substring(0,6)));
-                  return;
-                }
+          // If filter online peers
+          if (data.filter.online) {
+            var jobs = [];
+            _.forEach(res.peers, function(json) {
+              // Exclude, if not UP or on a too old block
+              if (json.status !== 'UP') return;
+              json.blockNumber = json.block && parseInt(json.block.split('-')[0]);
+              if (json.blockNumber && json.blockNumber < data.minOnlineBlockNumber) {
+                console.debug("[network] Exclude a too old peer document, on pubkey {0}".format(json.pubkey.substring(0,6)));
+                return;
+              }
 
-                jobs.push(addOrRefreshPeerFromJson(json, newPeers));
+              jobs.push(addOrRefreshPeerFromJson(json, newPeers));
 
-                // Mark WS2P
-                _.forEach(json.endpoints||[], function(ep) {
-                  if (ep.startsWith('WS2P')) {
-                    var key = json.pubkey + '-' + ep.split(' ')[1];
-                    if (data.ws2pHeads[key]) {
-                      data.ws2pHeads[key].hasEndpoint = true;
-                    }
+              // Mark WS2P
+              _.forEach(json.endpoints||[], function(ep) {
+                if (ep.startsWith('WS2P')) {
+                  var key = json.pubkey + '-' + ep.split(' ')[1];
+                  if (data.ws2pHeads[key]) {
+                    data.ws2pHeads[key].hasEndpoint = true;
                   }
-                });
+                }
               });
+            });
 
-              // Add private WS2P endpoints
-              var privateWs2pHeads = _.values(data.ws2pHeads);
-              if (privateWs2pHeads && privateWs2pHeads.length) {
-                var privateEPCount = 0;
-                //console.debug("[http] Found WS2P endpoints without endpoint:", data.ws2pHeads);
-                _.forEach(privateWs2pHeads, function(head) {
-
-                  if (!head.hasEndPoint) {
-                    var currentNumber = head.buid && parseInt(head.buid.split('-')[0]);
-                    // Exclude if on a too old block
-                    if (currentNumber && currentNumber < data.minOnlineBlockNumber) {
-                      console.debug("[network] Exclude a too old WS2P message, on pubkey {0}".format(head.pubkey.substring(0,6)));
-                      return;
-                    }
-
-                    var peer = new Peer({
-                      buid: head.buid,
-                      currentNumber: currentNumber,
-                      pubkey: head.pubkey,
-                      version: head.version,
-                      powPrefix: head.powPrefix,
-                      online: true,
-                      uid: data.uidsByPubkeys[head.pubkey],
-                      bma: {
-                        useWs2p: true,
-                        private: true,
-                        ws2pid: head.ws2pid
-                      },
-                      endpoints: [
-                        // fake endpoint
-                        'WS2P ' + head.ws2pid
-                      ]
-                    });
-                    peer.id = peer.keyID();
-                    if (peer.uid && data.expertMode && data.difficulties) {
-                      peer.difficulty = data.difficulties[peer.uid];
-                    }
-                    if (applyPeerFilter(peer)) {
-                      newPeers.push(peer);
-                      privateEPCount++;
-                    }
+            // Add private WS2P endpoints
+            var privateWs2pHeads = _.values(data.ws2pHeads);
+            if (privateWs2pHeads && privateWs2pHeads.length) {
+              var privateEPCount = 0;
+              //console.debug("[http] Found WS2P endpoints without endpoint:", data.ws2pHeads);
+              _.forEach(privateWs2pHeads, function(head) {
+
+                if (!head.hasEndPoint) {
+                  var currentNumber = head.buid && parseInt(head.buid.split('-')[0]);
+                  // Exclude if on a too old block
+                  if (currentNumber && currentNumber < data.minOnlineBlockNumber) {
+                    console.debug("[network] Exclude a too old WS2P message, on pubkey {0}".format(head.pubkey.substring(0,6)));
+                    return;
                   }
-                });
 
-                if (privateEPCount) {
-                  console.debug("[http] Found {0} WS2P endpoints without endpoint (private ?)".format(privateEPCount));
+                  var peer = new Peer({
+                    buid: head.buid,
+                    currentNumber: currentNumber,
+                    pubkey: head.pubkey,
+                    version: head.version,
+                    powPrefix: head.powPrefix,
+                    online: true,
+                    uid: data.uidsByPubkeys[head.pubkey],
+                    bma: {
+                      useWs2p: true,
+                      private: true,
+                      ws2pid: head.ws2pid
+                    },
+                    endpoints: [
+                      // fake endpoint
+                      'WS2P ' + head.ws2pid
+                    ]
+                  });
+                  peer.id = peer.keyID();
+                  if (peer.uid && data.expertMode && data.difficulties) {
+                    peer.difficulty = data.difficulties[peer.uid];
+                  }
+                  if (applyPeerFilter(peer)) {
+                    newPeers.push(peer);
+                    privateEPCount++;
+                  }
                 }
-              }
+              });
 
-              if (jobs.length) return $q.all(jobs);
+              if (privateEPCount) {
+                console.debug("[http] Found {0} WS2P endpoints without endpoint (private ?)".format(privateEPCount));
+              }
             }
 
-            // If filter offline peers
-            else {
-              return $q.all(_(res && res.peers || []).reduce(function(res, json) {
-                return res.concat(addOrRefreshPeerFromJson(json, newPeers));
-              }, []));
-            }
-          })
-          .then(function(){
-            data.searchingPeersOnNetwork = false;
-          })
-          .catch(function(err){
-            console.error(err);
-            data.searchingPeersOnNetwork = false;
-          });
-      },
-
-      /**
-       * Apply filter on a peer. (peer uid should have been filled BEFORE)
-       */
-      applyPeerFilter = function(peer) {
-        // no filter
-        if (!data.filter) return true;
-
-        // Filter member and mirror
-        if ((data.filter.member && !data.filter.mirror && !peer.uid) ||
-            (data.filter.mirror && !data.filter.member && peer.uid)) {
-          return false;
-        }
+            if (jobs.length) return $q.all(jobs);
+          }
 
-        // Filter on endpoint
-        if (data.filter.endpoint && !peer.hasEndpoint(data.filter.endpoint)) {
-          return false;
-        }
+          // If filter offline peers
+          else {
+            return $q.all(_(res && res.peers || []).reduce(function(res, json) {
+              return res.concat(addOrRefreshPeerFromJson(json, newPeers));
+            }, []));
+          }
+        })
+        .then(function(){
+          data.searchingPeersOnNetwork = false;
+        })
+        .catch(function(err){
+          console.error(err);
+          data.searchingPeersOnNetwork = false;
+        });
+    },
+
+    /**
+     * Apply filter on a peer. (peer uid should have been filled BEFORE)
+     */
+    applyPeerFilter = function(peer) {
+      // no filter
+      if (!data.filter) return true;
+
+      // Filter member and mirror
+      if ((data.filter.member && !data.filter.mirror && !peer.uid) ||
+          (data.filter.mirror && !data.filter.member && peer.uid)) {
+        return false;
+      }
 
-        // Filter on status
-        if ((data.filter.online && peer.status !== 'UP' && peer.oldBlock) || (!data.filter.online && peer.status === 'UP' && !peer.oldBlock)) {
-          return false;
-        }
+      // Filter on endpoint
+      if (data.filter.endpoint && !peer.hasEndpoint(data.filter.endpoint)) {
+        return false;
+      }
 
-        // Filter on bma
-        if (angular.isDefined(data.filter.bma) && peer.isBma() != data.filter.bma) {
-          return false;
-        }
+      // Filter on status
+      if ((data.filter.online && peer.status !== 'UP' && peer.oldBlock) || (!data.filter.online && peer.status === 'UP' && !peer.oldBlock)) {
+        return false;
+      }
 
-        // Filter on ws2p
-        if (angular.isDefined(data.filter.ws2p) && peer.isWs2p() != data.filter.ws2p) {
-          return false;
-        }
+      // Filter on bma
+      if (angular.isDefined(data.filter.bma) && peer.isBma() != data.filter.bma) {
+        return false;
+      }
 
-        // Filter on ssl
-        if (angular.isDefined(data.filter.ssl) && peer.isSsl() != data.filter.ssl) {
-          return false;
-        }
+      // Filter on ws2p
+      if (angular.isDefined(data.filter.ws2p) && peer.isWs2p() != data.filter.ws2p) {
+        return false;
+      }
 
-        // Filter on tor
-        if (angular.isDefined(data.filter.tor) && peer.isTor() != data.filter.tor) {
-          return false;
-        }
+      // Filter on ssl
+      if (angular.isDefined(data.filter.ssl) && peer.isSsl() != data.filter.ssl) {
+        return false;
+      }
 
-        return true;
-      },
+      // Filter on tor
+      if (angular.isDefined(data.filter.tor) && peer.isTor() != data.filter.tor) {
+        return false;
+      }
 
-      addOrRefreshPeerFromJson = function(json, list) {
-        list = list || data.newPeers;
-
-        // Analyze the peer document, and exclude using the online filter
-        json.blockNumber = json.block && parseInt(json.block.split('-')[0]);
-        json.oldBlock = (json.status === 'UP' && json.blockNumber && json.blockNumber < data.minOnlineBlockNumber);
-
-        var peers = createPeerEntities(json);
-        var hasUpdates = false;
-
-        var jobs = peers.reduce(function(jobs, peer) {
-            var existingPeer = _.findWhere(data.peers, {id: peer.id});
-            var existingMainBuid = existingPeer ? existingPeer.buid : null;
-            var existingOnline = existingPeer ? existingPeer.online : false;
-
-            return jobs.concat(
-              refreshPeer(peer)
-                .then(function (refreshedPeer) {
-                  if (existingPeer) {
-                    // remove existing peers, when reject or offline
-                    if (!refreshedPeer || (refreshedPeer.online !== data.filter.online && data.filter.online !== 'all')) {
-                      var existingIndex = data.peers.indexOf(existingPeer);
-                      if (existingIndex !== -1) {
-                        console.debug('[network] Peer [{0}] removed (cause: {1})'.format(peer.server, !refreshedPeer ? 'filtered' : (refreshedPeer.online ? 'UP' : 'DOWN')));
-                        data.peers.splice(existingIndex, 1);
-                        hasUpdates = true;
-                      }
-                    }
-                    else if (refreshedPeer.buid !== existingMainBuid){
-                      console.debug('[network] {0} endpoint [{1}] new current block'.format(
-                        refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
-                        refreshedPeer.server));
-                      hasUpdates = true;
-                    }
-                    else if (existingOnline !== refreshedPeer.online){
-                      console.debug('[network] {0} endpoint [{1}] is now {2}'.format(
-                        refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
-                        refreshedPeer.server,
-                        refreshedPeer.online ? 'UP' : 'DOWN'));
+      return true;
+    },
+
+    addOrRefreshPeerFromJson = function(json, list) {
+      list = list || data.newPeers;
+
+      // Analyze the peer document, and exclude using the online filter
+      json.blockNumber = json.block && parseInt(json.block.split('-')[0]);
+      json.oldBlock = (json.status === 'UP' && json.blockNumber && json.blockNumber < data.minOnlineBlockNumber);
+
+      var peers = createPeerEntities(json);
+      var hasUpdates = false;
+
+      var jobs = peers.reduce(function(jobs, peer) {
+          var existingPeer = _.findWhere(data.peers, {id: peer.id});
+          var existingMainBuid = existingPeer ? existingPeer.buid : null;
+          var existingOnline = existingPeer ? existingPeer.online : false;
+
+          return jobs.concat(
+            refreshPeer(peer)
+              .then(function (refreshedPeer) {
+                if (existingPeer) {
+                  // remove existing peers, when reject or offline
+                  if (!refreshedPeer || (refreshedPeer.online !== data.filter.online && data.filter.online !== 'all')) {
+                    var existingIndex = data.peers.indexOf(existingPeer);
+                    if (existingIndex !== -1) {
+                      console.debug('[network] Peer [{0}] removed (cause: {1})'.format(peer.server, !refreshedPeer ? 'filtered' : (refreshedPeer.online ? 'UP' : 'DOWN')));
+                      data.peers.splice(existingIndex, 1);
                       hasUpdates = true;
                     }
-                    else {
-                      console.debug("[network] {0} endpoint [{1}] unchanged".format(
-                        refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
-                        refreshedPeer.server));
-                    }
                   }
-                  else if (refreshedPeer && (refreshedPeer.online === data.filter.online || data.filter.online === 'all')) {
-                    console.debug("[network] {0} endpoint [{1}] is {2}".format(
+                  else if (refreshedPeer.buid !== existingMainBuid){
+                    console.debug('[network] {0} endpoint [{1}] new current block'.format(
+                      refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
+                      refreshedPeer.server));
+                    hasUpdates = true;
+                  }
+                  else if (existingOnline !== refreshedPeer.online){
+                    console.debug('[network] {0} endpoint [{1}] is now {2}'.format(
                       refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
                       refreshedPeer.server,
-                      refreshedPeer.online ? 'UP' : 'DOWN'
-                    ));
-                    list.push(refreshedPeer);
+                      refreshedPeer.online ? 'UP' : 'DOWN'));
                     hasUpdates = true;
                   }
-                })
-           );
-        }, []);
-        return (jobs.length === 1 ? jobs[0] : $q.all(jobs))
-          .then(function() {
-            return hasUpdates;
-          });
-      },
+                  else {
+                    console.debug("[network] {0} endpoint [{1}] unchanged".format(
+                      refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
+                      refreshedPeer.server));
+                  }
+                }
+                else if (refreshedPeer && (refreshedPeer.online === data.filter.online || data.filter.online === 'all')) {
+                  console.debug("[network] {0} endpoint [{1}] is {2}".format(
+                    refreshedPeer.bma && (refreshedPeer.bma.useBma ? 'BMA' : 'WS2P') || 'null',
+                    refreshedPeer.server,
+                    refreshedPeer.online ? 'UP' : 'DOWN'
+                  ));
+                  list.push(refreshedPeer);
+                  hasUpdates = true;
+                }
+              })
+         );
+      }, []);
+      return (jobs.length === 1 ? jobs[0] : $q.all(jobs))
+        .then(function() {
+          return hasUpdates;
+        });
+    },
 
-      createPeerEntities = function(json, ep) {
-        if (!json) return [];
-        var peer = new Peer(json);
+    createPeerEntities = function(json, ep) {
+      if (!json) return [];
+      var peer = new Peer(json);
 
-        // Read bma endpoints
-        if (!ep) {
-          var endpointsAsString = peer.getEndpoints();
-          if (!endpointsAsString) return []; // no BMA
+      // Read bma endpoints
+      if (!ep) {
+        var endpointsAsString = peer.getEndpoints();
+        if (!endpointsAsString) return []; // no BMA
 
-          var endpoints = endpointsAsString.reduce(function (res, epStr) {
-            var ep = BMA.node.parseEndPoint(epStr);
-            return ep ? res.concat(ep) : res;
-          }, []);
+        var endpoints = endpointsAsString.reduce(function (res, epStr) {
+          var ep = BMA.node.parseEndPoint(epStr);
+          return ep ? res.concat(ep) : res;
+        }, []);
 
-          // recursive call, on each endpoint
-          if (endpoints.length > 1) {
-            return endpoints.reduce(function (res, ep) {
-              return res.concat(createPeerEntities(json, ep));
-            }, []);
-          }
-          else {
-            // if only one endpoint: use it and continue
-            ep = endpoints[0];
-          }
+        // recursive call, on each endpoint
+        if (endpoints.length > 1) {
+          return endpoints.reduce(function (res, ep) {
+            return res.concat(createPeerEntities(json, ep));
+          }, []);
         }
-        peer.bma = ep;
-        peer.server = peer.getServer();
-        peer.dns = peer.getDns();
-        peer.buid = peer.buid || peer.block;
-        peer.blockNumber = peer.buid && parseInt(peer.buid.split('-')[0]);
-        peer.uid = peer.pubkey && data.uidsByPubkeys[peer.pubkey];
-        peer.id = peer.keyID();
-        return [peer];
-      },
-
-      refreshPeer = function(peer) {
+        else {
+          // if only one endpoint: use it and continue
+          ep = endpoints[0];
+        }
+      }
+      peer.bma = ep;
+      peer.server = peer.getServer();
+      peer.dns = peer.getDns();
+      peer.buid = peer.buid || peer.block;
+      peer.blockNumber = peer.buid && parseInt(peer.buid.split('-')[0]);
+      peer.uid = peer.pubkey && data.uidsByPubkeys[peer.pubkey];
+      peer.id = peer.keyID();
+      return [peer];
+    },
+
+    refreshPeer = function(peer) {
+
+      // Apply filter
+      if (!applyPeerFilter(peer)) return $q.when();
+
+      if (!data.filter.online || (!data.filter.online && peer.status === 'DOWN') || !peer.getHost() /*fix #537*/) {
+        peer.online = false;
+        return $q.when(peer);
+      }
 
-        // Apply filter
-        if (!applyPeerFilter(peer)) return $q.when();
+      if (peer.bma.useWs2p && data.ws2pHeads) {
+        var ws2pHeadKey = [peer.pubkey, peer.bma.ws2pid].join('-');
+        var head = data.ws2pHeads[ws2pHeadKey];
+        delete data.ws2pHeads[ws2pHeadKey];
+        if (head) {
+          peer.buid = head.buid;
+          peer.currentNumber=head.buid && parseInt(head.buid.split('-')[0]);
+          peer.version = head.version;
+          peer.powPrefix = head.powPrefix;
+        }
+        peer.online = !!peer.buid;
 
-        if (!data.filter.online || (!data.filter.online && peer.status === 'DOWN') || !peer.getHost() /*fix #537*/) {
-          peer.online = false;
-          return $q.when(peer);
+        if (peer.uid && data.expertMode && data.difficulties) {
+          peer.difficulty = data.difficulties[peer.uid];
         }
 
-        if (peer.bma.useWs2p && data.ws2pHeads) {
-          var ws2pHeadKey = [peer.pubkey, peer.bma.ws2pid].join('-');
-          var head = data.ws2pHeads[ws2pHeadKey];
-          delete data.ws2pHeads[ws2pHeadKey];
-          if (head) {
-            peer.buid = head.buid;
-            peer.currentNumber=head.buid && parseInt(head.buid.split('-')[0]);
-            peer.version = head.version;
-            peer.powPrefix = head.powPrefix;
-          }
-          peer.online = !!peer.buid;
+        return $q.when(peer);
+      }
 
-          if (peer.uid && data.expertMode && data.difficulties) {
-            peer.difficulty = data.difficulties[peer.uid];
-          }
+      // Cesium running in SSL: Do not try to access not SSL node,
+      if (!peer.bma.useWs2p && isHttpsMode && !peer.bma.useSsl) {
+        peer.online = (peer.status === 'UP');
+        peer.buid = constants.UNKNOWN_BUID;
+        delete peer.version;
 
-          return $q.when(peer);
+        if (peer.uid && data.expertMode && data.difficulties) {
+          peer.difficulty = data.difficulties[peer.uid];
         }
 
-        // Cesium running in SSL: Do not try to access not SSL node,
-        if (!peer.bma.useWs2p && isHttpsMode && !peer.bma.useSsl) {
-          peer.online = (peer.status === 'UP');
-          peer.buid = constants.UNKNOWN_BUID;
-          delete peer.version;
+        return $q.when(peer);
+      }
 
-          if (peer.uid && data.expertMode && data.difficulties) {
-            peer.difficulty = data.difficulties[peer.uid];
-          }
+      // Do not try to access TOR or WS2P endpoints
+      if (peer.bma.useTor || peer.bma.useWs2p) {
+        peer.online = (peer.status === 'UP');
+        peer.buid = constants.UNKNOWN_BUID;
+        delete peer.version;
 
-          return $q.when(peer);
+        if (peer.uid && data.expertMode && data.difficulties) {
+          peer.difficulty = data.difficulties[peer.uid];
         }
+        return $q.when(peer);
+      }
 
-        // Do not try to access TOR or WS2P endpoints
-        if (peer.bma.useTor || peer.bma.useWs2p) {
-          peer.online = (peer.status === 'UP');
-          peer.buid = constants.UNKNOWN_BUID;
-          delete peer.version;
-
-          if (peer.uid && data.expertMode && data.difficulties) {
-            peer.difficulty = data.difficulties[peer.uid];
+      peer.api = peer.api ||  BMA.lightInstance(peer.getHost(), peer.getPort(), peer.isSsl(), data.timeout);
+
+      // Get current block
+      return peer.api.blockchain.current(false/*no cache*/)
+        .then(function(block) {
+          peer.currentNumber = block.number;
+          peer.online = true;
+          peer.buid = buid(block);
+          peer.medianTime = block.medianTime;
+          if (data.knownBlocks.indexOf(peer.buid) === -1) {
+            data.knownBlocks.push(peer.buid);
           }
-          return $q.when(peer);
-        }
-
-        peer.api = peer.api ||  BMA.lightInstance(peer.getHost(), peer.getPort(), peer.isSsl(), data.timeout);
-
-        // Get current block
-        return peer.api.blockchain.current(false/*no cache*/)
-          .then(function(block) {
-            peer.currentNumber = block.number;
+          return peer;
+        })
+        .catch(function(err) {
+          // Special case for currency init (root block not exists): use fixed values
+          if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
             peer.online = true;
-            peer.buid = buid(block);
-            peer.medianTime = block.medianTime;
-            if (data.knownBlocks.indexOf(peer.buid) === -1) {
-              data.knownBlocks.push(peer.buid);
-            }
+            peer.buid = buid({number:0, hash: BMA.constants.ROOT_BLOCK_HASH});
+            peer.difficulty  = 0;
             return peer;
-          })
-          .catch(function(err) {
-            // Special case for currency init (root block not exists): use fixed values
-            if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
-              peer.online = true;
-              peer.buid = buid({number:0, hash: BMA.constants.ROOT_BLOCK_HASH});
-              peer.difficulty  = 0;
-              return peer;
-            }
-            if (!peer.secondTry) {
-              var bma = peer.bma || peer.getBMA();
-              if (bma.dns && peer.server.indexOf(bma.dns) === -1) {
-                // try again, using DNS instead of IPv4 / IPV6
-                peer.secondTry = true;
-                peer.api = BMA.lightInstance(bma.dns, bma.port, bma.useSsl);
-                return refreshPeer(peer); // recursive call
-              }
-            }
-
-            peer.buid = null;
-            peer.blockNumber = null;
-            peer.currentNumber = null;
-            peer.online=false;
-            peer.uid = data.uidsByPubkeys[peer.pubkey];
-            return peer;
-          })
-          .then(function(peer) {
-            // Exit if offline, or not expert mode or too small device
-            if (!data.filter.online || !peer || !peer.online || !data.expertMode) return peer;
-            var jobs = [];
-
-            // Get hardship (only for a member peer)
-            if (peer.uid) {
-              jobs.push(peer.api.blockchain.stats.hardship({pubkey: peer.pubkey})
-                .then(function (res) {
-                  peer.difficulty = res ? res.level : null;
-                })
-                .catch(function() {
-                  peer.difficulty = null; // continue
-                }));
+          }
+          if (!peer.secondTry) {
+            var bma = peer.bma || peer.getBMA();
+            if (bma.dns && peer.server.indexOf(bma.dns) === -1) {
+              // try again, using DNS instead of IPv4 / IPV6
+              peer.secondTry = true;
+              peer.api = BMA.lightInstance(bma.dns, bma.port, bma.useSsl);
+              return refreshPeer(peer); // recursive call
             }
+          }
 
-            // Get Version
-            jobs.push(peer.api.node.summary()
-              .then(function(res){
-                peer.software = res && res.duniter && res.duniter.software || undefined;
-                peer.version = res && res.duniter && res.duniter.version || '?';
+          peer.buid = null;
+          peer.blockNumber = null;
+          peer.currentNumber = null;
+          peer.online=false;
+          peer.uid = data.uidsByPubkeys[peer.pubkey];
+          return peer;
+        })
+        .then(function(peer) {
+          // Exit if offline, or not expert mode or too small device
+          if (!data.filter.online || !peer || !peer.online || !data.expertMode) return peer;
+          var jobs = [];
+
+          // Get hardship (only for a member peer)
+          if (peer.uid) {
+            jobs.push(peer.api.blockchain.stats.hardship({pubkey: peer.pubkey})
+              .then(function (res) {
+                peer.difficulty = res ? res.level : null;
               })
               .catch(function() {
-                peer.software = undefined;
-                peer.version = '?'; // continue
+                peer.difficulty = null; // continue
               }));
-
-            return $q.all(jobs)
-              .then(function(){
-                return peer;
-              });
-          });
-      },
-
-      flushNewPeersAndSort = function(newPeers, updateMainBuid) {
-        newPeers = newPeers || data.newPeers;
-        if (!newPeers.length) return;
-        var ids = _.map(data.peers, function(peer){
-          return peer.id;
-        });
-        var hasUpdates = false;
-        var newPeersAdded = 0;
-        _.forEach(newPeers.splice(0), function(peer) {
-          if (!ids[peer.id]) {
-            data.peers.push(peer);
-            ids[peer.id] = peer;
-            hasUpdates = true;
-            newPeersAdded++;
           }
-        });
-        if (hasUpdates) {
-          console.debug('[network] Flushing {0} new peers...'.format(newPeersAdded));
-          sortPeers(updateMainBuid);
-        }
-      },
 
-      computeScoreAlphaValue = function(value, nbChars, asc) {
-        if (!value) return 0;
-        var score = 0;
-        value = value.toLowerCase();
-        if (nbChars > value.length) {
-          nbChars = value.length;
-        }
-        score += value.charCodeAt(0);
-        for (var i=1; i < nbChars; i++) {
-          score += Math.pow(0.001, i) * value.charCodeAt(i);
-        }
-        return asc ? (1000 - score) : score;
-      },
+          // Get Version
+          jobs.push(peer.api.node.summary()
+            .then(function(res){
+              peer.software = res && res.duniter && res.duniter.software || undefined;
+              peer.version = res && res.duniter && res.duniter.version || '?';
+            })
+            .catch(function() {
+              peer.software = undefined;
+              peer.version = '?'; // continue
+            }));
 
-      sortPeers = function(updateMainBuid) {
-        // Construct a map of buid, with peer count and medianTime
-        var buids = {};
-        data.memberPeersCount = 0;
-        _.forEach(data.peers, function(peer){
-          if (peer.buid) {
-            var buid = buids[peer.buid];
-            if (!buid || !buid.medianTime) {
-              buid = {
-                buid: peer.buid,
-                medianTime: peer.medianTime,
-                count: 0
-              };
-              buids[peer.buid] = buid;
-            }
-            // If not already done, try to fill medianTime (need to compute consensusBlockDelta)
-            else if (!buid.medianTime && peer.medianTime) {
-              buid.medianTime = peer.medianTime;
-            }
-            if (buid.buid !== constants.UNKNOWN_BUID) {
-              buid.count++;
-            }
-          }
-          data.memberPeersCount += peer.uid ? 1 : 0;
+          return $q.all(jobs)
+            .then(function(){
+              return peer;
+            });
         });
-        var mainBlock = data.mainBlock;
-        if (data.filter.online) {
-          // Compute pct of use, per buid
-          _.forEach(_.values(buids), function(buid) {
-            buid.pct = buid.count * 100 / data.peers.length;
-          });
-          mainBlock = _.max(buids, function(obj) {
-            return obj.count;
-          });
-          _.forEach(data.peers, function(peer){
-            peer.hasMainConsensusBlock = peer.buid === mainBlock.buid;
-            peer.hasConsensusBlock = peer.buid && !peer.hasMainConsensusBlock && buids[peer.buid].count > 1;
-            if (peer.hasConsensusBlock) {
-              peer.consensusBlockDelta = buids[peer.buid].medianTime - mainBlock.medianTime;
-            }
-          });
+    },
+
+    flushNewPeersAndSort = function(newPeers, updateMainBuid) {
+      newPeers = newPeers || data.newPeers;
+      if (!newPeers.length) return;
+      var ids = _.map(data.peers, function(peer){
+        return peer.id;
+      });
+      var hasUpdates = false;
+      var newPeersAdded = 0;
+      _.forEach(newPeers.splice(0), function(peer) {
+        if (!ids[peer.id]) {
+          data.peers.push(peer);
+          ids[peer.id] = peer;
+          hasUpdates = true;
+          newPeersAdded++;
         }
-        data.peers = _.uniq(data.peers, false, function(peer) {
-          return peer.id;
-        });
-        data.peers = _.sortBy(data.peers, function(peer) {
-          var score = 0;
-          if (data.sort.type) {
-            score += (data.sort.type === 'uid' ? computeScoreAlphaValue(peer.uid||peer.pubkey, 3, data.sort.asc) : 0);
-            score += (data.sort.type === 'api') &&
-              ((peer.isWs2p() && (data.sort.asc ? 1 : -1) || 0) +
-              (peer.hasEndpoint('ES_USER_API') && (data.sort.asc ? 0.01 : -0.01) || 0) +
-              (peer.isSsl() && (data.sort.asc ? 0.75 : -0.75) || 0)) || 0;
-            score += (data.sort.type === 'difficulty' ? (peer.difficulty ? (data.sort.asc ? (10000-peer.difficulty) : peer.difficulty): 0) : 0);
-            score += (data.sort.type === 'current_block' ? (peer.currentNumber ? (data.sort.asc ? (1000000000 - peer.currentNumber) : peer.currentNumber) : 0) : 0);
+      });
+      if (hasUpdates) {
+        console.debug('[network] Flushing {0} new peers...'.format(newPeersAdded));
+        sortPeers(updateMainBuid);
+      }
+    },
+
+    computeScoreAlphaValue = function(value, nbChars, asc) {
+      if (!value) return 0;
+      var score = 0;
+      value = value.toLowerCase();
+      if (nbChars > value.length) {
+        nbChars = value.length;
+      }
+      score += value.charCodeAt(0);
+      for (var i=1; i < nbChars; i++) {
+        score += Math.pow(0.001, i) * value.charCodeAt(i);
+      }
+      return asc ? (1000 - score) : score;
+    },
+
+    sortPeers = function(updateMainBuid) {
+      // Construct a map of buid, with peer count and medianTime
+      var buids = {};
+      data.memberPeersCount = 0;
+      _.forEach(data.peers, function(peer){
+        if (peer.buid) {
+          var buid = buids[peer.buid];
+          if (!buid || !buid.medianTime) {
+            buid = {
+              buid: peer.buid,
+              medianTime: peer.medianTime,
+              count: 0
+            };
+            buids[peer.buid] = buid;
           }
-          score =  (10000000000 * score);
-          score += (1000000000 * (peer.online ? 1 : 0));
-          score += (100000000  * (peer.hasMainConsensusBlock ? 1 : 0));
-          score += (1000000    * (peer.hasConsensusBlock ? buids[peer.buid].pct : 0));
-          if (data.expertMode) {
-            score += (100     * (peer.difficulty ? (10000-peer.difficulty) : 0));
-            score += (1       * (peer.uid ? computeScoreAlphaValue(peer.uid, 2, true) : 0));
+          // If not already done, try to fill medianTime (need to compute consensusBlockDelta)
+          else if (!buid.medianTime && peer.medianTime) {
+            buid.medianTime = peer.medianTime;
           }
-          else {
-            score += (100     * (peer.uid ? computeScoreAlphaValue(peer.uid, 2, true) : 0));
-            score += (1       * (!peer.uid ? computeScoreAlphaValue(peer.pubkey, 2, true) : 0));
+          if (buid.buid !== constants.UNKNOWN_BUID) {
+            buid.count++;
           }
-          score += (peer.isBma() ? (peer.isSsl() ? 0.01 : 0.001) :0); // If many endpoints: BMAS first, then BMA
-          return -score;
+        }
+        data.memberPeersCount += peer.uid ? 1 : 0;
+      });
+      var mainBlock = data.mainBlock;
+      if (data.filter.online) {
+        // Compute pct of use, per buid
+        _.forEach(_.values(buids), function(buid) {
+          buid.pct = buid.count * 100 / data.peers.length;
         });
-
-        if (data.groupBy) {
-          var previousPeer;
-          data.peers.forEach(function(peer) {
-            peer.compacted = (previousPeer && peer[data.groupBy] && peer[data.groupBy] === previousPeer[data.groupBy]);
-            previousPeer = peer;
-          });
+        mainBlock = _.max(buids, function(obj) {
+          return obj.count;
+        });
+        _.forEach(data.peers, function(peer){
+          peer.hasMainConsensusBlock = peer.buid === mainBlock.buid;
+          peer.hasConsensusBlock = peer.buid && !peer.hasMainConsensusBlock && buids[peer.buid].count > 1;
+          if (peer.hasConsensusBlock) {
+            peer.consensusBlockDelta = buids[peer.buid].medianTime - mainBlock.medianTime;
+          }
+        });
+      }
+      data.peers = _.uniq(data.peers, false, function(peer) {
+        return peer.id;
+      });
+      data.peers = _.sortBy(data.peers, function(peer) {
+        var score = 0;
+        if (data.sort.type) {
+          score += (data.sort.type === 'uid' ? computeScoreAlphaValue(peer.uid||peer.pubkey, 3, data.sort.asc) : 0);
+          score += (data.sort.type === 'api') &&
+            ((peer.isWs2p() && (data.sort.asc ? 1 : -1) || 0) +
+            (peer.hasEndpoint('ES_USER_API') && (data.sort.asc ? 0.01 : -0.01) || 0) +
+            (peer.isSsl() && (data.sort.asc ? 0.75 : -0.75) || 0)) || 0;
+          score += (data.sort.type === 'difficulty' ? (peer.difficulty ? (data.sort.asc ? (10000-peer.difficulty) : peer.difficulty): 0) : 0);
+          score += (data.sort.type === 'current_block' ? (peer.currentNumber ? (data.sort.asc ? (1000000000 - peer.currentNumber) : peer.currentNumber) : 0) : 0);
         }
-
-        // Raise event on new main block
-        if (updateMainBuid && mainBlock && mainBlock.buid && (!data.mainBlock || data.mainBlock.buid !== mainBlock.buid)) {
-          data.mainBlock = mainBlock;
-          api.data.raise.mainBlockChanged(mainBlock);
+        score =  (10000000000 * score);
+        score += (1000000000 * (peer.online ? 1 : 0));
+        score += (100000000  * (peer.hasMainConsensusBlock ? 1 : 0));
+        score += (1000000    * (peer.hasConsensusBlock ? buids[peer.buid].pct : 0));
+        if (data.expertMode) {
+          score += (100     * (peer.difficulty ? (10000-peer.difficulty) : 0));
+          score += (1       * (peer.uid ? computeScoreAlphaValue(peer.uid, 2, true) : 0));
         }
-
-        // Raise event when changed
-        api.data.raise.changed(data); // raise event
-      },
-
-      removeListeners = function() {
-        _.forEach(data.listeners, function(remove){
-          remove();
+        else {
+          score += (100     * (peer.uid ? computeScoreAlphaValue(peer.uid, 2, true) : 0));
+          score += (1       * (!peer.uid ? computeScoreAlphaValue(peer.pubkey, 2, true) : 0));
+        }
+        score += (peer.isBma() ? (peer.isSsl() ? 0.01 : 0.001) :0); // If many endpoints: BMAS first, then BMA
+        return -score;
+      });
+
+      if (data.groupBy) {
+        var previousPeer;
+        data.peers.forEach(function(peer) {
+          peer.compacted = (previousPeer && peer[data.groupBy] && peer[data.groupBy] === previousPeer[data.groupBy]);
+          previousPeer = peer;
         });
-        data.listeners = [];
-      },
-
-      addListeners = function() {
-        data.listeners = [
-
-          // Listen for new block
-          data.bma.websocket.block().onListener(function(block) {
-            if (!block || data.loading) return;
-            var buid = [block.number, block.hash].join('-');
-            if (data.knownBlocks.indexOf(buid) === -1) {
-              console.debug('[network] Receiving block: ' + buid.substring(0, 20));
-              data.knownBlocks.push(buid);
-              // If first block: do NOT refresh peers (will be done in start() method)
-              var skipRefreshPeers = data.knownBlocks.length === 1;
-              if (!skipRefreshPeers) {
-                data.loading = true;
-                // We wait 2s when a new block is received, just to wait for network propagation
-                $timeout(function() {
-                  console.debug('[network] new block received by WS: will refresh peers');
-                  loadPeers();
-                }, 2000, false /*invokeApply*/);
-              }
-            }
-          }),
-
-          // Listen for new peer
-          data.bma.websocket.peer().onListener(function(json) {
-            if (!json || data.loading) return;
-            var newPeers = [];
-            addOrRefreshPeerFromJson(json, newPeers)
-              .then(function(hasUpdates) {
-                if (!hasUpdates) return;
-                if (newPeers.length>0) {
-                  flushNewPeersAndSort(newPeers, true);
-                }
-                else {
-                  console.debug('[network] [ws] Peers updated received');
-                  sortPeers(true);
-                }
-              });
-          })
-        ];
-      },
+      }
 
-      sort = function(options) {
-        options = options || {};
-        data.filter = options.filter ? angular.merge(data.filter, options.filter) : data.filter;
-        data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
-        sortPeers(false);
-      },
+      // Raise event on new main block
+      if (updateMainBuid && mainBlock && mainBlock.buid && (!data.mainBlock || data.mainBlock.buid !== mainBlock.buid)) {
+        data.mainBlock = mainBlock;
+        api.data.raise.mainBlockChanged(mainBlock);
+      }
 
-      start = function(bma, options) {
-        options = options || {};
-        return BMA.ready()
-          .then(function() {
-            close();
-
-            data.bma = bma || BMA;
-            data.filter = options.filter ? angular.merge(data.filter, options.filter) : data.filter;
-            data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
-            data.expertMode = angular.isDefined(options.expertMode) ? options.expertMode : data.expertMode;
-            data.timeout = angular.isDefined(options.timeout) ? options.timeout : csConfig.timeout;
-
-            // Init a min block number
-            data.minOnlineBlockNumber = data.mainBlock && data.mainBlock.buid && (parseInt(data.mainBlock.buid.split('-')[0]) - constants.MAX_BLOCK_OFFSET) || undefined;
-            if (data.minOnlineBlockNumber === undefined) {
-              return csCurrency.blockchain.current(true/*use cache*/)
-                .then(function(current) {
-                  data.minOnlineBlockNumber = current.number - constants.MAX_BLOCK_OFFSET;
-                });
+      // Raise event when changed
+      api.data.raise.changed(data); // raise event
+    },
+
+    removeListeners = function() {
+      _.forEach(data.listeners, function(remove){
+        remove();
+      });
+      data.listeners = [];
+    },
+
+    addListeners = function() {
+      data.listeners = [
+
+        // Listen for new block
+        data.bma.websocket.block().onListener(function(block) {
+          if (!block || data.loading) return;
+          var buid = [block.number, block.hash].join('-');
+          if (data.knownBlocks.indexOf(buid) === -1) {
+            console.debug('[network] Receiving block: ' + buid.substring(0, 20));
+            data.knownBlocks.push(buid);
+            // If first block: do NOT refresh peers (will be done in start() method)
+            var skipRefreshPeers = data.knownBlocks.length === 1;
+            if (!skipRefreshPeers) {
+              data.loading = true;
+              // We wait 2s when a new block is received, just to wait for network propagation
+              $timeout(function() {
+                console.debug('[network] new block received by WS: will refresh peers');
+                loadPeers();
+              }, 2000, false /*invokeApply*/);
             }
-          })
-          .then(function() {
-            console.info('[network] Starting network from [{0}]'.format(bma.server));
-            var now = Date.now();
-
-            addListeners();
-
-            return loadPeers()
-              .then(function(peers){
-                console.debug('[network] Started in '+(Date.now() - now)+'ms');
-                return peers;
+          }
+        }),
+
+        // Listen for new peer
+        data.bma.websocket.peer().onListener(function(json) {
+          if (!json || data.loading) return;
+          var newPeers = [];
+          addOrRefreshPeerFromJson(json, newPeers)
+            .then(function(hasUpdates) {
+              if (!hasUpdates) return;
+              if (newPeers.length>0) {
+                flushNewPeersAndSort(newPeers, true);
+              }
+              else {
+                console.debug('[network] [ws] Peers updated received');
+                sortPeers(true);
+              }
+            });
+        })
+      ];
+    },
+
+    sort = function(options) {
+      options = options || {};
+      data.filter = options.filter ? angular.merge(data.filter, options.filter) : data.filter;
+      data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
+      sortPeers(false);
+    },
+
+    start = function(bma, options) {
+      options = options || {};
+      return BMA.ready()
+        .then(function() {
+          close();
+
+          data.bma = bma || BMA;
+          data.filter = options.filter ? angular.merge(data.filter, options.filter) : data.filter;
+          data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
+          data.expertMode = angular.isDefined(options.expertMode) ? options.expertMode : data.expertMode;
+          data.timeout = angular.isDefined(options.timeout) ? options.timeout : csConfig.timeout;
+
+          // Init a min block number
+          data.minOnlineBlockNumber = data.mainBlock && data.mainBlock.buid && (parseInt(data.mainBlock.buid.split('-')[0]) - constants.MAX_BLOCK_OFFSET) || undefined;
+          if (data.minOnlineBlockNumber === undefined) {
+            return csCurrency.blockchain.current(true/*use cache*/)
+              .then(function(current) {
+                data.minOnlineBlockNumber = current.number - constants.MAX_BLOCK_OFFSET;
               });
-          });
-      },
-
-      close = function() {
-        if (data.bma) {
-          console.info('[network-service] Stopping...');
-          removeListeners();
-          resetData();
-        }
-      },
+          }
+        })
+        .then(function() {
+          console.info('[network] Starting network from [{0}]'.format(bma.server));
+          var now = Date.now();
 
-      isStarted = function() {
-        return !data.bma;
-      },
+          addListeners();
 
-      $q_started = function(callback) {
-        if (!isStarted()) { // start first
-          return start()
-            .then(function() {
-              return $q(callback);
+          return loadPeers()
+            .then(function(peers){
+              console.debug('[network] Started in '+(Date.now() - now)+'ms');
+              return peers;
             });
-        }
-        else {
-          return $q(callback);
-        }
-      },
-
-      getMainBlockUid = function() {
-        return $q_started(function(resolve, reject){
-          resolve (data.mainBuid);
         });
-      },
+    },
 
-      // Get peers on the main consensus blocks
-      getTrustedPeers = function() {
-        return $q_started(function(resolve, reject){
-          resolve(data.peers.reduce(function(res, peer){
-            return (peer.hasMainConsensusBlock && peer.uid) ? res.concat(peer) : res;
-          }, []));
-        });
+    close = function() {
+      if (data.bma) {
+        console.info('[network-service] Stopping...');
+        removeListeners();
+        resetData();
       }
-      ;
-
-    // Register extension points
-    api.registerEvent('data', 'changed');
-    api.registerEvent('data', 'mainBlockChanged');
-    api.registerEvent('data', 'rollback');
-
-    return {
-      id: id,
-      data: data,
-      start: start,
-      close: close,
-      hasPeers: hasPeers,
-      getPeers: getPeers,
-      sort: sort,
-      getTrustedPeers: getTrustedPeers,
-      getKnownBlocks: getKnownBlocks,
-      getMainBlockUid: getMainBlockUid,
-      loadPeers: loadPeers,
-      isBusy: isBusy,
-      // api extension
-      api: api
-    };
-  }
-
-  var service = new CsNetwork('default');
-
-  service.instance = function(id) {
-    return new CsNetwork(id);
-  };
+    },
+
+    isStarted = function() {
+      return !data.bma;
+    },
 
-  return service;
+    $q_started = function(callback) {
+      if (!isStarted()) { // start first
+        return start()
+          .then(function() {
+            return $q(callback);
+          });
+      }
+      else {
+        return $q(callback);
+      }
+    },
+
+    getMainBlockUid = function() {
+      return $q_started(function(resolve, reject){
+        resolve (data.mainBuid);
+      });
+    },
+
+    // Get peers on the main consensus blocks
+    getTrustedPeers = function() {
+      return $q_started(function(resolve, reject){
+        resolve(data.peers.reduce(function(res, peer){
+          return (peer.hasMainConsensusBlock && peer.uid) ? res.concat(peer) : res;
+        }, []));
+      });
+    }
+    ;
+
+  // Register extension points
+  api.registerEvent('data', 'changed');
+  api.registerEvent('data', 'mainBlockChanged');
+  api.registerEvent('data', 'rollback');
+
+  return {
+    data: data,
+    start: start,
+    close: close,
+    hasPeers: hasPeers,
+    getPeers: getPeers,
+    sort: sort,
+    getTrustedPeers: getTrustedPeers,
+    getKnownBlocks: getKnownBlocks,
+    getMainBlockUid: getMainBlockUid,
+    loadPeers: loadPeers,
+    isBusy: isBusy,
+    // api extension
+    api: api
+  };
 });
diff --git a/www/js/services/settings-services.js b/www/js/services/settings-services.js
index c4d03f90960e3ab27161b5c89b2976acfd35122c..d279f3799b4a98d0ac322cb9523599af9920b9a3 100644
--- a/www/js/services/settings-services.js
+++ b/www/js/services/settings-services.js
@@ -257,6 +257,12 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
       (csConfig.license[locale] ? csConfig.license[locale] : defaultSettings.license[csConfig.defaultLanguage || 'en'] || csConfig.license) : undefined;
   },
 
+  getFeedUrl = function() {
+    var locale = data.locale && data.locale.id || csConfig.defaultLanguage || 'en';
+    return (csConfig.feed && csConfig.feed.jsonFeed) ?
+      (csConfig.feed.jsonFeed[locale] ? csConfig.feed.jsonFeed[locale] : defaultSettings.feed.jsonFeed[csConfig.defaultLanguage || 'en'] || csConfig.feed) : undefined;
+  },
+
   // Detect locale successful changes, then apply to vendor libs
   onLocaleChange = function() {
     var locale = $translate.use();
@@ -344,6 +350,7 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
     store: store,
     restore: restore,
     getLicenseUrl: getLicenseUrl,
+    getFeedUrl: getFeedUrl,
     defaultSettings: defaultSettings,
     // api extension
     api: api,
diff --git a/www/js/services/tx-services.js b/www/js/services/tx-services.js
index 6a45626c79d98a679123ffcdf46303232935907b..67979b0ec1f238aca5878628e9c9541631b7a2e6 100644
--- a/www/js/services/tx-services.js
+++ b/www/js/services/tx-services.js
@@ -2,476 +2,462 @@
 angular.module('cesium.tx.services', ['ngApi', 'cesium.bma.services',
   'cesium.settings.services', 'cesium.wot.services' ])
 
-  .factory('csTx', function($q, $timeout, $filter, $translate, FileSaver, UIUtils, BMA, Api,
-                            csConfig, csSettings, csWot, csCurrency) {
-    'ngInject';
-
-    const defaultBMA = BMA;
-
-    function CsTx(id, BMA) {
-
-      BMA = BMA || defaultBMA;
-      var
-        api = new Api(this, "csTx-" + id);
-
-      function reduceTxAndPush(pubkey, txArray, result, processedTxMap, allowPendings) {
-        if (!txArray || !txArray.length) return; // Skip if empty
-
-        _.forEach(txArray, function(tx) {
-          if (tx.block_number !== null || allowPendings) {
-            var walletIsIssuer = false;
-            var otherIssuers = tx.issuers.reduce(function(res, issuer) {
-              walletIsIssuer = walletIsIssuer || (issuer === pubkey);
-              return (issuer !== pubkey) ? res.concat(issuer) : res;
-            }, []);
-            var otherRecipients = [],
-              outputBase,
-              sources = [],
-              lockedOutputs;
-
-            var amount = tx.outputs.reduce(function(sum, output, noffset) {
-              // FIXME duniter v1.4.13
-              var outputArray = (typeof output === 'string') ? output.split(':',3) : [output.amount,output.base,output.conditions];
-              outputBase = parseInt(outputArray[1]);
-              var outputAmount = powBase(parseInt(outputArray[0]), outputBase);
-              var outputCondition = outputArray[2];
-              var sigMatches =  BMA.regexp.TX_OUTPUT_SIG.exec(outputCondition);
-
-              // Simple unlock condition
-              if (sigMatches) {
-                var outputPubkey = sigMatches[1];
-                if (outputPubkey === pubkey) { // output is for the wallet
-                  if (!walletIsIssuer) {
-                    return sum + outputAmount;
-                  }
-                  // If pending: use output as new sources
-                  else if (tx.block_number === null) {
-                    sources.push({
-                      amount: parseInt(outputArray[0]),
-                      base: outputBase,
-                      type: 'T',
-                      identifier: tx.hash,
-                      noffset: noffset,
-                      consumed: false,
-                      conditions: outputCondition
-                    });
-                  }
-                }
-
-                // The output is for someone else
-                else {
-                  // Add into recipients list(if not a issuer)
-                  if (outputPubkey !== '' && !_.contains(otherIssuers, outputPubkey)) {
-                    otherRecipients.push(outputPubkey);
-                  }
-                  if (walletIsIssuer) {
-                    // TODO: should be fix, when TX has multiple issuers (need a repartition)
-                    return sum - outputAmount;
-                  }
-                }
+.factory('csTx', function($q, $timeout, $filter, $translate, FileSaver, UIUtils, BMA, Api,
+                          csConfig, csSettings, csWot, csCurrency) {
+  'ngInject';
+
+  var
+    api = new Api(this, "csTx");
+
+  function reduceTxAndPush(pubkey, txArray, result, processedTxMap, allowPendings) {
+    if (!txArray || !txArray.length) return; // Skip if empty
+
+    _.forEach(txArray, function(tx) {
+      if (tx.block_number !== null || allowPendings) {
+        var walletIsIssuer = false;
+        var otherIssuers = tx.issuers.reduce(function(res, issuer) {
+          walletIsIssuer = walletIsIssuer || (issuer === pubkey);
+          return (issuer !== pubkey) ? res.concat(issuer) : res;
+        }, []);
+        var otherRecipients = [],
+          outputBase,
+          sources = [],
+          lockedOutputs;
+
+        var amount = tx.outputs.reduce(function(sum, output, noffset) {
+          // FIXME duniter v1.4.13
+          var outputArray = (typeof output === 'string') ? output.split(':',3) : [output.amount,output.base,output.conditions];
+          outputBase = parseInt(outputArray[1]);
+          var outputAmount = powBase(parseInt(outputArray[0]), outputBase);
+          var outputCondition = outputArray[2];
+          var sigMatches =  BMA.regexp.TX_OUTPUT_SIG.exec(outputCondition);
+
+          // Simple unlock condition
+          if (sigMatches) {
+            var outputPubkey = sigMatches[1];
+            if (outputPubkey === pubkey) { // output is for the wallet
+              if (!walletIsIssuer) {
+                return sum + outputAmount;
               }
-
-              // Complex unlock condition, on the issuer pubkey
-              else if (outputCondition.indexOf('SIG('+pubkey+')') !== -1) {
-                var lockedOutput = BMA.tx.parseUnlockCondition(outputCondition);
-                if (lockedOutput) {
-                  // Add a source
-                  sources.push(angular.merge({
-                    amount: parseInt(outputArray[0]),
-                    base: outputBase,
-                    type: 'T',
-                    identifier: tx.hash,
-                    noffset: noffset,
-                    conditions: outputCondition,
-                    consumed: false
-                  }, lockedOutput));
-                  lockedOutput.amount = outputAmount;
-                  lockedOutputs = lockedOutputs || [];
-                  lockedOutputs.push(lockedOutput);
-                  console.debug('[tx] has locked output:', lockedOutput);
-
-                  return sum + outputAmount;
-                }
+              // If pending: use output as new sources
+              else if (tx.block_number === null) {
+                sources.push({
+                  amount: parseInt(outputArray[0]),
+                  base: outputBase,
+                  type: 'T',
+                  identifier: tx.hash,
+                  noffset: noffset,
+                  consumed: false,
+                  conditions: outputCondition
+                });
               }
-              return sum;
-            }, 0);
-
-            var txPubkeys = amount > 0 ? otherIssuers : otherRecipients;
-            var time = tx.time || tx.blockstampTime;
-
-            // Avoid duplicated tx, or tx to him self
-            var txKey = amount + ':' + tx.hash + ':' + time;
-            if (!processedTxMap[txKey]) {
-              processedTxMap[txKey] = true; // Mark as processed
-              var newTx = {
-                time: time,
-                amount: amount,
-                pubkey: txPubkeys.length === 1 ? txPubkeys[0] : undefined,
-                pubkeys: txPubkeys.length > 1 ? txPubkeys : undefined,
-                comment: tx.comment,
-                isUD: false,
-                hash: tx.hash,
-                locktime: tx.locktime,
-                block_number: tx.block_number
-              };
-
-                // If pending: store sources and inputs for a later use - see method processTransactionsAndSources()
-              if (walletIsIssuer && tx.block_number === null) {
-                newTx.inputs = tx.inputs;
-                newTx.sources = sources;
+            }
+
+            // The output is for someone else
+            else {
+              // Add into recipients list(if not a issuer)
+              if (outputPubkey !== '' && !_.contains(otherIssuers, outputPubkey)) {
+                otherRecipients.push(outputPubkey);
               }
-              if (lockedOutputs) {
-                newTx.lockedOutputs = lockedOutputs;
+              if (walletIsIssuer) {
+                // TODO: should be fix, when TX has multiple issuers (need a repartition)
+                return sum - outputAmount;
               }
-              result.push(newTx);
             }
           }
-        });
-      }
-
 
-      function loadTx(pubkey, fromTime) {
-        return $q(function(resolve, reject) {
-
-          var nowInSec = moment().utc().unix();
-          fromTime = fromTime || (nowInSec - csSettings.data.walletHistoryTimeSecond);
-          var tx = {
-            pendings: [],
-            validating: [],
-            history: [],
-            errors: []
+          // Complex unlock condition, on the issuer pubkey
+          else if (outputCondition.indexOf('SIG('+pubkey+')') !== -1) {
+            var lockedOutput = BMA.tx.parseUnlockCondition(outputCondition);
+            if (lockedOutput) {
+              // Add a source
+              sources.push(angular.merge({
+                amount: parseInt(outputArray[0]),
+                base: outputBase,
+                type: 'T',
+                identifier: tx.hash,
+                noffset: noffset,
+                conditions: outputCondition,
+                consumed: false
+              }, lockedOutput));
+              lockedOutput.amount = outputAmount;
+              lockedOutputs = lockedOutputs || [];
+              lockedOutputs.push(lockedOutput);
+              console.debug('[tx] has locked output:', lockedOutput);
+
+              return sum + outputAmount;
+            }
+          }
+          return sum;
+        }, 0);
+
+        var txPubkeys = amount > 0 ? otherIssuers : otherRecipients;
+        var time = tx.time || tx.blockstampTime;
+
+        // Avoid duplicated tx, or tx to him self
+        var txKey = amount + ':' + tx.hash + ':' + time;
+        if (!processedTxMap[txKey]) {
+          processedTxMap[txKey] = true; // Mark as processed
+          var newTx = {
+            time: time,
+            amount: amount,
+            pubkey: txPubkeys.length === 1 ? txPubkeys[0] : undefined,
+            pubkeys: txPubkeys.length > 1 ? txPubkeys : undefined,
+            comment: tx.comment,
+            isUD: false,
+            hash: tx.hash,
+            locktime: tx.locktime,
+            block_number: tx.block_number
           };
 
-          var processedTxMap = {};
+            // If pending: store sources and inputs for a later use - see method processTransactionsAndSources()
+          if (walletIsIssuer && tx.block_number === null) {
+            newTx.inputs = tx.inputs;
+            newTx.sources = sources;
+          }
+          if (lockedOutputs) {
+            newTx.lockedOutputs = lockedOutputs;
+          }
+          result.push(newTx);
+        }
+      }
+    });
+  }
 
-          var jobs = [
-            // get current block
-            csCurrency.blockchain.current(true),
 
-            // get pending tx
-            BMA.tx.history.pending({pubkey: pubkey})
-              .then(function (res) {
-                reduceTxAndPush(pubkey, res.history.sending, tx.pendings, processedTxMap, true /*allow pendings*/);
-                reduceTxAndPush(pubkey, res.history.pending, tx.pendings, processedTxMap, true /*allow pendings*/);
-              })
-          ];
-
-          // get TX history since
-          if (fromTime !== 'pending') {
-            var reduceTxFn = function (res) {
-              reduceTxAndPush(pubkey, res.history.sent, tx.history, processedTxMap, false);
-              reduceTxAndPush(pubkey, res.history.received, tx.history, processedTxMap, false);
-            };
-
-            // get TX from a given time
-            if (fromTime > 0) {
-              // Use slice, to be able to cache requests result
-              var sliceTime = csSettings.data.walletHistorySliceSecond;
-              fromTime = fromTime - (fromTime % sliceTime);
-              for(var i = fromTime; i - sliceTime < nowInSec; i += sliceTime)  {
-                jobs.push(BMA.tx.history.times({pubkey: pubkey, from: i, to: i+sliceTime-1}, true /*with cache*/)
-                  .then(reduceTxFn)
-                );
-              }
+  function loadTx(pubkey, fromTime) {
+    return $q(function(resolve, reject) {
 
-              // Last slide: no cache
-              jobs.push(BMA.tx.history.times({pubkey: pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999}, false/*no cache*/)
-                .then(reduceTxFn));
-            }
+      var nowInSec = moment().utc().unix();
+      fromTime = fromTime || (nowInSec - csSettings.data.walletHistoryTimeSecond);
+      var tx = {
+        pendings: [],
+        validating: [],
+        history: [],
+        errors: []
+      };
 
-            // get all TX
-            else {
-              jobs.push(BMA.tx.history.all({pubkey: pubkey})
-                .then(reduceTxFn)
-              );
-            }
+      var processedTxMap = {};
 
-            // get UD history
-            if (csSettings.data.showUDHistory && fromTime > 0) {
-              /*jobs.push(
-                BMA.ud.history({pubkey: pubkey})
-                  .then(function(res){
-                    udHistory = !res.history || !res.history.history ? [] :
-                      _.forEach(res.history.history, function(ud){
-                        if (ud.time < fromTime) return res; // skip to old UD
-                        var amount = powBase(ud.amount, ud.base);
-                        udHistory.push({
-                          time: ud.time,
-                          amount: amount,
-                          isUD: true,
-                          block_number: ud.block_number
-                        });
-                      });
-                  }));*/
-              // API extension
-              jobs.push(
-                api.data.raisePromise.loadUDs({
-                  pubkey: pubkey,
-                  fromTime: fromTime
-                })
-                  .then(function(res) {
-                    if (!res || !res.length) return;
-                    _.forEach(res, function(hits) {
-                      tx.history.push(hits);
-                    });
-                  })
+      var jobs = [
+        // get current block
+        csCurrency.blockchain.current(true),
 
-                  .catch(function(err) {
-                    console.debug('Error while loading UDs history, on extension point.');
-                    console.error(err);
-                  })
-              );
-            }
+        // get pending tx
+        BMA.tx.history.pending({pubkey: pubkey})
+          .then(function (res) {
+            reduceTxAndPush(pubkey, res.history.sending, tx.pendings, processedTxMap, true /*allow pendings*/);
+            reduceTxAndPush(pubkey, res.history.pending, tx.pendings, processedTxMap, true /*allow pendings*/);
+          })
+      ];
+
+      // get TX history since
+      if (fromTime !== 'pending') {
+        var reduceTxFn = function (res) {
+          reduceTxAndPush(pubkey, res.history.sent, tx.history, processedTxMap, false);
+          reduceTxAndPush(pubkey, res.history.received, tx.history, processedTxMap, false);
+        };
+
+        // get TX from a given time
+        if (fromTime > 0) {
+          // Use slice, to be able to cache requests result
+          var sliceTime = csSettings.data.walletHistorySliceSecond;
+          fromTime = fromTime - (fromTime % sliceTime);
+          for(var i = fromTime; i - sliceTime < nowInSec; i += sliceTime)  {
+            jobs.push(BMA.tx.history.times({pubkey: pubkey, from: i, to: i+sliceTime-1}, true /*with cache*/)
+              .then(reduceTxFn)
+            );
           }
 
-          // Execute jobs
-          $q.all(jobs)
-            .then(function(res){
-              var current = res[0];
-
-              // sort by time desc
-              tx.history.sort(function(tx1, tx2) {
-                return (tx2.time - tx1.time);
-              });
-              var firstValidatedTxIndex = tx.history.findIndex(function(tx){
-                return (tx.block_number <= current.number - csSettings.data.blockValidityWindow);
-              });
-              // remove validating from history
-              tx.validating = firstValidatedTxIndex > 0 ? tx.history.splice(0, firstValidatedTxIndex) : [];
-
-              tx.fromTime = fromTime !== 'pending' && fromTime || undefined;
-              tx.toTime = tx.history.length ? tx.history[0].time /*=max(tx.time)*/: tx.fromTime;
-
-              resolve(tx);
-            })
-            .catch(reject);
-        });
-      }
-
-      function powBase(amount, base) {
-        return base <= 0 ? amount : amount * Math.pow(10, base);
-      }
+          // Last slide: no cache
+          jobs.push(BMA.tx.history.times({pubkey: pubkey, from: nowInSec - (nowInSec % sliceTime), to: nowInSec+999999999}, false/*no cache*/)
+            .then(reduceTxFn));
+        }
 
-      function addSource(src, sources, sourcesIndexByKey) {
-        var srcKey = src.type+':'+src.identifier+':'+src.noffset;
-        if (angular.isUndefined(sourcesIndexByKey[srcKey])) {
-          sources.push(src);
-          sourcesIndexByKey[srcKey] = sources.length - 1;
+        // get all TX
+        else {
+          jobs.push(BMA.tx.history.all({pubkey: pubkey})
+            .then(reduceTxFn)
+          );
         }
-      }
 
-      function addSources(result, sources) {
-        _(sources).forEach(function(src) {
-          addSource(src, result.sources, result.sourcesIndexByKey);
-        });
-      }
+        // get UD history
+        if (csSettings.data.showUDHistory && fromTime > 0) {
+          /*jobs.push(
+            BMA.ud.history({pubkey: pubkey})
+              .then(function(res){
+                udHistory = !res.history || !res.history.history ? [] :
+                  _.forEach(res.history.history, function(ud){
+                    if (ud.time < fromTime) return res; // skip to old UD
+                    var amount = powBase(ud.amount, ud.base);
+                    udHistory.push({
+                      time: ud.time,
+                      amount: amount,
+                      isUD: true,
+                      block_number: ud.block_number
+                    });
+                  });
+              }));*/
+          // API extension
+          jobs.push(
+            api.data.raisePromise.loadUDs({
+              pubkey: pubkey,
+              fromTime: fromTime
+            })
+              .then(function(res) {
+                if (!res || !res.length) return;
+                _.forEach(res, function(hits) {
+                  tx.history.push(hits);
+                });
+              })
 
-      function loadSourcesAndBalance(pubkey) {
-        return BMA.tx.sources({pubkey: pubkey})
-          .then(function(res){
-            var data = {
-              sources: [],
-              sourcesIndexByKey: [],
-              balance: 0
-            };
-            if (res.sources && res.sources.length) {
-              _.forEach(res.sources, function(src) {
-                src.consumed = false;
-                data.balance += powBase(src.amount, src.base);
-              });
-              addSources(data, res.sources);
-            }
-            return data;
-          })
-          .catch(function(err) {
-            console.warn("[tx] Error while getting sources...", err);
-            throw err;
-          });
+              .catch(function(err) {
+                console.debug('Error while loading UDs history, on extension point.');
+                console.error(err);
+              })
+          );
+        }
       }
 
-      function loadData(pubkey, fromTime) {
-        var now = Date.now();
-
-        return $q.all([
-
-          // Load Sources
-          loadSourcesAndBalance(pubkey),
-
-          // Load Tx
-          loadTx(pubkey, fromTime)
-        ])
+      // Execute jobs
+      $q.all(jobs)
+        .then(function(res){
+          var current = res[0];
 
-          .then(function(res) {
-            // Copy sources and balance
-            var data = res[0];
-            data.tx = res[1];
-
-            var txPendings = [];
-            var txErrors = [];
-            var balanceFromSource = data.balance;
-            var balanceWithPending = data.balance;
-
-            function _processPendingTx(tx) {
-              var consumedSources = [];
-              var valid = true;
-              if (tx.amount > 0) { // do not check sources from received TX
-                valid = false;
-                // TODO get sources from the issuer ?
-              }
-              else {
-                _.find(tx.inputs, function(input) {
-                  var inputKey = input.split(':').slice(2).join(':');
-                  var srcIndex = data.sourcesIndexByKey[inputKey];
-                  if (angular.isDefined(srcIndex)) {
-                    consumedSources.push(data.sources[srcIndex]);
-                  }
-                  else {
-                    valid = false;
-                    return true; // break
-                  }
-                });
-                if (tx.sources) { // add source output
-                  addSources(data, tx.sources);
-                }
-                delete tx.sources;
-                delete tx.inputs;
-              }
-              if (valid) {
-                balanceWithPending += tx.amount; // update balance
-                txPendings.push(tx);
-                _.forEach(consumedSources, function(src) {
-                  src.consumed=true;
-                });
-              }
-              else {
-                txErrors.push(tx);
-              }
-            }
-
-            var txs = data.tx.pendings;
-            var retry = true;
-            while(txs && txs.length) {
-              // process TX pendings
-              _.forEach(txs, _processPendingTx);
-
-              // Retry once (TX could be chained and processed in a wrong order)
-              if (txErrors.length > 0 && txPendings.length > 0 && retry) {
-                txs = txErrors;
-                txErrors = [];
-                retry = false;
+          // sort by time desc
+          tx.history.sort(function(tx1, tx2) {
+            return (tx2.time - tx1.time);
+          });
+          var firstValidatedTxIndex = tx.history.findIndex(function(tx){
+            return (tx.block_number <= current.number - csSettings.data.blockValidityWindow);
+          });
+          // remove validating from history
+          tx.validating = firstValidatedTxIndex > 0 ? tx.history.splice(0, firstValidatedTxIndex) : [];
+
+          tx.fromTime = fromTime !== 'pending' && fromTime || undefined;
+          tx.toTime = tx.history.length ? tx.history[0].time /*=max(tx.time)*/: tx.fromTime;
+
+          resolve(tx);
+        })
+        .catch(reject);
+    });
+  }
+
+  function powBase(amount, base) {
+    return base <= 0 ? amount : amount * Math.pow(10, base);
+  }
+
+  function addSource(src, sources, sourcesIndexByKey) {
+    var srcKey = src.type+':'+src.identifier+':'+src.noffset;
+    if (angular.isUndefined(sourcesIndexByKey[srcKey])) {
+      sources.push(src);
+      sourcesIndexByKey[srcKey] = sources.length - 1;
+    }
+  }
+
+  function addSources(result, sources) {
+    _(sources).forEach(function(src) {
+      addSource(src, result.sources, result.sourcesIndexByKey);
+    });
+  }
+
+  function loadSourcesAndBalance(pubkey) {
+    return BMA.tx.sources({pubkey: pubkey})
+      .then(function(res){
+        var data = {
+          sources: [],
+          sourcesIndexByKey: [],
+          balance: 0
+        };
+        if (res.sources && res.sources.length) {
+          _.forEach(res.sources, function(src) {
+            src.consumed = false;
+            data.balance += powBase(src.amount, src.base);
+          });
+          addSources(data, res.sources);
+        }
+        return data;
+      })
+      .catch(function(err) {
+        console.warn("[tx] Error while getting sources...", err);
+        throw err;
+      });
+  }
+
+  function loadData(pubkey, fromTime) {
+    var now = Date.now();
+
+    return $q.all([
+
+      // Load Sources
+      loadSourcesAndBalance(pubkey),
+
+      // Load Tx
+      loadTx(pubkey, fromTime)
+    ])
+
+      .then(function(res) {
+        // Copy sources and balance
+        var data = res[0];
+        data.tx = res[1];
+
+        var txPendings = [];
+        var txErrors = [];
+        var balanceFromSource = data.balance;
+        var balanceWithPending = data.balance;
+
+        function _processPendingTx(tx) {
+          var consumedSources = [];
+          var valid = true;
+          if (tx.amount > 0) { // do not check sources from received TX
+            valid = false;
+            // TODO get sources from the issuer ?
+          }
+          else {
+            _.find(tx.inputs, function(input) {
+              var inputKey = input.split(':').slice(2).join(':');
+              var srcIndex = data.sourcesIndexByKey[inputKey];
+              if (angular.isDefined(srcIndex)) {
+                consumedSources.push(data.sources[srcIndex]);
               }
               else {
-                txs = null;
+                valid = false;
+                return true; // break
               }
-            }
-
-            data.tx = data.tx || {};
-            data.tx.pendings = txPendings.sort(function(tx1, tx2) {
-              return (tx2.time - tx1.time);
             });
-            data.tx.errors = txErrors.sort(function(tx1, tx2) {
-              return (tx2.time - tx1.time);
+            if (tx.sources) { // add source output
+              addSources(data, tx.sources);
+            }
+            delete tx.sources;
+            delete tx.inputs;
+          }
+          if (valid) {
+            balanceWithPending += tx.amount; // update balance
+            txPendings.push(tx);
+            _.forEach(consumedSources, function(src) {
+              src.consumed=true;
             });
-            // Negative balance not allow (use only source's balance) - fix #769
-            data.balance = (balanceWithPending < 0) ? balanceFromSource : balanceWithPending;
-
-            // Will add uid (+ plugin will add name, avatar, etc. if enable)
-            var allTx = (data.tx.history || []).concat(data.tx.validating||[], data.tx.pendings||[], data.tx.errors||[]);
-            return csWot.extendAll(allTx, 'pubkey')
-              .then(function() {
-                console.debug('[tx] TX and sources loaded in '+ (Date.now()-now) +'ms');
-                return data;
-              });
-          })
-          .catch(function(err) {
-            console.warn("[tx] Error while getting sources and tx...", err);
-            throw err;
-          });
-      }
-
-      function loadSources(pubkey) {
-        console.debug("[tx] Loading sources for " + pubkey.substring(0,8));
-        return loadData(pubkey, 'pending');
-      }
+          }
+          else {
+            txErrors.push(tx);
+          }
+        }
 
-      // Download TX history file
-      function downloadHistoryFile(pubkey, options) {
-
-        options = options || {};
-        options.fromTime = options.fromTime || -1;
-
-        console.debug("[tx] Exporting TX history for pubkey [{0}]".format(pubkey.substr(0,8)));
-
-        return $q.all([
-          $translate(['ACCOUNT.HEADERS.TIME',
-            'COMMON.UID',
-            'COMMON.PUBKEY',
-            'COMMON.UNIVERSAL_DIVIDEND',
-            'ACCOUNT.HEADERS.AMOUNT',
-            'ACCOUNT.HEADERS.COMMENT']),
-          csCurrency.blockchain.current(true/*withCache*/),
-          loadData(pubkey, options.fromTime)
-        ])
-          .then(function(result){
-            var translations = result[0];
-            var currentBlock = result[1];
-            var currentTime = (currentBlock && currentBlock.medianTime) || moment().utc().unix();
-            var currency = currentBlock && currentBlock.currency;
-
-            var data = result[2];
-
-            // no TX
-            if (!data || !data.tx || !data.tx.history) {
-              return UIUtils.toast.show('INFO.EMPTY_TX_HISTORY');
-            }
+        var txs = data.tx.pendings;
+        var retry = true;
+        while(txs && txs.length) {
+          // process TX pendings
+          _.forEach(txs, _processPendingTx);
+
+          // Retry once (TX could be chained and processed in a wrong order)
+          if (txErrors.length > 0 && txPendings.length > 0 && retry) {
+            txs = txErrors;
+            txErrors = [];
+            retry = false;
+          }
+          else {
+            txs = null;
+          }
+        }
 
-            return $translate('ACCOUNT.FILE_NAME', {currency: currency, pubkey: pubkey, currentTime : currentTime})
-              .then(function(filename){
-
-                var formatDecimal = $filter('formatDecimal');
-                var medianDate = $filter('medianDate');
-                var formatSymbol = $filter('currencySymbolNoHtml');
-
-                var headers = [
-                  translations['ACCOUNT.HEADERS.TIME'],
-                  translations['COMMON.UID'],
-                  translations['COMMON.PUBKEY'],
-                  translations['ACCOUNT.HEADERS.AMOUNT'] + ' (' + formatSymbol(currency) + ')',
-                  translations['ACCOUNT.HEADERS.COMMENT']
-                ];
-                var content = data.tx.history.concat(data.tx.validating).reduce(function(res, tx){
-                  return res.concat([
-                    medianDate(tx.time),
-                    tx.uid,
-                    tx.pubkey,
-                    formatDecimal(tx.amount/100),
-                    '"' + (tx.isUD ? translations['COMMON.UNIVERSAL_DIVIDEND'] : tx.comment) + '"'
-                  ].join(';') + '\n');
-                }, [headers.join(';') + '\n']);
-
-                var file = new Blob(content, {type: 'text/plain; charset=utf-8'});
-                FileSaver.saveAs(file, filename);
-              });
+        data.tx = data.tx || {};
+        data.tx.pendings = txPendings.sort(function(tx1, tx2) {
+          return (tx2.time - tx1.time);
+        });
+        data.tx.errors = txErrors.sort(function(tx1, tx2) {
+          return (tx2.time - tx1.time);
+        });
+        // Negative balance not allow (use only source's balance) - fix #769
+        data.balance = (balanceWithPending < 0) ? balanceFromSource : balanceWithPending;
+
+        // Will add uid (+ plugin will add name, avatar, etc. if enable)
+        var allTx = (data.tx.history || []).concat(data.tx.validating||[], data.tx.pendings||[], data.tx.errors||[]);
+        return csWot.extendAll(allTx, 'pubkey')
+          .then(function() {
+            console.debug('[tx] TX and sources loaded in '+ (Date.now()-now) +'ms');
+            return data;
           });
-      }
-
-      // Register extension points
-      api.registerEvent('data', 'loadUDs');
-
-      return {
-        id: id,
-        load: loadData,
-        loadSources: loadSources,
-        downloadHistoryFile: downloadHistoryFile,
-        // api extension
-        api: api
-      };
-    }
-
-    var service = new CsTx('default');
+      })
+      .catch(function(err) {
+        console.warn("[tx] Error while getting sources and tx...", err);
+        throw err;
+      });
+  }
+
+  function loadSources(pubkey) {
+    console.debug("[tx] Loading sources for " + pubkey.substring(0,8));
+    return loadData(pubkey, 'pending');
+  }
+
+  // Download TX history file
+  function downloadHistoryFile(pubkey, options) {
+
+    options = options || {};
+    options.fromTime = options.fromTime || -1;
+
+    console.debug("[tx] Exporting TX history for pubkey [{0}]".format(pubkey.substr(0,8)));
+
+    return $q.all([
+      $translate(['ACCOUNT.HEADERS.TIME',
+        'COMMON.UID',
+        'COMMON.PUBKEY',
+        'COMMON.UNIVERSAL_DIVIDEND',
+        'ACCOUNT.HEADERS.AMOUNT',
+        'ACCOUNT.HEADERS.COMMENT']),
+      csCurrency.blockchain.current(true/*withCache*/),
+      loadData(pubkey, options.fromTime)
+    ])
+      .then(function(result){
+        var translations = result[0];
+        var currentBlock = result[1];
+        var currentTime = (currentBlock && currentBlock.medianTime) || moment().utc().unix();
+        var currency = currentBlock && currentBlock.currency;
+
+        var data = result[2];
+
+        // no TX
+        if (!data || !data.tx || !data.tx.history) {
+          return UIUtils.toast.show('INFO.EMPTY_TX_HISTORY');
+        }
 
-    service.instance = function(id, bma) {
-      return new CsTx(id, bma);
-    };
-    return service;
-  });
+        return $translate('ACCOUNT.FILE_NAME', {currency: currency, pubkey: pubkey, currentTime : currentTime})
+          .then(function(filename){
+
+            var formatDecimal = $filter('formatDecimal');
+            var medianDate = $filter('medianDate');
+            var formatSymbol = $filter('currencySymbolNoHtml');
+
+            var headers = [
+              translations['ACCOUNT.HEADERS.TIME'],
+              translations['COMMON.UID'],
+              translations['COMMON.PUBKEY'],
+              translations['ACCOUNT.HEADERS.AMOUNT'] + ' (' + formatSymbol(currency) + ')',
+              translations['ACCOUNT.HEADERS.COMMENT']
+            ];
+            var content = data.tx.history.concat(data.tx.validating).reduce(function(res, tx){
+              return res.concat([
+                medianDate(tx.time),
+                tx.uid,
+                tx.pubkey,
+                formatDecimal(tx.amount/100),
+                '"' + (tx.isUD ? translations['COMMON.UNIVERSAL_DIVIDEND'] : tx.comment) + '"'
+              ].join(';') + '\n');
+            }, [headers.join(';') + '\n']);
+
+            var file = new Blob(content, {type: 'text/plain; charset=utf-8'});
+            FileSaver.saveAs(file, filename);
+          });
+      });
+  }
+
+  // Register extension points
+  api.registerEvent('data', 'loadUDs');
+
+  return {
+    load: loadData,
+    loadSources: loadSources,
+    downloadHistoryFile: downloadHistoryFile,
+    // api extension
+    api: api
+  };
+});
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index c2aa7b25d4b1ac65620075f0bb92e58507e68326..e182342a3b6d06105f89404fbd7b07d8f0d6725b 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;
@@ -695,12 +695,21 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
       return data;
     },
 
-    loadRequirements = function(withCache) {
+    loadRequirements = function(withCache, secondTry) {
       // Clean existing events
       cleanEventsByContext('requirements');
 
       // Get requirements
-      return csWot.loadRequirements(data, withCache);
+      return csWot.loadRequirements(data, withCache)
+        .catch(function(err) {
+          // Retry once (can be a timeout, because Duniter node are long to response)
+          if (!secondTry) {
+            console.error("[wallet] Unable to load requirements: Will retrying... ", err);
+            UIUtils.loading.update({template: "COMMON.LOADING_WAIT"});
+            return loadRequirements(withCache, true);
+          }
+          throw err;
+        });
     },
 
     loadTxAndSources = function(fromTime) {
@@ -1614,6 +1623,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 +1963,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 +2002,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 +2020,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,11 +2035,11 @@ 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 || []).reduce(function(res, wallet) {
           return Math.max(res, wallet.id);
-        }, 0) || data.childrenCount || 0 )+ 1;
+        }, 0) + 1;
       return service.instance(walletId, BMA);
     },
 
@@ -2036,6 +2050,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 +2172,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 +2198,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 +2209,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 +2220,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 +2422,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 +2442,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 +2450,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 a040950807b9d6b398ca3402107e8765f0f09249..94b656df31359e67edb69283f3caacbf5e9cbb95 100644
--- a/www/js/services/wot-services.js
+++ b/www/js/services/wot-services.js
@@ -5,1212 +5,1207 @@ 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) {
-
-    var
-      api = new Api(this, "csWot-" + id),
-      cachePrefix = 'csWot-',
-      identityCache = csCache.get(cachePrefix + 'idty-', csCache.constants.MEDIUM),
-      requirementsCache = csCache.get(cachePrefix + 'requirements-', csCache.constants.MEDIUM),
-
-      // Add id, and remove duplicated id
-      _addUniqueIds = function(idties) {
-        var idtyKeys = {};
-        return idties.reduce(function(res, idty) {
-          idty.id = idty.id || idty.uid + '-' + idty.pubkey;
-          if (!idtyKeys[idty.id]) {
-            idtyKeys[idty.id] = true;
-            return res.concat(idty);
-          }
-          return res;
-        }, []);
-      },
-
-      _sortAndSliceIdentities = function(idties, offset, size) {
-        offset = offset || 0;
-
-        // Add unique ids
-        idties = _addUniqueIds(idties);
-
-        // Sort by block and
-        idties = _.sortBy(idties, function(idty){
-          var score = 1;
-          score += (1000000 * (idty.block));
-          score += (10      * (900 - idty.uid.toLowerCase().charCodeAt(0)));
-          return -score;
-        });
-        if (angular.isDefined(size) && idties.length > size) {
-          idties = idties.slice(offset, offset+size); // limit if more than expected size
-        }
-
 
-        return idties;
-      },
-
-      _sortCertifications = function(certifications) {
-        certifications = _.sortBy(certifications, function(cert){
-          var score = 1;
-          score += (1000000000000 * (cert.expiresIn ? cert.expiresIn : 0));
-          score += (10000000      * (cert.isMember ? 1 : 0));
-          score += (10            * (cert.block ? cert.block : 0));
-          return -score;
-        });
-        return certifications;
-      },
-
-      _resetRequirements = function(data) {
-        data.requirements = {
-          loaded: false,
-          meta: {},
-          hasSelf: false,
-          needSelf: true,
-          needMembership: true,
-          canMembershipOut: false,
-          needRenew: false,
-          pendingMembership: false,
-          isMember: false,
-          wasMember: false,
-          certificationCount: 0,
-          needCertifications: false,
-          needCertificationCount: 0,
-          willNeedCertificationCount: 0,
-          alternatives: undefined
-        };
-        data.blockUid = null;
-        data.isMember = false;
-        data.sigDate = null;
-        data.hasSelf = false;
-      },
-
-      _fillRequirements = function(requirements, currencyParameters) {
-        // Add useful custom fields
-        requirements.hasSelf = !!requirements.meta.timestamp;
-        requirements.needSelf = !requirements.hasSelf || requirements.meta.invalid;
-        requirements.wasMember = angular.isDefined(requirements.wasMember) ? requirements.wasMember : false; // Compat with Duniter 0.9
-        requirements.needMembership = (!requirements.revoked && requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn <= 0 && !requirements.wasMember);
-        requirements.needRenew = (!requirements.needMembership && !requirements.revoked &&
-          requirements.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership &&
-          requirements.membershipPendingExpiresIn <= 0) ||
-          (requirements.wasMember && !requirements.revoked && requirements.membershipExpiresIn === 0 &&
-          requirements.membershipPendingExpiresIn === 0);
-        requirements.canMembershipOut = (!requirements.revoked && requirements.membershipExpiresIn > 0);
-        requirements.pendingMembership = (!requirements.revoked && requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn > 0);
-        requirements.isMember = (!requirements.revoked && requirements.membershipExpiresIn > 0);
-        requirements.blockUid = requirements.meta.timestamp;
-        // Force certification count to 0, is not a member yet - fix #269
-        requirements.certificationCount = ((requirements.isMember || (requirements.wasMember && !requirements.expired)) && requirements.certifications) ? requirements.certifications.length : 0;
-        requirements.willExpireCertificationCount = requirements.certifications ? requirements.certifications.reduce(function(count, cert){
-          return count + (cert.expiresIn <= csSettings.data.timeWarningExpire ? 1 : 0);
-        }, 0) : 0;
-        requirements.willExpire = requirements.willExpireCertificationCount > 0;
-        requirements.pendingRevocation = !requirements.revoked && !!requirements.revocation_sig;
-        //requirements.outdistanced = requirements.outdistanced; // outdistanced is always present in requirement - see #777
-
-        // Fix pending certifications count - Fix #624
-        if (!requirements.isMember && !requirements.wasMember) {
-          var certifiers = _.union(
-            _.pluck(requirements.pendingCerts || [], 'from'),
-            _.pluck(requirements.certifications || [], 'from')
-          );
-          requirements.pendingCertificationCount = _.size(certifiers);
-        }
-        else {
-          requirements.pendingCertificationCount = angular.isDefined(requirements.pendingCerts) ? requirements.pendingCerts.length : 0 ;
+  var
+    api = new Api(this, "csWot"),
+    cachePrefix = 'csWot-',
+    identityCache = csCache.get(cachePrefix + 'idty-', csCache.constants.MEDIUM),
+    requirementsCache = csCache.get(cachePrefix + 'requirements-', csCache.constants.MEDIUM),
+
+    // Add id, and remove duplicated id
+    _addUniqueIds = function(idties) {
+      var idtyKeys = {};
+      return idties.reduce(function(res, idty) {
+        idty.id = idty.id || idty.uid + '-' + idty.pubkey;
+        if (!idtyKeys[idty.id]) {
+          idtyKeys[idty.id] = true;
+          return res.concat(idty);
         }
+        return res;
+      }, []);
+    },
+
+    _sortAndSliceIdentities = function(idties, offset, size) {
+      offset = offset || 0;
+
+      // Add unique ids
+      idties = _addUniqueIds(idties);
+
+      // Sort by block and
+      idties = _.sortBy(idties, function(idty){
+        var score = 1;
+        score += (1000000 * (idty.block));
+        score += (10      * (900 - idty.uid.toLowerCase().charCodeAt(0)));
+        return -score;
+      });
+      if (angular.isDefined(size) && idties.length > size) {
+        idties = idties.slice(offset, offset+size); // limit if more than expected size
+      }
 
-        // Compute
-        requirements.needCertificationCount = (!requirements.needSelf && (requirements.certificationCount < currencyParameters.sigQty)) ?
-          (currencyParameters.sigQty - requirements.certificationCount) : 0;
-        requirements.willNeedCertificationCount = (!requirements.needMembership && !requirements.needCertificationCount &&
-        (requirements.certificationCount - requirements.willExpireCertificationCount) < currencyParameters.sigQty) ?
-          (currencyParameters.sigQty - requirements.certificationCount + requirements.willExpireCertificationCount) : 0;
-
-        // Mark as loaded - need by csWallet.isDataLoaded()
-        requirements.loaded = true;
-
-
-        return requirements;
-      },
 
-      _fillIdentitiesMeta = function(identities) {
-        if (!identities) return $q.when(identities);
+      return idties;
+    },
+
+    _sortCertifications = function(certifications) {
+      certifications = _.sortBy(certifications, function(cert){
+        var score = 1;
+        score += (1000000000000 * (cert.expiresIn ? cert.expiresIn : 0));
+        score += (10000000      * (cert.isMember ? 1 : 0));
+        score += (10            * (cert.block ? cert.block : 0));
+        return -score;
+      });
+      return certifications;
+    },
+
+    _resetRequirements = function(data) {
+      data.requirements = {
+        loaded: false,
+        meta: {},
+        hasSelf: false,
+        needSelf: true,
+        needMembership: true,
+        canMembershipOut: false,
+        needRenew: false,
+        pendingMembership: false,
+        isMember: false,
+        wasMember: false,
+        certificationCount: 0,
+        needCertifications: false,
+        needCertificationCount: 0,
+        willNeedCertificationCount: 0,
+        alternatives: undefined
+      };
+      data.blockUid = null;
+      data.isMember = false;
+      data.sigDate = null;
+      data.hasSelf = false;
+    },
+
+    _fillRequirements = function(requirements, currencyParameters) {
+      // Add useful custom fields
+      requirements.hasSelf = !!requirements.meta.timestamp;
+      requirements.needSelf = !requirements.hasSelf || requirements.meta.invalid;
+      requirements.wasMember = angular.isDefined(requirements.wasMember) ? requirements.wasMember : false; // Compat with Duniter 0.9
+      requirements.needMembership = (!requirements.revoked && requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn <= 0 && !requirements.wasMember);
+      requirements.needRenew = (!requirements.needMembership && !requirements.revoked &&
+        requirements.membershipExpiresIn <= csSettings.data.timeWarningExpireMembership &&
+        requirements.membershipPendingExpiresIn <= 0) ||
+        (requirements.wasMember && !requirements.revoked && requirements.membershipExpiresIn === 0 &&
+        requirements.membershipPendingExpiresIn === 0);
+      requirements.canMembershipOut = (!requirements.revoked && requirements.membershipExpiresIn > 0);
+      requirements.pendingMembership = (!requirements.revoked && requirements.membershipExpiresIn <= 0 && requirements.membershipPendingExpiresIn > 0);
+      requirements.isMember = (!requirements.revoked && requirements.membershipExpiresIn > 0);
+      requirements.blockUid = requirements.meta.timestamp;
+      // Force certification count to 0, is not a member yet - fix #269
+      requirements.certificationCount = ((requirements.isMember || (requirements.wasMember && !requirements.expired)) && requirements.certifications) ? requirements.certifications.length : 0;
+      requirements.willExpireCertificationCount = requirements.certifications ? requirements.certifications.reduce(function(count, cert){
+        return count + (cert.expiresIn <= csSettings.data.timeWarningExpire ? 1 : 0);
+      }, 0) : 0;
+      requirements.willExpire = requirements.willExpireCertificationCount > 0;
+      requirements.pendingRevocation = !requirements.revoked && !!requirements.revocation_sig;
+      //requirements.outdistanced = requirements.outdistanced; // outdistanced is always present in requirement - see #777
+
+      // Fix pending certifications count - Fix #624
+      if (!requirements.isMember && !requirements.wasMember) {
+        var certifiers = _.union(
+          _.pluck(requirements.pendingCerts || [], 'from'),
+          _.pluck(requirements.certifications || [], 'from')
+        );
+        requirements.pendingCertificationCount = _.size(certifiers);
+      }
+      else {
+        requirements.pendingCertificationCount = angular.isDefined(requirements.pendingCerts) ? requirements.pendingCerts.length : 0 ;
+      }
 
-        var blocks = [];
-        _.forEach(identities, function(identity) {
-          var blockUid = identity.meta.timestamp.split('-', 2);
-          identity.meta.number = parseInt(blockUid[0]);
-          identity.meta.hash = blockUid[1];
-          identity.meta.sig = identity.meta.sig || identity.sig;
-          delete identity.sig;
-          blocks.push(identity.meta.number);
-        });
+      // Compute
+      requirements.needCertificationCount = (!requirements.needSelf && (requirements.certificationCount < currencyParameters.sigQty)) ?
+        (currencyParameters.sigQty - requirements.certificationCount) : 0;
+      requirements.willNeedCertificationCount = (!requirements.needMembership && !requirements.needCertificationCount &&
+      (requirements.certificationCount - requirements.willExpireCertificationCount) < currencyParameters.sigQty) ?
+        (currencyParameters.sigQty - requirements.certificationCount + requirements.willExpireCertificationCount) : 0;
+
+      // Mark as loaded - need by csWallet.isDataLoaded()
+      requirements.loaded = true;
+
+
+      return requirements;
+    },
+
+    _fillIdentitiesMeta = function(identities) {
+      if (!identities) return $q.when(identities);
+
+      var blocks = [];
+      _.forEach(identities, function(identity) {
+        var blockUid = identity.meta.timestamp.split('-', 2);
+        identity.meta.number = parseInt(blockUid[0]);
+        identity.meta.hash = blockUid[1];
+        identity.meta.sig = identity.meta.sig || identity.sig;
+        delete identity.sig;
+        blocks.push(identity.meta.number);
+      });
+
+      // Get identities blocks, to fill self and revocation time
+      return BMA.blockchain.blocks(_.uniq(blocks))
+        .then(function(blocks) {
+          _.forEach(identities, function(identity) {
+            var block = _.findWhere(blocks, {number: identity.meta.number});
+            identity.meta.time = block && block.medianTime;
+
+            // Check if self has been done on a valid block
+            if (block && identity.meta.number !== 0 && identity.meta.hash !== block.hash) {
+              identity.meta.invalid = true;
+            }
+          });
 
-        // Get identities blocks, to fill self and revocation time
-        return BMA.blockchain.blocks(_.uniq(blocks))
-          .then(function(blocks) {
+          return identities;
+        })
+        .catch(function(err){
+          // Special case for currency init (root block not exists): use now
+          if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND) {
             _.forEach(identities, function(identity) {
-              var block = _.findWhere(blocks, {number: identity.meta.number});
-              identity.meta.time = block && block.medianTime;
-
-              // Check if self has been done on a valid block
-              if (block && identity.meta.number !== 0 && identity.meta.hash !== block.hash) {
-                identity.meta.invalid = true;
+              if (identity.number === 0) {
+                identity.meta.time = moment().utc().unix();
               }
             });
-
             return identities;
-          })
-          .catch(function(err){
-            // Special case for currency init (root block not exists): use now
-            if (err && err.ucode == BMA.errorCodes.BLOCK_NOT_FOUND) {
-              _.forEach(identities, function(identity) {
-                if (identity.number === 0) {
-                  identity.meta.time = moment().utc().unix();
-                }
-              });
-              return identities;
-            }
-            else {
-              throw err;
-            }
-          });
-      },
-
-      loadRequirements = function(inputData, withCache) {
-        if (!inputData || (!inputData.pubkey && !inputData.uid)) return $q.when(inputData);
-
-        var cacheKey =  inputData.pubkey||inputData.uid;
-        var data = (withCache !== false) ? requirementsCache.get(cacheKey) : null;
-        if (data) {
-          console.debug("[wot] Requirements " + cacheKey + " found in cache");
-          // Update data with cache
-          angular.merge(inputData, data);
-          return $q.when(data);
-        }
-        data = {pubkey: inputData.pubkey, uid: inputData.uid};
+          }
+          else {
+            throw err;
+          }
+        });
+    },
+
+    loadRequirements = function(inputData, withCache) {
+      if (!inputData || (!inputData.pubkey && !inputData.uid)) return $q.when(inputData);
+
+      var cacheKey =  inputData.pubkey||inputData.uid;
+      var data = (withCache !== false) ? requirementsCache.get(cacheKey) : null;
+      if (data) {
+        console.debug("[wot] Requirements " + cacheKey + " found in cache");
+        // Update data with cache
+        angular.merge(inputData, data);
+        return $q.when(data);
+      }
+      data = {pubkey: inputData.pubkey, uid: inputData.uid};
 
-        var now = Date.now();
+      // Alert user, when request is too long (> 2s)
+      $timeout(function() {
+        if (!data.requirements || !data.requirements.loaded) UIUtils.loading.update({template: "COMMON.LOADING_WAIT"});
+      }, 2000);
 
-        return $q.all([
-          // Get currency
-          csCurrency.get(),
+      var now = Date.now();
+      return $q.all([
+        // Get currency
+        csCurrency.get(),
 
-          // Get requirements
-          BMA.wot.requirements({pubkey: data.pubkey||data.uid}, false/*no cache*/)
-            .then(function(res) {
-              return _fillIdentitiesMeta(res && res.identities);
-            })
-        ])
-          .then(function(res){
-            var currency = res[0];
-            var identities = res[1];
-
-            if (!identities || !identities.length) return;
-
-            // Sort to select the best identity
-            if (identities.length > 1) {
-              // Select the best identity, by sorting using this order
-              //  - same wallet uid
-              //  - is member
-              //  - has a pending membership
-              //  - is not expired (in sandbox)
-              //  - is not outdistanced
-              //  - if has certifications
-              //      max(count(certification)
-              //    else
-              //      max(membershipPendingExpiresIn) = must recent membership
-              identities = _.sortBy(identities, function(idty) {
-                var score = 0;
-                score += (1000000000000* ((data.uid && idty.uid === data.uid) ? 1 : 0));
-                score += (100000000000 * (!idty.meta.invalid ? 1 : 0));
-                score += (10000000000  * ((data.blockUid && idty.meta.timestamp && idty.meta.timestamp === data.blockUid) ? 1 : 0));
-                score += (1000000000   * (idty.membershipExpiresIn > 0 ? 1 : 0));
-                score += (100000000    * (idty.membershipPendingExpiresIn > 0 ? 1 : 0));
-                score += (10000000     * (!idty.expired ? 1 : 0));
-                score += (1000000      * (!idty.outdistanced ? 1 : 0));
-                score += (100000       * (idty.wasMember ? 1 : 0));
-                var certCount = !idty.expired && idty.certifications ? idty.certifications.length : 0;
-                score += (1            * (certCount ? certCount : 0));
-                score += (1            * (!certCount && idty.membershipPendingExpiresIn > 0 ? idty.membershipPendingExpiresIn/1000 : 0));
-                return -score;
-              });
-              console.debug('[wot] Found {0} identities (in requirements). Will selected the best one'.format(identities.length));
-            }
+        // Get requirements
+        BMA.wot.requirements({pubkey: data.pubkey||data.uid}, false/*no cache*/)
+          .then(function(res) {
+            return _fillIdentitiesMeta(res && res.identities);
+          })
+      ])
+        .then(function(res){
+          var currency = res[0];
+          var identities = res[1];
+
+          if (!identities || !identities.length) return;
+
+          // Sort to select the best identity
+          if (identities.length > 1) {
+            // Select the best identity, by sorting using this order
+            //  - same wallet uid
+            //  - is member
+            //  - has a pending membership
+            //  - is not expired (in sandbox)
+            //  - is not outdistanced
+            //  - if has certifications
+            //      max(count(certification)
+            //    else
+            //      max(membershipPendingExpiresIn) = must recent membership
+            identities = _.sortBy(identities, function(idty) {
+              var score = 0;
+              score += (1000000000000* ((data.uid && idty.uid === data.uid) ? 1 : 0));
+              score += (100000000000 * (!idty.meta.invalid ? 1 : 0));
+              score += (10000000000  * ((data.blockUid && idty.meta.timestamp && idty.meta.timestamp === data.blockUid) ? 1 : 0));
+              score += (1000000000   * (idty.membershipExpiresIn > 0 ? 1 : 0));
+              score += (100000000    * (idty.membershipPendingExpiresIn > 0 ? 1 : 0));
+              score += (10000000     * (!idty.expired ? 1 : 0));
+              score += (1000000      * (!idty.outdistanced ? 1 : 0));
+              score += (100000       * (idty.wasMember ? 1 : 0));
+              var certCount = !idty.expired && idty.certifications ? idty.certifications.length : 0;
+              score += (1            * (certCount ? certCount : 0));
+              score += (1            * (!certCount && idty.membershipPendingExpiresIn > 0 ? idty.membershipPendingExpiresIn/1000 : 0));
+              return -score;
+            });
+            console.debug('[wot] Found {0} identities (in requirements). Will selected the best one'.format(identities.length));
+          }
 
-            // Select the first identity
-            data.requirements = _fillRequirements(identities[0], currency.parameters);
-
-            // Copy some useful properties into data
-            data.pubkey = data.requirements.pubkey;
-            data.uid = data.requirements.uid;
-            data.isMember =  data.requirements.isMember;
-            data.blockUid =  data.requirements.meta &&  data.requirements.meta.timestamp;
-            data.hasSelf =  data.requirements.hasSelf;
-            data.sigDate =  data.requirements.meta && data.requirements.meta.time;
-
-            // Prepare alternatives identities if any
-            if (!data.requirements.isMember && !data.requirements.wasMember && identities.length > 1) {
-              data.requirements.alternatives = identities.splice(1);
-              _.forEach(data.requirements.alternatives, function(requirements) {
-                _fillRequirements(requirements, currency.parameters);
-              });
-            }
+          // Select the first identity
+          data.requirements = _fillRequirements(identities[0], currency.parameters);
+
+          // Copy some useful properties into data
+          data.pubkey = data.requirements.pubkey;
+          data.uid = data.requirements.uid;
+          data.isMember =  data.requirements.isMember;
+          data.blockUid =  data.requirements.meta &&  data.requirements.meta.timestamp;
+          data.hasSelf =  data.requirements.hasSelf;
+          data.sigDate =  data.requirements.meta && data.requirements.meta.time;
+
+          // Prepare alternatives identities if any
+          if (!data.requirements.isMember && !data.requirements.wasMember && identities.length > 1) {
+            data.requirements.alternatives = identities.splice(1);
+            _.forEach(data.requirements.alternatives, function(requirements) {
+              _fillRequirements(requirements, currency.parameters);
+            });
+          }
 
-            /// Save to cache
-            requirementsCache.put(cacheKey, data);
+          /// Save to cache
+          requirementsCache.put(cacheKey, data);
 
-            angular.merge(inputData, data); // Update the input data
+          angular.merge(inputData, data); // Update the input data
 
-            console.debug("[wot] Requirements for '{0}' loaded in {1}ms".format((data.pubkey && data.pubkey.substring(0,8))||data.uid, Date.now() - now));
+          console.debug("[wot] Requirements for '{0}' loaded in {1}ms".format((data.pubkey && data.pubkey.substring(0,8))||data.uid, Date.now() - now));
 
+          return inputData;
+        })
+        .catch(function(err) {
+          data.requirements = {loaded: true}; // Mark has loaded - need by the previous $timeout
+          _resetRequirements(inputData);
+          // If not a member: continue
+          if (!!err &&
+              (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER ||
+               err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) {
+            inputData.requirements.loaded = true;
             return inputData;
-          })
-          .catch(function(err) {
-            _resetRequirements(inputData);
-            // If not a member: continue
-            if (!!err &&
-                (err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER ||
-                 err.ucode == BMA.errorCodes.NO_IDTY_MATCHING_PUB_OR_UID)) {
-              inputData.requirements.loaded = true;
-              return inputData;
-            }
-            throw err;
-          });
-      },
-
-
-
-      loadIdentityByLookup = function(pubkey, uid) {
-        var data = {
-          pubkey: pubkey,
-          uid: uid,
-          hasSelf: false
-        };
-        return BMA.wot.lookup({ search: pubkey||uid })
-          .then(function(res){
-            var identities = res.results.reduce(function(idties, res) {
-              return idties.concat(res.uids.reduce(function(uids, idty) {
-                var blockUid = idty.meta.timestamp.split('-', 2);
-                var blockNumber = parseInt(blockUid[0]);
-                return uids.concat({
-                  uid: idty.uid,
-                  pubkey: res.pubkey,
-                  meta: {
-                    timestamp: idty.meta.timestamp,
-                    number: blockNumber,
-                    hash: blockUid[1],
-                    sig: idty.self
-                  },
-                  revoked: idty.revoked,
-                  revoked_on: idty.revoked_on
-                });
-              }, []));
-            }, []);
-
-            // Fill identities meta (self)
-            return _fillIdentitiesMeta(identities)
-              .then(function(identities) {
-                return {
-                  identities: identities,
-                  results: res.results
-                };
-              });
-          })
-          .then(function(res){
-            var identities = res.identities;
-
-            // Sort identities if need
-            if (identities.length > 1) {
-              // Select the best identity, by sorting using this order
-              //  - valid block
-              //  - same given uid
-              //  - not revoked
-              //  - max(block_number)
-              res.identities = _.sortBy(identities, function(idty) {
-                var score = 0;
-                score += (100000000000 * ((data.uid && idty.uid === data.uid) ? 1 : 0));
-                score += (10000000000  * (!idty.meta.invalid ? 1 : 0));
-                score += (1000000000  * ((data.blockUid && idty.meta.timestamp && idty.meta.timestamp === data.blockUid) ? 1 : 0));
-                score += (100000000   * (!idty.revoked ? 1 : 0));
-                score += (1            * (idty.meta.number ? idty.meta.number : 0) / 1000);
-                return -score;
+          }
+          throw err;
+        });
+    },
+
+    loadIdentityByLookup = function(pubkey, uid) {
+      var data = {
+        pubkey: pubkey,
+        uid: uid,
+        hasSelf: false
+      };
+      return BMA.wot.lookup({ search: pubkey||uid })
+        .then(function(res){
+          var identities = res.results.reduce(function(idties, res) {
+            return idties.concat(res.uids.reduce(function(uids, idty) {
+              var blockUid = idty.meta.timestamp.split('-', 2);
+              var blockNumber = parseInt(blockUid[0]);
+              return uids.concat({
+                uid: idty.uid,
+                pubkey: res.pubkey,
+                meta: {
+                  timestamp: idty.meta.timestamp,
+                  number: blockNumber,
+                  hash: blockUid[1],
+                  sig: idty.self
+                },
+                revoked: idty.revoked,
+                revoked_on: idty.revoked_on
               });
-              console.debug('[wot] Found {0} identities (in lookup). Will selected the best one'.format(identities.length));
-            }
+            }, []));
+          }, []);
 
-            // Prepare alternatives identities
-            _.forEach(identities, function(idty) {
-              idty.hasSelf = !!(idty.uid && idty.meta.timestamp && idty.meta.sig);
+          // Fill identities meta (self)
+          return _fillIdentitiesMeta(identities)
+            .then(function(identities) {
+              return {
+                identities: identities,
+                results: res.results
+              };
+            });
+        })
+        .then(function(res){
+          var identities = res.identities;
+
+          // Sort identities if need
+          if (identities.length > 1) {
+            // Select the best identity, by sorting using this order
+            //  - valid block
+            //  - same given uid
+            //  - not revoked
+            //  - max(block_number)
+            res.identities = _.sortBy(identities, function(idty) {
+              var score = 0;
+              score += (100000000000 * ((data.uid && idty.uid === data.uid) ? 1 : 0));
+              score += (10000000000  * (!idty.meta.invalid ? 1 : 0));
+              score += (1000000000  * ((data.blockUid && idty.meta.timestamp && idty.meta.timestamp === data.blockUid) ? 1 : 0));
+              score += (100000000   * (!idty.revoked ? 1 : 0));
+              score += (1            * (idty.meta.number ? idty.meta.number : 0) / 1000);
+              return -score;
             });
+            console.debug('[wot] Found {0} identities (in lookup). Will selected the best one'.format(identities.length));
+          }
 
-            // Select the first identity
-            data.requirements = identities[0];
+          // Prepare alternatives identities
+          _.forEach(identities, function(idty) {
+            idty.hasSelf = !!(idty.uid && idty.meta.timestamp && idty.meta.sig);
+          });
 
-            // Copy some useful properties into data
-            data.pubkey = data.requirements.pubkey;
-            data.uid = data.requirements.uid;
-            data.blockUid = data.requirements.meta && data.requirements.meta.timestamp;
-            data.hasSelf = data.requirements.hasSelf;
-            data.sigDate =  data.requirements.meta && data.requirements.meta.time;
+          // Select the first identity
+          data.requirements = identities[0];
 
-            if (identities.length > 1) {
-              data.requirements.alternatives = identities.splice(1);
-            }
+          // Copy some useful properties into data
+          data.pubkey = data.requirements.pubkey;
+          data.uid = data.requirements.uid;
+          data.blockUid = data.requirements.meta && data.requirements.meta.timestamp;
+          data.hasSelf = data.requirements.hasSelf;
+          data.sigDate =  data.requirements.meta && data.requirements.meta.time;
 
-            // Store additional data (e.g. certs)
-            data.lookup = {};
-
-            // Store received certifications (can be usefull later)
-            var certPubkeys = {};
-            data.lookup.certifications = (res.results || []).reduce(function(certsMap, res) {
-              return res.uids.reduce(function(certsMap, idty) {
-                var idtyFullKey = idty.uid + '-' + (idty.meta ? idty.meta.timestamp : '');
-                certsMap[idtyFullKey] = (idty.others||[]).reduce(function(certs, cert) {
-                  var certFullKey = idtyFullKey + '-' + cert.pubkey;
-                  var result = {
-                    pubkey: cert.pubkey,
-                    uid: cert.uids[0],
-                    cert_time:  {
-                      block: (cert.meta && cert.meta.block_number)  ? cert.meta.block_number : 0,
-                      block_hash: (cert.meta && cert.meta.block_hash)  ? cert.meta.block_hash : null
-                    },
-                    isMember: cert.isMember,
-                    wasMember: cert.wasMember,
-                  };
-                  if (!certPubkeys[certFullKey]) {
-                    certPubkeys[certFullKey] = result;
-                  }
-                  else { // if duplicated cert: keep the most recent
-                    if (result.cert_time.block > certPubkeys[certFullKey].cert_time.block) {
-                      certPubkeys[certFullKey] = result;
-                      certs.splice(_.findIndex(certs, {pubkey: cert.pubkey}), 1, result);
-                      return certs;
-                    }
-                    else {
-                      return certs; // skip this cert
-                    }
-                  }
-                  return certs.concat(result);
-                }, []);
-                return certsMap;
-              }, certsMap);
-            }, {});
-
-            // Store given certifications
-            certPubkeys = {};
-            data.lookup.givenCertifications = (res.results || []).reduce(function(certs, res) {
-              return (res.signed || []).reduce(function(certs, cert) {
+          if (identities.length > 1) {
+            data.requirements.alternatives = identities.splice(1);
+          }
+
+          // Store additional data (e.g. certs)
+          data.lookup = {};
+
+          // Store received certifications (can be usefull later)
+          var certPubkeys = {};
+          data.lookup.certifications = (res.results || []).reduce(function(certsMap, res) {
+            return res.uids.reduce(function(certsMap, idty) {
+              var idtyFullKey = idty.uid + '-' + (idty.meta ? idty.meta.timestamp : '');
+              certsMap[idtyFullKey] = (idty.others||[]).reduce(function(certs, cert) {
+                var certFullKey = idtyFullKey + '-' + cert.pubkey;
                 var result = {
                   pubkey: cert.pubkey,
-                  uid: cert.uid,
+                  uid: cert.uids[0],
                   cert_time:  {
-                    block: (cert.cert_time && cert.cert_time.block)  ? cert.cert_time.block : 0,
-                    block_hash: (cert.cert_time && cert.cert_time.block_hash)  ? cert.cert_time.block_hash : null
+                    block: (cert.meta && cert.meta.block_number)  ? cert.meta.block_number : 0,
+                    block_hash: (cert.meta && cert.meta.block_hash)  ? cert.meta.block_hash : null
                   },
-                  sigDate: cert.meta ? cert.meta.timestamp : null,
                   isMember: cert.isMember,
-                  wasMember: cert.wasMember
+                  wasMember: cert.wasMember,
                 };
-                if (!certPubkeys[cert.pubkey]) {
-                  certPubkeys[cert.pubkey] = result;
+                if (!certPubkeys[certFullKey]) {
+                  certPubkeys[certFullKey] = result;
                 }
                 else { // if duplicated cert: keep the most recent
-                  if (result.block > certPubkeys[cert.pubkey].block) {
-                    certPubkeys[cert.pubkey] = result;
-                    // TODO: Replace the existing one ? May be not, to be able to see renewal
-                    // (see issue #806)
-                    //  If yes (need to replace), check this code works:
-                    //certs.splice(_.findIndex(certs, {pubkey: cert.pubkey}), 1, result);
-                    //return certs;
+                  if (result.cert_time.block > certPubkeys[certFullKey].cert_time.block) {
+                    certPubkeys[certFullKey] = result;
+                    certs.splice(_.findIndex(certs, {pubkey: cert.pubkey}), 1, result);
+                    return certs;
                   }
                   else {
                     return certs; // skip this cert
                   }
                 }
                 return certs.concat(result);
-              }, certs);
-            }, []);
-
+              }, []);
+              return certsMap;
+            }, certsMap);
+          }, {});
+
+          // Store given certifications
+          certPubkeys = {};
+          data.lookup.givenCertifications = (res.results || []).reduce(function(certs, res) {
+            return (res.signed || []).reduce(function(certs, cert) {
+              var result = {
+                pubkey: cert.pubkey,
+                uid: cert.uid,
+                cert_time:  {
+                  block: (cert.cert_time && cert.cert_time.block)  ? cert.cert_time.block : 0,
+                  block_hash: (cert.cert_time && cert.cert_time.block_hash)  ? cert.cert_time.block_hash : null
+                },
+                sigDate: cert.meta ? cert.meta.timestamp : null,
+                isMember: cert.isMember,
+                wasMember: cert.wasMember
+              };
+              if (!certPubkeys[cert.pubkey]) {
+                certPubkeys[cert.pubkey] = result;
+              }
+              else { // if duplicated cert: keep the most recent
+                if (result.block > certPubkeys[cert.pubkey].block) {
+                  certPubkeys[cert.pubkey] = result;
+                  // TODO: Replace the existing one ? May be not, to be able to see renewal
+                  // (see issue #806)
+                  //  If yes (need to replace), check this code works:
+                  //certs.splice(_.findIndex(certs, {pubkey: cert.pubkey}), 1, result);
+                  //return certs;
+                }
+                else {
+                  return certs; // skip this cert
+                }
+              }
+              return certs.concat(result);
+            }, certs);
+          }, []);
+
+          return data;
+        })
+        .catch(function(err) {
+          if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) { // Identity not found (if no self)
+            _resetRequirements(data);
             return data;
-          })
-          .catch(function(err) {
-            if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) { // Identity not found (if no self)
-              _resetRequirements(data);
-              return data;
-            }
-            else {
-              throw err;
-            }
-          });
-      },
+          }
+          else {
+            throw err;
+          }
+        });
+    },
 
-      loadCertifications = function(getFunction, pubkey, lookupCertifications, parameters, medianTime, certifiersOf) {
+    loadCertifications = function(getFunction, pubkey, lookupCertifications, parameters, medianTime, certifiersOf) {
 
-        function _certId(pubkey, block) {
-          return pubkey + '-' + block;
-        }
+      function _certId(pubkey, block) {
+        return pubkey + '-' + block;
+      }
 
-        // TODO : remove this later (when all node will use duniter v0.50+)
-        var lookupHasCertTime = true; // Will be set ti FALSE before Duniter v0.50
-        var lookupCerticationsByCertId = lookupCertifications ? lookupCertifications.reduce(function(res, cert){
-          var certId = _certId(cert.pubkey, cert.cert_time ? cert.cert_time.block : cert.sigDate);
-          if (!cert.cert_time) lookupHasCertTime = false;
-          res[certId] = cert;
-          return res;
-        }, {}) : {};
+      // TODO : remove this later (when all node will use duniter v0.50+)
+      var lookupHasCertTime = true; // Will be set ti FALSE before Duniter v0.50
+      var lookupCerticationsByCertId = lookupCertifications ? lookupCertifications.reduce(function(res, cert){
+        var certId = _certId(cert.pubkey, cert.cert_time ? cert.cert_time.block : cert.sigDate);
+        if (!cert.cert_time) lookupHasCertTime = false;
+        res[certId] = cert;
+        return res;
+      }, {}) : {};
 
-        var isMember = true;
+      var isMember = true;
 
-        return getFunction({ pubkey: pubkey })
-          .then(function(res) {
-            return (res && res.certifications || []).reduce(function (res, cert) {
-              // Rappel :
-              //   cert.sigDate = blockstamp de l'identité
-              //   cert.cert_time.block : block au moment de la certification
-              //   cert.written.number : block où la certification est écrite
-
-              var pending = !cert.written;
-              var certTime = cert.cert_time ? cert.cert_time.medianTime : null;
-              var expiresIn = (!certTime) ? 0 : (pending ?
-                (certTime + parameters.sigWindow - medianTime) :
-                (certTime + parameters.sigValidity - medianTime));
-              expiresIn = (expiresIn < 0) ? 0 : expiresIn;
-              // Remove from lookup certs
-              var certId = _certId(cert.pubkey, lookupHasCertTime && cert.cert_time ? cert.cert_time.block : cert.sigDate);
-              delete lookupCerticationsByCertId[certId];
-
-              // Add to result list
+      return getFunction({ pubkey: pubkey })
+        .then(function(res) {
+          return (res && res.certifications || []).reduce(function (res, cert) {
+            // Rappel :
+            //   cert.sigDate = blockstamp de l'identité
+            //   cert.cert_time.block : block au moment de la certification
+            //   cert.written.number : block où la certification est écrite
+
+            var pending = !cert.written;
+            var certTime = cert.cert_time ? cert.cert_time.medianTime : null;
+            var expiresIn = (!certTime) ? 0 : (pending ?
+              (certTime + parameters.sigWindow - medianTime) :
+              (certTime + parameters.sigValidity - medianTime));
+            expiresIn = (expiresIn < 0) ? 0 : expiresIn;
+            // Remove from lookup certs
+            var certId = _certId(cert.pubkey, lookupHasCertTime && cert.cert_time ? cert.cert_time.block : cert.sigDate);
+            delete lookupCerticationsByCertId[certId];
+
+            // Add to result list
+            return res.concat({
+              pubkey: cert.pubkey,
+              uid: cert.uid,
+              time: certTime,
+              isMember: cert.isMember,
+              wasMember: cert.wasMember,
+              expiresIn: expiresIn,
+              willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
+              pending: pending,
+              block: (cert.written !== null) ? cert.written.number :
+                (cert.cert_time ? cert.cert_time.block : null),
+              valid: (expiresIn > 0)
+            });
+          }, []);
+        })
+        .catch(function(err) {
+          if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER) { // member not found
+            isMember = false;
+            return []; // continue (append pendings cert if exists in lookup)
+          }
+          /*FIXME: workaround for Duniter issue #1309 */
+          else if (!!err && err.ucode == 1002) {
+            console.warn("[wallet-service] Detecting Duniter issue #1309 ! Applying workaround... ");
+            isMember = false;
+            return []; // not found
+          }
+          else {
+            throw err;
+          }
+        })
+
+        // Add pending certs (found in lookup - see loadIdentityByLookup())
+        .then(function(certifications) {
+          var pendingCertifications = _.values(lookupCerticationsByCertId);
+          if (!pendingCertifications.length) return certifications; // No more pending continue
+
+          // Special case for initPhase - issue #
+          if (csCurrency.data.initPhase) {
+            return pendingCertifications.reduce(function(res, cert) {
               return res.concat({
                 pubkey: cert.pubkey,
                 uid: cert.uid,
-                time: certTime,
                 isMember: cert.isMember,
                 wasMember: cert.wasMember,
-                expiresIn: expiresIn,
-                willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
-                pending: pending,
-                block: (cert.written !== null) ? cert.written.number :
-                  (cert.cert_time ? cert.cert_time.block : null),
-                valid: (expiresIn > 0)
+                time: null,
+                expiresIn: parameters.sigWindow,
+                willExpire: false,
+                pending: true,
+                block: 0,
+                valid: true
               });
-            }, []);
-          })
-          .catch(function(err) {
-            if (!!err && err.ucode == BMA.errorCodes.NO_MATCHING_MEMBER) { // member not found
-              isMember = false;
-              return []; // continue (append pendings cert if exists in lookup)
-            }
-            /*FIXME: workaround for Duniter issue #1309 */
-            else if (!!err && err.ucode == 1002) {
-              console.warn("[wallet-service] Detecting Duniter issue #1309 ! Applying workaround... ");
-              isMember = false;
-              return []; // not found
-            }
-            else {
-              throw err;
-            }
-          })
-
-          // Add pending certs (found in lookup - see loadIdentityByLookup())
-          .then(function(certifications) {
-            var pendingCertifications = _.values(lookupCerticationsByCertId);
-            if (!pendingCertifications.length) return certifications; // No more pending continue
+            }, certifications);
+          }
 
-            // Special case for initPhase - issue #
-            if (csCurrency.data.initPhase) {
-              return pendingCertifications.reduce(function(res, cert) {
+          var pendingCertByBlocks = pendingCertifications.reduce(function(res, cert){
+            var block = lookupHasCertTime && cert.cert_time ? cert.cert_time.block :
+              (cert.sigDate ? cert.sigDate.split('-')[0] : null);
+            if (angular.isDefined(block)) {
+              if (!res[block]) {
+                res[block] = [cert];
+              }
+              else {
+                res[block].push(cert);
+              }
+            }
+            return res;
+          }, {});
+
+          // Set time to pending cert, from blocks
+          return BMA.blockchain.blocks(_.keys(pendingCertByBlocks)).then(function(blocks){
+            certifications = blocks.reduce(function(res, block){
+              return res.concat(pendingCertByBlocks[block.number].reduce(function(res, cert) {
+                var certTime = block.medianTime;
+                var expiresIn = Math.max(0, certTime + parameters.sigWindow - medianTime);
+                var validBuid = (!cert.cert_time || !cert.cert_time.block_hash || cert.cert_time.block_hash == block.hash);
+                if (!validBuid) {
+                  console.debug("[wot] Invalid cert {0}: block hash changed".format(cert.pubkey.substring(0,8)));
+                }
+                var valid = (expiresIn > 0) && (!certifiersOf || cert.isMember) && validBuid;
                 return res.concat({
                   pubkey: cert.pubkey,
                   uid: cert.uid,
                   isMember: cert.isMember,
                   wasMember: cert.wasMember,
-                  time: null,
-                  expiresIn: parameters.sigWindow,
-                  willExpire: false,
+                  time: certTime,
+                  expiresIn: expiresIn,
+                  willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
                   pending: true,
-                  block: 0,
-                  valid: true
+                  block: lookupHasCertTime && cert.cert_time ? cert.cert_time.block :
+                  (cert.sigDate ? cert.sigDate.split('-')[0] : null),
+                  valid: valid
                 });
-              }, certifications);
-            }
-
-            var pendingCertByBlocks = pendingCertifications.reduce(function(res, cert){
-              var block = lookupHasCertTime && cert.cert_time ? cert.cert_time.block :
-                (cert.sigDate ? cert.sigDate.split('-')[0] : null);
-              if (angular.isDefined(block)) {
-                if (!res[block]) {
-                  res[block] = [cert];
-                }
-                else {
-                  res[block].push(cert);
-                }
-              }
-              return res;
-            }, {});
-
-            // Set time to pending cert, from blocks
-            return BMA.blockchain.blocks(_.keys(pendingCertByBlocks)).then(function(blocks){
-              certifications = blocks.reduce(function(res, block){
-                return res.concat(pendingCertByBlocks[block.number].reduce(function(res, cert) {
-                  var certTime = block.medianTime;
-                  var expiresIn = Math.max(0, certTime + parameters.sigWindow - medianTime);
-                  var validBuid = (!cert.cert_time || !cert.cert_time.block_hash || cert.cert_time.block_hash == block.hash);
-                  if (!validBuid) {
-                    console.debug("[wot] Invalid cert {0}: block hash changed".format(cert.pubkey.substring(0,8)));
-                  }
-                  var valid = (expiresIn > 0) && (!certifiersOf || cert.isMember) && validBuid;
-                  return res.concat({
-                    pubkey: cert.pubkey,
-                    uid: cert.uid,
-                    isMember: cert.isMember,
-                    wasMember: cert.wasMember,
-                    time: certTime,
-                    expiresIn: expiresIn,
-                    willExpire: (expiresIn && expiresIn <= csSettings.data.timeWarningExpire),
-                    pending: true,
-                    block: lookupHasCertTime && cert.cert_time ? cert.cert_time.block :
-                    (cert.sigDate ? cert.sigDate.split('-')[0] : null),
-                    valid: valid
-                  });
-                }, []));
-              }, certifications);
-              return certifications;
-            });
-          })
+              }, []));
+            }, certifications);
+            return certifications;
+          });
+        })
 
-          // Sort and return result
-          .then(function(certifications) {
+        // Sort and return result
+        .then(function(certifications) {
 
-            // Remove pending cert duplicated with a written & valid cert
-            var writtenCertByPubkey = certifications.reduce(function(res, cert) {
-              if (!cert.pending && cert.valid && cert.expiresIn >= parameters.sigWindow) {
-                res[cert.pubkey] = true;
+          // Remove pending cert duplicated with a written & valid cert
+          var writtenCertByPubkey = certifications.reduce(function(res, cert) {
+            if (!cert.pending && cert.valid && cert.expiresIn >= parameters.sigWindow) {
+              res[cert.pubkey] = true;
+            }
+            return res;
+          }, {});
+
+          // Final sort
+          certifications = _sortCertifications(certifications);
+
+          // Split into valid/pending/error
+          var pendingCertifications = [];
+          var errorCertifications = [];
+          var validCertifications = certifications.reduce(function(res, cert) {
+            if (cert.pending) {
+              if (cert.valid && !writtenCertByPubkey[cert.pubkey]) {
+                pendingCertifications.push(cert);
               }
-              return res;
-            }, {});
-
-            // Final sort
-            certifications = _sortCertifications(certifications);
-
-            // Split into valid/pending/error
-            var pendingCertifications = [];
-            var errorCertifications = [];
-            var validCertifications = certifications.reduce(function(res, cert) {
-              if (cert.pending) {
-                if (cert.valid && !writtenCertByPubkey[cert.pubkey]) {
-                  pendingCertifications.push(cert);
-                }
-                else if (!cert.valid && !writtenCertByPubkey[cert.pubkey]){
-                  errorCertifications.push(cert);
-                }
-                return res;
+              else if (!cert.valid && !writtenCertByPubkey[cert.pubkey]){
+                errorCertifications.push(cert);
               }
-              return res.concat(cert);
-            }, []);
-
-            return {
-              valid: validCertifications,
-              pending: pendingCertifications,
-              error: errorCertifications
-            };
-          })
-          ;
-      },
-
-      // Add events on given account
-      addEvents = function(data) {
+              return res;
+            }
+            return res.concat(cert);
+          }, []);
 
-        if (data.requirements.revoked) {
-          delete data.requirements.meta.invalid;
-          if (data.requirements.revoked_on) {
-            addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_REVOKED_WITH_TIME', messageParams: {revocationTime: data.requirements.revoked_on}});
-            console.debug("[wot] Identity [{0}] has been revoked on {1}".format(data.uid, data.requirements.revoked_on));
-          }
-          else {
-            addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_REVOKED'});
-            console.debug("[wot] Identity [{0}] has been revoked".format(data.uid));
-          }
-        }
-        else if (data.requirements.pendingRevocation) {
-          delete data.requirements.meta.invalid;
-          addEvent(data, {type:'error', message: 'ERROR.IDENTITY_PENDING_REVOCATION'});
-          console.debug("[wot] Identity [{0}] has pending revocation".format(data.uid));
-        }
-        else if (data.requirements.meta && data.requirements.meta.invalid) {
-          if (!data.isMember) {
-            addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_INVALID_BLOCK_HASH'});
-            console.debug("[wot] Invalid membership for uid {0}: block hash changed".format(data.uid));
-          }
-        }
-        else if (data.requirements.expired) {
-          addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_EXPIRED'});
-          console.debug("[wot] Identity {0} expired (in sandbox)".format(data.uid));
-        }
-        else if (data.requirements.willNeedCertificationCount > 0) {
-          addEvent(data, {type: 'error', message: 'INFO.IDENTITY_WILL_MISSING_CERTIFICATIONS', messageParams: data.requirements});
-          console.debug("[wot] Identity {0} will need {1} certification(s)".format(data.uid, data.requirements.willNeedCertificationCount));
+          return {
+            valid: validCertifications,
+            pending: pendingCertifications,
+            error: errorCertifications
+          };
+        })
+        ;
+    },
+
+    // Add events on given account
+    addEvents = function(data) {
+
+      if (data.requirements.revoked) {
+        delete data.requirements.meta.invalid;
+        if (data.requirements.revoked_on) {
+          addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_REVOKED_WITH_TIME', messageParams: {revocationTime: data.requirements.revoked_on}});
+          console.debug("[wot] Identity [{0}] has been revoked on {1}".format(data.uid, data.requirements.revoked_on));
         }
-        else if (!data.requirements.needSelf && data.requirements.needMembership) {
-          addEvent(data, {type: 'error', message: 'INFO.IDENTITY_NEED_MEMBERSHIP'});
-          console.debug("[wot] Identity {0} has a self but no membership".format(data.uid));
+        else {
+          addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_REVOKED'});
+          console.debug("[wot] Identity [{0}] has been revoked".format(data.uid));
         }
-        if (!data.isMember && data.requirements.alternatives) {
-          addEvent(data, {type: 'info', message: 'INFO.HAS_ALTERNATIVE_IDENTITIES'});
+      }
+      else if (data.requirements.pendingRevocation) {
+        delete data.requirements.meta.invalid;
+        addEvent(data, {type:'error', message: 'ERROR.IDENTITY_PENDING_REVOCATION'});
+        console.debug("[wot] Identity [{0}] has pending revocation".format(data.uid));
+      }
+      else if (data.requirements.meta && data.requirements.meta.invalid) {
+        if (!data.isMember) {
+          addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_INVALID_BLOCK_HASH'});
+          console.debug("[wot] Invalid membership for uid {0}: block hash changed".format(data.uid));
         }
-      },
+      }
+      else if (data.requirements.expired) {
+        addEvent(data, {type: 'error', message: 'ERROR.IDENTITY_EXPIRED'});
+        console.debug("[wot] Identity {0} expired (in sandbox)".format(data.uid));
+      }
+      else if (data.requirements.willNeedCertificationCount > 0) {
+        addEvent(data, {type: 'error', message: 'INFO.IDENTITY_WILL_MISSING_CERTIFICATIONS', messageParams: data.requirements});
+        console.debug("[wot] Identity {0} will need {1} certification(s)".format(data.uid, data.requirements.willNeedCertificationCount));
+      }
+      else if (!data.requirements.needSelf && data.requirements.needMembership) {
+        addEvent(data, {type: 'error', message: 'INFO.IDENTITY_NEED_MEMBERSHIP'});
+        console.debug("[wot] Identity {0} has a self but no membership".format(data.uid));
+      }
+      if (!data.isMember && data.requirements.alternatives) {
+        addEvent(data, {type: 'info', message: 'INFO.HAS_ALTERNATIVE_IDENTITIES'});
+      }
+    },
 
-      loadData = function(pubkey, uid, options) {
+    loadData = function(pubkey, uid, options) {
 
-        options = options || {};
-        var data;
+      options = options || {};
+      var data;
 
-        if (!pubkey && uid && !options.force) {
-          return BMA.wot.member.getByUid(uid)
-            .then(function(member) {
-              if (member) return loadData(member.pubkey, member.uid, options); // recursive call, with a pubkey
-              //throw {message: 'NOT_A_MEMBER'};
-              var options = angular.copy(options || {});
-              options.force = true;
-              return loadData(pubkey, uid, options); // Loop with force=true
-            });
-        }
+      if (!pubkey && uid && !options.force) {
+        return BMA.wot.member.getByUid(uid)
+          .then(function(member) {
+            if (member) return loadData(member.pubkey, member.uid, options); // recursive call, with a pubkey
+            //throw {message: 'NOT_A_MEMBER'};
+            var options = angular.copy(options || {});
+            options.force = true;
+            return loadData(pubkey, uid, options); // Loop with force=true
+          });
+      }
 
-        // Check cached data
-        if (pubkey) {
-          data = (options.cache !== false) ? identityCache.get(pubkey) : null;
-          if (data && (!uid || data.uid === uid) && (!options.blockUid || data.blockUid === options.blockUid)) {
-            console.debug("[wot] Identity " + pubkey.substring(0, 8) + " found in cache");
-            return $q.when(data);
-          }
-          console.debug("[wot] Loading identity " + pubkey.substring(0, 8) + "...");
-          data = {
-            pubkey: pubkey,
-            uid: uid
-          };
-        }
-        else {
-          console.debug("[wot] Loading identity from uid " + uid);
-          data = {
-            uid: uid
-          };
-        }
-        if (options.blockUid) {
-          data.blockUid = options.blockUid;
+      // Check cached data
+      if (pubkey) {
+        data = (options.cache !== false) ? identityCache.get(pubkey) : null;
+        if (data && (!uid || data.uid === uid) && (!options.blockUid || data.blockUid === options.blockUid)) {
+          console.debug("[wot] Identity " + pubkey.substring(0, 8) + " found in cache");
+          return $q.when(data);
         }
+        console.debug("[wot] Loading identity " + pubkey.substring(0, 8) + "...");
+        data = {
+          pubkey: pubkey,
+          uid: uid
+        };
+      }
+      else {
+        console.debug("[wot] Loading identity from uid " + uid);
+        data = {
+          uid: uid
+        };
+      }
+      if (options.blockUid) {
+        data.blockUid = options.blockUid;
+      }
 
-        var now = Date.now();
-        var parameters;
-        var medianTime;
+      var now = Date.now();
+      var parameters;
+      var medianTime;
 
-        return $q.all([
+      return $q.all([
 
-            // Get parameters
-            csCurrency.parameters()
-              .then(function(res) {
-                parameters = res;
+          // Get parameters
+          csCurrency.parameters()
+            .then(function(res) {
+              parameters = res;
+            }),
+
+          // Get current time
+          csCurrency.blockchain.current(true)
+            .then(function(current) {
+              medianTime = current.medianTime;
+            })
+            .catch(function(err){
+              // Special case for currency init (root block not exists): use now
+              if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
+                medianTime = moment.utc().unix();
+              }
+              else {
+                throw err;
+              }
+            }),
+
+          // Get requirements
+          loadRequirements(data, options.cache !== false),
+
+          // Get identity using lookup
+          loadIdentityByLookup(pubkey, uid)
+
+        ])
+        .then(function(res) {
+          var dataByLookup = res[3];
+
+          // If no requirements found: copy from lookup data
+          if (!data.requirements.uid) {
+            console.debug("[wot] No requirements found: using data from lookup");
+            angular.merge(data, dataByLookup);
+            delete data.lookup; // not need
+            return;
+          }
+
+          var idtyFullKey = data.requirements.uid + '-' + data.requirements.meta.timestamp;
+
+          return $q.all([
+            // Get received certifications
+            loadCertifications(BMA.wot.certifiersOf, data.pubkey, dataByLookup.lookup ? dataByLookup.lookup.certifications[idtyFullKey] : null, parameters, medianTime, true /*certifiersOf*/)
+              .then(function (res) {
+                data.received_cert = res.valid;
+                data.received_cert_pending = res.pending;
+                data.received_cert_error = res.error;
               }),
 
-            // Get current time
-            csCurrency.blockchain.current(true)
-              .then(function(current) {
-                medianTime = current.medianTime;
+            // Get given certifications
+            loadCertifications(BMA.wot.certifiedBy, data.pubkey, dataByLookup.lookup ? dataByLookup.lookup.givenCertifications : null, parameters, medianTime, false/*certifiersOf*/)
+              .then(function (res) {
+                data.given_cert = res.valid;
+                data.given_cert_pending = res.pending;
+                data.given_cert_error = res.error;
               })
-              .catch(function(err){
-                // Special case for currency init (root block not exists): use now
-                if (err && err.ucode == BMA.errorCodes.NO_CURRENT_BLOCK) {
-                  medianTime = moment.utc().unix();
-                }
-                else {
-                  throw err;
-                }
-              }),
+          ]);
+        })
+        .then(function() {
 
-            // Get requirements
-            loadRequirements(data, options.cache !== false),
+          // Add compute some additional requirements (that required all data like certifications)
+          data.requirements.pendingCertificationCount = data.received_cert_pending ? data.received_cert_pending.length : data.requirements.pendingCertificationCount;
+          // Use /wot/lookup.revoked when requirements not filled
+          data.requirements.revoked = angular.isDefined(data.requirements.revoked) ? data.requirements.revoked : data.revoked;
 
-            // Get identity using lookup
-            loadIdentityByLookup(pubkey, uid)
+          // Add account events
+          addEvents(data);
 
-          ])
-          .then(function(res) {
-            var dataByLookup = res[3];
-
-            // If no requirements found: copy from lookup data
-            if (!data.requirements.uid) {
-              console.debug("[wot] No requirements found: using data from lookup");
-              angular.merge(data, dataByLookup);
-              delete data.lookup; // not need
-              return;
-            }
+          // API extension
+          return api.data.raisePromise.load(data)
+            .catch(function(err) {
+              console.debug('Error while loading identity data, on extension point.');
+              console.error(err);
+            });
+        })
+        .then(function() {
+          if (!data.pubkey) return undefined; // not found
+          identityCache.put(data.pubkey, data); // add to cache
+          console.debug('[wot] Identity '+ data.pubkey.substring(0, 8) +' loaded in '+ (Date.now()-now) +'ms');
+          return data;
+        });
+    },
 
-            var idtyFullKey = data.requirements.uid + '-' + data.requirements.meta.timestamp;
-
-            return $q.all([
-              // Get received certifications
-              loadCertifications(BMA.wot.certifiersOf, data.pubkey, dataByLookup.lookup ? dataByLookup.lookup.certifications[idtyFullKey] : null, parameters, medianTime, true /*certifiersOf*/)
-                .then(function (res) {
-                  data.received_cert = res.valid;
-                  data.received_cert_pending = res.pending;
-                  data.received_cert_error = res.error;
-                }),
-
-              // Get given certifications
-              loadCertifications(BMA.wot.certifiedBy, data.pubkey, dataByLookup.lookup ? dataByLookup.lookup.givenCertifications : null, parameters, medianTime, false/*certifiersOf*/)
-                .then(function (res) {
-                  data.given_cert = res.valid;
-                  data.given_cert_pending = res.pending;
-                  data.given_cert_error = res.error;
-                })
-            ]);
-          })
-          .then(function() {
+    search = function(text, options) {
+      if (!text || text.trim() !== text) {
+        return $q.when(undefined);
+      }
 
-            // Add compute some additional requirements (that required all data like certifications)
-            data.requirements.pendingCertificationCount = data.received_cert_pending ? data.received_cert_pending.length : data.requirements.pendingCertificationCount;
-            // Use /wot/lookup.revoked when requirements not filled
-            data.requirements.revoked = angular.isDefined(data.requirements.revoked) ? data.requirements.revoked : data.revoked;
+      // Remove first special characters (to avoid request error)
+      var safeText = text.replace(/(^|\s)#\w+/g, ''); // remove tags
+      safeText = safeText.replace(/[^a-zA-Z0-9_-\s]+/g, '');
+      safeText = safeText.replace(/\s+/g, ' ').trim();
 
-            // Add account events
-            addEvents(data);
+      options = options || {};
+      options.addUniqueId = angular.isDefined(options.addUniqueId) ? options.addUniqueId : true;
+      options.allowExtension = angular.isDefined(options.allowExtension) ? options.allowExtension : true;
+      options.excludeRevoked = angular.isDefined(options.excludeRevoked) ? options.excludeRevoked : false;
 
-            // API extension
-            return api.data.raisePromise.load(data)
-              .catch(function(err) {
-                console.debug('Error while loading identity data, on extension point.');
-                console.error(err);
-              });
+      var promise;
+      if (!safeText) {
+        promise = $q.when([]);
+      }
+      else {
+        promise = $q.all(
+          safeText.split(' ').reduce(function(res, text) {
+            console.debug('[wot] Will search on: \'' + text + '\'');
+            return res.concat(BMA.wot.lookup({ search: text }));
+          }, [])
+        ).then(function(res){
+            return res.reduce(function(idties, res) {
+              return idties.concat(res.results.reduce(function(idties, res) {
+                return idties.concat(res.uids.reduce(function(uids, idty) {
+                  var blocUid = idty.meta.timestamp.split('-', 2);
+                  var revoked = !idty.revoked && idty.revocation_sig;
+                  if (!options.excludeRevoked || !revoked) {
+                    return uids.concat({
+                      uid: idty.uid,
+                      pubkey: res.pubkey,
+                      number: blocUid[0],
+                      hash: blocUid[1],
+                      revoked: revoked
+                    });
+                  }
+                  return uids;
+                }, []));
+              }, []));
+            }, []);
           })
-          .then(function() {
-            if (!data.pubkey) return undefined; // not found
-            identityCache.put(data.pubkey, data); // add to cache
-            console.debug('[wot] Identity '+ data.pubkey.substring(0, 8) +' loaded in '+ (Date.now()-now) +'ms');
-            return data;
+          .catch(function(err) {
+            if (err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) {
+              return [];
+            }
+            else {
+              throw err;
+            }
           });
-      },
-
-      search = function(text, options) {
-        if (!text || text.trim() !== text) {
-          return $q.when(undefined);
-        }
-
-        // Remove first special characters (to avoid request error)
-        var safeText = text.replace(/(^|\s)#\w+/g, ''); // remove tags
-        safeText = safeText.replace(/[^a-zA-Z0-9_-\s]+/g, '');
-        safeText = safeText.replace(/\s+/g, ' ').trim();
+      }
 
-        options = options || {};
-        options.addUniqueId = angular.isDefined(options.addUniqueId) ? options.addUniqueId : true;
-        options.allowExtension = angular.isDefined(options.allowExtension) ? options.allowExtension : true;
-        options.excludeRevoked = angular.isDefined(options.excludeRevoked) ? options.excludeRevoked : false;
+      return promise
+        .then(function(idties) {
+          if (!options.allowExtension) {
+            // Add unique id (if enable)
+            return options.addUniqueId ? _addUniqueIds(idties) : idties;
+          }
+          var lookupResultCount = idties.length;
+          // call extension point
+          return api.data.raisePromise.search(text, idties, 'pubkey')
+            .then(function() {
 
-        var promise;
-        if (!safeText) {
-          promise = $q.when([]);
-        }
-        else {
-          promise = $q.all(
-            safeText.split(' ').reduce(function(res, text) {
-              console.debug('[wot] Will search on: \'' + text + '\'');
-              return res.concat(BMA.wot.lookup({ search: text }));
-            }, [])
-          ).then(function(res){
-              return res.reduce(function(idties, res) {
-                return idties.concat(res.results.reduce(function(idties, res) {
-                  return idties.concat(res.uids.reduce(function(uids, idty) {
-                    var blocUid = idty.meta.timestamp.split('-', 2);
-                    var revoked = !idty.revoked && idty.revocation_sig;
-                    if (!options.excludeRevoked || !revoked) {
-                      return uids.concat({
-                        uid: idty.uid,
-                        pubkey: res.pubkey,
-                        number: blocUid[0],
-                        hash: blocUid[1],
-                        revoked: revoked
+              // Make sure to add uid to new results - fix #488
+              if (idties.length > lookupResultCount) {
+                var idtiesWithoutUid = _.filter(idties, function(idty) {
+                  return !idty.uid && idty.pubkey;
+                });
+                if (idtiesWithoutUid.length) {
+                  return BMA.wot.member.uids()
+                    .then(function(uids) {
+                      _.forEach(idties, function(idty) {
+                        if (!idty.uid && idty.pubkey) {
+                          idty.uid = uids[idty.pubkey];
+                        }
                       });
-                    }
-                    return uids;
-                  }, []));
-                }, []));
-              }, []);
-            })
-            .catch(function(err) {
-              if (err && err.ucode == BMA.errorCodes.NO_MATCHING_IDENTITY) {
-                return [];
-              }
-              else {
-                throw err;
+                    });
+                }
               }
-            });
-        }
-
-        return promise
-          .then(function(idties) {
-            if (!options.allowExtension) {
+            })
+            .then(function() {
               // Add unique id (if enable)
               return options.addUniqueId ? _addUniqueIds(idties) : idties;
-            }
-            var lookupResultCount = idties.length;
-            // call extension point
-            return api.data.raisePromise.search(text, idties, 'pubkey')
-              .then(function() {
-
-                // Make sure to add uid to new results - fix #488
-                if (idties.length > lookupResultCount) {
-                  var idtiesWithoutUid = _.filter(idties, function(idty) {
-                    return !idty.uid && idty.pubkey;
+            });
+        });
+    },
+
+    getNewcomers = function(offset, size) {
+      offset = offset || 0;
+      size = size || 20;
+      var total;
+      return $q.all([
+          csCurrency.blockchain.current(true)
+            .then(function(block) {
+              total = block.membersCount || 0;
+            }),
+          BMA.blockchain.stats.newcomers()
+        ])
+        .then(function(res) {
+          res = res[1];
+          if (!res || !res.result || !res.result.blocks || !res.result.blocks.length) return null; // no result
+          var blocks = _.sortBy(res.result.blocks, function (n) {
+            return -n;
+          });
+          return getNewcomersRecursive(blocks, 0, 5, offset+size);
+        })
+        .then(function(idties){
+          if (!idties || !idties.length) {
+            return null;
+          }
+          idties = _sortAndSliceIdentities(idties, offset, size);
+
+          // Extension point
+          return extendAll(idties, 'pubkey', true/*skipAddUid*/);
+        })
+        .then(function(idties) {
+          return {
+            hits: idties,
+            total: total
+          };
+        });
+    },
+
+
+    getNewcomersRecursive = function(blocks, offset, size, maxResultSize) {
+      return $q(function(resolve, reject) {
+        var result = [];
+        var jobs = [];
+        _.each(blocks.slice(offset, offset+size), function(number) {
+          jobs.push(
+            BMA.blockchain.block({block: number})
+              .then(function(block){
+                if (!block || !block.joiners) return;
+                _.each(block.joiners, function(joiner){
+                  var parts = joiner.split(':');
+                  var idtyKey = parts[parts.length-1]/*uid*/ + '-' + parts[0]/*pubkey*/;
+                  result.push({
+                    id: idtyKey,
+                    uid: parts[parts.length-1],
+                    pubkey:parts[0],
+                    memberDate: block.medianTime,
+                    block: block.number
                   });
-                  if (idtiesWithoutUid.length) {
-                    return BMA.wot.member.uids()
-                      .then(function(uids) {
-                        _.forEach(idties, function(idty) {
-                          if (!idty.uid && idty.pubkey) {
-                            idty.uid = uids[idty.pubkey];
-                          }
-                        });
-                      });
-                  }
-                }
+                });
               })
-              .then(function() {
-                // Add unique id (if enable)
-                return options.addUniqueId ? _addUniqueIds(idties) : idties;
-              });
-          });
-      },
-
-      getNewcomers = function(offset, size) {
-        offset = offset || 0;
-        size = size || 20;
-        var total;
-        return $q.all([
-            csCurrency.blockchain.current(true)
-              .then(function(block) {
-                total = block.membersCount || 0;
-              }),
-            BMA.blockchain.stats.newcomers()
-          ])
-          .then(function(res) {
-            res = res[1];
-            if (!res || !res.result || !res.result.blocks || !res.result.blocks.length) return null; // no result
-            var blocks = _.sortBy(res.result.blocks, function (n) {
-              return -n;
-            });
-            return getNewcomersRecursive(blocks, 0, 5, offset+size);
-          })
-          .then(function(idties){
-            if (!idties || !idties.length) {
-              return null;
-            }
-            idties = _sortAndSliceIdentities(idties, offset, size);
+          );
+        });
 
-            // Extension point
-            return extendAll(idties, 'pubkey', true/*skipAddUid*/);
-          })
-          .then(function(idties) {
-            return {
-              hits: idties,
-              total: total
-            };
-          });
-      },
-
-
-      getNewcomersRecursive = function(blocks, offset, size, maxResultSize) {
-        return $q(function(resolve, reject) {
-          var result = [];
-          var jobs = [];
-          _.each(blocks.slice(offset, offset+size), function(number) {
-            jobs.push(
-              BMA.blockchain.block({block: number})
-                .then(function(block){
-                  if (!block || !block.joiners) return;
-                  _.each(block.joiners, function(joiner){
-                    var parts = joiner.split(':');
-                    var idtyKey = parts[parts.length-1]/*uid*/ + '-' + parts[0]/*pubkey*/;
-                    result.push({
-                      id: idtyKey,
-                      uid: parts[parts.length-1],
-                      pubkey:parts[0],
-                      memberDate: block.medianTime,
-                      block: block.number
-                    });
+        $q.all(jobs)
+          .then(function() {
+            if (result.length < maxResultSize && offset < blocks.length - 1) {
+              $timeout(function() {
+                getNewcomersRecursive(blocks, offset+size, size, maxResultSize - result.length)
+                  .then(function(res) {
+                    resolve(result.concat(res));
+                  })
+                  .catch(function(err) {
+                    reject(err);
                   });
-                })
-            );
+              }, 1000);
+            }
+            else {
+              resolve(result);
+            }
+          })
+          .catch(function(err){
+            if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
+              resolve(result);
+            }
+            else {
+              reject(err);
+            }
           });
-
-          $q.all(jobs)
-            .then(function() {
-              if (result.length < maxResultSize && offset < blocks.length - 1) {
-                $timeout(function() {
-                  getNewcomersRecursive(blocks, offset+size, size, maxResultSize - result.length)
-                    .then(function(res) {
-                      resolve(result.concat(res));
-                    })
-                    .catch(function(err) {
-                      reject(err);
-                    });
-                }, 1000);
-              }
-              else {
-                resolve(result);
+      });
+    },
+
+    getPending = function(offset, size) {
+      offset = offset || 0;
+      size = size || 20;
+      var now = Date.now();
+      return $q.all([
+        BMA.wot.member.uids(),
+        BMA.wot.member.pending()
+          .then(function(res) {
+            return (res.memberships && res.memberships.length) ? res.memberships : undefined;
+          })
+        ])
+        .then(function(res) {
+          var uids = res[0];
+          var memberships = res[1];
+          if (!memberships) return;
+
+          var idtiesByBlock = {};
+          var idtiesByPubkey = {};
+          _.forEach(memberships, function(ms){
+            if (ms.membership == 'IN' && !uids[ms.pubkey]) {
+              var idty = {
+                uid: ms.uid,
+                pubkey: ms.pubkey,
+                block: ms.blockNumber,
+                blockHash: ms.blockHash
+              };
+              var otherIdtySamePubkey = idtiesByPubkey[ms.pubkey];
+              if (otherIdtySamePubkey && idty.block > otherIdtySamePubkey.block) {
+                return; // skip
               }
-            })
-            .catch(function(err){
-              if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
-                resolve(result);
+              idtiesByPubkey[idty.pubkey] = idty;
+              if (!idtiesByBlock[idty.block]) {
+                idtiesByBlock[idty.block] = [idty];
               }
               else {
-                reject(err);
+                idtiesByBlock[idty.block].push(idty);
               }
-            });
-        });
-      },
-
-      getPending = function(offset, size) {
-        offset = offset || 0;
-        size = size || 20;
-        var now = Date.now();
-        return $q.all([
-          BMA.wot.member.uids(),
-          BMA.wot.member.pending()
-            .then(function(res) {
-              return (res.memberships && res.memberships.length) ? res.memberships : undefined;
-            })
-          ])
-          .then(function(res) {
-            var uids = res[0];
-            var memberships = res[1];
-            if (!memberships) return;
-
-            var idtiesByBlock = {};
-            var idtiesByPubkey = {};
-            _.forEach(memberships, function(ms){
-              if (ms.membership == 'IN' && !uids[ms.pubkey]) {
-                var idty = {
-                  uid: ms.uid,
-                  pubkey: ms.pubkey,
-                  block: ms.blockNumber,
-                  blockHash: ms.blockHash
-                };
-                var otherIdtySamePubkey = idtiesByPubkey[ms.pubkey];
-                if (otherIdtySamePubkey && idty.block > otherIdtySamePubkey.block) {
-                  return; // skip
-                }
-                idtiesByPubkey[idty.pubkey] = idty;
-                if (!idtiesByBlock[idty.block]) {
-                  idtiesByBlock[idty.block] = [idty];
-                }
-                else {
-                  idtiesByBlock[idty.block].push(idty);
-                }
 
-                // Remove previous idty from map
-                if (otherIdtySamePubkey) {
-                  idtiesByBlock[otherIdtySamePubkey.block] = idtiesByBlock[otherIdtySamePubkey.block].reduce(function(res, aidty){
-                    if (aidty.pubkey == otherIdtySamePubkey.pubkey) return res; // if match idty to remove, to NOT add
-                    return (res||[]).concat(aidty);
-                  }, null);
-                  if (idtiesByBlock[otherIdtySamePubkey.block] === null) {
-                    delete idtiesByBlock[otherIdtySamePubkey.block];
-                  }
+              // Remove previous idty from map
+              if (otherIdtySamePubkey) {
+                idtiesByBlock[otherIdtySamePubkey.block] = idtiesByBlock[otherIdtySamePubkey.block].reduce(function(res, aidty){
+                  if (aidty.pubkey == otherIdtySamePubkey.pubkey) return res; // if match idty to remove, to NOT add
+                  return (res||[]).concat(aidty);
+                }, null);
+                if (idtiesByBlock[otherIdtySamePubkey.block] === null) {
+                  delete idtiesByBlock[otherIdtySamePubkey.block];
                 }
               }
-            });
-
-            var idties = _.values(idtiesByPubkey);
-            var total = idties.length; // get total BEFORE slice
-
-            idties = _sortAndSliceIdentities(idties, offset, size);
-            var blocks = idties.reduce(function(res, aidty) {
-              return res.concat(aidty.block);
-            }, []);
+            }
+          });
 
-            return  $q.all([
-              // Get time from blocks
-              BMA.blockchain.blocks(_.uniq(blocks))
-              .then(function(blocks) {
-
-                _.forEach(blocks, function(block){
-                  _.forEach(idtiesByBlock[block.number], function(idty) {
-                    idty.sigDate = block.medianTime;
-                    if (block.number !== 0 && idty.blockHash !== block.hash) {
-                      addEvent(idty, {type:'error', message: 'ERROR.WOT_PENDING_INVALID_BLOCK_HASH'});
-                      console.debug("Invalid membership for uid={0}: block hash changed".format(idty.uid));
-                    }
-                  });
+          var idties = _.values(idtiesByPubkey);
+          var total = idties.length; // get total BEFORE slice
+
+          idties = _sortAndSliceIdentities(idties, offset, size);
+          var blocks = idties.reduce(function(res, aidty) {
+            return res.concat(aidty.block);
+          }, []);
+
+          return  $q.all([
+            // Get time from blocks
+            BMA.blockchain.blocks(_.uniq(blocks))
+            .then(function(blocks) {
+
+              _.forEach(blocks, function(block){
+                _.forEach(idtiesByBlock[block.number], function(idty) {
+                  idty.sigDate = block.medianTime;
+                  if (block.number !== 0 && idty.blockHash !== block.hash) {
+                    addEvent(idty, {type:'error', message: 'ERROR.WOT_PENDING_INVALID_BLOCK_HASH'});
+                    console.debug("Invalid membership for uid={0}: block hash changed".format(idty.uid));
+                  }
                 });
-              }),
-
-              // Extension point
-              extendAll(idties, 'pubkey', true/*skipAddUid*/)
-            ])
-            .then(function() {
-              console.debug("[ES] [wot] Loaded {0}/{1} pending identities in {2} ms".format(idties && idties.length || 0, total, Date.now() - now));
-              return {
-                hits: idties,
-                total: total
-              };
-            });
-          });
-      },
+              });
+            }),
 
-      getAll = function() {
-        var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','u','v','w','x','y','z'];
-        return getAllRecursive(letters, 0, BMA.constants.LIMIT_REQUEST_COUNT)
-          .then(function(idties) {
-            return extendAll(idties, 'pubkey', true/*skipAddUid*/);
-          })
-          .then(_addUniqueIds)
+            // Extension point
+            extendAll(idties, 'pubkey', true/*skipAddUid*/)
+          ])
           .then(function() {
+            console.debug("[ES] [wot] Loaded {0}/{1} pending identities in {2} ms".format(idties && idties.length || 0, total, Date.now() - now));
             return {
               hits: idties,
-              total: idties.length
+              total: total
             };
           });
-      },
-
-      getAllRecursive = function(letters, offset, size) {
-        return $q(function(resolve, reject) {
-          var result = [];
-          var pubkeys = {};
-          var jobs = [];
-          _.each(letters.slice(offset, offset+size), function(letter) {
-            jobs.push(
-              search(letter, {
-                addUniqueId: false, // will be done in parent method
-                allowExtension: false // extension point will be called in parent method
-              })
-            .then(function(idties){
-                if (!idties || !idties.length) return;
-                result = idties.reduce(function(res, idty) {
-                  if (!pubkeys[idty.pubkey]) {
-                    pubkeys[idty.pubkey] = true;
-                    return res.concat(idty);
-                  }
-                  return res;
-                }, result);
-              })
-            );
-          });
+        });
+    },
+
+    getAll = function() {
+      var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','u','v','w','x','y','z'];
+      return getAllRecursive(letters, 0, BMA.constants.LIMIT_REQUEST_COUNT)
+        .then(function(idties) {
+          return extendAll(idties, 'pubkey', true/*skipAddUid*/);
+        })
+        .then(_addUniqueIds)
+        .then(function() {
+          return {
+            hits: idties,
+            total: idties.length
+          };
+        });
+    },
 
-          $q.all(jobs)
-            .then(function() {
-              if (offset < letters.length - 1) {
-                $timeout(function() {
-                  getAllRecursive(letters, offset+size, size)
-                    .then(function(idties) {
-                      if (!idties || !idties.length) {
-                        resolve(result);
-                        return;
-                      }
-                      resolve(idties.reduce(function(res, idty) {
-                        if (!pubkeys[idty.pubkey]) {
-                          pubkeys[idty.pubkey] = true;
-                          return res.concat(idty);
-                        }
-                        return res;
-                      }, result));
-                    })
-                    .catch(function(err) {
-                      reject(err);
-                    });
-                }, BMA.constants.LIMIT_REQUEST_DELAY);
-              }
-              else {
-                resolve(result);
-              }
+    getAllRecursive = function(letters, offset, size) {
+      return $q(function(resolve, reject) {
+        var result = [];
+        var pubkeys = {};
+        var jobs = [];
+        _.each(letters.slice(offset, offset+size), function(letter) {
+          jobs.push(
+            search(letter, {
+              addUniqueId: false, // will be done in parent method
+              allowExtension: false // extension point will be called in parent method
             })
-            .catch(function(err){
-              if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
-                resolve(result);
-              }
-              else {
-                reject(err);
-              }
-            });
+          .then(function(idties){
+              if (!idties || !idties.length) return;
+              result = idties.reduce(function(res, idty) {
+                if (!pubkeys[idty.pubkey]) {
+                  pubkeys[idty.pubkey] = true;
+                  return res.concat(idty);
+                }
+                return res;
+              }, result);
+            })
+          );
         });
-      },
 
-      extend = function(idty, pubkeyAttributeName, skipAddUid) {
-        return extendAll([idty], pubkeyAttributeName, skipAddUid)
-          .then(function(res) {
-            return res[0];
+        $q.all(jobs)
+          .then(function() {
+            if (offset < letters.length - 1) {
+              $timeout(function() {
+                getAllRecursive(letters, offset+size, size)
+                  .then(function(idties) {
+                    if (!idties || !idties.length) {
+                      resolve(result);
+                      return;
+                    }
+                    resolve(idties.reduce(function(res, idty) {
+                      if (!pubkeys[idty.pubkey]) {
+                        pubkeys[idty.pubkey] = true;
+                        return res.concat(idty);
+                      }
+                      return res;
+                    }, result));
+                  })
+                  .catch(function(err) {
+                    reject(err);
+                  });
+              }, BMA.constants.LIMIT_REQUEST_DELAY);
+            }
+            else {
+              resolve(result);
+            }
+          })
+          .catch(function(err){
+            if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
+              resolve(result);
+            }
+            else {
+              reject(err);
+            }
           });
-      },
-
-      extendAll = function(idties, pubkeyAttributeName, skipAddUid) {
-
-        pubkeyAttributeName = pubkeyAttributeName || 'pubkey';
-
-        var jobs = [];
-        if (!skipAddUid) jobs.push(BMA.wot.member.uids());
-
-        jobs.push(api.data.raisePromise.search(null, idties, pubkeyAttributeName)
-          .catch(function(err) {
-            console.debug('Error while search identities, on extension point.');
-            console.error(err);
-          }));
+      });
+    },
 
-        return $q.all(jobs)
+    extend = function(idty, pubkeyAttributeName, skipAddUid) {
+      return extendAll([idty], pubkeyAttributeName, skipAddUid)
         .then(function(res) {
-          if (!skipAddUid) {
-            var uidsByPubkey = res[0];
-            // Set uid (on every data)
-            _.forEach(idties, function(data) {
-              if (!data.uid && data[pubkeyAttributeName]) {
-                data.uid = uidsByPubkey[data[pubkeyAttributeName]];
-                // Remove name if redundant with uid
-                if (data.uid && data.uid == data.name) {
-                  delete data.name;
-                }
+          return res[0];
+        });
+    },
+
+    extendAll = function(idties, pubkeyAttributeName, skipAddUid) {
+
+      pubkeyAttributeName = pubkeyAttributeName || 'pubkey';
+
+      var jobs = [];
+      if (!skipAddUid) jobs.push(BMA.wot.member.uids());
+
+      jobs.push(api.data.raisePromise.search(null, idties, pubkeyAttributeName)
+        .catch(function(err) {
+          console.debug('Error while search identities, on extension point.');
+          console.error(err);
+        }));
+
+      return $q.all(jobs)
+      .then(function(res) {
+        if (!skipAddUid) {
+          var uidsByPubkey = res[0];
+          // Set uid (on every data)
+          _.forEach(idties, function(data) {
+            if (!data.uid && data[pubkeyAttributeName]) {
+              data.uid = uidsByPubkey[data[pubkeyAttributeName]];
+              // Remove name if redundant with uid
+              if (data.uid && data.uid == data.name) {
+                delete data.name;
               }
-            });
-          }
+            }
+          });
+        }
 
-          return idties;
-        });
-      },
-
-      addEvent = function(data, event) {
-        event = event || {};
-        event.type = event.type || 'info';
-        event.message = event.message || '';
-        event.messageParams = event.messageParams || {};
-        data.events = data.events || [];
-        data.events.push(event);
-      },
-
-      cleanCache = function() {
-        console.debug("[wot] Cleaning cache...");
-        csCache.clear(cachePrefix);
-      }
-    ;
-
-    // Register extension points
-    api.registerEvent('data', 'load');
-    api.registerEvent('data', 'search');
-
-    // Listen if node changed
-    BMA.api.node.on.stop($rootScope, cleanCache, this);
-
-    return {
-      id: id,
-      load: loadData,
-      loadRequirements: loadRequirements,
-      search: search,
-      newcomers: getNewcomers,
-      pending: getPending,
-      all: getAll,
-      extend: extend,
-      extendAll: extendAll,
-      // api extension
-      api: api
-    };
-  }
-
-  var service = factory('default', BMA);
-
-  service.instance = factory;
-  return service;
+        return idties;
+      });
+    },
+
+    addEvent = function(data, event) {
+      event = event || {};
+      event.type = event.type || 'info';
+      event.message = event.message || '';
+      event.messageParams = event.messageParams || {};
+      data.events = data.events || [];
+      data.events.push(event);
+    },
+
+    cleanCache = function() {
+      console.debug("[wot] Cleaning cache...");
+      csCache.clear(cachePrefix);
+    }
+  ;
+
+  // Register extension points
+  api.registerEvent('data', 'load');
+  api.registerEvent('data', 'search');
+
+  // Listen if node changed
+  BMA.api.node.on.stop($rootScope, cleanCache, this);
+
+  return {
+    load: loadData,
+    loadRequirements: loadRequirements,
+    search: search,
+    newcomers: getNewcomers,
+    pending: getPending,
+    all: getAll,
+    extend: extend,
+    extendAll: extendAll,
+    // api extension
+    api: api
+  };
 });
diff --git a/www/plugins/es/css/style.css b/www/plugins/es/css/style.css
index c95bc0e824ad5d60b2039f82e704992a8aeb526c..edeb37ef823a1e190a908a8680121d6c4b35f2be 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 4895256972e5a41e5360c82311e414976f207b63..eec0d9c4fc65cb58420e525554eb30cf95e030b9 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 4895256972e5a41e5360c82311e414976f207b63..eec0d9c4fc65cb58420e525554eb30cf95e030b9 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 fe8978016c0049c54249f5a63b6d221236bf555b..8572520b29465529e1e5ac6e8a1e5a4e59ed1679 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 49be7a99468121da776c1951e2ad340bcea5f13f..50077c822aa462a01aea4d5d8e757da3797257bb 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 2b3175d285698edd6fa18b99cdf13ed5d0f744e1..34c22911185e57f43f88e9e3ad0e29a55babae6b 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 9227ffe45248986fbb5a194d314a71fad97f184a..e0e79b488dec8cf859c8a95346f28120ddeffa8a 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 4d6894532f95f2ca1ed53cf4deb280bbd7e2572a..4b04e73f69375530e1070372f839b17879da7b6c 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/like-controllers.js b/www/plugins/es/js/controllers/like-controllers.js
new file mode 100644
index 0000000000000000000000000000000000000000..a29609136cc0f51597039e9dd121867dc6d9f81c
--- /dev/null
+++ b/www/plugins/es/js/controllers/like-controllers.js
@@ -0,0 +1,282 @@
+angular.module('cesium.es.like.controllers', ['ngResource', 'cesium.es.services'])
+
+  .controller('ESLikesCtrl', ESLikesController)
+;
+
+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 || $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 04b3c29fb109e1e58a6f46fa5a4d4ef1242b4f81..7d1cc4c221d9e7608200d6400eca545a4b0315eb 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 ad1ba4bfb175e0b249f0ea88046b4ecb0c6e4229..eb1d12a5b8e98afd6e8240f6da7c02fa25885d85 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 44f9461da0037478858c356824570376eab6ece7..fe5ac90c9bf13a9ef8961bf04742e8dfc1975717 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, esLike, esModals, esWallet, esProfile, esInvitation) {
   'ngInject';
 
+  $scope.options = $scope.options || {};
+  $scope.options.like = $scope.options.like || {
+    kinds: esLike.constants.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 f928c32002304b8ba750b9ab1704920109a9e380..eee62d735428ae7f91a93e26a0e46aaec6cd9ff9 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/plugin.js b/www/plugins/es/js/plugin.js
index c1b1dc07f908a45d59bbaba51397066ab4d9366b..05a69ab169be59e629611595a90d1b7c20519d83 100644
--- a/www/plugins/es/js/plugin.js
+++ b/www/plugins/es/js/plugin.js
@@ -18,6 +18,7 @@ angular.module('cesium.es.plugin', [
     'cesium.es.group.controllers',
     'cesium.es.invitation.controllers',
     'cesium.es.subscription.controllers',
-    'cesium.es.document.controllers'
+    'cesium.es.document.controllers',
+    'cesium.es.like.controllers'
   ])
 ;
diff --git a/www/plugins/es/js/services.js b/www/plugins/es/js/services.js
index 0bb32bb2e7c0a718b8ca65a31deffb44e2a8be2e..ff48151ce2ba7d3658e88ddffe49c42961df2fa5 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 5044924bbcb0dd285ad80ff54954b2ca53fbc0b1..b5f5c40d40e0cb8c0b72945847ad5dba22aae26e 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 b4a7e6e01b5cc70d103405a8558f3bd75eefda8d..a1fa4428985f71cb7225a2563c804d8f35989733 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 0000000000000000000000000000000000000000..b622ae722b240a8b1cba8eca4e94dcccab18a456
--- /dev/null
+++ b/www/plugins/es/js/services/like-services.js
@@ -0,0 +1,154 @@
+angular.module('cesium.es.like.services', ['ngResource', 'cesium.services',
+  'cesium.es.http.services'])
+
+  .factory('esLike', function($q, csWallet, esHttp) {
+    'ngInject';
+
+
+    var constants = {
+      KINDS: ['LIKE', 'ABUSE']
+    };
+
+    function EsLike(index, type) {
+
+      var that = this;
+      that.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')
+        };
+
+      function getLikeIds(id, options) {
+        options = options || {};
+        options.kind = options.kind || 'LIKE';
+        var queryString = that.raw.searchBaseQueryString + id;
+        if (options.kind) queryString += ' AND kind:' + options.kind.toUpperCase();
+        if (options.issuer) queryString += ' AND issuer:' + options.issuer;
+
+        return that.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 that.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 that.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 that.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,
+        toggle: toggleLike,
+        add: addLike,
+        remove: removeLike,
+        count: countLike
+      };
+    }
+
+    return {
+      constants: constants,
+      instance: EsLike
+    };
+  })
+;
diff --git a/www/plugins/es/js/services/network-services.js b/www/plugins/es/js/services/network-services.js
index de4da6d2d06158e05b0b00a6894f34e1ab0232c6..6b653b1b6fb4fc128ddf807b470242edad086f92 100644
--- a/www/plugins/es/js/services/network-services.js
+++ b/www/plugins/es/js/services/network-services.js
@@ -220,13 +220,13 @@ angular.module('cesium.es.network.services', ['ngApi', 'cesium.es.http.services'
                       hasUpdates = true;
                     }
                     else {
-                      console.debug("[network] {0} endpoint [{1}] unchanged".format(
+                      console.debug("[ES] [network] {0} endpoint [{1}] unchanged".format(
                         refreshedPeer.ep && refreshedPeer.ep.api || '',
                         refreshedPeer.server));
                     }
                   }
                   else if (refreshedPeer && (refreshedPeer.online === data.filter.online || data.filter.online === 'all')) {
-                    console.debug("[network] {0} endpoint [{1}] is {2}".format(
+                    console.debug("[ES] [network] {0} endpoint [{1}] is {2}".format(
                       refreshedPeer.ep && refreshedPeer.ep.api || '',
                       refreshedPeer.server,
                       refreshedPeer.online ? 'UP' : 'DOWN'
@@ -328,10 +328,10 @@ angular.module('cesium.es.network.services', ['ngApi', 'cesium.es.http.services'
             }
             if (!peer.secondTry) {
               var ep = peer.ep || peer.getEP();
-              if (ep.dns && peer.server.indexOf(ep.dns) == -1) {
+              if (ep.dns && peer.server.indexOf(ep.dns) === -1) {
                 // try again, using DNS instead of IPv4 / IPV6
                 peer.secondTry = true;
-                peer.api = esHttp.lightInstance(ep.dns, ep.port, ep.useSsl);
+                peer.api = esHttp.lightInstance(ep.dns, peer.getPort(), peer.isSsl(), data.timeout);
                 return refreshPeer(peer); // recursive call
               }
             }
@@ -574,14 +574,14 @@ angular.module('cesium.es.network.services', ['ngApi', 'cesium.es.http.services'
             data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
             data.expertMode = angular.isDefined(options.expertMode) ? options.expertMode : data.expertMode;
             data.timeout = angular.isDefined(options.timeout) ? options.timeout : csConfig.timeout;
-            console.info('[network] Starting network from [{0}]'.format(data.pod.server));
+            console.info('[ES] [network] Starting network from [{0}]'.format(data.pod.server));
             var now = Date.now();
 
             addListeners();
 
             return loadPeers()
               .then(function(peers){
-                console.debug('[network] Started in '+(Date.now() - now)+'ms');
+                console.debug('[ES] [network] Started in '+(Date.now() - now)+'ms');
                 return peers;
               });
           });
@@ -589,7 +589,7 @@ angular.module('cesium.es.network.services', ['ngApi', 'cesium.es.http.services'
 
       close = function() {
         if (data.pod) {
-          console.info('[network-service] Stopping...');
+          console.info('[ES] [network-service] Stopping...');
           removeListeners();
           resetData();
         }
diff --git a/www/plugins/es/js/services/profile-services.js b/www/plugins/es/js/services/profile-services.js
index c36f3123b0ef714d60e5cbdf0e99f52c8a5743b7..95cbe011ea5aeaec3a32c5a5d4a61a8e74627633 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 800da207cfcc1bf1580efd7413bcbf138b82c7b1..1007bfff16b19c600002e0c364db3c342e67b6e8 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 0000000000000000000000000000000000000000..6e2c9f93f477693f1ee4c69a3b9de6c037db345e
--- /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 0000000000000000000000000000000000000000..5441724dde0c3040bdc1d24dc1695f79db06c094
--- /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 efbad7b3920cda9343dd16cb7643dc1bdcb2252a..69de08a637434d3e5ca30ba5ecd4c64166be5d64 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/network/view_es_peer.html b/www/plugins/es/templates/network/view_es_peer.html
index 64bc49fa0e647d6f24ad1002f3935aea88f38073..882ff8550024a634ed5142215fe17c701bf01e8f 100644
--- a/www/plugins/es/templates/network/view_es_peer.html
+++ b/www/plugins/es/templates/network/view_es_peer.html
@@ -4,7 +4,7 @@
     <span translate>ES_SETTINGS.PLUGIN_NAME</span>
   </ion-nav-title>
 
-  <ion-content class="has-header" scroll="true">
+  <ion-content>
 
     <div class="row no-padding">
       <div class="col col-20 hidden-xs hidden-sm">&nbsp;
diff --git a/www/plugins/es/templates/notification/view_notifications.html b/www/plugins/es/templates/notification/view_notifications.html
index b77c2b9fede90529865dc7ab4297640bdc0ec5f7..98226cfecd5be975dde4028bf026cc0d07413653 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 4a972edb21eaf0a965d0e1805f821b09451ea828..3f69517d142f9fcf69f02d40d836e9c9779cbb68 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 74cecad9da0322d8b5e9867ee870b55814dd4043..03730c7cb6311890db7001837e7037d6974cb1db 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 7fe4439c48daf9b864fa491695f3ccb0979df416..ef2db1aa6c4f1b73197c58e5e5cd44554e012c92 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 a5aa6df8a6f55dc669ebe60369d13df9a1c323ff..0c47c382b7258b80a9164ab57079f6dfdfa579ef 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 0000000000000000000000000000000000000000..c3ba6ad5f683a8fba1423dc29d766c1fe35e1a12
--- /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 5f5b3fa9b008a414457c3299a92dca7457c08ccb..5504af3a91f78664d311919ffe9d5b63e1c9583a 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 65ff216dbc2b9417ebb031c94e8af2aa22f0f7cb..0000000000000000000000000000000000000000
--- 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 65ff216dbc2b9417ebb031c94e8af2aa22f0f7cb..1c44635813213c5b0852677a7935e1fecc3f360c 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 b8cd1c04adaf3a3830be19e9902bec5c9dc7ec7d..1c44635813213c5b0852677a7935e1fecc3f360c 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 f6c4c5e197c22beda5bf1bfe24a7312a37138833..11fa547c807fbe4d04f4a6ebdbdbee58458c6d4c 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 088160024482bf91835ad178b557107e8d044f56..5b6317b3710b08d334c83a8f1992f7d1a70e7359 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 e150d77954b8ff52af5e5e1ef65277e8e975c445..71a1891fe8b38b3fcf15117acf72f589d2baff94 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 f4f62451b10c6a81ac63c47e9a3d86230a1566e0..2e07444c263951f084c022e84de382996fd8f651 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/api/home.html b/www/templates/api/home.html
index d669e13eca05942a4b2b3c4f821cfe23cd5f4b34..852bbf79597429ae39491f639c61101f697b9b7a 100644
--- a/www/templates/api/home.html
+++ b/www/templates/api/home.html
@@ -14,7 +14,7 @@
     </button>
   </ion-nav-buttons>
 
-  <ion-content class="has-header no-padding-xs no-padding-sm positive-900-bg">
+  <ion-content class="no-padding-xs no-padding-sm positive-900-bg">
 
     <br class="hidden-xs"/>
 
diff --git a/www/templates/api/transfer.html b/www/templates/api/transfer.html
index efb9ced6dff3641e81dc6a63622b5d96902a1de9..f0520fcff6102b399b9135b1b14d08af7f823743 100644
--- a/www/templates/api/transfer.html
+++ b/www/templates/api/transfer.html
@@ -24,7 +24,7 @@
     </button>
   </ion-nav-buttons>
 
-  <ion-content class=" has-header no-padding-xs positive-900-bg">
+  <ion-content class="no-padding-xs positive-900-bg">
 
     <br class="hidden-xs"/>
 
diff --git a/www/templates/blockchain/list_blocks_lg.html b/www/templates/blockchain/list_blocks_lg.html
index 8c9d6763eb79ca78be12c950a51146d46b8f5c8a..8d8d1ca97d0161951466502c6c09bc564d836190 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/home/home.html b/www/templates/home/home.html
index 6645457332a9d880ce9423ced9517b1abc813b37..946ed0ff6320cbb11c1bb3d293dff4baba60b319 100644
--- a/www/templates/home/home.html
+++ b/www/templates/home/home.html
@@ -13,98 +13,156 @@
     </button>
   </ion-nav-buttons>
 
-  <ion-content
-    class="has-header text-center no-padding-xs positive-900-bg circle-bg-dark">
+  <ion-content class="positive-900-bg circle-bg-dark">
+
+    <div class="row no-padding-xs responsive-lg">
+      <div class="col col-30 hidden-xs hidden-sm">&nbsp;</div>
+      <div class="col text-center no-padding-xs main-container">
+
+        <div id="helptip-home-logo" class="logo"></div>
+
+        <h4>
+          <span class="hidden-xs" translate>HOME.WELCOME</span>
+          <b ng-show="!loading" translate-values=":currency:{currency: $root.currency.name}" translate>HOME.MESSAGE</b>
+        </h4>
+
+        <div class="center padding" ng-if="loading">
+          <ion-spinner icon="android" ></ion-spinner>
+        </div>
+
+        <div class="center padding animate-fade-in animate-show-hide ng-hide" ng-show="!loading && error">
+          <div class="card card-item padding">
+            <p class="item-content item-text-wrap">
+                <span class="dark" trust-as-html="'HOME.CONNECTION_ERROR'|translate:node"></span>
+            </p>
+
+            <!-- Retry -->
+            <button type="button"
+                    class="button button-positive icon icon-left ion-refresh ink"
+                    ng-click="reload()">{{'COMMON.BTN_REFRESH'|translate}}</button>
+          </div>
+        </div>
+
+        <div class="center animate-fade-in animate-show-hide ng-hide" ng-show="!loading && !error">
+
+          <!-- Help tour (NOT ready yet for small device) -->
+          <button type="button"
+                  class="button button-block button-stable button-raised icon-left icon ion-easel ink-dark hidden-xs"
+                  ng-show="login"
+                  ng-click="startHelpTour()" >
+            {{'COMMON.BTN_HELP_TOUR'|translate}}
+          </button>
+
+          <button type="button"
+                  class="button button-block button-positive button-raised ink-dark"
+                  ng-click="showJoinModal()" ng-if="!login" translate>LOGIN.CREATE_FREE_ACCOUNT</button>
+
+          <button type="button"
+                  class="item button button-block button-positive button-raised icon icon-left ion-person ink-dark"
+                  ui-sref="app.view_wallet" ng-show="login" translate>MENU.ACCOUNT</button>
+
+          <button type="button"
+                  class="item button button-block button-positive button-raised icon icon-left ion-card ink-dark visible-xs"
+                  ui-sref="app.view_wallet_tx" ng-if="login" translate>MENU.TRANSACTIONS</button>
+
+          <br class="visible-xs visible-sm"/>
+
+          <!-- join link -->
+          <div class="text-center no-padding" ng-show="!login">
+            <br class="visible-xs visible-sm"/>
+            {{'LOGIN.HAVE_ACCOUNT_QUESTION'|translate}}
+            <b>
+              <a class="assertive hidden-xs hidden-sm" ui-sref="app.view_wallet" translate>
+                COMMON.BTN_LOGIN
+              </a>
+            </b>
+          </div>
+
+          <!-- disconnect link -->
+          <div class="text-center no-padding" ng-show="login">
+            <br class="visible-xs visible-sm"/>
+            <span ng-bind-html="'HOME.NOT_YOUR_ACCOUNT_QUESTION'|translate:{pubkey: walletData.pubkey}"></span>
+            <br/>
+            <b>
+              <a class="assertive hidden-xs hidden-sm" ng-click="logout()" translate>
+                HOME.BTN_CHANGE_ACCOUNT
+              </a>
+            </b>
+          </div>
+
+          <button type="button"
+                  class="button button-block button-stable button-raised ink visible-xs visible-sm"
+                  ui-sref="app.view_wallet" ng-if="!login" translate>COMMON.BTN_LOGIN</button>
+          <button type="button"
+                  class="button button-block button-assertive button-raised icon icon-left ion-log-out ink-dark visible-xs visible-sm"
+                  ng-click="logout()" ng-if="login" translate>COMMON.BTN_LOGOUT</button>
+
+
+          <div class="text-center no-padding visible-xs stable">
+            <br/>
+            <!-- version -->
+            {{'COMMON.APP_VERSION'|translate:{version: config.version} }}
+            |
+            <!-- about -->
+            <a href="#" ng-click="showAboutModal()" translate>HOME.BTN_ABOUT</a>
+          </div>
+
+        </div>
 
-    <div id="helptip-home-logo" class="logo"></div>
 
-    <h4>
-      <span class="hidden-xs" translate>HOME.WELCOME</span>
-      <b ng-show="!loading" translate-values=":currency:{currency: $root.currency.name}" translate>HOME.MESSAGE</b>
-    </h4>
-
-    <div class="center padding" ng-if="loading">
-      <ion-spinner icon="android" ></ion-spinner>
-    </div>
-
-    <div class="center padding animate-fade-in animate-show-hide ng-hide" ng-show="!loading && error">
-      <div class="card card-item padding">
-        <p class="item-content item-text-wrap">
-            <span class="dark" trust-as-html="'HOME.CONNECTION_ERROR'|translate:node"></span>
-        </p>
-
-        <!-- Retry -->
-        <button type="button"
-                class="button button-positive icon icon-left ion-refresh ink"
-                ng-click="reload()">{{'COMMON.BTN_REFRESH'|translate}}</button>
       </div>
-    </div>
-
-    <div class="center animate-fade-in animate-show-hide ng-hide" ng-show="!loading && !error">
-
-      <!-- Help tour (NOT ready yet for small device) -->
-      <button type="button"
-              class="button button-block button-stable button-raised icon-left icon ion-easel ink-dark hidden-xs"
-              ng-show="login"
-              ng-click="startHelpTour()" >
-        {{'COMMON.BTN_HELP_TOUR'|translate}}
-      </button>
-
-      <button type="button"
-              class="button button-block button-positive button-raised ink-dark"
-              ng-click="showJoinModal()" ng-if="!login" translate>LOGIN.CREATE_FREE_ACCOUNT</button>
-
-      <button type="button"
-              class="item button button-block button-positive button-raised icon icon-left ion-person ink-dark"
-              ui-sref="app.view_wallet" ng-show="login" translate>MENU.ACCOUNT</button>
-
-      <button type="button"
-              class="item button button-block button-positive button-raised icon icon-left ion-card ink-dark visible-xs"
-              ui-sref="app.view_wallet_tx" ng-if="login" translate>MENU.TRANSACTIONS</button>
-
-      <br class="visible-xs visible-sm"/>
-
-      <!-- join link -->
-      <div class="text-center no-padding" ng-show="!login">
-        <br class="visible-xs visible-sm"/>
-        {{'LOGIN.HAVE_ACCOUNT_QUESTION'|translate}}
-        <b>
-          <a class="assertive hidden-xs hidden-sm" ui-sref="app.view_wallet" translate>
-            COMMON.BTN_LOGIN
-          </a>
-        </b>
+      <div class="col col-30 no-padding">
+
+        <!-- feed -->
+        <div ng-if="feed" class="feed padding padding-top">
+          <h3 class="padding-left">
+            <i class="icon ion-speakerphone"></i>
+            {{feed.title}}
+            <small><a ng-click="openLink($event, feed.home_page_url)" class="gray">
+              <span translate>HOME.SHOW_ALL_FEED</span>
+              <i class="icon ion-chevron-right"></i>
+            </a></small>
+          </h3>
+
+          <!-- feed items -->
+          <div ng-repeat="item in feed.items"
+             class="card padding">
+
+            <div class="header ">
+              <!-- author -->
+              <i ng-if="item.author.avatar" class="avatar" style="background-image: url({{item.author.avatar}});"></i>
+              <a ng-class="{'avatar-left-padding': item.author.avatar}" class="author"
+                 ng-click="item.author.url && openLink($event, item.author.url)">
+                {{item.author.name}}
+              </a>
+
+              <!-- time -->
+              <a ng-if="item.time"
+                 title="{{item.time|formatDate}}"
+                 ng-click="openLink($event, item.url)"
+                 class="item-note">
+                <small><i class="icon ion-clock"></i>&nbsp;{{item.time|formatFromNow}}</small>
+              </a>
+            </div>
+            <!-- title -->
+            <h2 class="title feed-title">
+              <a ng-click="openLink($event, item.url)">{{item.title}}</a></h2>
+            <div ng-if="item.content"
+                 class="content feed-content"
+                 trust-as-html="item.content"></div>
+            <h4 class="card-footer feed-footer text-right positive-100">
+              <a ng-click="openLink($event, item.url)">
+                <span ng-if="item.truncated" translate>HOME.READ_MORE</span>
+                <span ng-if="!item.truncated" translate>COMMON.BTN_SHOW</span>
+                <i class="icon ion-chevron-right"></i>
+              </a>
+            </h4>
+          </div>
+        </div>
       </div>
+    </div>
 
-      <!-- disconnect link -->
-      <div class="text-center no-padding" ng-show="login">
-        <br class="visible-xs visible-sm"/>
-        <span ng-bind-html="'HOME.NOT_YOUR_ACCOUNT_QUESTION'|translate:{pubkey: walletData.pubkey}"></span>
-        <br/>
-        <b>
-          <a class="assertive hidden-xs hidden-sm" ng-click="logout()" translate>
-            HOME.BTN_CHANGE_ACCOUNT
-          </a>
-        </b>
-      </div>
 
-      <button type="button"
-              class="button button-block button-stable button-raised ink visible-xs visible-sm"
-              ui-sref="app.view_wallet" ng-if="!login" translate>COMMON.BTN_LOGIN</button>
-      <button type="button"
-              class="button button-block button-assertive button-raised icon icon-left ion-log-out ink-dark visible-xs visible-sm"
-              ng-click="logout()" ng-if="login" translate>COMMON.BTN_LOGOUT</button>
-
-
-      <div class="text-center no-padding visible-xs stable">
-        <br/>
-        <!-- version -->
-        {{'COMMON.APP_VERSION'|translate:{version: config.version} }}
-        |
-        <!-- about -->
-        <a href="#" ng-click="showAboutModal()" translate>HOME.BTN_ABOUT</a>
-      </div>
-
-    </div>
   </ion-content>
 
 </ion-view>
diff --git a/www/templates/network/view_peer.html b/www/templates/network/view_peer.html
index c90092d4e8d187a99eefd26c5ae85ef086d48262..b081f75939d63df63d3250f134743a2f5048d832 100644
--- a/www/templates/network/view_peer.html
+++ b/www/templates/network/view_peer.html
@@ -3,7 +3,7 @@
     <span translate>PEER.VIEW.TITLE</span>
   </ion-nav-title>
 
-  <ion-content class="has-header" scroll="true">
+  <ion-content>
 
     <div class="row no-padding">
       <div class="col col-20 hidden-xs hidden-sm">&nbsp;
diff --git a/www/templates/wallet/view_wallet.html b/www/templates/wallet/view_wallet.html
index 18d39744f0676fdd0754f0435927787fc2ad54db..0b1e7d4cb2c100d39fcf1502fd3cb0c9bb1e1d2a 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/tabs/tab_received_certifications.html b/www/templates/wot/tabs/tab_received_certifications.html
index d553b658d7fee71093ecd1bab9a86322ec0ab616..98ee69959499aec77b8a88555948630bab189e38 100644
--- a/www/templates/wot/tabs/tab_received_certifications.html
+++ b/www/templates/wot/tabs/tab_received_certifications.html
@@ -4,7 +4,7 @@
     </button>
   </ion-nav-buttons>
 
-  <ion-content ng-init="motions.givenCertifications=false; motions.avatar=false;" class="has-header">
+  <ion-content ng-init="motions.givenCertifications=false; motions.avatar=false;">
     <div class="center padding" ng-if="loading">
       <ion-spinner icon="android"></ion-spinner>
     </div>
diff --git a/www/templates/wot/view_identity.html b/www/templates/wot/view_identity.html
index 055a26520abe9c522af014ae8ad8693718511b97..3a6604907316df6f07cc65535df3dc090b92dc9e 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 -->