diff --git a/component.json b/component.json
index 827bda4238..58b5ae64f3 100644
--- a/component.json
+++ b/component.json
@@ -23,6 +23,7 @@
"sinon": "~1.7",
"sinon-chai": "~2.4",
"js-fixtures": "https://github.com/badunk/js-fixtures/archive/master.zip",
- "bootstrap-multiselect": "https://github.com/davidstutz/bootstrap-multiselect.git"
+ "bootstrap-multiselect": "https://github.com/davidstutz/bootstrap-multiselect.git",
+ "zxcvbn" : "https://github.com/lowe/zxcvbn.git"
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php
index 58b47aaecd..21d6e17e4c 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/Login.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php
@@ -191,7 +191,12 @@ class Login implements ControllerProviderInterface
'no_collection_selected' => _('No collection selected'),
'one_collection_selected' => _('%d collection selected'),
'collections_selected' => _('%d collections selected'),
- 'all_collections' => _('Select all collections')
+ 'all_collections' => _('Select all collections'),
+ // password strength
+ 'weak' => _('Weak'),
+ 'ordinary' => _('Ordinary'),
+ 'good' => _('Good'),
+ 'great' => _('Great'),
));
$response->setExpires(new \DateTime('+1 day'));
diff --git a/templates/web/account/change-password.html.twig b/templates/web/account/change-password.html.twig
index a179b439c4..9eab4bcd13 100644
--- a/templates/web/account/change-password.html.twig
+++ b/templates/web/account/change-password.html.twig
@@ -54,5 +54,6 @@
{% block scripts %}
{{ parent() }}
+
{% endblock %}
diff --git a/templates/web/common/password_strength_widget.html.twig b/templates/web/common/password_strength_widget.html.twig
new file mode 100644
index 0000000000..bc32bc4631
--- /dev/null
+++ b/templates/web/common/password_strength_widget.html.twig
@@ -0,0 +1,15 @@
+
diff --git a/templates/web/login/common/macros.html.twig b/templates/web/login/common/macros.html.twig
index 9ba280c3e9..02df8b0e0e 100644
--- a/templates/web/login/common/macros.html.twig
+++ b/templates/web/login/common/macros.html.twig
@@ -28,6 +28,9 @@
{{ _self.fieldInput(passField, form_name, icon_name, custom_attributes) }}
+ {% if loop.first %}
+ {% include 'common/password_strength_widget.html.twig' %}
+ {% endif %}
{% endfor %}
diff --git a/templates/web/login/register-classic.html.twig b/templates/web/login/register-classic.html.twig
index 2c79282a5a..7f59986fb3 100644
--- a/templates/web/login/register-classic.html.twig
+++ b/templates/web/login/register-classic.html.twig
@@ -101,5 +101,6 @@
{% block scripts %}
{{ parent() }}
+
{% endblock %}
diff --git a/templates/web/login/renew-password.html.twig b/templates/web/login/renew-password.html.twig
index 2d8b3a5804..d2b4906943 100644
--- a/templates/web/login/renew-password.html.twig
+++ b/templates/web/login/renew-password.html.twig
@@ -50,5 +50,6 @@
{% block scripts %}
{{ parent() }}
+
{% endblock %}
diff --git a/www/scripts/apps/login/home/recoverPassword.js b/www/scripts/apps/login/home/recoverPassword.js
index 85c00ac991..f2e0d87301 100644
--- a/www/scripts/apps/login/home/recoverPassword.js
+++ b/www/scripts/apps/login/home/recoverPassword.js
@@ -11,7 +11,7 @@ require([
"jquery",
"i18n",
"apps/login/home/common",
- "apps/login/home/views/form"
+ "apps/login/home/views/forms/passwordSetter"
], function($, i18n, Common, RenewPassword) {
i18n.init({
resGetPath: Common.languagePath,
diff --git a/www/scripts/apps/login/home/register.js b/www/scripts/apps/login/home/register.js
index 36ae325925..874b8905af 100644
--- a/www/scripts/apps/login/home/register.js
+++ b/www/scripts/apps/login/home/register.js
@@ -12,7 +12,7 @@ require([
"jquery",
"i18n",
"apps/login/home/common",
- "apps/login/home/views/form"
+ "apps/login/home/views/forms/passwordSetter"
], function($, i18n, Common, RegisterForm) {
var fieldsConfiguration = [];
diff --git a/www/scripts/apps/login/home/renewPassword.js b/www/scripts/apps/login/home/renewPassword.js
index 06c4844ccf..71c3eb2f0c 100644
--- a/www/scripts/apps/login/home/renewPassword.js
+++ b/www/scripts/apps/login/home/renewPassword.js
@@ -11,7 +11,7 @@ require([
"jquery",
"i18n",
"apps/login/home/common",
- "apps/login/home/views/form"
+ "apps/login/home/views/forms/passwordSetter"
], function($, i18n, Common, RenewPassword) {
i18n.init({
resGetPath: Common.languagePath,
diff --git a/www/scripts/apps/login/home/views/form.js b/www/scripts/apps/login/home/views/form.js
index d30e2d5a77..bc8904acec 100644
--- a/www/scripts/apps/login/home/views/form.js
+++ b/www/scripts/apps/login/home/views/form.js
@@ -15,7 +15,7 @@ define([
"common/validator",
"apps/login/home/views/input"
], function($, _, Backbone, bootstrap, Validator, InputView) {
- var RegisterForm = Backbone.View.extend({
+ var Form = Backbone.View.extend({
events: {
"submit": "_onSubmit"
},
@@ -82,5 +82,5 @@ define([
}
});
- return RegisterForm;
+ return Form;
});
diff --git a/www/scripts/apps/login/home/views/forms/passwordSetter.js b/www/scripts/apps/login/home/views/forms/passwordSetter.js
new file mode 100644
index 0000000000..05d1c1042f
--- /dev/null
+++ b/www/scripts/apps/login/home/views/forms/passwordSetter.js
@@ -0,0 +1,80 @@
+/*
+ * This file is part of Phraseanet
+ *
+ * (c) 2005-2013 Alchemy
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+define([
+ "jquery",
+ "underscore",
+ "i18n",
+ "backbone",
+ "bootstrap",
+ "apps/login/home/views/form"
+], function($, _, i18n, Backbone, bootstrap, FormView) {
+ var PasswordSetterForm = FormView.extend({
+ events: function(){
+ return _.extend({},FormView.prototype.events,{
+ 'keyup input[type=password]' : 'onPasswordKeyup'
+ });
+ },
+ onPasswordKeyup : function(event) {
+ var input = $(event.target);
+ var password = input.val();
+ var inputView = this.inputViews[input.attr("name")];
+ var bg = $(".password_strength_bg", inputView.$el);
+ var label = $(".password_strength_label", inputView.$el);
+ var desc = $(".password_strength_desc", inputView.$el);
+ var css = {
+ "width": "0%",
+ "background-color": "rgb(39, 39, 30)"
+ };
+ var result = "";
+
+ if (password.length > 0 ) {
+ var passMeter = zxcvbn(input.val());
+
+ switch (passMeter.score) {
+ case 0:
+ case 1:
+ css = {
+ "width": "25%",
+ "background-color": "rgb(200, 24, 24)"
+ };
+ result = i18n.t("weak");
+ break;
+ case 2:
+ css = {
+ "width": "50%",
+ "background-color": "rgb(255, 172, 29)"
+ };
+ result = i18n.t("ordinary");
+ break;
+ case 3:
+ css = {
+ "width": "75%",
+ "background-color": "rgb(166, 192, 96)"
+ };
+ result = i18n.t("good");
+ break;
+ case 4:
+ css = {
+ "width": "100%",
+ "background-color": "rgb(39, 179, 15)"
+ };
+ result = i18n.t("great");
+ break;
+ }
+ }
+
+ bg.css(css);
+ label.css({"color": css["background-color"]});
+ desc.css({"color": css["background-color"]}).html(result);
+ }
+ });
+
+ return PasswordSetterForm;
+});
diff --git a/www/skins/login/less/skin.less b/www/skins/login/less/skin.less
index 7df32e498c..0beb044be1 100644
--- a/www/skins/login/less/skin.less
+++ b/www/skins/login/less/skin.less
@@ -753,6 +753,50 @@ form[name=registerForm] .multiselect-group {
font-size: @fontSizeLarge;
}
+.password_strength_widget, .password_strength_widget tr, .password_strength_widget td {
+ border: none !important;
+}
+
+.password_strength_label, .password_strength_desc {
+ font-size: @fontSizeMini;
+ color: lighten(@backgroundSideBar, 5%) ;
+}
+
+.password_strength_desc {
+ text-align: right;
+}
+
+.password_strength_container {
+ position: relative;
+ width: 100%;
+ margin: 5px auto 0 auto;
+ height: 10px;
+}
+
+.password_strength_bg {
+ height: 4px;
+ background-color: lighten(@backgroundSideBar, 5%);
+ width: 100%;
+ position: absolute;
+ left: 0;
+}
+
+.password_strength {
+ height: 4px;
+ background-color: #c81818;
+ width: 0%;
+ position: absolute;
+ left: 0;
+}
+
+.password_strength_separator {
+ height: 4px;
+ width: 2px;
+ background-color: @backgroundSideBar;
+ position: absolute;
+ left: 0;
+}
+
/** IE Fixes */
.lt-ie8 authentication-sidebar-language .caret {