diff --git a/main/admin/user_add.php b/main/admin/user_add.php
index da2ecbda51..a72250e569 100644
--- a/main/admin/user_add.php
+++ b/main/admin/user_add.php
@@ -23,12 +23,33 @@ api_protect_admin_script(true);
$is_platform_admin = api_is_platform_admin() ? 1 : 0;
$message = null;
-
+$htmlHeadXtra[] = api_get_password_checker_js('#password');
$htmlHeadXtra[] = '';
$htmlHeadXtra[] = '';
+
+if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+ $htmlHeadXtra[] = '
+ ';
+}
+
$htmlHeadXtra[] = '
';
if (!empty($_GET['message'])) {
@@ -157,9 +179,14 @@ if (count($extAuthSource) > 0) {
}
$group[] = $form->createElement('radio', 'password_auto', get_lang('Password'), get_lang('AutoGeneratePassword').'
', 1);
$group[] = $form->createElement('radio', 'password_auto', 'id="radio_user_password"', null, 0);
-$group[] = $form->createElement('password', 'password', null, array('onkeydown' => 'javascript: password_switch_radio_button();'));
+$group[] = $form->createElement('password', 'password', null, array('id'=> 'password', 'onkeydown' => 'javascript: password_switch_radio_button();'));
+
$form->addGroup($group, 'password', get_lang('Password'), '');
+if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+ $form->addElement('label', null, '
');
+}
+
// Status
$status = array();
$status[COURSEMANAGER] = get_lang('Teacher');
@@ -337,4 +364,4 @@ $tpl = new Template($tool_name);
//$tpl->assign('actions', $actions);
$tpl->assign('message', $message);
$tpl->assign('content', $content);
-$tpl->display_one_col_template();
\ No newline at end of file
+$tpl->display_one_col_template();
diff --git a/main/auth/inscription.php b/main/auth/inscription.php
index e34bae3f76..8d27aea151 100644
--- a/main/auth/inscription.php
+++ b/main/auth/inscription.php
@@ -17,6 +17,8 @@ require_once '../inc/global.inc.php';
require_once api_get_path(CONFIGURATION_PATH).'profile.conf.php';
require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
+$htmlHeadXtra[] = api_get_password_checker_js('#pass1');
+
if (api_get_setting('allow_registration') === 'false') {
api_not_allowed(true);
}
@@ -51,10 +53,10 @@ if ($user_already_registered_show_terms == false) {
if (api_is_western_name_order()) {
// FIRST NAME and LAST NAME
$form->addElement('text', 'firstname', get_lang('FirstName'), array('size' => 40));
- $form->addElement('text', 'lastname', get_lang('LastName'), array('size' => 40));
+ $form->addElement('text', 'lastname', get_lang('LastName'), array('size' => 40));
} else {
// LAST NAME and FIRST NAME
- $form->addElement('text', 'lastname', get_lang('LastName'), array('size' => 40));
+ $form->addElement('text', 'lastname', get_lang('LastName'), array('size' => 40));
$form->addElement('text', 'firstname', get_lang('FirstName'), array('size' => 40));
}
$form->applyFilter(array('lastname', 'firstname'), 'trim');
@@ -68,7 +70,7 @@ if ($user_already_registered_show_terms == false) {
}
if (api_get_setting('login_is_email') == 'true') {
- $form->applyFilter('email','trim');
+ $form->applyFilter('email', 'trim');
if (api_get_setting('registration', 'email') != 'true') {
$form->addRule('email', get_lang('ThisFieldIsRequired'), 'required');
}
@@ -99,8 +101,13 @@ if ($user_already_registered_show_terms == false) {
}
// PASSWORD
- $form->addElement('password', 'pass1', get_lang('Pass'), array('size' => 20, 'autocomplete' => 'off'));
- $form->addElement('password', 'pass2', get_lang('Confirmation'), array('size' => 20, 'autocomplete' => 'off'));
+ $form->addElement('password', 'pass1', get_lang('Pass'), array('id' => 'pass1', 'size' => 20, 'autocomplete' => 'off'));
+
+ if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+ $form->addElement('label', null, '');
+ }
+
+ $form->addElement('password', 'pass2', get_lang('Confirmation'), array('id' => 'pass2', 'size' => 20, 'autocomplete' => 'off'));
$form->addRule('pass1', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('pass2', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule(array('pass1', 'pass2'), get_lang('PassTwo'), 'compare');
diff --git a/main/auth/profile.php b/main/auth/profile.php
index fa05dac6ca..0cd137e755 100644
--- a/main/auth/profile.php
+++ b/main/auth/profile.php
@@ -23,6 +23,9 @@ if (api_get_setting('allow_social_tool') == 'true') {
$this_section = SECTION_MYPROFILE;
}
+
+$htmlHeadXtra[] = api_get_password_checker_js('#password1');
+
$_SESSION['this_section'] = $this_section;
if (!(isset($_user['user_id']) && $_user['user_id']) || api_is_anonymous($_user['user_id'], true)) {
@@ -257,7 +260,11 @@ if (api_get_setting('extended_profile') == 'true') {
// PASSWORD, if auth_source is platform
if (is_platform_authentication() && is_profile_editable() && api_get_setting('profile', 'password') == 'true') {
$form->addElement('password', 'password0', array(get_lang('Pass'), get_lang('Enter2passToChange')), array('size' => 40));
- $form->addElement('password', 'password1', get_lang('NewPass'), array('size' => 40));
+ $form->addElement('password', 'password1', get_lang('NewPass'), array('id'=> 'password1', 'size' => 40));
+
+ if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+ $form->addElement('label', null, '');
+ }
$form->addElement('password', 'password2', get_lang('Confirmation'), array('size' => 40));
// user must enter identical password twice so we can prevent some user errors
$form->addRule(array('password1', 'password2'), get_lang('PassTwo'), 'compare');
diff --git a/main/css/base.css b/main/css/base.css
index 9e072a9296..88534f0806 100644
--- a/main/css/base.css
+++ b/main/css/base.css
@@ -27,7 +27,7 @@ legend {
#header_right {
text-align: right;
float: right;
-
+
}
@@ -175,9 +175,9 @@ header #logo {
#main .nav .dropdown {
width:150px;
text-align: right;
-
-}
-
+
+}
+
#main .nav .dropdown:nth-child(n) {
width:auto;
}
@@ -198,7 +198,7 @@ header {
.breadcrumb li img {
width: 0%;
}
-
+
.breadcrumb li img:nth-child(n) {
width: auto;
}
@@ -434,7 +434,7 @@ footer #footer_left {
vertical-align:middle;
}
.actions .actions-pagination {
- float:right;
+ float:right;
margin-top:2px;
}
.actions .actions-pagination * {
@@ -4875,3 +4875,9 @@ i.size-32.icon-new-work{
background-color: #e6e6e6;
}
+.password-verdict {
+ padding-left:5px;
+}
+#password_progress {
+ width: 220px;
+}
diff --git a/main/inc/lib/javascript/strength/strength.js b/main/inc/lib/javascript/strength/strength.js
new file mode 100644
index 0000000000..730f8917d4
--- /dev/null
+++ b/main/inc/lib/javascript/strength/strength.js
@@ -0,0 +1,315 @@
+/*jslint vars: false, browser: true, nomen: true, regexp: true */
+/*global jQuery */
+
+/*
+* jQuery Password Strength plugin for Twitter Bootstrap
+*
+* Copyright (c) 2008-2013 Tane Piper
+* Copyright (c) 2013 Alejandro Blanco
+* Dual licensed under the MIT and GPL licenses.
+*
+*/
+
+(function ($) {
+ "use strict";
+
+ var options = {
+ errors: [],
+ // Options
+ minChar: 8,
+ errorMessages: {
+ password_to_short: "The Password is too short",
+ same_as_username: "Your password cannot be the same as your username"
+ },
+ scores: [17, 26, 40, 50],
+ verdicts: ["Weak", "Normal", "Medium", "Strong", "Very Strong"],
+ showVerdicts: true,
+ raisePower: 1.4,
+ usernameField: "#username",
+ onLoad: undefined,
+ onKeyUp: undefined,
+ viewports: {
+ progress: undefined,
+ verdict: undefined,
+ errors: undefined
+ },
+ // Rules stuff
+ ruleScores: {
+ wordNotEmail: -100,
+ wordLength: -100,
+ wordSimilarToUsername: -100,
+ wordLowercase: 1,
+ wordUppercase: 3,
+ wordOneNumber: 3,
+ wordThreeNumbers: 5,
+ wordOneSpecialChar: 3,
+ wordTwoSpecialChar: 5,
+ wordUpperLowerCombo: 2,
+ wordLetterNumberCombo: 2,
+ wordLetterNumberCharCombo: 2
+ },
+ rules: {
+ wordNotEmail: true,
+ wordLength: true,
+ wordSimilarToUsername: true,
+ wordLowercase: true,
+ wordUppercase: true,
+ wordOneNumber: true,
+ wordThreeNumbers: true,
+ wordOneSpecialChar: true,
+ wordTwoSpecialChar: true,
+ wordUpperLowerCombo: true,
+ wordLetterNumberCombo: true,
+ wordLetterNumberCharCombo: true
+ },
+ validationRules: {
+ wordNotEmail: function (options, word, score) {
+ return word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i) && score;
+ },
+ wordLength: function (options, word, score) {
+ var wordlen = word.length,
+ lenScore = Math.pow(wordlen, options.raisePower);
+ if (wordlen < options.minChar) {
+ lenScore = (lenScore + score);
+ options.errors.push(options.errorMessages.password_to_short);
+ }
+ return lenScore;
+ },
+ wordSimilarToUsername: function (options, word, score) {
+ var username = $(options.usernameField).val();
+ if (username && word.toLowerCase().match(username.toLowerCase())) {
+ options.errors.push(options.errorMessages.same_as_username);
+ return score;
+ }
+ return true;
+ },
+ wordLowercase: function (options, word, score) {
+ return word.match(/[a-z]/) && score;
+ },
+ wordUppercase: function (options, word, score) {
+ return word.match(/[A-Z]/) && score;
+ },
+ wordOneNumber : function (options, word, score) {
+ return word.match(/\d+/) && score;
+ },
+ wordThreeNumbers : function (options, word, score) {
+ return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
+ },
+ wordOneSpecialChar : function (options, word, score) {
+ return word.match(/.[!,@,#,$,%,\^,&,*,?,_,~]/) && score;
+ },
+ wordTwoSpecialChar : function (options, word, score) {
+ return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
+ },
+ wordUpperLowerCombo : function (options, word, score) {
+ return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
+ },
+ wordLetterNumberCombo : function (options, word, score) {
+ return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
+ },
+ wordLetterNumberCharCombo : function (options, word, score) {
+ return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score;
+ }
+ }
+ },
+
+ setProgressBar = function ($el, score) {
+ var options = $el.data("pwstrength"),
+ progressbar = options.progressbar,
+ $verdict;
+
+ if (options.showVerdicts) {
+ if (options.viewports.verdict) {
+ $verdict = $(options.viewports.verdict).find(".password-verdict");
+ } else {
+ $verdict = $el.parent().find(".password-verdict");
+ if ($verdict.length === 0) {
+ $verdict = $('');
+ $verdict.insertAfter($el);
+ }
+ }
+ }
+
+ if (score < options.scores[0]) {
+ progressbar.addClass("progress-danger").removeClass("progress-warning").removeClass("progress-success");
+ progressbar.find(".bar").css("width", "5%");
+ if (options.showVerdicts) {
+ $verdict.text(options.verdicts[0]);
+ }
+ } else if (score >= options.scores[0] && score < options.scores[1]) {
+ progressbar.addClass("progress-danger").removeClass("progress-warning").removeClass("progress-success");
+ progressbar.find(".bar").css("width", "25%");
+ if (options.showVerdicts) {
+ $verdict.text(options.verdicts[1]);
+ }
+ } else if (score >= options.scores[1] && score < options.scores[2]) {
+ progressbar.addClass("progress-warning").removeClass("progress-danger").removeClass("progress-success");
+ progressbar.find(".bar").css("width", "50%");
+ if (options.showVerdicts) {
+ $verdict.text(options.verdicts[2]);
+ }
+ } else if (score >= options.scores[2] && score < options.scores[3]) {
+ progressbar.addClass("progress-warning").removeClass("progress-danger").removeClass("progress-success");
+ progressbar.find(".bar").css("width", "75%");
+ if (options.showVerdicts) {
+ $verdict.text(options.verdicts[3]);
+ }
+ } else if (score >= options.scores[3]) {
+ progressbar.addClass("progress-success").removeClass("progress-warning").removeClass("progress-danger");
+ progressbar.find(".bar").css("width", "100%");
+ if (options.showVerdicts) {
+ $verdict.text(options.verdicts[4]);
+ }
+ }
+ },
+
+ calculateScore = function ($el) {
+ var self = this,
+ word = $el.val(),
+ totalScore = 0,
+ options = $el.data("pwstrength");
+
+ $.each(options.rules, function (rule, active) {
+ if (active === true) {
+ var score = options.ruleScores[rule],
+ result = options.validationRules[rule](options, word, score);
+ if (result) {
+ totalScore += result;
+ }
+ }
+ });
+ setProgressBar($el, totalScore);
+ return totalScore;
+ },
+
+ progressWidget = function () {
+ return '';
+ },
+
+ methods = {
+ init: function (settings) {
+ var self = this,
+ allOptions = $.extend(options, settings);
+
+ return this.each(function (idx, el) {
+ var $el = $(el),
+ progressbar,
+ verdict;
+
+ $el.data("pwstrength", allOptions);
+
+ $el.on("keyup", function (event) {
+ var options = $el.data("pwstrength");
+ options.errors = [];
+ calculateScore.call(self, $el);
+ if ($.isFunction(options.onKeyUp)) {
+ options.onKeyUp(event);
+ }
+ });
+
+ progressbar = $(progressWidget());
+ if (allOptions.viewports.progress) {
+ $(allOptions.viewports.progress).append(progressbar);
+ } else {
+ progressbar.insertAfter($el);
+ }
+ progressbar.find(".bar").css("width", "0%");
+ $el.data("pwstrength").progressbar = progressbar;
+
+ if (allOptions.showVerdicts) {
+ verdict = $('' + allOptions.verdicts[0] + '');
+ if (allOptions.viewports.verdict) {
+ $(allOptions.viewports.verdict).append(verdict);
+ } else {
+ verdict.insertAfter($el);
+ }
+ }
+
+ if ($.isFunction(allOptions.onLoad)) {
+ allOptions.onLoad();
+ }
+ });
+ },
+
+ destroy: function () {
+ this.each(function (idx, el) {
+ var $el = $(el);
+ $el.parent().find("span.password-verdict").remove();
+ $el.parent().find("div.progress").remove();
+ $el.parent().find("ul.error-list").remove();
+ $el.removeData("pwstrength");
+ });
+ },
+
+ forceUpdate: function () {
+ var self = this;
+ this.each(function (idx, el) {
+ var $el = $(el),
+ options = $el.data("pwstrength");
+ options.errors = [];
+ calculateScore.call(self, $el);
+ });
+ },
+
+ outputErrorList: function () {
+ this.each(function (idx, el) {
+ var output = '',
+ $el = $(el),
+ errors = $el.data("pwstrength").errors,
+ viewports = $el.data("pwstrength").viewports,
+ verdict;
+ $el.parent().find("ul.error-list").remove();
+
+ if (errors.length > 0) {
+ $.each(errors, function (i, item) {
+ output += '- ' + item + '
';
+ });
+ output += '
';
+ if (viewports.errors) {
+ $(viewports.errors).html(output);
+ } else {
+ output = $(output);
+ verdict = $el.parent().find("span.password-verdict");
+ if (verdict.length > 0) {
+ el = verdict;
+ }
+ output.insertAfter(el);
+ }
+ }
+ });
+ },
+
+ addRule: function (name, method, score, active) {
+ this.each(function (idx, el) {
+ var options = $(el).data("pwstrength");
+ options.rules[name] = active;
+ options.ruleScores[name] = score;
+ options.validationRules[name] = method;
+ });
+ },
+
+ changeScore: function (rule, score) {
+ this.each(function (idx, el) {
+ $(el).data("pwstrength").ruleScores[rule] = score;
+ });
+ },
+
+ ruleActive: function (rule, active) {
+ this.each(function (idx, el) {
+ $(el).data("pwstrength").rules[rule] = active;
+ });
+ }
+ };
+
+ $.fn.pwstrength = function (method) {
+ var result;
+ if (methods[method]) {
+ result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else if (typeof method === "object" || !method) {
+ result = methods.init.apply(this, arguments);
+ } else {
+ $.error("Method " + method + " does not exist on jQuery.pwstrength");
+ }
+ return result;
+ };
+}(jQuery));
diff --git a/main/inc/lib/main_api.lib.php b/main/inc/lib/main_api.lib.php
index 4413ff35b6..ed02c3829c 100644
--- a/main/inc/lib/main_api.lib.php
+++ b/main/inc/lib/main_api.lib.php
@@ -6451,3 +6451,44 @@ function api_get_user_info_from_official_code($official_code = '') {
}
return false;
}
+
+function api_get_password_checker_js($inputId)
+{
+ global $_configuration;
+ $useStrengthPassChecker = isset($_configuration['allow_strength_pass_checker']) ? $_configuration['allow_strength_pass_checker'] : false;
+
+ if ($useStrengthPassChecker == false) {
+ return null;
+ }
+
+ $verdicts = array(get_lang('Weak'), get_lang('Normal'), get_lang('Medium'), get_lang('Strong'), get_lang('VeryStrong'));
+ $js = api_get_js('strength/strength.js');
+ $js .= "";
+ return $js;
+
+}